diff --git a/pkg/vm/lib/bytecode/assembler.dart b/pkg/vm/lib/bytecode/assembler.dart index 5926c7719c0..f4969eb2cf6 100644 --- a/pkg/vm/lib/bytecode/assembler.dart +++ b/pkg/vm/lib/bytecode/assembler.dart @@ -394,6 +394,11 @@ class BytecodeAssembler { _emitInstructionDF(Opcode.kDirectCall, rd, rf); } + void emitUncheckedDirectCall(int rd, int rf) { + emitSourcePositionForCall(); + _emitInstructionDF(Opcode.kUncheckedDirectCall, rd, rf); + } + void emitInterfaceCall(int rd, int rf) { emitSourcePositionForCall(); _emitInstructionDF(Opcode.kInterfaceCall, rd, rf); @@ -565,4 +570,9 @@ class BytecodeAssembler { emitSourcePosition(); _emitInstructionD(Opcode.kAllocateClosure, rd); } + + void emitCheckReceiverForNull(int rd) { + emitSourcePosition(); + _emitInstructionD(Opcode.kCheckReceiverForNull, rd); + } } diff --git a/pkg/vm/lib/bytecode/constant_pool.dart b/pkg/vm/lib/bytecode/constant_pool.dart index eeee7fcb36d..481e0c27b52 100644 --- a/pkg/vm/lib/bytecode/constant_pool.dart +++ b/pkg/vm/lib/bytecode/constant_pool.dart @@ -113,6 +113,13 @@ type ConstantDynamicCall extends ConstantPoolEntry { PackedObject argDesc; } +// Occupies 2 entries in the constant pool. +type ConstantDirectCallViaDynamicForwarder extends ConstantPoolEntry { + Byte tag = 32; + PackedObject target; + PackedObject argDesc; +} + */ enum ConstantTag { @@ -148,6 +155,7 @@ enum ConstantTag { kInterfaceCall, kInstantiatedInterfaceCall, kDynamicCall, + kDirectCallViaDynamicForwarder, } String constantTagToString(ConstantTag tag) => @@ -204,6 +212,8 @@ abstract class ConstantPoolEntry { return new ConstantInstantiatedInterfaceCall.read(reader); case ConstantTag.kDynamicCall: return new ConstantDynamicCall.read(reader); + case ConstantTag.kDirectCallViaDynamicForwarder: + return new ConstantDirectCallViaDynamicForwarder.read(reader); // Make analyzer happy. case ConstantTag.kUnused1: case ConstantTag.kUnused2: @@ -553,6 +563,41 @@ class ConstantDirectCall extends ConstantPoolEntry { this.argDesc == other.argDesc; } +class ConstantDirectCallViaDynamicForwarder extends ConstantPoolEntry { + final ObjectHandle target; + final ObjectHandle argDesc; + + ConstantDirectCallViaDynamicForwarder(this.target, this.argDesc); + + // Reserve 1 extra slot for arguments descriptor, following target slot. + int get numReservedEntries => 1; + + @override + ConstantTag get tag => ConstantTag.kDirectCallViaDynamicForwarder; + + @override + void writeValue(BufferedWriter writer) { + writer.writePackedObject(target); + writer.writePackedObject(argDesc); + } + + ConstantDirectCallViaDynamicForwarder.read(BufferedReader reader) + : target = reader.readPackedObject(), + argDesc = reader.readPackedObject(); + + @override + String toString() => "DirectCallViaDynamicForwarder '$target', $argDesc"; + + @override + int get hashCode => _combineHashes(target.hashCode, argDesc.hashCode); + + @override + bool operator ==(other) => + other is ConstantDirectCallViaDynamicForwarder && + this.target == other.target && + this.argDesc == other.argDesc; +} + class ConstantInterfaceCall extends ConstantPoolEntry { final ObjectHandle target; final ObjectHandle argDesc; @@ -700,12 +745,15 @@ class ConstantPool { hasReceiver: hasReceiver, isFactory: isFactory))); int addDirectCall( - InvocationKind invocationKind, Member target, ObjectHandle argDesc) => - _add(new ConstantDirectCall( - objectTable.getMemberHandle(target, - isGetter: invocationKind == InvocationKind.getter, - isSetter: invocationKind == InvocationKind.setter), - argDesc)); + InvocationKind invocationKind, Member target, ObjectHandle argDesc, + [bool isDynamicForwarder = false]) { + final targetHandle = objectTable.getMemberHandle(target, + isGetter: invocationKind == InvocationKind.getter, + isSetter: invocationKind == InvocationKind.setter); + return _add(isDynamicForwarder + ? new ConstantDirectCallViaDynamicForwarder(targetHandle, argDesc) + : new ConstantDirectCall(targetHandle, argDesc)); + } int addInterfaceCall( InvocationKind invocationKind, Member target, ObjectHandle argDesc) => @@ -772,6 +820,11 @@ class ConstantPool { int addObjectRef(Node node) => _add(new ConstantObjectRef(objectTable.getHandle(node))); + int addSelectorName(Name name, InvocationKind invocationKind) => + _add(new ConstantObjectRef(objectTable.getSelectorNameHandle(name, + isGetter: invocationKind == InvocationKind.getter, + isSetter: invocationKind == InvocationKind.setter))); + int _add(ConstantPoolEntry entry) { return _canonicalizationCache.putIfAbsent(entry, () { int index = entries.length; diff --git a/pkg/vm/lib/bytecode/dbc.dart b/pkg/vm/lib/bytecode/dbc.dart index 790b10b6d28..6005219b0ed 100644 --- a/pkg/vm/lib/bytecode/dbc.dart +++ b/pkg/vm/lib/bytecode/dbc.dart @@ -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 = 20; +const int currentBytecodeFormatVersion = 21; enum Opcode { kUnusedOpcode000, @@ -210,8 +210,8 @@ enum Opcode { // Calls. kDirectCall, kDirectCall_Wide, - kUnused21, // Reserved for DirectCall1 - kUnused22, // Reserved for DirectCall1_Wide + kUncheckedDirectCall, + kUncheckedDirectCall_Wide, kInterfaceCall, kInterfaceCall_Wide, kUnused23, // Reserved for InterfaceCall1 @@ -258,8 +258,8 @@ enum Opcode { // Null operations. kEqualsNull, - kUnused36, // Reserved for CheckNull - kUnused37, // Reserved for CheckNull_Wide + kCheckReceiverForNull, + kCheckReceiverForNull_Wide, // Int operations. kNegateInt, @@ -468,6 +468,8 @@ const Map BytecodeFormats = const { Encoding.k0, const [Operand.none, Operand.none, Operand.none]), Opcode.kEqualsNull: const Format( Encoding.k0, const [Operand.none, Operand.none, Operand.none]), + Opcode.kCheckReceiverForNull: const Format( + Encoding.kD, const [Operand.lit, Operand.none, Operand.none]), Opcode.kNegateInt: const Format( Encoding.k0, const [Operand.none, Operand.none, Operand.none]), Opcode.kAddInt: const Format( @@ -502,6 +504,8 @@ const Map BytecodeFormats = const { Encoding.k0, const [Operand.none, Operand.none, Operand.none]), Opcode.kDirectCall: const Format( Encoding.kDF, const [Operand.lit, Operand.imm, Operand.none]), + Opcode.kUncheckedDirectCall: const Format( + Encoding.kDF, const [Operand.lit, Operand.imm, Operand.none]), Opcode.kAllocateClosure: const Format( Encoding.kD, const [Operand.lit, Operand.none, Operand.none]), Opcode.kUncheckedClosureCall: const Format( @@ -608,6 +612,7 @@ bool isThrow(Opcode opcode) => opcode == Opcode.kThrow; bool isCall(Opcode opcode) { switch (opcode) { case Opcode.kDirectCall: + case Opcode.kUncheckedDirectCall: case Opcode.kInterfaceCall: case Opcode.kInstantiatedInterfaceCall: case Opcode.kUncheckedClosureCall: diff --git a/pkg/vm/lib/bytecode/gen_bytecode.dart b/pkg/vm/lib/bytecode/gen_bytecode.dart index 1f4fe40d466..ee527dbcd1e 100644 --- a/pkg/vm/lib/bytecode/gen_bytecode.dart +++ b/pkg/vm/lib/bytecode/gen_bytecode.dart @@ -53,6 +53,8 @@ import 'recognized_methods.dart' show RecognizedMethods; import 'recursive_types_validator.dart' show IllegalRecursiveTypeException; import 'source_positions.dart' show LineStarts, SourcePositions; import '../metadata/bytecode.dart'; +import '../metadata/direct_call.dart' + show DirectCallMetadata, DirectCallMetadataRepository; import 'dart:convert' show utf8; import 'dart:developer'; @@ -118,6 +120,7 @@ class BytecodeGenerator extends RecursiveVisitor { ObjectTable objectTable; Component bytecodeComponent; NullabilityDetector nullabilityDetector; + Map directCallMetadata; List classDeclarations; List fieldDeclarations; @@ -168,6 +171,9 @@ class BytecodeGenerator extends RecursiveVisitor { bytecodeComponent.mainLibrary = objectTable.getHandle(component.mainMethod.enclosingLibrary); } + + directCallMetadata = + component.metadata[DirectCallMetadataRepository.repositoryTag]?.mapping; } @override @@ -1135,21 +1141,32 @@ class BytecodeGenerator extends RecursiveVisitor { } void _genDirectCall(Member target, ObjectHandle argDesc, int totalArgCount, - {bool isGet: false, bool isSet: false, TreeNode context}) { + {bool isGet: false, + bool isSet: false, + bool isDynamicForwarder: false, + bool isUnchecked: false, + TreeNode context}) { assert(!isGet || !isSet); final kind = isGet ? InvocationKind.getter : (isSet ? InvocationKind.setter : InvocationKind.method); - final cpIndex = cp.addDirectCall(kind, target, argDesc); + final cpIndex = cp.addDirectCall(kind, target, argDesc, isDynamicForwarder); if (totalArgCount >= argumentsLimit) { throw new TooManyArgumentsException(context.fileOffset); } - asm.emitDirectCall(cpIndex, totalArgCount); + if (isUnchecked) { + asm.emitUncheckedDirectCall(cpIndex, totalArgCount); + } else { + asm.emitDirectCall(cpIndex, totalArgCount); + } } void _genDirectCallWithArgs(Member target, Arguments args, - {bool hasReceiver: false, bool isFactory: false, TreeNode context}) { + {bool hasReceiver: false, + bool isFactory: false, + bool isUnchecked: false, + TreeNode context}) { final argDesc = objectTable.getArgDescHandleByArguments(args, hasReceiver: hasReceiver, isFactory: isFactory); @@ -1163,7 +1180,8 @@ class BytecodeGenerator extends RecursiveVisitor { totalArgCount++; } - _genDirectCall(target, argDesc, totalArgCount, context: context); + _genDirectCall(target, argDesc, totalArgCount, + isUnchecked: isUnchecked, context: context); } void _genTypeArguments(List typeArgs, {Class instantiatingClass}) { @@ -1517,7 +1535,8 @@ class BytecodeGenerator extends RecursiveVisitor { savedMaxSourcePositions = []; maxSourcePosition = node.fileOffset; - locals = new LocalVariables(node, options, typeEnvironment); + locals = + new LocalVariables(node, options, typeEnvironment, directCallMetadata); locals.enterScope(node); assert(!locals.isSyncYieldingFrame); @@ -3023,27 +3042,62 @@ class BytecodeGenerator extends RecursiveVisitor { return; } - _genArguments(node.receiver, args); + final directCall = + directCallMetadata != null ? directCallMetadata[node] : null; + if (directCall != null && directCall.checkReceiverForNull) { + final int receiverTemp = locals.tempIndexInFrame(node); + _genArguments(node.receiver, args, storeReceiverToLocal: receiverTemp); + asm.emitPush(receiverTemp); + asm.emitCheckReceiverForNull( + cp.addSelectorName(node.name, InvocationKind.method)); + } else { + _genArguments(node.receiver, args); + } Member interfaceTarget = node.interfaceTarget; if (interfaceTarget is Field || interfaceTarget is Procedure && interfaceTarget.isGetter) { // Call via field or getter. Treat it as a dynamic call because // interface target doesn't fully represent what is being called. + assert(directCall == null); interfaceTarget = null; } + final argDesc = objectTable.getArgDescHandleByArguments(args, hasReceiver: true); - _genInstanceCall(InvocationKind.method, interfaceTarget, node.name, - node.receiver, totalArgCount, argDesc); + + if (directCall != null) { + final isDynamicForwarder = (interfaceTarget == null); + final isUnchecked = + isUncheckedCall(interfaceTarget, node.receiver, typeEnvironment); + _genDirectCall(directCall.target, argDesc, totalArgCount, + isDynamicForwarder: isDynamicForwarder, isUnchecked: isUnchecked); + } else { + _genInstanceCall(InvocationKind.method, interfaceTarget, node.name, + node.receiver, totalArgCount, argDesc); + } } @override visitPropertyGet(PropertyGet node) { _generateNode(node.receiver); final argDesc = objectTable.getArgDescHandle(1); - _genInstanceCall(InvocationKind.getter, node.interfaceTarget, node.name, - node.receiver, 1, argDesc); + + final directCall = + directCallMetadata != null ? directCallMetadata[node] : null; + if (directCall != null) { + if (directCall.checkReceiverForNull) { + final int receiverTemp = locals.tempIndexInFrame(node); + asm.emitStoreLocal(receiverTemp); + asm.emitPush(receiverTemp); + asm.emitCheckReceiverForNull( + cp.addSelectorName(node.name, InvocationKind.getter)); + } + _genDirectCall(directCall.target, argDesc, 1, isGet: true); + } else { + _genInstanceCall(InvocationKind.getter, node.interfaceTarget, node.name, + node.receiver, 1, argDesc); + } } @override @@ -3052,15 +3106,38 @@ class BytecodeGenerator extends RecursiveVisitor { final bool hasResult = !isExpressionWithoutResult(node); _generateNode(node.receiver); - _generateNode(node.value); + + final directCall = + directCallMetadata != null ? directCallMetadata[node] : null; + if (directCall != null && directCall.checkReceiverForNull) { + asm.emitStoreLocal(temp); + _generateNode(node.value); + asm.emitPush(temp); + asm.emitCheckReceiverForNull( + cp.addSelectorName(node.name, InvocationKind.setter)); + } else { + _generateNode(node.value); + } if (hasResult) { asm.emitStoreLocal(temp); } - final argDesc = objectTable.getArgDescHandle(2); - _genInstanceCall(InvocationKind.setter, node.interfaceTarget, node.name, - node.receiver, 2, argDesc); + const int numArguments = 2; + final argDesc = objectTable.getArgDescHandle(numArguments); + + if (directCall != null) { + final isDynamicForwarder = (node.interfaceTarget == null); + final isUnchecked = + isUncheckedCall(node.interfaceTarget, node.receiver, typeEnvironment); + _genDirectCall(directCall.target, argDesc, numArguments, + isSet: true, + isDynamicForwarder: isDynamicForwarder, + isUnchecked: isUnchecked); + } else { + _genInstanceCall(InvocationKind.setter, node.interfaceTarget, node.name, + node.receiver, numArguments, argDesc); + } asm.emitDrop1(); @@ -3087,7 +3164,8 @@ class BytecodeGenerator extends RecursiveVisitor { return; } _genArguments(new ThisExpression(), args); - _genDirectCallWithArgs(target, args, hasReceiver: true, context: node); + _genDirectCallWithArgs(target, args, + hasReceiver: true, isUnchecked: true, context: node); } @override @@ -3124,7 +3202,8 @@ class BytecodeGenerator extends RecursiveVisitor { } assert(target is Field || (target is Procedure && target.isSetter)); - _genDirectCall(target, objectTable.getArgDescHandle(2), 2, isSet: true); + _genDirectCall(target, objectTable.getArgDescHandle(2), 2, + isSet: true, isUnchecked: true); } asm.emitDrop1(); diff --git a/pkg/vm/lib/bytecode/local_vars.dart b/pkg/vm/lib/bytecode/local_vars.dart index 4176254509a..642169445ca 100644 --- a/pkg/vm/lib/bytecode/local_vars.dart +++ b/pkg/vm/lib/bytecode/local_vars.dart @@ -14,6 +14,7 @@ import 'package:vm/bytecode/generics.dart'; import 'dbc.dart'; import 'options.dart' show BytecodeOptions; +import '../metadata/direct_call.dart' show DirectCallMetadata; class LocalVariables { final Map _scopes = {}; @@ -30,6 +31,7 @@ class LocalVariables { {}; final BytecodeOptions options; final TypeEnvironment typeEnvironment; + final Map directCallMetadata; Scope _currentScope; Frame _currentFrame; @@ -191,7 +193,8 @@ class LocalVariables { List get sortedNamedParameters => _currentFrame.sortedNamedParameters; - LocalVariables(Member node, this.options, this.typeEnvironment) { + LocalVariables(Member node, this.options, this.typeEnvironment, + this.directCallMetadata) { final scopeBuilder = new _ScopeBuilder(this); node.accept(scopeBuilder); @@ -1210,6 +1213,11 @@ class _Allocator extends RecursiveVisitor { int numTemps = 0; if (isUncheckedClosureCall(node, locals.typeEnvironment, locals.options)) { numTemps = 1; + } else if (locals.directCallMetadata != null) { + final directCall = locals.directCallMetadata[node]; + if (directCall != null && directCall.checkReceiverForNull) { + numTemps = 1; + } } _visit(node, temps: numTemps); } @@ -1219,6 +1227,18 @@ class _Allocator extends RecursiveVisitor { _visit(node, temps: 1); } + @override + visitPropertyGet(PropertyGet node) { + int numTemps = 0; + if (locals.directCallMetadata != null) { + final directCall = locals.directCallMetadata[node]; + if (directCall != null && directCall.checkReceiverForNull) { + numTemps = 1; + } + } + _visit(node, temps: numTemps); + } + @override visitDirectPropertySet(DirectPropertySet node) { _visit(node, temps: 1); diff --git a/pkg/vm/lib/bytecode/object_table.dart b/pkg/vm/lib/bytecode/object_table.dart index 57452d05f01..4170bbb73f4 100644 --- a/pkg/vm/lib/bytecode/object_table.dart +++ b/pkg/vm/lib/bytecode/object_table.dart @@ -1705,7 +1705,6 @@ class ObjectTable implements ObjectWriter, ObjectReader { } else { throw "Unexpected Member's parent ${parent.runtimeType} $parent"; } - if (member is Constructor || member is Procedure && member.isFactory) {} final nameHandle = getNameHandle( member.name.library, mangleMemberName(member, isGetter, isSetter)); bool isField = member is Field && !isGetter && !isSetter; diff --git a/pkg/vm/lib/metadata/direct_call.dart b/pkg/vm/lib/metadata/direct_call.dart index 9c2594ef6f6..fcb9f32d009 100644 --- a/pkg/vm/lib/metadata/direct_call.dart +++ b/pkg/vm/lib/metadata/direct_call.dart @@ -26,8 +26,10 @@ class DirectCallMetadata { /// Repository for [DirectCallMetadata]. class DirectCallMetadataRepository extends MetadataRepository { + static const repositoryTag = 'vm.direct-call.metadata'; + @override - final String tag = 'vm.direct-call.metadata'; + String get tag => repositoryTag; @override final Map mapping = diff --git a/pkg/vm/testcases/bytecode/super_calls.dart.expect b/pkg/vm/testcases/bytecode/super_calls.dart.expect index 32781bbc0f9..7d346c07933 100644 --- a/pkg/vm/testcases/bytecode/super_calls.dart.expect +++ b/pkg/vm/testcases/bytecode/super_calls.dart.expect @@ -123,7 +123,7 @@ Bytecode { Push FP[-6] PushConstant CP#1 PushInt 2 - DirectCall CP#2, 4 + UncheckedDirectCall CP#2, 4 ReturnTOS } ConstantPool { @@ -201,7 +201,7 @@ Bytecode { CheckStack 0 Push FP[-5] PushInt 3 - DirectCall CP#0, 2 + UncheckedDirectCall CP#0, 2 Drop1 PushNull ReturnTOS diff --git a/pkg/vm/testcases/bytecode/type_ops.dart.expect b/pkg/vm/testcases/bytecode/type_ops.dart.expect index de2ecc3d15b..4ab42ff6a19 100644 --- a/pkg/vm/testcases/bytecode/type_ops.dart.expect +++ b/pkg/vm/testcases/bytecode/type_ops.dart.expect @@ -645,7 +645,7 @@ L1: Push FP[-7] Push FP[-6] Push FP[-5] - DirectCall CP#9, 5 + UncheckedDirectCall CP#9, 5 ReturnTOS } Parameter flags: [0, 1, 2] diff --git a/runtime/vm/compiler/assembler/disassembler_kbc.cc b/runtime/vm/compiler/assembler/disassembler_kbc.cc index 1cf9e00cc52..8552753e217 100644 --- a/runtime/vm/compiler/assembler/disassembler_kbc.cc +++ b/runtime/vm/compiler/assembler/disassembler_kbc.cc @@ -259,6 +259,8 @@ static intptr_t GetConstantPoolIndex(const KBCInstr* instr) { case KernelBytecode::kInstantiateType_Wide: case KernelBytecode::kDirectCall: case KernelBytecode::kDirectCall_Wide: + case KernelBytecode::kUncheckedDirectCall: + case KernelBytecode::kUncheckedDirectCall_Wide: case KernelBytecode::kInterfaceCall: case KernelBytecode::kInterfaceCall_Wide: case KernelBytecode::kInstantiatedInterfaceCall: diff --git a/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc b/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc index 349db75a93f..be093214da6 100644 --- a/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc +++ b/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc @@ -797,7 +797,7 @@ void BytecodeFlowGraphBuilder::BuildPush() { LoadLocal(local_index); } -void BytecodeFlowGraphBuilder::BuildDirectCall() { +void BytecodeFlowGraphBuilder::BuildDirectCallCommon(bool is_unchecked_call) { if (is_generating_interpreter()) { UNIMPLEMENTED(); // TODO(alexmarkov): interpreter } @@ -857,7 +857,7 @@ void BytecodeFlowGraphBuilder::BuildDirectCall() { *ic_data_array_, B->GetNextDeoptId(), target.IsDynamicFunction() ? ICData::kSuper : ICData::kStatic); - if (target.MayHaveUncheckedEntryPoint(isolate())) { + if (is_unchecked_call) { call->set_entry_kind(Code::EntryKind::kUnchecked); } @@ -867,6 +867,14 @@ void BytecodeFlowGraphBuilder::BuildDirectCall() { B->Push(call); } +void BytecodeFlowGraphBuilder::BuildDirectCall() { + BuildDirectCallCommon(/* is_unchecked_call = */ false); +} + +void BytecodeFlowGraphBuilder::BuildUncheckedDirectCall() { + BuildDirectCallCommon(/* is_unchecked_call = */ true); +} + static void ComputeTokenKindAndCheckedArguments( const String& name, const ArgumentsDescriptor& arg_desc, @@ -1346,6 +1354,19 @@ void BytecodeFlowGraphBuilder::BuildAssertSubtype() { code_ <<= instr; } +void BytecodeFlowGraphBuilder::BuildCheckReceiverForNull() { + if (is_generating_interpreter()) { + UNIMPLEMENTED(); // TODO(alexmarkov): interpreter + } + + const String& selector = String::Cast(ConstantAt(DecodeOperandD()).value()); + + LocalVariable* receiver_temp = B->MakeTemporary(); + code_ += + B->CheckNull(position_, receiver_temp, selector, /*clear_temp=*/false); + code_ += B->Drop(); +} + void BytecodeFlowGraphBuilder::BuildJump() { if (is_generating_interpreter()) { UNIMPLEMENTED(); // TODO(alexmarkov): interpreter diff --git a/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.h b/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.h index 1e84f92e49c..814b8df2b29 100644 --- a/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.h +++ b/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.h @@ -167,6 +167,7 @@ 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 BuildDirectCallCommon(bool is_unchecked_call); void BuildInterfaceCallCommon(bool is_unchecked_call, bool is_instantiated_call); diff --git a/runtime/vm/compiler/frontend/bytecode_reader.cc b/runtime/vm/compiler/frontend/bytecode_reader.cc index 6f2240aa336..0f3eb24fd63 100644 --- a/runtime/vm/compiler/frontend/bytecode_reader.cc +++ b/runtime/vm/compiler/frontend/bytecode_reader.cc @@ -715,6 +715,7 @@ intptr_t BytecodeReaderHelper::ReadConstantPool(const Function& function, kInterfaceCall, kInstantiatedInterfaceCall, kDynamicCall, + kDirectCallViaDynamicForwarder, }; enum InvocationKind { @@ -915,6 +916,23 @@ intptr_t BytecodeReaderHelper::ReadConstantPool(const Function& function, ASSERT(i < obj_count); obj = Object::null(); } break; + case ConstantPoolTag::kDirectCallViaDynamicForwarder: { + // DirectCallViaDynamicForwarder constant occupies 2 entries. + // The first entry is used for target function. + obj = ReadObject(); + ASSERT(obj.IsFunction()); + name = Function::Cast(obj).name(); + name = Function::CreateDynamicInvocationForwarderName(name); + obj = Function::Cast(obj).GetDynamicInvocationForwarder(name); + + pool.SetTypeAt(i, ObjectPool::EntryType::kTaggedObject, + ObjectPool::Patchability::kNotPatchable); + pool.SetObjectAt(i, obj); + ++i; + ASSERT(i < obj_count); + // The second entry is used for arguments descriptor. + obj = ReadObject(); + } break; default: UNREACHABLE(); } diff --git a/runtime/vm/constants_kbc.h b/runtime/vm/constants_kbc.h index f8d4489a81f..a89756e4ccb 100644 --- a/runtime/vm/constants_kbc.h +++ b/runtime/vm/constants_kbc.h @@ -649,8 +649,8 @@ namespace dart { V(JumpIfNotNull_Wide, T, WIDE, tgt, ___, ___) \ V(DirectCall, D_F, ORDN, num, num, ___) \ V(DirectCall_Wide, D_F, WIDE, num, num, ___) \ - V(Unused21, 0, RESV, ___, ___, ___) \ - V(Unused22, 0, RESV, ___, ___, ___) \ + V(UncheckedDirectCall, D_F, ORDN, num, num, ___) \ + V(UncheckedDirectCall_Wide, D_F, WIDE, num, num, ___) \ V(InterfaceCall, D_F, ORDN, num, num, ___) \ V(InterfaceCall_Wide, D_F, WIDE, num, num, ___) \ V(Unused23, 0, RESV, ___, ___, ___) \ @@ -689,8 +689,8 @@ namespace dart { V(MoveSpecial_Wide, A_Y, WIDE, num, xeg, ___) \ V(BooleanNegateTOS, 0, ORDN, ___, ___, ___) \ V(EqualsNull, 0, ORDN, ___, ___, ___) \ - V(Unused36, 0, RESV, ___, ___, ___) \ - V(Unused37, 0, RESV, ___, ___, ___) \ + V(CheckReceiverForNull, D, ORDN, lit, ___, ___) \ + V(CheckReceiverForNull_Wide, D, WIDE, lit, ___, ___) \ V(NegateInt, 0, ORDN, ___, ___, ___) \ V(AddInt, 0, ORDN, ___, ___, ___) \ V(SubInt, 0, ORDN, ___, ___, ___) \ @@ -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 = 20; + static const intptr_t kMaxSupportedBytecodeFormatVersion = 21; enum Opcode { #define DECLARE_BYTECODE(name, encoding, kind, op1, op2, op3) k##name, @@ -981,6 +981,8 @@ class KernelBytecode { case KernelBytecode::kDebugCheck: case KernelBytecode::kDirectCall: case KernelBytecode::kDirectCall_Wide: + case KernelBytecode::kUncheckedDirectCall: + case KernelBytecode::kUncheckedDirectCall_Wide: case KernelBytecode::kInterfaceCall: case KernelBytecode::kInterfaceCall_Wide: case KernelBytecode::kInstantiatedInterfaceCall: diff --git a/runtime/vm/interpreter.cc b/runtime/vm/interpreter.cc index 9371fc6cc4c..d0de7f38de5 100644 --- a/runtime/vm/interpreter.cc +++ b/runtime/vm/interpreter.cc @@ -1851,6 +1851,27 @@ SwitchDispatch: DISPATCH(); } + { + BYTECODE(UncheckedDirectCall, D_F); + DEBUG_CHECK; + // Invoke target function. + { + const uint32_t argc = rF; + const uint32_t kidx = rD; + + InterpreterHelpers::IncrementUsageCounter(FrameFunction(FP)); + *++SP = LOAD_CONSTANT(kidx); + RawObject** call_base = SP - argc; + RawObject** call_top = SP; + argdesc_ = static_cast(LOAD_CONSTANT(kidx + 1)); + if (!Invoke(thread, call_base, call_top, &pc, &FP, &SP)) { + HANDLE_EXCEPTION; + } + } + + DISPATCH(); + } + { BYTECODE(InterfaceCall, D_F); DEBUG_CHECK; @@ -2676,6 +2697,19 @@ SwitchDispatch: DISPATCH(); } + { + BYTECODE(CheckReceiverForNull, D); + SP -= 1; + + if (UNLIKELY(SP[0] == null_value)) { + // Load selector. + SP[0] = LOAD_CONSTANT(rD); + goto ThrowNullError; + } + + DISPATCH(); + } + { BYTECODE(NegateInt, 0); DEBUG_CHECK;