[vm,aot,bytecode] Handle direct-call metadata when generating bytecode

AOT transformations add vm.direct-call.metadata with devirtualization
information to AST nodes.

Bytecode generator should take this information into account when
generating bytecode in case of AOT. Bytecode format is extended with
CheckReceiverForNull and UncheckedDirectCall instructions, and
DirectCallViaDynamicForwarder constant pool entry in order to represent
devirtualized calls.

Change-Id: I697432ddd0b58d2d0413715132ba5e90eb606ec1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/119201
Reviewed-by: Régis Crelier <regis@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Alexander Markov
2019-10-01 00:20:48 +00:00
committed by commit-bot@chromium.org
parent 67ae444f29
commit 5920048fa2
15 changed files with 287 additions and 41 deletions
+10
View File
@@ -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);
}
}
+59 -6
View File
@@ -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;
+10 -5
View File
@@ -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<Opcode, Format> 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<Opcode, Format> 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:
+96 -17
View File
@@ -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<Null> {
ObjectTable objectTable;
Component bytecodeComponent;
NullabilityDetector nullabilityDetector;
Map<TreeNode, DirectCallMetadata> directCallMetadata;
List<ClassDeclaration> classDeclarations;
List<FieldDeclaration> fieldDeclarations;
@@ -168,6 +171,9 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
bytecodeComponent.mainLibrary =
objectTable.getHandle(component.mainMethod.enclosingLibrary);
}
directCallMetadata =
component.metadata[DirectCallMetadataRepository.repositoryTag]?.mapping;
}
@override
@@ -1135,21 +1141,32 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
}
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<Null> {
totalArgCount++;
}
_genDirectCall(target, argDesc, totalArgCount, context: context);
_genDirectCall(target, argDesc, totalArgCount,
isUnchecked: isUnchecked, context: context);
}
void _genTypeArguments(List<DartType> typeArgs, {Class instantiatingClass}) {
@@ -1517,7 +1535,8 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
savedMaxSourcePositions = <int>[];
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<Null> {
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<Null> {
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<Null> {
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<Null> {
}
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();
+21 -1
View File
@@ -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<TreeNode, Scope> _scopes = <TreeNode, Scope>{};
@@ -30,6 +31,7 @@ class LocalVariables {
<ForInStatement, VariableDeclaration>{};
final BytecodeOptions options;
final TypeEnvironment typeEnvironment;
final Map<TreeNode, DirectCallMetadata> directCallMetadata;
Scope _currentScope;
Frame _currentFrame;
@@ -191,7 +193,8 @@ class LocalVariables {
List<VariableDeclaration> 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<Null> {
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<Null> {
_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);
-1
View File
@@ -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;
+3 -1
View File
@@ -26,8 +26,10 @@ class DirectCallMetadata {
/// Repository for [DirectCallMetadata].
class DirectCallMetadataRepository
extends MetadataRepository<DirectCallMetadata> {
static const repositoryTag = 'vm.direct-call.metadata';
@override
final String tag = 'vm.direct-call.metadata';
String get tag => repositoryTag;
@override
final Map<TreeNode, DirectCallMetadata> mapping =
@@ -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
@@ -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]
@@ -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:
@@ -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
@@ -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);
@@ -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();
}
+7 -5
View File
@@ -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:
+34
View File
@@ -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<RawArray*>(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;