mirror of
https://github.com/dart-lang/sdk
synced 2024-11-05 18:22:09 +00:00
[vm/bytecode] Support exactness tracking in bytecode
MicroClosureCreateTearoffClassSecondTime(RunTime) 3922 us -> 2030 us. Issue: https://github.com/dart-lang/sdk/issues/36429 Change-Id: I3a6f93950a469969d82243fd1744d5a4f7fb1e78 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/111868 Reviewed-by: Ryan Macnak <rmacnak@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
parent
df606cc566
commit
ac45ee7e63
13 changed files with 217 additions and 43 deletions
|
@ -381,6 +381,11 @@ class BytecodeAssembler {
|
|||
_emitInstructionDF(Opcode.kInterfaceCall, rd, rf);
|
||||
}
|
||||
|
||||
void emitInstantiatedInterfaceCall(int rd, int rf) {
|
||||
emitSourcePosition();
|
||||
_emitInstructionDF(Opcode.kInstantiatedInterfaceCall, rd, rf);
|
||||
}
|
||||
|
||||
void emitUncheckedClosureCall(int rd, int rf) {
|
||||
emitSourcePosition();
|
||||
_emitInstructionDF(Opcode.kUncheckedClosureCall, rd, rf);
|
||||
|
|
|
@ -112,6 +112,14 @@ type ConstantInterfaceCall extends ConstantPoolEntry {
|
|||
PackedObject argDesc;
|
||||
}
|
||||
|
||||
// Occupies 3 entries in the constant pool.
|
||||
type ConstantInstantiatedInterfaceCall extends ConstantPoolEntry {
|
||||
Byte tag = 30;
|
||||
PackedObject target;
|
||||
PackedObject argDesc;
|
||||
PackedObject staticReceiverType;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
enum ConstantTag {
|
||||
|
@ -145,6 +153,7 @@ enum ConstantTag {
|
|||
kObjectRef,
|
||||
kDirectCall,
|
||||
kInterfaceCall,
|
||||
kInstantiatedInterfaceCall,
|
||||
}
|
||||
|
||||
String constantTagToString(ConstantTag tag) =>
|
||||
|
@ -199,6 +208,8 @@ abstract class ConstantPoolEntry {
|
|||
return new ConstantDirectCall.read(reader);
|
||||
case ConstantTag.kInterfaceCall:
|
||||
return new ConstantInterfaceCall.read(reader);
|
||||
case ConstantTag.kInstantiatedInterfaceCall:
|
||||
return new ConstantInstantiatedInterfaceCall.read(reader);
|
||||
// Make analyzer happy.
|
||||
case ConstantTag.kUnused1:
|
||||
case ConstantTag.kUnused2:
|
||||
|
@ -639,6 +650,49 @@ class ConstantInterfaceCall extends ConstantPoolEntry {
|
|||
this.argDesc == other.argDesc;
|
||||
}
|
||||
|
||||
class ConstantInstantiatedInterfaceCall extends ConstantPoolEntry {
|
||||
final ObjectHandle target;
|
||||
final ObjectHandle argDesc;
|
||||
final ObjectHandle staticReceiverType;
|
||||
|
||||
ConstantInstantiatedInterfaceCall(
|
||||
this.target, this.argDesc, this.staticReceiverType);
|
||||
|
||||
// Reserve 2 extra slots (3 slots total).
|
||||
int get numReservedEntries => 2;
|
||||
|
||||
@override
|
||||
ConstantTag get tag => ConstantTag.kInstantiatedInterfaceCall;
|
||||
|
||||
@override
|
||||
void writeValue(BufferedWriter writer) {
|
||||
writer.writePackedObject(target);
|
||||
writer.writePackedObject(argDesc);
|
||||
writer.writePackedObject(staticReceiverType);
|
||||
}
|
||||
|
||||
ConstantInstantiatedInterfaceCall.read(BufferedReader reader)
|
||||
: target = reader.readPackedObject(),
|
||||
argDesc = reader.readPackedObject(),
|
||||
staticReceiverType = reader.readPackedObject();
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
"InstantiatedInterfaceCall '$target', $argDesc, receiver $staticReceiverType";
|
||||
|
||||
@override
|
||||
int get hashCode => _combineHashes(
|
||||
_combineHashes(target.hashCode, argDesc.hashCode),
|
||||
staticReceiverType.hashCode);
|
||||
|
||||
@override
|
||||
bool operator ==(other) =>
|
||||
other is ConstantInstantiatedInterfaceCall &&
|
||||
this.target == other.target &&
|
||||
this.argDesc == other.argDesc &&
|
||||
this.staticReceiverType == other.staticReceiverType;
|
||||
}
|
||||
|
||||
/// Reserved constant pool entry.
|
||||
class _ReservedConstantPoolEntry extends ConstantPoolEntry {
|
||||
const _ReservedConstantPoolEntry();
|
||||
|
@ -699,6 +753,15 @@ class ConstantPool {
|
|||
isSetter: invocationKind == InvocationKind.setter),
|
||||
argDesc));
|
||||
|
||||
int addInstantiatedInterfaceCall(InvocationKind invocationKind, Member target,
|
||||
ObjectHandle argDesc, DartType staticReceiverType) =>
|
||||
_add(new ConstantInstantiatedInterfaceCall(
|
||||
objectTable.getMemberHandle(target,
|
||||
isGetter: invocationKind == InvocationKind.getter,
|
||||
isSetter: invocationKind == InvocationKind.setter),
|
||||
argDesc,
|
||||
objectTable.getHandle(staticReceiverType)));
|
||||
|
||||
int addInstanceCall(InvocationKind invocationKind, Member target,
|
||||
Name targetName, ObjectHandle argDesc) =>
|
||||
(target == null)
|
||||
|
|
|
@ -10,7 +10,7 @@ library vm.bytecode.dbc;
|
|||
/// Before bumping current bytecode version format, make sure that
|
||||
/// all users have switched to a VM which is able to consume new
|
||||
/// version of bytecode.
|
||||
const int currentBytecodeFormatVersion = 17;
|
||||
const int currentBytecodeFormatVersion = 18;
|
||||
|
||||
enum Opcode {
|
||||
kUnusedOpcode000,
|
||||
|
@ -216,8 +216,8 @@ enum Opcode {
|
|||
kInterfaceCall_Wide,
|
||||
kUnused23, // Reserved for InterfaceCall1
|
||||
kUnused24, // Reserved for InterfaceCall1_Wide
|
||||
kUnused25, // Reserved for InterfaceCall2
|
||||
kUnused26, // Reserved for InterfaceCall2_Wide
|
||||
kInstantiatedInterfaceCall,
|
||||
kInstantiatedInterfaceCall_Wide,
|
||||
kUncheckedClosureCall,
|
||||
kUncheckedClosureCall_Wide,
|
||||
kUncheckedInterfaceCall,
|
||||
|
@ -438,6 +438,8 @@ const Map<Opcode, Format> BytecodeFormats = const {
|
|||
Encoding.kT, const [Operand.tgt, Operand.none, Operand.none]),
|
||||
Opcode.kInterfaceCall: const Format(
|
||||
Encoding.kDF, const [Operand.lit, Operand.imm, Operand.none]),
|
||||
Opcode.kInstantiatedInterfaceCall: const Format(
|
||||
Encoding.kDF, const [Operand.lit, Operand.imm, Operand.none]),
|
||||
Opcode.kDynamicCall: const Format(
|
||||
Encoding.kDF, const [Operand.lit, Operand.imm, Operand.none]),
|
||||
Opcode.kNativeCall: const Format(
|
||||
|
@ -607,6 +609,7 @@ bool isCall(Opcode opcode) {
|
|||
switch (opcode) {
|
||||
case Opcode.kDirectCall:
|
||||
case Opcode.kInterfaceCall:
|
||||
case Opcode.kInstantiatedInterfaceCall:
|
||||
case Opcode.kUncheckedClosureCall:
|
||||
case Opcode.kUncheckedInterfaceCall:
|
||||
case Opcode.kDynamicCall:
|
||||
|
|
|
@ -39,9 +39,11 @@ import 'generics.dart'
|
|||
flattenInstantiatorTypeArguments,
|
||||
getDefaultFunctionTypeArguments,
|
||||
getInstantiatorTypeArguments,
|
||||
getStaticType,
|
||||
hasFreeTypeParameters,
|
||||
hasInstantiatorTypeArguments,
|
||||
isAllDynamic,
|
||||
isInstantiatedInterfaceCall,
|
||||
isUncheckedCall,
|
||||
isUncheckedClosureCall;
|
||||
import 'local_variable_table.dart' show LocalVariableTable;
|
||||
|
@ -2796,8 +2798,28 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
|
|||
}
|
||||
|
||||
void _genInstanceCall(
|
||||
int totalArgCount, int callCpIndex, bool isDynamic, bool isUnchecked,
|
||||
[TreeNode context]) {
|
||||
InvocationKind invocationKind,
|
||||
Member interfaceTarget,
|
||||
Name targetName,
|
||||
Expression receiver,
|
||||
int totalArgCount,
|
||||
ObjectHandle argDesc) {
|
||||
final isDynamic = interfaceTarget == null;
|
||||
final isUnchecked = invocationKind != InvocationKind.getter &&
|
||||
isUncheckedCall(interfaceTarget, receiver, typeEnvironment);
|
||||
|
||||
if (invocationKind != InvocationKind.getter && !isDynamic && !isUnchecked) {
|
||||
final staticReceiverType = getStaticType(receiver, typeEnvironment);
|
||||
if (isInstantiatedInterfaceCall(interfaceTarget, staticReceiverType)) {
|
||||
final callCpIndex = cp.addInstantiatedInterfaceCall(
|
||||
invocationKind, interfaceTarget, argDesc, staticReceiverType);
|
||||
asm.emitInstantiatedInterfaceCall(callCpIndex, totalArgCount);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final callCpIndex = cp.addInstanceCall(
|
||||
invocationKind, interfaceTarget, targetName, argDesc);
|
||||
if (isDynamic) {
|
||||
assert(!isUnchecked);
|
||||
asm.emitDynamicCall(callCpIndex, totalArgCount);
|
||||
|
@ -2844,24 +2866,18 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
|
|||
// interface target doesn't fully represent what is being called.
|
||||
interfaceTarget = null;
|
||||
}
|
||||
final isDynamic = interfaceTarget == null;
|
||||
final isUnchecked =
|
||||
isUncheckedCall(interfaceTarget, node.receiver, typeEnvironment);
|
||||
final argDesc =
|
||||
objectTable.getArgDescHandleByArguments(args, hasReceiver: true);
|
||||
final callCpIndex = cp.addInstanceCall(
|
||||
InvocationKind.method, interfaceTarget, node.name, argDesc);
|
||||
_genInstanceCall(totalArgCount, callCpIndex, isDynamic, isUnchecked, node);
|
||||
_genInstanceCall(InvocationKind.method, interfaceTarget, node.name,
|
||||
node.receiver, totalArgCount, argDesc);
|
||||
}
|
||||
|
||||
@override
|
||||
visitPropertyGet(PropertyGet node) {
|
||||
_generateNode(node.receiver);
|
||||
final isDynamic = node.interfaceTarget == null;
|
||||
final argDesc = objectTable.getArgDescHandle(1);
|
||||
final callCpIndex = cp.addInstanceCall(
|
||||
InvocationKind.getter, node.interfaceTarget, node.name, argDesc);
|
||||
_genInstanceCall(1, callCpIndex, isDynamic, /*isUnchecked=*/ false);
|
||||
_genInstanceCall(InvocationKind.getter, node.interfaceTarget, node.name,
|
||||
node.receiver, 1, argDesc);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -2876,13 +2892,10 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
|
|||
asm.emitStoreLocal(temp);
|
||||
}
|
||||
|
||||
final isDynamic = node.interfaceTarget == null;
|
||||
final isUnchecked =
|
||||
isUncheckedCall(node.interfaceTarget, node.receiver, typeEnvironment);
|
||||
final argDesc = objectTable.getArgDescHandle(2);
|
||||
final callCpIndex = cp.addInstanceCall(
|
||||
InvocationKind.setter, node.interfaceTarget, node.name, argDesc);
|
||||
_genInstanceCall(2, callCpIndex, isDynamic, isUnchecked);
|
||||
_genInstanceCall(InvocationKind.setter, node.interfaceTarget, node.name,
|
||||
node.receiver, 2, argDesc);
|
||||
|
||||
asm.emitDrop1();
|
||||
|
||||
if (hasResult) {
|
||||
|
|
|
@ -94,6 +94,11 @@ bool isAllDynamic(List<DartType> typeArgs) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool isInstantiatedGenericType(DartType type) =>
|
||||
(type is InterfaceType) &&
|
||||
type.typeArguments.isNotEmpty &&
|
||||
!hasFreeTypeParameters(type.typeArguments);
|
||||
|
||||
bool hasFreeTypeParameters(List<DartType> typeArgs) {
|
||||
final findTypeParams = new FindFreeTypeParametersVisitor();
|
||||
return typeArgs.any((t) => t.accept(findTypeParams));
|
||||
|
@ -210,6 +215,23 @@ bool isUncheckedCall(Member interfaceTarget, Expression receiver,
|
|||
return false;
|
||||
}
|
||||
|
||||
/// If receiver type at run time matches static type we can omit argument type
|
||||
/// checks. This condition can be efficiently tested if static receiver type is
|
||||
/// fully instantiated (e.g. doesn't have type parameters).
|
||||
/// [isInstantiatedInterfaceCall] tests if an instance call to
|
||||
/// [interfaceTarget] with given [staticReceiverType] may benefit from
|
||||
/// this optimization.
|
||||
bool isInstantiatedInterfaceCall(
|
||||
Member interfaceTarget, DartType staticReceiverType) {
|
||||
// Providing instantiated receiver type wouldn't help in case of a
|
||||
// dynamic call or call without any parameter type checks.
|
||||
if (interfaceTarget == null ||
|
||||
!_hasGenericCovariantParameters(interfaceTarget)) {
|
||||
return false;
|
||||
}
|
||||
return isInstantiatedGenericType(staticReceiverType);
|
||||
}
|
||||
|
||||
bool _hasGenericCovariantParameters(Member target) {
|
||||
if (target is Field) {
|
||||
return target.isGenericCovariantImpl;
|
||||
|
|
|
@ -1194,10 +1194,10 @@ L2:
|
|||
Push r3
|
||||
Push r0
|
||||
StoreFieldTOS CP#5
|
||||
InterfaceCall CP#17, 2
|
||||
InstantiatedInterfaceCall CP#17, 2
|
||||
Drop1
|
||||
Push r4
|
||||
AllocateClosure CP#19
|
||||
AllocateClosure CP#20
|
||||
StoreLocal r3
|
||||
Push r3
|
||||
PushNull
|
||||
|
@ -1209,12 +1209,12 @@ L2:
|
|||
PushConstant CP#12
|
||||
StoreFieldTOS CP#13
|
||||
Push r3
|
||||
PushConstant CP#19
|
||||
PushConstant CP#20
|
||||
StoreFieldTOS CP#15
|
||||
Push r3
|
||||
Push r0
|
||||
StoreFieldTOS CP#5
|
||||
InterfaceCall CP#17, 2
|
||||
InstantiatedInterfaceCall CP#17, 2
|
||||
Drop1
|
||||
Push r0
|
||||
CloneContext 1, 1
|
||||
|
@ -1257,13 +1257,14 @@ ConstantPool {
|
|||
[14] = Reserved
|
||||
[15] = InstanceField dart:core::_Closure::_function (field)
|
||||
[16] = Reserved
|
||||
[17] = InterfaceCall 'dart:core::List::add', ArgDesc num-args 2, num-type-args 0, names []
|
||||
[17] = InstantiatedInterfaceCall 'dart:core::List::add', ArgDesc num-args 2, num-type-args 0, names [], receiver dart:core::List < dart:core::Function >
|
||||
[18] = Reserved
|
||||
[19] = ClosureFunction 1
|
||||
[20] = Type dart:core::int
|
||||
[21] = ObjectRef 'ii'
|
||||
[22] = SubtypeTestCache
|
||||
[23] = EndClosureFunctionScope
|
||||
[19] = Reserved
|
||||
[20] = ClosureFunction 1
|
||||
[21] = Type dart:core::int
|
||||
[22] = ObjectRef 'ii'
|
||||
[23] = SubtypeTestCache
|
||||
[24] = EndClosureFunctionScope
|
||||
}
|
||||
Closure #lib::C::testForLoop::'<anonymous closure>' () -> dart:core::int
|
||||
ClosureCode {
|
||||
|
@ -1290,11 +1291,11 @@ ClosureCode {
|
|||
PopLocal r0
|
||||
JumpIfUnchecked L1
|
||||
Push FP[-5]
|
||||
PushConstant CP#20
|
||||
PushNull
|
||||
PushNull
|
||||
PushConstant CP#21
|
||||
AssertAssignable 1, CP#22
|
||||
PushNull
|
||||
PushNull
|
||||
PushConstant CP#22
|
||||
AssertAssignable 1, CP#23
|
||||
Drop1
|
||||
L1:
|
||||
Push r0
|
||||
|
|
|
@ -259,6 +259,8 @@ static intptr_t GetConstantPoolIndex(const KBCInstr* instr) {
|
|||
case KernelBytecode::kDirectCall_Wide:
|
||||
case KernelBytecode::kInterfaceCall:
|
||||
case KernelBytecode::kInterfaceCall_Wide:
|
||||
case KernelBytecode::kInstantiatedInterfaceCall:
|
||||
case KernelBytecode::kInstantiatedInterfaceCall_Wide:
|
||||
case KernelBytecode::kUncheckedClosureCall:
|
||||
case KernelBytecode::kUncheckedClosureCall_Wide:
|
||||
case KernelBytecode::kUncheckedInterfaceCall:
|
||||
|
|
|
@ -843,7 +843,8 @@ void BytecodeFlowGraphBuilder::BuildDirectCall() {
|
|||
}
|
||||
|
||||
void BytecodeFlowGraphBuilder::BuildInterfaceCallCommon(
|
||||
bool is_unchecked_call) {
|
||||
bool is_unchecked_call,
|
||||
bool is_instantiated_call) {
|
||||
if (is_generating_interpreter()) {
|
||||
UNIMPLEMENTED(); // TODO(alexmarkov): interpreter
|
||||
}
|
||||
|
@ -887,16 +888,34 @@ void BytecodeFlowGraphBuilder::BuildInterfaceCallCommon(
|
|||
call->set_entry_kind(Code::EntryKind::kUnchecked);
|
||||
}
|
||||
|
||||
if (is_instantiated_call) {
|
||||
const AbstractType& static_receiver_type =
|
||||
AbstractType::Cast(ConstantAt(DecodeOperandD(), 2).value());
|
||||
call->set_receivers_static_type(&static_receiver_type);
|
||||
} else {
|
||||
const Class& owner = Class::Handle(Z, interface_target.Owner());
|
||||
const AbstractType& type =
|
||||
AbstractType::ZoneHandle(Z, owner.DeclarationType());
|
||||
call->set_receivers_static_type(&type);
|
||||
}
|
||||
|
||||
code_ <<= call;
|
||||
B->Push(call);
|
||||
}
|
||||
|
||||
void BytecodeFlowGraphBuilder::BuildInterfaceCall() {
|
||||
BuildInterfaceCallCommon(/*is_unchecked_call=*/false);
|
||||
BuildInterfaceCallCommon(/*is_unchecked_call=*/false,
|
||||
/*is_instantiated_call=*/false);
|
||||
}
|
||||
|
||||
void BytecodeFlowGraphBuilder::BuildInstantiatedInterfaceCall() {
|
||||
BuildInterfaceCallCommon(/*is_unchecked_call=*/false,
|
||||
/*is_instantiated_call=*/true);
|
||||
}
|
||||
|
||||
void BytecodeFlowGraphBuilder::BuildUncheckedInterfaceCall() {
|
||||
BuildInterfaceCallCommon(/*is_unchecked_call=*/true);
|
||||
BuildInterfaceCallCommon(/*is_unchecked_call=*/true,
|
||||
/*is_instantiated_call=*/false);
|
||||
}
|
||||
|
||||
void BytecodeFlowGraphBuilder::BuildUncheckedClosureCall() {
|
||||
|
|
|
@ -147,7 +147,8 @@ class BytecodeFlowGraphBuilder {
|
|||
int num_args);
|
||||
void BuildIntOp(const String& name, Token::Kind token_kind, int num_args);
|
||||
void BuildDoubleOp(const String& name, Token::Kind token_kind, int num_args);
|
||||
void BuildInterfaceCallCommon(bool is_unchecked_call);
|
||||
void BuildInterfaceCallCommon(bool is_unchecked_call,
|
||||
bool is_instantiated_call);
|
||||
|
||||
void BuildInstruction(KernelBytecode::Opcode opcode);
|
||||
void BuildFfiAsFunction();
|
||||
|
|
|
@ -717,6 +717,7 @@ intptr_t BytecodeReaderHelper::ReadConstantPool(const Function& function,
|
|||
kObjectRef,
|
||||
kDirectCall,
|
||||
kInterfaceCall,
|
||||
kInstantiatedInterfaceCall,
|
||||
};
|
||||
|
||||
enum InvocationKind {
|
||||
|
@ -884,6 +885,26 @@ intptr_t BytecodeReaderHelper::ReadConstantPool(const Function& function,
|
|||
// The second entry is used for arguments descriptor.
|
||||
obj = ReadObject();
|
||||
} break;
|
||||
case ConstantPoolTag::kInstantiatedInterfaceCall: {
|
||||
elem = ReadObject();
|
||||
ASSERT(elem.IsFunction());
|
||||
// InstantiatedInterfaceCall constant occupies 3 entries:
|
||||
// 1) Interface target.
|
||||
pool.SetTypeAt(i, ObjectPool::EntryType::kTaggedObject,
|
||||
ObjectPool::Patchability::kNotPatchable);
|
||||
pool.SetObjectAt(i, elem);
|
||||
++i;
|
||||
ASSERT(i < obj_count);
|
||||
// 2) Arguments descriptor.
|
||||
obj = ReadObject();
|
||||
pool.SetTypeAt(i, ObjectPool::EntryType::kTaggedObject,
|
||||
ObjectPool::Patchability::kNotPatchable);
|
||||
pool.SetObjectAt(i, obj);
|
||||
++i;
|
||||
ASSERT(i < obj_count);
|
||||
// 3) Static receiver type.
|
||||
obj = ReadObject();
|
||||
} break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
|
|
@ -655,8 +655,8 @@ namespace dart {
|
|||
V(InterfaceCall_Wide, D_F, WIDE, num, num, ___) \
|
||||
V(Unused23, 0, RESV, ___, ___, ___) \
|
||||
V(Unused24, 0, RESV, ___, ___, ___) \
|
||||
V(Unused25, 0, RESV, ___, ___, ___) \
|
||||
V(Unused26, 0, RESV, ___, ___, ___) \
|
||||
V(InstantiatedInterfaceCall, D_F, ORDN, num, num, ___) \
|
||||
V(InstantiatedInterfaceCall_Wide, D_F, WIDE, num, num, ___) \
|
||||
V(UncheckedClosureCall, D_F, ORDN, num, num, ___) \
|
||||
V(UncheckedClosureCall_Wide, D_F, WIDE, num, num, ___) \
|
||||
V(UncheckedInterfaceCall, D_F, ORDN, num, num, ___) \
|
||||
|
@ -749,7 +749,7 @@ class KernelBytecode {
|
|||
// Maximum bytecode format version supported by VM.
|
||||
// The range of supported versions should include version produced by bytecode
|
||||
// generator (currentBytecodeFormatVersion in pkg/vm/lib/bytecode/dbc.dart).
|
||||
static const intptr_t kMaxSupportedBytecodeFormatVersion = 17;
|
||||
static const intptr_t kMaxSupportedBytecodeFormatVersion = 18;
|
||||
|
||||
enum Opcode {
|
||||
#define DECLARE_BYTECODE(name, encoding, kind, op1, op2, op3) k##name,
|
||||
|
@ -985,6 +985,8 @@ class KernelBytecode {
|
|||
case KernelBytecode::kDirectCall_Wide:
|
||||
case KernelBytecode::kInterfaceCall:
|
||||
case KernelBytecode::kInterfaceCall_Wide:
|
||||
case KernelBytecode::kInstantiatedInterfaceCall:
|
||||
case KernelBytecode::kInstantiatedInterfaceCall_Wide:
|
||||
case KernelBytecode::kUncheckedClosureCall:
|
||||
case KernelBytecode::kUncheckedClosureCall_Wide:
|
||||
case KernelBytecode::kUncheckedInterfaceCall:
|
||||
|
|
|
@ -1992,6 +1992,28 @@ SwitchDispatch:
|
|||
|
||||
DISPATCH();
|
||||
}
|
||||
{
|
||||
BYTECODE(InstantiatedInterfaceCall, D_F);
|
||||
DEBUG_CHECK;
|
||||
{
|
||||
const uint32_t argc = rF;
|
||||
const uint32_t kidx = rD;
|
||||
|
||||
RawObject** call_base = SP - argc + 1;
|
||||
RawObject** call_top = SP + 1;
|
||||
|
||||
InterpreterHelpers::IncrementUsageCounter(FrameFunction(FP));
|
||||
RawString* target_name =
|
||||
static_cast<RawFunction*>(LOAD_CONSTANT(kidx))->ptr()->name_;
|
||||
argdesc_ = static_cast<RawArray*>(LOAD_CONSTANT(kidx + 1));
|
||||
if (!InterfaceCall(thread, target_name, call_base, call_top, &pc, &FP,
|
||||
&SP)) {
|
||||
HANDLE_EXCEPTION;
|
||||
}
|
||||
}
|
||||
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
{
|
||||
BYTECODE(UncheckedClosureCall, D_F);
|
||||
|
|
|
@ -35,5 +35,5 @@ MINOR 5
|
|||
PATCH 0
|
||||
PRERELEASE 0
|
||||
PRERELEASE_PATCH 0
|
||||
ABI_VERSION 9
|
||||
ABI_VERSION 10
|
||||
OLDEST_SUPPORTED_ABI_VERSION 9
|
||||
|
|
Loading…
Reference in a new issue