[vm/bytecode] Add UncheckedClosureCall instruction
This improves performance of closure calls: DeltaBlueClosures: +25.14% DartMicroBenchMM.MaxLib +40.91% DartMicroBenchMM.MinLib +42.58% Change-Id: I13000e20b9433ce77bb2ba5209513de575656036 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/109220 Reviewed-by: Ryan Macnak <rmacnak@google.com> Reviewed-by: Régis Crelier <regis@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
8dc4148516
commit
f450cf399e
@@ -379,6 +379,11 @@ class BytecodeAssembler {
|
||||
_emitInstructionDF(Opcode.kInterfaceCall, rd, rf);
|
||||
}
|
||||
|
||||
void emitUncheckedClosureCall(int rd, int rf) {
|
||||
emitSourcePosition();
|
||||
_emitInstructionDF(Opcode.kUncheckedClosureCall, rd, rf);
|
||||
}
|
||||
|
||||
void emitUncheckedInterfaceCall(int rd, int rf) {
|
||||
emitSourcePosition();
|
||||
_emitInstructionDF(Opcode.kUncheckedInterfaceCall, rd, rf);
|
||||
|
||||
@@ -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 = 14;
|
||||
const int currentBytecodeFormatVersion = 15;
|
||||
|
||||
enum Opcode {
|
||||
kUnusedOpcode000,
|
||||
@@ -218,8 +218,8 @@ enum Opcode {
|
||||
kUnused24, // Reserved for InterfaceCall1_Wide
|
||||
kUnused25, // Reserved for InterfaceCall2
|
||||
kUnused26, // Reserved for InterfaceCall2_Wide
|
||||
kUnused27, // Reserved for InterfaceCall3
|
||||
kUnused28, // Reserved for InterfaceCall3_Wide
|
||||
kUncheckedClosureCall,
|
||||
kUncheckedClosureCall_Wide,
|
||||
kUncheckedInterfaceCall,
|
||||
kUncheckedInterfaceCall_Wide,
|
||||
kDynamicCall,
|
||||
@@ -500,6 +500,8 @@ const Map<Opcode, Format> BytecodeFormats = const {
|
||||
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(
|
||||
Encoding.kDF, const [Operand.lit, Operand.imm, Operand.none]),
|
||||
Opcode.kUncheckedInterfaceCall: const Format(
|
||||
Encoding.kDF, const [Operand.lit, Operand.imm, Operand.none]),
|
||||
Opcode.kNegateDouble: const Format(
|
||||
@@ -603,6 +605,7 @@ bool isCall(Opcode opcode) {
|
||||
switch (opcode) {
|
||||
case Opcode.kDirectCall:
|
||||
case Opcode.kInterfaceCall:
|
||||
case Opcode.kUncheckedClosureCall:
|
||||
case Opcode.kUncheckedInterfaceCall:
|
||||
case Opcode.kDynamicCall:
|
||||
case Opcode.kNativeCall:
|
||||
|
||||
@@ -42,7 +42,8 @@ import 'generics.dart'
|
||||
getInstantiatorTypeArguments,
|
||||
hasFreeTypeParameters,
|
||||
hasInstantiatorTypeArguments,
|
||||
isUncheckedCall;
|
||||
isUncheckedCall,
|
||||
isUncheckedClosureCall;
|
||||
import 'local_variable_table.dart' show LocalVariableTable;
|
||||
import 'local_vars.dart' show LocalVariables;
|
||||
import 'nullability_detector.dart' show NullabilityDetector;
|
||||
@@ -928,11 +929,15 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
|
||||
initializedFields.add(field);
|
||||
}
|
||||
|
||||
void _genArguments(Expression receiver, Arguments arguments) {
|
||||
void _genArguments(Expression receiver, Arguments arguments,
|
||||
{int storeReceiverToLocal}) {
|
||||
if (arguments.types.isNotEmpty) {
|
||||
_genTypeArguments(arguments.types);
|
||||
}
|
||||
_generateNode(receiver);
|
||||
if (storeReceiverToLocal != null) {
|
||||
asm.emitStoreLocal(storeReceiverToLocal);
|
||||
}
|
||||
_generateNodeList(arguments.positional);
|
||||
arguments.named.forEach((NamedExpression ne) => _generateNode(ne.value));
|
||||
}
|
||||
@@ -1366,7 +1371,7 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
|
||||
savedMaxSourcePositions = <int>[];
|
||||
maxSourcePosition = node.fileOffset;
|
||||
|
||||
locals = new LocalVariables(node, options);
|
||||
locals = new LocalVariables(node, options, typeEnvironment);
|
||||
locals.enterScope(node);
|
||||
assert(!locals.isSyncYieldingFrame);
|
||||
|
||||
@@ -2678,9 +2683,6 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
|
||||
void _genInstanceCall(
|
||||
int totalArgCount, int callCpIndex, bool isDynamic, bool isUnchecked,
|
||||
[TreeNode context]) {
|
||||
if (totalArgCount >= argumentsLimit) {
|
||||
throw new TooManyArgumentsException(context.fileOffset);
|
||||
}
|
||||
if (isDynamic) {
|
||||
assert(!isUnchecked);
|
||||
asm.emitDynamicCall(callCpIndex, totalArgCount);
|
||||
@@ -2699,6 +2701,27 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
|
||||
return;
|
||||
}
|
||||
final args = node.arguments;
|
||||
final totalArgCount = args.positional.length +
|
||||
args.named.length +
|
||||
1 /* receiver */ +
|
||||
(args.types.isNotEmpty ? 1 : 0) /* type arguments */;
|
||||
if (totalArgCount >= argumentsLimit) {
|
||||
throw new TooManyArgumentsException(node.fileOffset);
|
||||
}
|
||||
// Front-end guarantees that all calls with known function type
|
||||
// do not need any argument type checks.
|
||||
if (isUncheckedClosureCall(node, typeEnvironment)) {
|
||||
final int receiverTemp = locals.tempIndexInFrame(node);
|
||||
_genArguments(node.receiver, args, storeReceiverToLocal: receiverTemp);
|
||||
// Duplicate receiver (closure) for UncheckedClosureCall.
|
||||
asm.emitPush(receiverTemp);
|
||||
final argDescCpIndex = cp.addArgDescByArguments(args, hasReceiver: true);
|
||||
asm.emitUncheckedClosureCall(argDescCpIndex, totalArgCount);
|
||||
return;
|
||||
}
|
||||
|
||||
_genArguments(node.receiver, args);
|
||||
|
||||
Member interfaceTarget = node.interfaceTarget;
|
||||
if (interfaceTarget is Field ||
|
||||
interfaceTarget is Procedure && interfaceTarget.isGetter) {
|
||||
@@ -2709,15 +2732,10 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
|
||||
final isDynamic = interfaceTarget == null;
|
||||
final isUnchecked =
|
||||
isUncheckedCall(interfaceTarget, node.receiver, typeEnvironment);
|
||||
_genArguments(node.receiver, args);
|
||||
final argDesc =
|
||||
objectTable.getArgDescHandleByArguments(args, hasReceiver: true);
|
||||
final callCpIndex = cp.addInstanceCall(
|
||||
InvocationKind.method, interfaceTarget, node.name, argDesc);
|
||||
final totalArgCount = args.positional.length +
|
||||
args.named.length +
|
||||
1 /* receiver */ +
|
||||
(args.types.isNotEmpty ? 1 : 0) /* type arguments */;
|
||||
_genInstanceCall(totalArgCount, callCpIndex, isDynamic, isUnchecked, node);
|
||||
}
|
||||
|
||||
|
||||
@@ -176,9 +176,9 @@ bool isSealedType(DartType type, CoreTypes coreTypes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns true if an instance call to [interfaceTarget] with given
|
||||
// [receiver] can omit argument type checks needed due to generic-covariant
|
||||
// parameters.
|
||||
/// Returns true if an instance call to [interfaceTarget] with given
|
||||
/// [receiver] can omit argument type checks needed due to generic-covariant
|
||||
/// parameters.
|
||||
bool isUncheckedCall(Member interfaceTarget, Expression receiver,
|
||||
TypeEnvironment typeEnvironment) {
|
||||
if (interfaceTarget == null) {
|
||||
@@ -229,3 +229,10 @@ bool _hasGenericCovariantParameters(Member target) {
|
||||
throw 'Unexpected instance call target ${target.runtimeType} $target';
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if invocation [node] is a closure call with statically known
|
||||
/// function type. Such invocations can omit argument type checks.
|
||||
bool isUncheckedClosureCall(
|
||||
MethodInvocation node, TypeEnvironment typeEnvironment) =>
|
||||
node.name.name == 'call' &&
|
||||
getStaticType(node.receiver, typeEnvironment) is FunctionType;
|
||||
|
||||
@@ -9,6 +9,8 @@ import 'dart:math' show max;
|
||||
import 'package:kernel/ast.dart';
|
||||
import 'package:kernel/transformations/continuation.dart'
|
||||
show ContinuationVariables;
|
||||
import 'package:kernel/type_environment.dart';
|
||||
import 'package:vm/bytecode/generics.dart';
|
||||
|
||||
import 'dbc.dart';
|
||||
import 'options.dart' show BytecodeOptions;
|
||||
@@ -27,6 +29,7 @@ class LocalVariables {
|
||||
final Map<ForInStatement, VariableDeclaration> _capturedIteratorVars =
|
||||
<ForInStatement, VariableDeclaration>{};
|
||||
final BytecodeOptions options;
|
||||
final TypeEnvironment typeEnvironment;
|
||||
|
||||
Scope _currentScope;
|
||||
Frame _currentFrame;
|
||||
@@ -185,7 +188,7 @@ class LocalVariables {
|
||||
List<VariableDeclaration> get sortedNamedParameters =>
|
||||
_currentFrame.sortedNamedParameters;
|
||||
|
||||
LocalVariables(Member node, this.options) {
|
||||
LocalVariables(Member node, this.options, this.typeEnvironment) {
|
||||
final scopeBuilder = new _ScopeBuilder(this);
|
||||
node.accept(scopeBuilder);
|
||||
|
||||
@@ -1191,6 +1194,15 @@ class _Allocator extends RecursiveVisitor<Null> {
|
||||
_visit(node, temps: 1);
|
||||
}
|
||||
|
||||
@override
|
||||
visitMethodInvocation(MethodInvocation node) {
|
||||
int numTemps = 0;
|
||||
if (isUncheckedClosureCall(node, locals.typeEnvironment)) {
|
||||
numTemps = 1;
|
||||
}
|
||||
_visit(node, temps: numTemps);
|
||||
}
|
||||
|
||||
@override
|
||||
visitPropertySet(PropertySet node) {
|
||||
_visit(node, temps: 1);
|
||||
|
||||
@@ -41,18 +41,22 @@ Function 'test2', static, reflectable, debuggable
|
||||
return-type void
|
||||
|
||||
Bytecode {
|
||||
Entry 0
|
||||
Entry 1
|
||||
CheckStack 0
|
||||
JumpIfNoAsserts L1
|
||||
Push FP[-6]
|
||||
DynamicCall CP#1, 1
|
||||
StoreLocal r0
|
||||
Push r0
|
||||
UncheckedClosureCall CP#0, 1
|
||||
AssertBoolean 0
|
||||
JumpIfTrue L1
|
||||
PushInt 0
|
||||
PushInt 0
|
||||
Push FP[-5]
|
||||
DynamicCall CP#2, 1
|
||||
DirectCall CP#3, 3
|
||||
StoreLocal r0
|
||||
Push r0
|
||||
UncheckedClosureCall CP#0, 1
|
||||
DirectCall CP#1, 3
|
||||
Drop1
|
||||
L1:
|
||||
PushNull
|
||||
@@ -60,10 +64,8 @@ L1:
|
||||
}
|
||||
ConstantPool {
|
||||
[0] = ObjectRef ArgDesc num-args 1, num-type-args 0, names []
|
||||
[1] = ICData dynamic target-name 'call', arg-desc CP#0
|
||||
[2] = ICData dynamic target-name 'call', arg-desc CP#0
|
||||
[3] = DirectCall 'dart:core::_AssertionError::_throwNew', ArgDesc num-args 3, num-type-args 0, names []
|
||||
[4] = Reserved
|
||||
[1] = DirectCall 'dart:core::_AssertionError::_throwNew', ArgDesc num-args 3, num-type-args 0, names []
|
||||
[2] = Reserved
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -41,8 +41,10 @@ Bytecode {
|
||||
StoreFieldTOS CP#1
|
||||
PopLocal r2
|
||||
Push r2
|
||||
StoreLocal r3
|
||||
PushInt 3
|
||||
DynamicCall CP#17, 2
|
||||
Push r3
|
||||
UncheckedClosureCall CP#16, 2
|
||||
Drop1
|
||||
Push r0
|
||||
LoadContextVar 0, 0
|
||||
@@ -66,7 +68,6 @@ ConstantPool {
|
||||
[14] = InstanceField dart:core::_Closure::_function (field)
|
||||
[15] = Reserved
|
||||
[16] = ObjectRef ArgDesc num-args 2, num-type-args 0, names []
|
||||
[17] = ICData dynamic target-name 'call', arg-desc CP#16
|
||||
}
|
||||
Closure #lib::simpleClosure::'<anonymous closure>' (dart:core::int y) -> dart:core::Null
|
||||
ClosureCode {
|
||||
@@ -594,13 +595,17 @@ Bytecode {
|
||||
Push r1
|
||||
StoreFieldTOS CP#1
|
||||
PopLocal r3
|
||||
PushConstant CP#43
|
||||
PushConstant CP#40
|
||||
Push r3
|
||||
DynamicCall CP#44, 2
|
||||
StoreLocal r4
|
||||
Push r4
|
||||
UncheckedClosureCall CP#37, 2
|
||||
Drop1
|
||||
PushConstant CP#45
|
||||
PushConstant CP#41
|
||||
Push r3
|
||||
DynamicCall CP#46, 2
|
||||
StoreLocal r4
|
||||
Push r4
|
||||
UncheckedClosureCall CP#37, 2
|
||||
Drop1
|
||||
PushNull
|
||||
ReturnTOS
|
||||
@@ -641,18 +646,13 @@ ConstantPool {
|
||||
[32] = InstanceField dart:core::_Closure::_function (field)
|
||||
[33] = Reserved
|
||||
[34] = ObjectRef ArgDesc num-args 1, num-type-args 0, names []
|
||||
[35] = ICData dynamic target-name 'call', arg-desc CP#34
|
||||
[36] = EndClosureFunctionScope
|
||||
[37] = ObjectRef < #lib::C7, #lib::C8 >
|
||||
[38] = ObjectRef ArgDesc num-args 1, num-type-args 2, names []
|
||||
[39] = ICData dynamic target-name 'call', arg-desc CP#38
|
||||
[40] = ObjectRef < dart:core::List < #lib::C7 >, dart:core::List < #lib::C8 > >
|
||||
[41] = ICData dynamic target-name 'call', arg-desc CP#38
|
||||
[42] = EndClosureFunctionScope
|
||||
[43] = ObjectRef < #lib::C5, #lib::C6 >
|
||||
[44] = ICData dynamic target-name 'call', arg-desc CP#38
|
||||
[45] = ObjectRef < dart:core::List < #lib::C5 >, dart:core::List < #lib::C6 > >
|
||||
[46] = ICData dynamic target-name 'call', arg-desc CP#38
|
||||
[35] = EndClosureFunctionScope
|
||||
[36] = ObjectRef < #lib::C7, #lib::C8 >
|
||||
[37] = ObjectRef ArgDesc num-args 1, num-type-args 2, names []
|
||||
[38] = ObjectRef < dart:core::List < #lib::C7 >, dart:core::List < #lib::C8 > >
|
||||
[39] = EndClosureFunctionScope
|
||||
[40] = ObjectRef < #lib::C5, #lib::C6 >
|
||||
[41] = ObjectRef < dart:core::List < #lib::C5 >, dart:core::List < #lib::C6 > >
|
||||
}
|
||||
Closure #lib::A::foo::'nested1' <dart:core::Object T5, dart:core::Object T6> () -> void
|
||||
ClosureCode {
|
||||
@@ -698,13 +698,17 @@ L2:
|
||||
Push r1
|
||||
StoreFieldTOS CP#1
|
||||
PopLocal r3
|
||||
PushConstant CP#37
|
||||
PushConstant CP#36
|
||||
Push r3
|
||||
DynamicCall CP#39, 2
|
||||
StoreLocal r4
|
||||
Push r4
|
||||
UncheckedClosureCall CP#37, 2
|
||||
Drop1
|
||||
PushConstant CP#40
|
||||
PushConstant CP#38
|
||||
Push r3
|
||||
DynamicCall CP#41, 2
|
||||
StoreLocal r4
|
||||
Push r4
|
||||
UncheckedClosureCall CP#37, 2
|
||||
Drop1
|
||||
PushNull
|
||||
ReturnTOS
|
||||
@@ -755,7 +759,9 @@ L2:
|
||||
StoreFieldTOS CP#1
|
||||
PopLocal r3
|
||||
Push r3
|
||||
DynamicCall CP#35, 1
|
||||
StoreLocal r4
|
||||
Push r4
|
||||
UncheckedClosureCall CP#34, 1
|
||||
Drop1
|
||||
PushNull
|
||||
ReturnTOS
|
||||
@@ -908,28 +914,32 @@ Bytecode {
|
||||
StoreFieldTOS CP#1
|
||||
PopLocal r3
|
||||
Push r3
|
||||
StoreLocal r4
|
||||
PushInt 10
|
||||
DynamicCall CP#25, 2
|
||||
Push r4
|
||||
UncheckedClosureCall CP#23, 2
|
||||
Drop1
|
||||
Push r3
|
||||
StoreLocal r4
|
||||
PushInt 11
|
||||
DynamicCall CP#26, 2
|
||||
Push r4
|
||||
UncheckedClosureCall CP#23, 2
|
||||
Drop1
|
||||
Push r2
|
||||
DirectCall CP#21, 1
|
||||
DirectCall CP#20, 1
|
||||
Drop1
|
||||
Push r0
|
||||
LoadContextVar 0, 2
|
||||
DirectCall CP#21, 1
|
||||
DirectCall CP#20, 1
|
||||
Drop1
|
||||
Push r0
|
||||
LoadContextVar 0, 1
|
||||
DirectCall CP#21, 1
|
||||
DirectCall CP#20, 1
|
||||
Drop1
|
||||
Push r0
|
||||
PushInt 42
|
||||
StoreContextVar 0, 3
|
||||
AllocateClosure CP#27
|
||||
AllocateClosure CP#24
|
||||
StoreLocal r3
|
||||
Push r3
|
||||
PushNull
|
||||
@@ -941,14 +951,16 @@ Bytecode {
|
||||
PushConstant CP#14
|
||||
StoreFieldTOS CP#15
|
||||
Push r3
|
||||
PushConstant CP#27
|
||||
PushConstant CP#24
|
||||
StoreFieldTOS CP#17
|
||||
Push r3
|
||||
Push r0
|
||||
StoreFieldTOS CP#1
|
||||
PopLocal r2
|
||||
Push r2
|
||||
DynamicCall CP#31, 1
|
||||
StoreLocal r3
|
||||
Push r3
|
||||
UncheckedClosureCall CP#19, 1
|
||||
Drop1
|
||||
PushNull
|
||||
ReturnTOS
|
||||
@@ -974,18 +986,14 @@ ConstantPool {
|
||||
[17] = InstanceField dart:core::_Closure::_function (field)
|
||||
[18] = Reserved
|
||||
[19] = ObjectRef ArgDesc num-args 1, num-type-args 0, names []
|
||||
[20] = ICData dynamic target-name 'call', arg-desc CP#19
|
||||
[21] = DirectCall 'dart:core::print', ArgDesc num-args 1, num-type-args 0, names []
|
||||
[22] = Reserved
|
||||
[23] = EndClosureFunctionScope
|
||||
[24] = ObjectRef ArgDesc num-args 2, num-type-args 0, names []
|
||||
[25] = ICData dynamic target-name 'call', arg-desc CP#24
|
||||
[26] = ICData dynamic target-name 'call', arg-desc CP#24
|
||||
[27] = ClosureFunction 2
|
||||
[28] = InterfaceCall '#lib::B::set:foo', ArgDesc num-args 2, num-type-args 0, names []
|
||||
[29] = Reserved
|
||||
[30] = EndClosureFunctionScope
|
||||
[31] = ICData dynamic target-name 'call', arg-desc CP#19
|
||||
[20] = DirectCall 'dart:core::print', ArgDesc num-args 1, num-type-args 0, names []
|
||||
[21] = Reserved
|
||||
[22] = EndClosureFunctionScope
|
||||
[23] = ObjectRef ArgDesc num-args 2, num-type-args 0, names []
|
||||
[24] = ClosureFunction 2
|
||||
[25] = InterfaceCall '#lib::B::set:foo', ArgDesc num-args 2, num-type-args 0, names []
|
||||
[26] = Reserved
|
||||
[27] = EndClosureFunctionScope
|
||||
}
|
||||
Closure #lib::B::topLevel::'<anonymous closure>' (dart:core::int y) -> dart:core::Null
|
||||
ClosureCode {
|
||||
@@ -1045,11 +1053,13 @@ ClosureCode {
|
||||
StoreFieldTOS CP#1
|
||||
PopLocal r3
|
||||
Push r3
|
||||
DynamicCall CP#20, 1
|
||||
StoreLocal r2
|
||||
Push r2
|
||||
UncheckedClosureCall CP#19, 1
|
||||
Drop1
|
||||
Push r0
|
||||
LoadContextVar 1, 1
|
||||
DirectCall CP#21, 1
|
||||
DirectCall CP#20, 1
|
||||
Drop1
|
||||
L1:
|
||||
PushNull
|
||||
@@ -1095,7 +1105,7 @@ ClosureCode {
|
||||
LoadContextVar 0, 0
|
||||
Push r0
|
||||
LoadContextVar 0, 3
|
||||
InterfaceCall CP#28, 2
|
||||
InterfaceCall CP#25, 2
|
||||
Drop1
|
||||
PushNull
|
||||
ReturnTOS
|
||||
@@ -1331,11 +1341,13 @@ L2:
|
||||
StoreFieldTOS CP#7
|
||||
PopLocal r3
|
||||
Push r3
|
||||
DynamicCall CP#20, 1
|
||||
StoreLocal r4
|
||||
Push r4
|
||||
UncheckedClosureCall CP#19, 1
|
||||
Drop1
|
||||
Push r0
|
||||
LoadContextVar 0, 0
|
||||
DirectCall CP#21, 1
|
||||
DirectCall CP#20, 1
|
||||
Drop1
|
||||
Push r0
|
||||
LoadContextParent
|
||||
@@ -1366,9 +1378,8 @@ ConstantPool {
|
||||
[17] = InstanceField dart:core::_Closure::_function (field)
|
||||
[18] = Reserved
|
||||
[19] = ObjectRef ArgDesc num-args 1, num-type-args 0, names []
|
||||
[20] = ICData dynamic target-name 'call', arg-desc CP#19
|
||||
[21] = DirectCall 'dart:core::print', ArgDesc num-args 1, num-type-args 0, names []
|
||||
[22] = Reserved
|
||||
[20] = DirectCall 'dart:core::print', ArgDesc num-args 1, num-type-args 0, names []
|
||||
[21] = Reserved
|
||||
}
|
||||
Closure #lib::C::testForInLoop::'<anonymous closure>' () -> dart:core::Null
|
||||
ClosureCode {
|
||||
@@ -1533,8 +1544,7 @@ ConstantPool {
|
||||
[13] = InstanceField dart:core::_Closure::_function (field)
|
||||
[14] = Reserved
|
||||
[15] = ObjectRef ArgDesc num-args 1, num-type-args 0, names []
|
||||
[16] = ICData dynamic target-name 'call', arg-desc CP#15
|
||||
[17] = EndClosureFunctionScope
|
||||
[16] = EndClosureFunctionScope
|
||||
}
|
||||
Closure #lib::D::bar::'<anonymous closure>' () -> dart:core::Null
|
||||
ClosureCode {
|
||||
@@ -1564,7 +1574,9 @@ ClosureCode {
|
||||
StoreFieldTOS CP#1
|
||||
PopLocal r2
|
||||
Push r2
|
||||
DynamicCall CP#16, 1
|
||||
StoreLocal r3
|
||||
Push r3
|
||||
UncheckedClosureCall CP#15, 1
|
||||
Drop1
|
||||
PushNull
|
||||
ReturnTOS
|
||||
|
||||
@@ -237,7 +237,9 @@ Try #0 start:
|
||||
StoreFieldTOS CP#1
|
||||
PopLocal r4
|
||||
Push r4
|
||||
DynamicCall CP#18, 1
|
||||
StoreLocal r5
|
||||
Push r5
|
||||
UncheckedClosureCall CP#17, 1
|
||||
Drop1
|
||||
Push r0
|
||||
LoadContextVar 0, 1
|
||||
@@ -262,7 +264,7 @@ Try #0 handler:
|
||||
StoreLocal r5
|
||||
Push r5
|
||||
PushInt 0
|
||||
PushConstant CP#19
|
||||
PushConstant CP#18
|
||||
StoreIndexedTOS
|
||||
Push r5
|
||||
PushInt 1
|
||||
@@ -270,17 +272,17 @@ Try #0 handler:
|
||||
StoreIndexedTOS
|
||||
Push r5
|
||||
PushInt 2
|
||||
PushConstant CP#20
|
||||
PushConstant CP#19
|
||||
StoreIndexedTOS
|
||||
Push r5
|
||||
PushInt 3
|
||||
Push r0
|
||||
LoadContextVar 0, 2
|
||||
StoreIndexedTOS
|
||||
DirectCall CP#21, 1
|
||||
DirectCall CP#20, 1
|
||||
DirectCall CP#4, 1
|
||||
Drop1
|
||||
AllocateClosure CP#23
|
||||
AllocateClosure CP#22
|
||||
StoreLocal r5
|
||||
Push r5
|
||||
PushNull
|
||||
@@ -292,7 +294,7 @@ Try #0 handler:
|
||||
PushConstant CP#12
|
||||
StoreFieldTOS CP#13
|
||||
Push r5
|
||||
PushConstant CP#23
|
||||
PushConstant CP#22
|
||||
StoreFieldTOS CP#15
|
||||
Push r5
|
||||
Push r0
|
||||
@@ -308,7 +310,7 @@ L1:
|
||||
ReturnTOS
|
||||
}
|
||||
ExceptionsTable {
|
||||
try-index 0, outer -1, start 20, end 80, handler 80, needs-stack-trace, types [CP#6]
|
||||
try-index 0, outer -1, start 20, end 84, handler 84, needs-stack-trace, types [CP#6]
|
||||
}
|
||||
ConstantPool {
|
||||
[0] = ClosureFunction 0
|
||||
@@ -329,19 +331,18 @@ ConstantPool {
|
||||
[15] = InstanceField dart:core::_Closure::_function (field)
|
||||
[16] = Reserved
|
||||
[17] = ObjectRef ArgDesc num-args 1, num-type-args 0, names []
|
||||
[18] = ICData dynamic target-name 'call', arg-desc CP#17
|
||||
[19] = ObjectRef 'caught '
|
||||
[20] = ObjectRef ' '
|
||||
[21] = DirectCall 'dart:core::_StringBase::_interpolate', ArgDesc num-args 1, num-type-args 0, names []
|
||||
[22] = Reserved
|
||||
[23] = ClosureFunction 1
|
||||
[24] = ObjectRef 'danger bar'
|
||||
[25] = Type dart:core::Error
|
||||
[26] = InterfaceCall 'dart:core::Object::_simpleInstanceOf', ArgDesc num-args 2, num-type-args 0, names []
|
||||
[27] = Reserved
|
||||
[28] = ObjectRef 'error '
|
||||
[29] = ObjectRef ', captured stack trace: '
|
||||
[30] = EndClosureFunctionScope
|
||||
[18] = ObjectRef 'caught '
|
||||
[19] = ObjectRef ' '
|
||||
[20] = DirectCall 'dart:core::_StringBase::_interpolate', ArgDesc num-args 1, num-type-args 0, names []
|
||||
[21] = Reserved
|
||||
[22] = ClosureFunction 1
|
||||
[23] = ObjectRef 'danger bar'
|
||||
[24] = Type dart:core::Error
|
||||
[25] = InterfaceCall 'dart:core::Object::_simpleInstanceOf', ArgDesc num-args 2, num-type-args 0, names []
|
||||
[26] = Reserved
|
||||
[27] = ObjectRef 'error '
|
||||
[28] = ObjectRef ', captured stack trace: '
|
||||
[29] = EndClosureFunctionScope
|
||||
}
|
||||
Closure #lib::testTryCatch3::'foo' () -> void
|
||||
ClosureCode {
|
||||
@@ -389,7 +390,7 @@ ClosureCode {
|
||||
Push r0
|
||||
PopLocal r2
|
||||
Try #0 start:
|
||||
PushConstant CP#24
|
||||
PushConstant CP#23
|
||||
DirectCall CP#4, 1
|
||||
Drop1
|
||||
Jump L1
|
||||
@@ -401,8 +402,8 @@ Try #0 handler:
|
||||
MoveSpecial exception, r2
|
||||
MoveSpecial stackTrace, r3
|
||||
Push r2
|
||||
PushConstant CP#25
|
||||
InterfaceCall CP#26, 2
|
||||
PushConstant CP#24
|
||||
InterfaceCall CP#25, 2
|
||||
JumpIfFalse L2
|
||||
Push r2
|
||||
PopLocal r4
|
||||
@@ -412,7 +413,7 @@ Try #0 handler:
|
||||
StoreLocal r5
|
||||
Push r5
|
||||
PushInt 0
|
||||
PushConstant CP#28
|
||||
PushConstant CP#27
|
||||
StoreIndexedTOS
|
||||
Push r5
|
||||
PushInt 1
|
||||
@@ -420,14 +421,14 @@ Try #0 handler:
|
||||
StoreIndexedTOS
|
||||
Push r5
|
||||
PushInt 2
|
||||
PushConstant CP#29
|
||||
PushConstant CP#28
|
||||
StoreIndexedTOS
|
||||
Push r5
|
||||
PushInt 3
|
||||
Push r0
|
||||
LoadContextVar 0, 2
|
||||
StoreIndexedTOS
|
||||
DirectCall CP#21, 1
|
||||
DirectCall CP#20, 1
|
||||
DirectCall CP#4, 1
|
||||
Drop1
|
||||
Jump L1
|
||||
@@ -646,7 +647,9 @@ Try #1 start:
|
||||
StoreFieldTOS CP#7
|
||||
PopLocal r7
|
||||
Push r7
|
||||
DynamicCall CP#20, 1
|
||||
StoreLocal r8
|
||||
Push r8
|
||||
UncheckedClosureCall CP#19, 1
|
||||
Drop1
|
||||
Jump L4
|
||||
Try #1 end:
|
||||
@@ -656,7 +659,7 @@ Try #1 handler:
|
||||
PopLocal r0
|
||||
MoveSpecial exception, r5
|
||||
MoveSpecial stackTrace, r6
|
||||
PushConstant CP#22
|
||||
PushConstant CP#21
|
||||
DirectCall CP#3, 1
|
||||
Drop1
|
||||
Push r5
|
||||
@@ -665,7 +668,7 @@ Try #1 handler:
|
||||
L4:
|
||||
Push r5
|
||||
PopLocal r0
|
||||
PushConstant CP#22
|
||||
PushConstant CP#21
|
||||
DirectCall CP#3, 1
|
||||
Drop1
|
||||
Jump L5
|
||||
@@ -676,7 +679,7 @@ Try #0 handler:
|
||||
PopLocal r0
|
||||
MoveSpecial exception, r3
|
||||
MoveSpecial stackTrace, r4
|
||||
PushConstant CP#24
|
||||
PushConstant CP#23
|
||||
DirectCall CP#3, 1
|
||||
Drop1
|
||||
Push r3
|
||||
@@ -685,12 +688,12 @@ Try #0 handler:
|
||||
L5:
|
||||
Push r3
|
||||
PopLocal r0
|
||||
PushConstant CP#24
|
||||
PushConstant CP#23
|
||||
DirectCall CP#3, 1
|
||||
Drop1
|
||||
Jump L2
|
||||
L2:
|
||||
PushConstant CP#25
|
||||
PushConstant CP#24
|
||||
DirectCall CP#3, 1
|
||||
Drop1
|
||||
Jump L3
|
||||
@@ -699,8 +702,8 @@ L3:
|
||||
ReturnTOS
|
||||
}
|
||||
ExceptionsTable {
|
||||
try-index 0, outer -1, start 53, end 158, handler 158, needs-stack-trace, types [CP#21]
|
||||
try-index 1, outer 0, start 70, end 120, handler 120, needs-stack-trace, types [CP#21]
|
||||
try-index 0, outer -1, start 53, end 162, handler 162, needs-stack-trace, types [CP#20]
|
||||
try-index 1, outer 0, start 70, end 124, handler 124, needs-stack-trace, types [CP#20]
|
||||
}
|
||||
ConstantPool {
|
||||
[0] = InterfaceCall 'dart:core::Object::==', ArgDesc num-args 2, num-type-args 0, names []
|
||||
@@ -723,12 +726,11 @@ ConstantPool {
|
||||
[17] = InstanceField dart:core::_Closure::_function (field)
|
||||
[18] = Reserved
|
||||
[19] = ObjectRef ArgDesc num-args 1, num-type-args 0, names []
|
||||
[20] = ICData dynamic target-name 'call', arg-desc CP#19
|
||||
[21] = Type dynamic
|
||||
[22] = ObjectRef 'finally 1'
|
||||
[23] = ObjectRef 'after try 1'
|
||||
[24] = ObjectRef 'finally 2'
|
||||
[25] = ObjectRef 'case 2'
|
||||
[20] = Type dynamic
|
||||
[21] = ObjectRef 'finally 1'
|
||||
[22] = ObjectRef 'after try 1'
|
||||
[23] = ObjectRef 'finally 2'
|
||||
[24] = ObjectRef 'case 2'
|
||||
}
|
||||
Closure #lib::testTryFinally2::'foo' () -> void
|
||||
ClosureCode {
|
||||
|
||||
@@ -259,6 +259,8 @@ static intptr_t GetConstantPoolIndex(const KBCInstr* instr) {
|
||||
case KernelBytecode::kDirectCall_Wide:
|
||||
case KernelBytecode::kInterfaceCall:
|
||||
case KernelBytecode::kInterfaceCall_Wide:
|
||||
case KernelBytecode::kUncheckedClosureCall:
|
||||
case KernelBytecode::kUncheckedClosureCall_Wide:
|
||||
case KernelBytecode::kUncheckedInterfaceCall:
|
||||
case KernelBytecode::kUncheckedInterfaceCall_Wide:
|
||||
case KernelBytecode::kDynamicCall:
|
||||
|
||||
@@ -882,6 +882,28 @@ Fragment BaseFlowGraphBuilder::DebugStepCheck(TokenPosition position) {
|
||||
#endif
|
||||
}
|
||||
|
||||
Fragment BaseFlowGraphBuilder::CheckNull(TokenPosition position,
|
||||
LocalVariable* receiver,
|
||||
const String& function_name,
|
||||
bool clear_the_temp /* = true */) {
|
||||
Fragment instructions = LoadLocal(receiver);
|
||||
|
||||
CheckNullInstr* check_null =
|
||||
new (Z) CheckNullInstr(Pop(), function_name, GetNextDeoptId(), position);
|
||||
|
||||
instructions <<= check_null;
|
||||
|
||||
if (clear_the_temp) {
|
||||
// Null out receiver to make sure it is not saved into the frame before
|
||||
// doing the call.
|
||||
instructions += NullConstant();
|
||||
instructions += StoreLocal(TokenPosition::kNoSource, receiver);
|
||||
instructions += Drop();
|
||||
}
|
||||
|
||||
return instructions;
|
||||
}
|
||||
|
||||
} // namespace kernel
|
||||
} // namespace dart
|
||||
|
||||
|
||||
@@ -303,6 +303,15 @@ class BaseFlowGraphBuilder {
|
||||
|
||||
Fragment DebugStepCheck(TokenPosition position);
|
||||
|
||||
// Loads 'receiver' and checks it for null. Throws NoSuchMethod if it is null.
|
||||
// 'function_name' is a selector which is being called (reported in
|
||||
// NoSuchMethod message).
|
||||
// Sets 'receiver' to 'null' after the check if 'clear_the_temp'.
|
||||
Fragment CheckNull(TokenPosition position,
|
||||
LocalVariable* receiver,
|
||||
const String& function_name,
|
||||
bool clear_the_temp = true);
|
||||
|
||||
protected:
|
||||
intptr_t AllocateBlockId() { return ++last_used_block_id_; }
|
||||
|
||||
|
||||
@@ -879,6 +879,35 @@ void BytecodeFlowGraphBuilder::BuildUncheckedInterfaceCall() {
|
||||
BuildInterfaceCallCommon(/*is_unchecked_call=*/true);
|
||||
}
|
||||
|
||||
void BytecodeFlowGraphBuilder::BuildUncheckedClosureCall() {
|
||||
if (is_generating_interpreter()) {
|
||||
UNIMPLEMENTED(); // TODO(alexmarkov): interpreter
|
||||
}
|
||||
|
||||
const Array& arg_desc_array =
|
||||
Array::Cast(ConstantAt(DecodeOperandD()).value());
|
||||
const ArgumentsDescriptor arg_desc(arg_desc_array);
|
||||
|
||||
const intptr_t argc = DecodeOperandF().value();
|
||||
|
||||
LocalVariable* receiver_temp = B->MakeTemporary();
|
||||
code_ += B->CheckNull(position_, receiver_temp, Symbols::Call(),
|
||||
/*clear_temp=*/false);
|
||||
|
||||
code_ += B->LoadNativeField(Slot::Closure_function());
|
||||
Value* function = Pop();
|
||||
|
||||
const ArgumentArray arguments = GetArguments(argc);
|
||||
|
||||
ClosureCallInstr* call = new (Z) ClosureCallInstr(
|
||||
function, arguments, arg_desc.TypeArgsLen(),
|
||||
Array::ZoneHandle(Z, arg_desc.GetArgumentNames()), position_,
|
||||
B->GetNextDeoptId(), Code::EntryKind::kUnchecked);
|
||||
|
||||
code_ <<= call;
|
||||
B->Push(call);
|
||||
}
|
||||
|
||||
void BytecodeFlowGraphBuilder::BuildDynamicCall() {
|
||||
if (is_generating_interpreter()) {
|
||||
UNIMPLEMENTED(); // TODO(alexmarkov): interpreter
|
||||
|
||||
@@ -479,28 +479,6 @@ Fragment FlowGraphBuilder::Return(TokenPosition position,
|
||||
return instructions;
|
||||
}
|
||||
|
||||
Fragment FlowGraphBuilder::CheckNull(TokenPosition position,
|
||||
LocalVariable* receiver,
|
||||
const String& function_name,
|
||||
bool clear_the_temp /* = true */) {
|
||||
Fragment instructions = LoadLocal(receiver);
|
||||
|
||||
CheckNullInstr* check_null =
|
||||
new (Z) CheckNullInstr(Pop(), function_name, GetNextDeoptId(), position);
|
||||
|
||||
instructions <<= check_null;
|
||||
|
||||
if (clear_the_temp) {
|
||||
// Null out receiver to make sure it is not saved into the frame before
|
||||
// doing the call.
|
||||
instructions += NullConstant();
|
||||
instructions += StoreLocal(TokenPosition::kNoSource, receiver);
|
||||
instructions += Drop();
|
||||
}
|
||||
|
||||
return instructions;
|
||||
}
|
||||
|
||||
Fragment FlowGraphBuilder::StaticCall(TokenPosition position,
|
||||
const Function& target,
|
||||
intptr_t argument_count,
|
||||
|
||||
@@ -165,10 +165,6 @@ class FlowGraphBuilder : public BaseFlowGraphBuilder {
|
||||
Fragment InitStaticField(const Field& field);
|
||||
Fragment NativeCall(const String* name, const Function* function);
|
||||
Fragment Return(TokenPosition position, bool omit_result_type_check = false);
|
||||
Fragment CheckNull(TokenPosition position,
|
||||
LocalVariable* receiver,
|
||||
const String& function_name,
|
||||
bool clear_the_temp = true);
|
||||
void SetResultTypeForStaticCall(StaticCallInstr* call,
|
||||
const Function& target,
|
||||
intptr_t argument_count,
|
||||
|
||||
@@ -657,8 +657,8 @@ namespace dart {
|
||||
V(Unused24, 0, RESV, ___, ___, ___) \
|
||||
V(Unused25, 0, RESV, ___, ___, ___) \
|
||||
V(Unused26, 0, RESV, ___, ___, ___) \
|
||||
V(Unused27, 0, RESV, ___, ___, ___) \
|
||||
V(Unused28, 0, RESV, ___, ___, ___) \
|
||||
V(UncheckedClosureCall, D_F, ORDN, num, num, ___) \
|
||||
V(UncheckedClosureCall_Wide, D_F, WIDE, num, num, ___) \
|
||||
V(UncheckedInterfaceCall, D_F, ORDN, num, num, ___) \
|
||||
V(UncheckedInterfaceCall_Wide, D_F, WIDE, num, num, ___) \
|
||||
V(DynamicCall, 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 = 14;
|
||||
static const intptr_t kMaxSupportedBytecodeFormatVersion = 15;
|
||||
|
||||
enum Opcode {
|
||||
#define DECLARE_BYTECODE(name, encoding, kind, op1, op2, op3) k##name,
|
||||
@@ -973,6 +973,8 @@ class KernelBytecode {
|
||||
case KernelBytecode::kDirectCall_Wide:
|
||||
case KernelBytecode::kInterfaceCall:
|
||||
case KernelBytecode::kInterfaceCall_Wide:
|
||||
case KernelBytecode::kUncheckedClosureCall:
|
||||
case KernelBytecode::kUncheckedClosureCall_Wide:
|
||||
case KernelBytecode::kUncheckedInterfaceCall:
|
||||
case KernelBytecode::kUncheckedInterfaceCall_Wide:
|
||||
case KernelBytecode::kDynamicCall:
|
||||
|
||||
@@ -1996,6 +1996,33 @@ SwitchDispatch:
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
{
|
||||
BYTECODE(UncheckedClosureCall, D_F);
|
||||
DEBUG_CHECK;
|
||||
{
|
||||
const uint32_t argc = rF;
|
||||
const uint32_t kidx = rD;
|
||||
|
||||
RawClosure* receiver = Closure::RawCast(*SP--);
|
||||
RawObject** call_base = SP - argc + 1;
|
||||
RawObject** call_top = SP + 1;
|
||||
|
||||
InterpreterHelpers::IncrementUsageCounter(FrameFunction(FP));
|
||||
if (UNLIKELY(receiver == null_value)) {
|
||||
SP[0] = Symbols::Call().raw();
|
||||
goto ThrowNullError;
|
||||
}
|
||||
argdesc_ = static_cast<RawArray*>(LOAD_CONSTANT(kidx));
|
||||
call_top[0] = receiver->ptr()->function_;
|
||||
|
||||
if (!Invoke(thread, call_base, call_top, &pc, &FP, &SP)) {
|
||||
HANDLE_EXCEPTION;
|
||||
}
|
||||
}
|
||||
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
{
|
||||
BYTECODE(UncheckedInterfaceCall, D_F);
|
||||
DEBUG_CHECK;
|
||||
|
||||
Reference in New Issue
Block a user