[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:
committed by
commit-bot@chromium.org
parent
67ae444f29
commit
5920048fa2
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,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:
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user