Reland "[vm,dart2bytecode,modular_aot] Variable-length closure objects"
This is a reland of commit 88496ba1c3
Fixes on top of the original change:
* Closure layout is changed to avoid gap between fixed fields and
variable-length elements on compressed pointers architecture.
This gap was causing crashes in the GC when scanning closure
objects.
* pkg/vm_snapshot_analysis/test/instruction_sizes_test is fixed
on arm64 by decreasing threshold for detecting size changes.
Original change's description:
> [vm,dart2bytecode,modular_aot] Variable-length closure objects
>
> Extend closure objects with variable number of elements to capture.
> This is needed to support capturing multiple independent contexts
> after capturing is computed in the front-end.
>
> The following fixed Closure fields are moved into variable-length
> elements:
> - delayed type arguments;
> - instantiator type arguments;
> - function type arguments;
> - context.
>
> Number of elements and presence/indices of various type arguments
> are encoded into the new length_and_flags field in the Closure.
>
> Most closure objects don't need any of the type arguments so this
> change will reduce average Closure object size.
TEST=ci
Issue: https://github.com/dart-lang/sdk/issues/61572
Issue: https://github.com/dart-lang/sdk/issues/61635
Change-Id: I8685e632e2d0832766ecdc470f3cf9a6b880de48
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/494243
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Tess Strickland <sstrickl@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
This commit is contained in:
committed by
Commit Queue
parent
432adf747e
commit
51d1c8923a
@@ -1531,7 +1531,8 @@ class AstToIr extends ast.RecursiveVisitor {
|
||||
_typeTranslator.translate(node.constructedType),
|
||||
typeArguments: typeArguments,
|
||||
);
|
||||
final argsWithoutTypes = ast.Arguments(args.positional, named: args.named);
|
||||
final argsWithoutTypes = ast.Arguments(args.positional, named: args.named)
|
||||
..parent = node;
|
||||
final inputCount = _translateArguments(null, argsWithoutTypes);
|
||||
if (_handleUnreachableExpression(inputCount + 1)) return;
|
||||
builder.addDirectCall(
|
||||
|
||||
@@ -47,7 +47,7 @@ which reside in different sections such as libraries, classes, members, code, et
|
||||
```
|
||||
type BytecodeFile {
|
||||
UInt32 magic = 0x44424333; // 'DBC3'
|
||||
UInt32 formatVersion = 1;
|
||||
UInt32 formatVersion = 2;
|
||||
|
||||
// Descriptors of the sections below.
|
||||
// Each section has a fixed index in the descriptors array.
|
||||
@@ -816,6 +816,14 @@ type ConstantDeferredLibraryPrefix extends ConstantPoolEntry {
|
||||
PackedObject enclosingLibrary;
|
||||
PackedObject targetLibrary;
|
||||
}
|
||||
|
||||
// Occupies 2 entries in the constant pool
|
||||
type ConstantAllocateClosure extends ConstantPoolEntry {
|
||||
Byte tag = 18;
|
||||
UInt closureIndex;
|
||||
UInt numElements;
|
||||
UInt flags = (hasDelayedTypeArguments, hasInstantiatorTypeArguments, hasFunctionTypeArguments);
|
||||
}
|
||||
```
|
||||
|
||||
### Exceptions table
|
||||
@@ -1423,7 +1431,15 @@ SP[0] = SP[-1] <op> SP[0] ? true : false
|
||||
|
||||
#### AllocateClosure D
|
||||
|
||||
Allocate closure object for closure function ConstantPool[D].
|
||||
Allocate closure object described by ConstantAllocateClosure in ConstantPool[D].
|
||||
|
||||
#### LoadClosureElement D
|
||||
|
||||
Load element [D] from closure SP[0] and push it onto the stack.
|
||||
|
||||
#### StoreClosureElement D
|
||||
|
||||
Store object SP[0] into the element [D] of closure SP[-1].
|
||||
|
||||
#### Nop
|
||||
|
||||
|
||||
@@ -787,9 +787,21 @@ class BytecodeAssembler {
|
||||
}
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
void emitAllocateClosure() {
|
||||
void emitAllocateClosure(int rd) {
|
||||
emitSourcePosition();
|
||||
_emitInstruction0(Opcode.kAllocateClosure);
|
||||
_emitInstructionD(Opcode.kAllocateClosure, rd);
|
||||
}
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
void emitLoadClosureElement(int rd) {
|
||||
emitSourcePosition();
|
||||
_emitInstructionD(Opcode.kLoadClosureElement, rd);
|
||||
}
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
void emitStoreClosureElement(int rd) {
|
||||
emitSourcePosition();
|
||||
_emitInstructionD(Opcode.kStoreClosureElement, rd);
|
||||
}
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
|
||||
@@ -1010,8 +1010,6 @@ class BytecodeGenerator extends RecursiveVisitor {
|
||||
'_interpolate',
|
||||
);
|
||||
|
||||
late Class closureClass = libraryIndex.getClass('dart:core', '_Closure');
|
||||
|
||||
late Procedure objectInstanceOf = libraryIndex.getProcedure(
|
||||
'dart:core',
|
||||
'Object',
|
||||
@@ -1024,36 +1022,6 @@ class BytecodeGenerator extends RecursiveVisitor {
|
||||
'_simpleInstanceOf',
|
||||
);
|
||||
|
||||
late Field closureInstantiatorTypeArguments = libraryIndex.getField(
|
||||
'dart:core',
|
||||
'_Closure',
|
||||
'_instantiator_type_arguments',
|
||||
);
|
||||
|
||||
late Field closureFunctionTypeArguments = libraryIndex.getField(
|
||||
'dart:core',
|
||||
'_Closure',
|
||||
'_function_type_arguments',
|
||||
);
|
||||
|
||||
late Field closureDelayedTypeArguments = libraryIndex.getField(
|
||||
'dart:core',
|
||||
'_Closure',
|
||||
'_delayed_type_arguments',
|
||||
);
|
||||
|
||||
late Field closureFunction = libraryIndex.getField(
|
||||
'dart:core',
|
||||
'_Closure',
|
||||
'_function',
|
||||
);
|
||||
|
||||
late Field closureContext = libraryIndex.getField(
|
||||
'dart:core',
|
||||
'_Closure',
|
||||
'_context',
|
||||
);
|
||||
|
||||
late Procedure prependTypeArguments = libraryIndex.getTopLevelProcedure(
|
||||
'dart:_internal',
|
||||
'_prependTypeArguments',
|
||||
@@ -2226,7 +2194,7 @@ class BytecodeGenerator extends RecursiveVisitor {
|
||||
|
||||
if (isClosure) {
|
||||
asm.emitPush(locals.closureVarIndexInFrame);
|
||||
asm.emitLoadFieldTOS(cp.addInstanceField(closureContext));
|
||||
asm.emitLoadClosureElement(contextClosureElement);
|
||||
asm.emitPopLocal(locals.contextVarIndexInFrame);
|
||||
}
|
||||
|
||||
@@ -2294,7 +2262,7 @@ class BytecodeGenerator extends RecursiveVisitor {
|
||||
final int numParentTypeArgs = locals.numParentTypeArguments;
|
||||
asm.emitPush(locals.functionTypeArgsVarIndexInFrame);
|
||||
asm.emitPush(locals.closureVarIndexInFrame);
|
||||
asm.emitLoadFieldTOS(cp.addInstanceField(closureFunctionTypeArguments));
|
||||
asm.emitLoadClosureElement(functionTypeArgumentsClosureElement);
|
||||
_genPushInt(numParentTypeArgs);
|
||||
_genPushInt(numParentTypeArgs + function.typeParameters.length);
|
||||
_genDirectCall(
|
||||
@@ -2305,17 +2273,35 @@ class BytecodeGenerator extends RecursiveVisitor {
|
||||
asm.emitPopLocal(locals.functionTypeArgsVarIndexInFrame);
|
||||
} else {
|
||||
asm.emitPush(locals.closureVarIndexInFrame);
|
||||
asm.emitLoadFieldTOS(cp.addInstanceField(closureFunctionTypeArguments));
|
||||
asm.emitLoadClosureElement(functionTypeArgumentsClosureElement);
|
||||
asm.emitPopLocal(locals.functionTypeArgsVarIndexInFrame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool get closureHasDelayedTypeArguments =>
|
||||
enclosingFunction!.typeParameters.isNotEmpty;
|
||||
bool get closureHasInstantiatorTypeArguments =>
|
||||
instantiatorTypeArguments != null;
|
||||
bool get closureHasFunctionTypeArguments =>
|
||||
locals.hasFunctionTypeArgsVar && locals.numParentTypeArguments > 0;
|
||||
|
||||
int get delayedTypeArgumentsClosureElement => 0;
|
||||
int get instantiatorTypeArgumentsClosureElement =>
|
||||
(closureHasDelayedTypeArguments ? 1 : 0);
|
||||
int get functionTypeArgumentsClosureElement =>
|
||||
(closureHasDelayedTypeArguments ? 1 : 0) +
|
||||
(closureHasInstantiatorTypeArguments ? 1 : 0);
|
||||
int get contextClosureElement =>
|
||||
(closureHasDelayedTypeArguments ? 1 : 0) +
|
||||
(closureHasInstantiatorTypeArguments ? 1 : 0) +
|
||||
(closureHasFunctionTypeArguments ? 1 : 0);
|
||||
|
||||
void _handleDelayedTypeArguments(Label doneCheckingTypeArguments) {
|
||||
Label noDelayedTypeArgs = new Label();
|
||||
|
||||
asm.emitPush(locals.closureVarIndexInFrame);
|
||||
asm.emitLoadFieldTOS(cp.addInstanceField(closureDelayedTypeArguments));
|
||||
asm.emitLoadClosureElement(delayedTypeArgumentsClosureElement);
|
||||
asm.emitStoreLocal(locals.functionTypeArgsVarIndexInFrame);
|
||||
asm.emitPushConstant(cp.addEmptyTypeArguments());
|
||||
asm.emitJumpIfEqStrict(noDelayedTypeArgs);
|
||||
@@ -2347,7 +2333,7 @@ class BytecodeGenerator extends RecursiveVisitor {
|
||||
(t) => containsTypeParameter(t, functionTypeParametersSet!),
|
||||
)) {
|
||||
asm.emitPush(locals.closureVarIndexInFrame);
|
||||
asm.emitLoadFieldTOS(cp.addInstanceField(closureFunctionTypeArguments));
|
||||
asm.emitLoadClosureElement(functionTypeArgumentsClosureElement);
|
||||
asm.emitPopLocal(locals.functionTypeArgsVarIndexInFrame);
|
||||
}
|
||||
|
||||
@@ -2802,7 +2788,7 @@ class BytecodeGenerator extends RecursiveVisitor {
|
||||
);
|
||||
closures.add(closure);
|
||||
|
||||
final int closureFunctionIndex = cp.addClosureFunction(closureIndex);
|
||||
cp.addClosureFunction(closureIndex);
|
||||
|
||||
_recordSourcePosition(function.fileOffset, SourcePositions.syntheticFlag);
|
||||
_genPrologue(node, function);
|
||||
@@ -2856,7 +2842,7 @@ class BytecodeGenerator extends RecursiveVisitor {
|
||||
|
||||
_popAssemblerState();
|
||||
|
||||
return closureFunctionIndex;
|
||||
return closureIndex;
|
||||
}
|
||||
|
||||
ClosureDeclaration getClosureDeclaration(
|
||||
@@ -2972,40 +2958,59 @@ class BytecodeGenerator extends RecursiveVisitor {
|
||||
|
||||
void _genAllocateClosureInstance(
|
||||
TreeNode node,
|
||||
int closureFunctionIndex,
|
||||
int closureIndex,
|
||||
FunctionNode function,
|
||||
) {
|
||||
asm.emitPushConstant(closureFunctionIndex);
|
||||
asm.emitPush(locals.contextVarIndexInFrame);
|
||||
_genPushInstantiatorTypeArguments();
|
||||
asm.emitAllocateClosure();
|
||||
final bool hasDelayedTypeArguments = function.typeParameters.isNotEmpty;
|
||||
final bool hasInstantiatorTypeArguments = instantiatorTypeArguments != null;
|
||||
final bool hasFunctionTypeArguments = locals.hasFunctionTypeArgsVar;
|
||||
final numElements =
|
||||
(hasDelayedTypeArguments ? 1 : 0) +
|
||||
(hasInstantiatorTypeArguments ? 1 : 0) +
|
||||
(hasFunctionTypeArguments ? 1 : 0) + /* context */
|
||||
1;
|
||||
|
||||
final bool storeFunctionTAV = locals.hasFunctionTypeArgsVar;
|
||||
final bool setEmptyDelayedTAV = function.typeParameters.isNotEmpty;
|
||||
asm.emitAllocateClosure(
|
||||
cp.addAllocateClosure(
|
||||
closureIndex,
|
||||
numElements,
|
||||
hasDelayedTypeArguments: hasDelayedTypeArguments,
|
||||
hasInstantiatorTypeArguments: hasInstantiatorTypeArguments,
|
||||
hasFunctionTypeArguments: hasFunctionTypeArguments,
|
||||
),
|
||||
);
|
||||
|
||||
if (storeFunctionTAV || setEmptyDelayedTAV) {
|
||||
final int temp = locals.tempIndexInFrame(node);
|
||||
asm.emitStoreLocal(temp);
|
||||
final int temp = locals.tempIndexInFrame(node);
|
||||
asm.emitStoreLocal(temp);
|
||||
|
||||
if (storeFunctionTAV) {
|
||||
asm.emitPush(temp);
|
||||
_genPushFunctionTypeArguments();
|
||||
asm.emitStoreFieldTOS(
|
||||
cp.addInstanceField(closureFunctionTypeArguments),
|
||||
);
|
||||
}
|
||||
|
||||
if (setEmptyDelayedTAV) {
|
||||
asm.emitPush(temp);
|
||||
asm.emitPushConstant(cp.addEmptyTypeArguments());
|
||||
asm.emitStoreFieldTOS(cp.addInstanceField(closureDelayedTypeArguments));
|
||||
}
|
||||
var elementIndex = 0;
|
||||
if (hasDelayedTypeArguments) {
|
||||
asm.emitPush(temp);
|
||||
asm.emitPushConstant(cp.addEmptyTypeArguments());
|
||||
asm.emitStoreClosureElement(elementIndex++);
|
||||
}
|
||||
|
||||
if (hasInstantiatorTypeArguments) {
|
||||
asm.emitPush(temp);
|
||||
_genPushInstantiatorTypeArguments();
|
||||
asm.emitStoreClosureElement(elementIndex++);
|
||||
}
|
||||
|
||||
if (hasFunctionTypeArguments) {
|
||||
asm.emitPush(temp);
|
||||
_genPushFunctionTypeArguments();
|
||||
asm.emitStoreClosureElement(elementIndex++);
|
||||
}
|
||||
|
||||
asm.emitPush(temp);
|
||||
asm.emitPush(locals.contextVarIndexInFrame);
|
||||
asm.emitStoreClosureElement(elementIndex++);
|
||||
assert(elementIndex == numElements);
|
||||
}
|
||||
|
||||
void _genClosure(LocalFunction node, String name, FunctionNode function) {
|
||||
final int closureFunctionIndex = _genClosureBytecode(node, name, function);
|
||||
_genAllocateClosureInstance(node, closureFunctionIndex, function);
|
||||
final int closureIndex = _genClosureBytecode(node, name, function);
|
||||
_genAllocateClosureInstance(node, closureIndex, function);
|
||||
}
|
||||
|
||||
void _allocateContextIfNeeded() {
|
||||
@@ -4654,7 +4659,7 @@ class BytecodeGenerator extends RecursiveVisitor {
|
||||
// 1. Restore context from closure var.
|
||||
// This context has a context level at frame entry.
|
||||
asm.emitPush(locals.closureVarIndexInFrame);
|
||||
asm.emitLoadFieldTOS(cp.addInstanceField(closureContext));
|
||||
asm.emitLoadClosureElement(contextClosureElement);
|
||||
asm.emitPopLocal(locals.contextVarIndexInFrame);
|
||||
|
||||
// 2. Restore context from captured :saved_try_context_var${depth}.
|
||||
|
||||
@@ -33,6 +33,7 @@ enum ConstantTag {
|
||||
kExternalCall,
|
||||
kFfiCall,
|
||||
kDeferredLibraryPrefix,
|
||||
kAllocateClosure,
|
||||
}
|
||||
|
||||
String constantTagToString(ConstantTag tag) =>
|
||||
@@ -93,6 +94,8 @@ abstract class ConstantPoolEntry {
|
||||
return new ConstantFfiCall.read(reader);
|
||||
case ConstantTag.kDeferredLibraryPrefix:
|
||||
return new ConstantDeferredLibraryPrefix.read(reader);
|
||||
case ConstantTag.kAllocateClosure:
|
||||
return new ConstantAllocateClosure.read(reader);
|
||||
}
|
||||
throw 'Unexpected constant tag $tag';
|
||||
}
|
||||
@@ -607,6 +610,52 @@ class ConstantDeferredLibraryPrefix extends ConstantPoolEntry {
|
||||
this.targetLibrary == other.targetLibrary;
|
||||
}
|
||||
|
||||
class ConstantAllocateClosure extends ConstantPoolEntry {
|
||||
static const int hasDelayedTypeArguments = 1 << 0;
|
||||
static const int hasInstantiatorTypeArguments = 1 << 1;
|
||||
static const int hasFunctionTypeArguments = 1 << 2;
|
||||
|
||||
final int closureIndex;
|
||||
final int numElements;
|
||||
final int flags;
|
||||
|
||||
ConstantAllocateClosure(this.closureIndex, this.numElements, this.flags);
|
||||
|
||||
@override
|
||||
ConstantTag get tag => ConstantTag.kAllocateClosure;
|
||||
|
||||
// 2 slots: function, encoded length and flags.
|
||||
@override
|
||||
int get numReservedEntries => 1;
|
||||
|
||||
@override
|
||||
void writeValue(BufferedWriter writer) {
|
||||
writer.writePackedUInt30(closureIndex);
|
||||
writer.writePackedUInt30(numElements);
|
||||
writer.writePackedUInt30(flags);
|
||||
}
|
||||
|
||||
ConstantAllocateClosure.read(BufferedReader reader)
|
||||
: closureIndex = reader.readPackedUInt30(),
|
||||
numElements = reader.readPackedUInt30(),
|
||||
flags = reader.readPackedUInt30();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AllocateClosure $closureIndex, num-elements: $numElements, flags: $flags';
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => closureIndex.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(other) =>
|
||||
other is ConstantAllocateClosure &&
|
||||
this.closureIndex == other.closureIndex &&
|
||||
this.flags == other.flags &&
|
||||
this.numElements == other.numElements;
|
||||
}
|
||||
|
||||
/// Reserved constant pool entry.
|
||||
class _ReservedConstantPoolEntry extends ConstantPoolEntry {
|
||||
const _ReservedConstantPoolEntry();
|
||||
@@ -788,6 +837,28 @@ class ConstantPool {
|
||||
),
|
||||
);
|
||||
|
||||
int addAllocateClosure(
|
||||
int closureIndex,
|
||||
int numElements, {
|
||||
required bool hasDelayedTypeArguments,
|
||||
required bool hasInstantiatorTypeArguments,
|
||||
required bool hasFunctionTypeArguments,
|
||||
}) => _add(
|
||||
ConstantAllocateClosure(
|
||||
closureIndex,
|
||||
numElements,
|
||||
(hasDelayedTypeArguments
|
||||
? ConstantAllocateClosure.hasDelayedTypeArguments
|
||||
: 0) |
|
||||
(hasInstantiatorTypeArguments
|
||||
? ConstantAllocateClosure.hasInstantiatorTypeArguments
|
||||
: 0) |
|
||||
(hasFunctionTypeArguments
|
||||
? ConstantAllocateClosure.hasFunctionTypeArguments
|
||||
: 0),
|
||||
),
|
||||
);
|
||||
|
||||
int _add(ConstantPoolEntry entry) {
|
||||
int? index = _canonicalizationCache[entry];
|
||||
if (index == null) {
|
||||
|
||||
@@ -7,7 +7,7 @@ library;
|
||||
|
||||
/// Version of bytecode format
|
||||
/// (should match runtime/vm/constants_kbc.h).
|
||||
const int bytecodeFormatVersion = 1;
|
||||
const int bytecodeFormatVersion = 2;
|
||||
|
||||
enum Opcode {
|
||||
kTrap,
|
||||
@@ -34,8 +34,14 @@ enum Opcode {
|
||||
kAllocate_Wide,
|
||||
kAllocateT,
|
||||
kCreateArrayTOS,
|
||||
|
||||
// Closure allocation and access.
|
||||
kAllocateClosure,
|
||||
kUnused03,
|
||||
kAllocateClosure_Wide,
|
||||
kLoadClosureElement,
|
||||
kLoadClosureElement_Wide,
|
||||
kStoreClosureElement,
|
||||
kStoreClosureElement_Wide,
|
||||
|
||||
// Context allocation and access.
|
||||
kAllocateContext,
|
||||
@@ -677,9 +683,19 @@ const Map<Opcode, Format> BytecodeFormats = const {
|
||||
Operand.imm,
|
||||
Operand.none,
|
||||
]),
|
||||
Opcode.kAllocateClosure: const Format(Encoding.k0, const [
|
||||
Opcode.kAllocateClosure: const Format(Encoding.kD, const [
|
||||
Operand.lit,
|
||||
Operand.none,
|
||||
Operand.none,
|
||||
]),
|
||||
Opcode.kLoadClosureElement: const Format(Encoding.kD, const [
|
||||
Operand.imm,
|
||||
Operand.none,
|
||||
Operand.none,
|
||||
]),
|
||||
Opcode.kStoreClosureElement: const Format(Encoding.kD, const [
|
||||
Operand.imm,
|
||||
Operand.none,
|
||||
Operand.none,
|
||||
]),
|
||||
Opcode.kUncheckedClosureCall: const Format(Encoding.kDF, const [
|
||||
|
||||
@@ -1076,37 +1076,19 @@ class _Allocator extends RecursiveVisitor {
|
||||
_visitFunction(node);
|
||||
}
|
||||
|
||||
// A temporary is only needed for function declarations or expressions when:
|
||||
// * There are function type arguments to capture.
|
||||
// * The function is generic and so the delayed type arguments field of the
|
||||
// closure must be empty-initialized, not null-initialized.
|
||||
bool _closureAllocationNeedsTemp(FunctionNode function) =>
|
||||
_currentFrame.functionTypeArgsVar != null ||
|
||||
function.typeParameters.isNotEmpty;
|
||||
|
||||
@override
|
||||
void visitFunctionDeclaration(FunctionDeclaration node) {
|
||||
_allocateVariable(node.variable);
|
||||
final needsTemp = _closureAllocationNeedsTemp(node.function);
|
||||
if (needsTemp) {
|
||||
_allocateTemp(node);
|
||||
}
|
||||
_allocateTemp(node);
|
||||
_visitFunction(node);
|
||||
if (needsTemp) {
|
||||
_freeTemp(node);
|
||||
}
|
||||
_freeTemp(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitFunctionExpression(FunctionExpression node) {
|
||||
final needsTemp = _closureAllocationNeedsTemp(node.function);
|
||||
if (needsTemp) {
|
||||
_allocateTemp(node);
|
||||
}
|
||||
_allocateTemp(node);
|
||||
_visitFunction(node);
|
||||
if (needsTemp) {
|
||||
_freeTemp(node);
|
||||
}
|
||||
_freeTemp(node);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -9,52 +9,53 @@ Class '', script = 'DART_SDK/pkg/dart2bytecode/testcases/async.dart'
|
||||
Field 'asyncInFieldInitializer', type = FunctionType (dart:async::Future < dart:core::int >) -> dart:async::Future < Null >, getter = 'get:asyncInFieldInitializer', reflectable, static, is-late, has-initializer
|
||||
initializer
|
||||
Bytecode {
|
||||
Entry 2
|
||||
Entry 3
|
||||
CheckStack 0
|
||||
PushConstant CP#0
|
||||
AllocateClosure CP#15
|
||||
StoreLocal r2
|
||||
Push r2
|
||||
Push r0
|
||||
PushNull
|
||||
AllocateClosure
|
||||
StoreClosureElement 0
|
||||
ReturnTOS
|
||||
}
|
||||
ConstantPool {
|
||||
[0] = ClosureFunction 0
|
||||
[1] = InstanceField dart:core::_Closure::_context (field)
|
||||
[2] = Reserved
|
||||
[3] = Type dart:async::Future < dart:core::int >
|
||||
[4] = ObjectRef 'x'
|
||||
[5] = SubtypeTestCache
|
||||
[6] = ObjectRef < Null >
|
||||
[7] = DirectCall 'dart:async::_SuspendState::_initAsync', ArgDesc num-args 0, num-type-args 1, names []
|
||||
[8] = Reserved
|
||||
[9] = Type dynamic
|
||||
[10] = DirectCall 'dart:async::_SuspendState::_await', ArgDesc num-args 2, num-type-args 0, names []
|
||||
[1] = Type dart:async::Future < dart:core::int >
|
||||
[2] = ObjectRef 'x'
|
||||
[3] = SubtypeTestCache
|
||||
[4] = ObjectRef < Null >
|
||||
[5] = DirectCall 'dart:async::_SuspendState::_initAsync', ArgDesc num-args 0, num-type-args 1, names []
|
||||
[6] = Reserved
|
||||
[7] = Type dynamic
|
||||
[8] = DirectCall 'dart:async::_SuspendState::_await', ArgDesc num-args 2, num-type-args 0, names []
|
||||
[9] = Reserved
|
||||
[10] = DirectCall 'dart:async::_SuspendState::_returnAsync', ArgDesc num-args 2, num-type-args 0, names []
|
||||
[11] = Reserved
|
||||
[12] = DirectCall 'dart:async::_SuspendState::_returnAsync', ArgDesc num-args 2, num-type-args 0, names []
|
||||
[12] = DirectCall 'dart:async::_SuspendState::_handleException', ArgDesc num-args 3, num-type-args 0, names []
|
||||
[13] = Reserved
|
||||
[14] = DirectCall 'dart:async::_SuspendState::_handleException', ArgDesc num-args 3, num-type-args 0, names []
|
||||
[15] = Reserved
|
||||
[16] = EndClosureFunctionScope
|
||||
[14] = EndClosureFunctionScope
|
||||
[15] = AllocateClosure 0, num-elements: 1, flags: 0
|
||||
[16] = Reserved
|
||||
}
|
||||
Closure DART_SDK/pkg/dart2bytecode/testcases/async.dart::asyncInFieldInitializer (field)::'<anonymous closure>' async (dart:async::Future < dart:core::int > x) -> dart:async::Future < Null >
|
||||
ClosureCode {
|
||||
EntrySuspendable 2, 0, 0
|
||||
Frame 5
|
||||
Push r1
|
||||
LoadFieldTOS CP#1
|
||||
LoadClosureElement 0
|
||||
PopLocal r3
|
||||
CheckStack 0
|
||||
JumpIfUnchecked L1
|
||||
Push r2
|
||||
PushConstant CP#3
|
||||
PushConstant CP#1
|
||||
PushNull
|
||||
PushNull
|
||||
PushConstant CP#4
|
||||
AssertAssignable 0, CP#5
|
||||
PushConstant CP#2
|
||||
AssertAssignable 0, CP#3
|
||||
Drop1
|
||||
L1:
|
||||
PushConstant CP#6
|
||||
DirectCall CP#7, 1
|
||||
PushConstant CP#4
|
||||
DirectCall CP#5, 1
|
||||
PopLocal r0
|
||||
Try #0 start:
|
||||
Push r2
|
||||
@@ -62,7 +63,7 @@ Try #0 start:
|
||||
Suspend L2
|
||||
Push r0
|
||||
Push r6
|
||||
DirectCall CP#10, 2
|
||||
DirectCall CP#8, 2
|
||||
ReturnTOS
|
||||
L2:
|
||||
Drop1
|
||||
@@ -72,7 +73,7 @@ L2:
|
||||
Push r5
|
||||
PushNull
|
||||
PopLocal r0
|
||||
DirectCall CP#12, 2
|
||||
DirectCall CP#10, 2
|
||||
ReturnTOS
|
||||
Try #0 end:
|
||||
Try #0 handler:
|
||||
@@ -84,7 +85,7 @@ Try #0 handler:
|
||||
Push r0
|
||||
MoveSpecial stackTrace, r0
|
||||
Push r0
|
||||
DirectCall CP#14, 3
|
||||
DirectCall CP#12, 3
|
||||
ReturnTOS
|
||||
L3:
|
||||
MoveSpecial exception, r0
|
||||
@@ -529,7 +530,7 @@ Function 'closure', static, reflectable, debuggable
|
||||
return-type dynamic
|
||||
|
||||
Bytecode {
|
||||
Entry 3
|
||||
Entry 4
|
||||
CheckStack 0
|
||||
AllocateContext 0, 2
|
||||
PopLocal r0
|
||||
@@ -539,43 +540,44 @@ Bytecode {
|
||||
Push r0
|
||||
PushInt 3
|
||||
StoreContextVar 0, 1
|
||||
PushConstant CP#0
|
||||
AllocateClosure CP#15
|
||||
StoreLocal r3
|
||||
Push r3
|
||||
Push r0
|
||||
PushNull
|
||||
AllocateClosure
|
||||
StoreClosureElement 0
|
||||
PopLocal r2
|
||||
Push r2
|
||||
ReturnTOS
|
||||
}
|
||||
ConstantPool {
|
||||
[0] = ClosureFunction 0
|
||||
[1] = InstanceField dart:core::_Closure::_context (field)
|
||||
[2] = Reserved
|
||||
[3] = ObjectRef < dart:core::int >
|
||||
[4] = DirectCall 'dart:async::_SuspendState::_initAsync', ArgDesc num-args 0, num-type-args 1, names []
|
||||
[5] = Reserved
|
||||
[6] = Type dynamic
|
||||
[7] = DirectCall 'dart:async::_SuspendState::_await', ArgDesc num-args 2, num-type-args 0, names []
|
||||
[8] = Reserved
|
||||
[9] = ObjectRef 'fin'
|
||||
[10] = DirectCall 'dart:core::print', ArgDesc num-args 1, num-type-args 0, names []
|
||||
[1] = ObjectRef < dart:core::int >
|
||||
[2] = DirectCall 'dart:async::_SuspendState::_initAsync', ArgDesc num-args 0, num-type-args 1, names []
|
||||
[3] = Reserved
|
||||
[4] = Type dynamic
|
||||
[5] = DirectCall 'dart:async::_SuspendState::_await', ArgDesc num-args 2, num-type-args 0, names []
|
||||
[6] = Reserved
|
||||
[7] = ObjectRef 'fin'
|
||||
[8] = DirectCall 'dart:core::print', ArgDesc num-args 1, num-type-args 0, names []
|
||||
[9] = Reserved
|
||||
[10] = DirectCall 'dart:async::_SuspendState::_returnAsync', ArgDesc num-args 2, num-type-args 0, names []
|
||||
[11] = Reserved
|
||||
[12] = DirectCall 'dart:async::_SuspendState::_returnAsync', ArgDesc num-args 2, num-type-args 0, names []
|
||||
[12] = DirectCall 'dart:async::_SuspendState::_handleException', ArgDesc num-args 3, num-type-args 0, names []
|
||||
[13] = Reserved
|
||||
[14] = DirectCall 'dart:async::_SuspendState::_handleException', ArgDesc num-args 3, num-type-args 0, names []
|
||||
[15] = Reserved
|
||||
[16] = EndClosureFunctionScope
|
||||
[14] = EndClosureFunctionScope
|
||||
[15] = AllocateClosure 0, num-elements: 1, flags: 0
|
||||
[16] = Reserved
|
||||
}
|
||||
Closure DART_SDK/pkg/dart2bytecode/testcases/async.dart::closure::'nested' async () -> dart:async::Future < dart:core::int >
|
||||
ClosureCode {
|
||||
EntrySuspendable 1, 0, 0
|
||||
Frame 8
|
||||
Push r1
|
||||
LoadFieldTOS CP#1
|
||||
LoadClosureElement 0
|
||||
PopLocal r2
|
||||
CheckStack 0
|
||||
PushConstant CP#3
|
||||
DirectCall CP#4, 1
|
||||
PushConstant CP#1
|
||||
DirectCall CP#2, 1
|
||||
PopLocal r0
|
||||
Try #0 start:
|
||||
PushInt 4
|
||||
@@ -592,7 +594,7 @@ Try #1 start:
|
||||
Suspend L1
|
||||
Push r0
|
||||
Push r8
|
||||
DirectCall CP#7, 2
|
||||
DirectCall CP#5, 2
|
||||
ReturnTOS
|
||||
L1:
|
||||
PopLocal r5
|
||||
@@ -609,8 +611,8 @@ Try #1 handler:
|
||||
PopLocal r2
|
||||
MoveSpecial exception, r6
|
||||
MoveSpecial stackTrace, r7
|
||||
PushConstant CP#9
|
||||
DirectCall CP#10, 1
|
||||
PushConstant CP#7
|
||||
DirectCall CP#8, 1
|
||||
Drop1
|
||||
Push r6
|
||||
Push r7
|
||||
@@ -618,8 +620,8 @@ Try #1 handler:
|
||||
L2:
|
||||
Push r6
|
||||
PopLocal r2
|
||||
PushConstant CP#9
|
||||
DirectCall CP#10, 1
|
||||
PushConstant CP#7
|
||||
DirectCall CP#8, 1
|
||||
Drop1
|
||||
Push r4
|
||||
PopLocal r4
|
||||
@@ -627,7 +629,7 @@ L2:
|
||||
Push r4
|
||||
PushNull
|
||||
PopLocal r0
|
||||
DirectCall CP#12, 2
|
||||
DirectCall CP#10, 2
|
||||
ReturnTOS
|
||||
Try #0 end:
|
||||
Try #0 handler:
|
||||
@@ -639,7 +641,7 @@ Try #0 handler:
|
||||
Push r0
|
||||
MoveSpecial stackTrace, r0
|
||||
Push r0
|
||||
DirectCall CP#14, 3
|
||||
DirectCall CP#12, 3
|
||||
ReturnTOS
|
||||
L3:
|
||||
MoveSpecial exception, r0
|
||||
|
||||
@@ -19,10 +19,11 @@ Bytecode {
|
||||
Push r0
|
||||
PushInt 5
|
||||
StoreContextVar 0, 0
|
||||
PushConstant CP#0
|
||||
AllocateClosure CP#5
|
||||
StoreLocal r3
|
||||
Push r3
|
||||
Push r0
|
||||
PushNull
|
||||
AllocateClosure
|
||||
StoreClosureElement 0
|
||||
PopLocal r2
|
||||
Push r2
|
||||
StoreLocal r3
|
||||
@@ -36,28 +37,28 @@ Bytecode {
|
||||
}
|
||||
ConstantPool {
|
||||
[0] = ClosureFunction 0
|
||||
[1] = InstanceField dart:core::_Closure::_context (field)
|
||||
[2] = Reserved
|
||||
[3] = Type dart:core::int
|
||||
[4] = ObjectRef 'y'
|
||||
[5] = SubtypeTestCache
|
||||
[6] = EndClosureFunctionScope
|
||||
[1] = Type dart:core::int
|
||||
[2] = ObjectRef 'y'
|
||||
[3] = SubtypeTestCache
|
||||
[4] = EndClosureFunctionScope
|
||||
[5] = AllocateClosure 0, num-elements: 1, flags: 0
|
||||
[6] = Reserved
|
||||
[7] = ObjectRef ArgDesc num-args 2, num-type-args 0, names []
|
||||
}
|
||||
Closure DART_SDK/pkg/dart2bytecode/testcases/closures.dart::simpleClosure::'<anonymous closure>' (dart:core::int y) -> Null
|
||||
ClosureCode {
|
||||
Entry 2
|
||||
Push FP[-6]
|
||||
LoadFieldTOS CP#1
|
||||
LoadClosureElement 0
|
||||
PopLocal r0
|
||||
CheckStack 0
|
||||
JumpIfUnchecked L1
|
||||
Push FP[-5]
|
||||
PushConstant CP#3
|
||||
PushConstant CP#1
|
||||
PushNull
|
||||
PushNull
|
||||
PushConstant CP#4
|
||||
AssertAssignable 1, CP#5
|
||||
PushConstant CP#2
|
||||
AssertAssignable 1, CP#3
|
||||
Drop1
|
||||
L1:
|
||||
Push r0
|
||||
@@ -188,51 +189,47 @@ Function 'testPartialInstantiation', static, reflectable, debuggable
|
||||
Bytecode {
|
||||
Entry 5
|
||||
CheckStack 0
|
||||
PushConstant CP#0
|
||||
Push r0
|
||||
PushNull
|
||||
AllocateClosure
|
||||
AllocateClosure CP#8
|
||||
StoreLocal r3
|
||||
Push r3
|
||||
PushConstant CP#5
|
||||
StoreFieldTOS CP#3
|
||||
PushConstant CP#1
|
||||
StoreClosureElement 0
|
||||
Push r3
|
||||
Push r0
|
||||
StoreClosureElement 1
|
||||
PopLocal r2
|
||||
Push r2
|
||||
PushConstant CP#14
|
||||
DirectCall CP#15, 2
|
||||
PushConstant CP#10
|
||||
DirectCall CP#11, 2
|
||||
PopLocal r4
|
||||
Push r4
|
||||
ReturnTOS
|
||||
}
|
||||
ConstantPool {
|
||||
[0] = ClosureFunction 0
|
||||
[1] = InstanceField dart:core::_Closure::_context (field)
|
||||
[2] = Reserved
|
||||
[3] = InstanceField dart:core::_Closure::_delayed_type_arguments (field)
|
||||
[4] = Reserved
|
||||
[5] = EmptyTypeArguments
|
||||
[6] = InstanceField dart:core::_Closure::_function_type_arguments (field)
|
||||
[7] = Reserved
|
||||
[8] = DirectCall 'dart:_internal::_prependTypeArguments', ArgDesc num-args 4, num-type-args 0, names []
|
||||
[1] = EmptyTypeArguments
|
||||
[2] = DirectCall 'dart:_internal::_prependTypeArguments', ArgDesc num-args 4, num-type-args 0, names []
|
||||
[3] = Reserved
|
||||
[4] = Type DART_SDK/pkg/dart2bytecode/testcases/closures.dart::testPartialInstantiation::Closure/0::TypeParam/0
|
||||
[5] = ObjectRef 't'
|
||||
[6] = SubtypeTestCache
|
||||
[7] = EndClosureFunctionScope
|
||||
[8] = AllocateClosure 0, num-elements: 2, flags: 1
|
||||
[9] = Reserved
|
||||
[10] = Type DART_SDK/pkg/dart2bytecode/testcases/closures.dart::testPartialInstantiation::Closure/0::TypeParam/0
|
||||
[11] = ObjectRef 't'
|
||||
[12] = SubtypeTestCache
|
||||
[13] = EndClosureFunctionScope
|
||||
[14] = ObjectRef < dart:core::int >
|
||||
[15] = DirectCall 'dart:_internal::_instantiateClosure', ArgDesc num-args 2, num-type-args 0, names []
|
||||
[16] = Reserved
|
||||
[10] = ObjectRef < dart:core::int >
|
||||
[11] = DirectCall 'dart:_internal::_instantiateClosure', ArgDesc num-args 2, num-type-args 0, names []
|
||||
[12] = Reserved
|
||||
}
|
||||
Closure DART_SDK/pkg/dart2bytecode/testcases/closures.dart::testPartialInstantiation::'foo' type-params <'T' extends dart:core::Object? (default dynamic)> (DART_SDK/pkg/dart2bytecode/testcases/closures.dart::testPartialInstantiation::Closure/0::TypeParam/0 t) -> void
|
||||
ClosureCode {
|
||||
Entry 3
|
||||
Push FP[-6]
|
||||
LoadFieldTOS CP#1
|
||||
LoadClosureElement 1
|
||||
PopLocal r1
|
||||
Push FP[-6]
|
||||
LoadFieldTOS CP#3
|
||||
LoadClosureElement 0
|
||||
StoreLocal r0
|
||||
PushConstant CP#5
|
||||
PushConstant CP#1
|
||||
JumpIfEqStrict L1
|
||||
CheckFunctionTypeArgs 0, r2
|
||||
Jump L2
|
||||
@@ -242,18 +239,18 @@ L2:
|
||||
CheckStack 0
|
||||
Push r0
|
||||
Push FP[-6]
|
||||
LoadFieldTOS CP#6
|
||||
LoadClosureElement 1
|
||||
PushInt 0
|
||||
PushInt 1
|
||||
DirectCall CP#8, 4
|
||||
DirectCall CP#2, 4
|
||||
PopLocal r0
|
||||
JumpIfUnchecked L3
|
||||
Push FP[-5]
|
||||
PushConstant CP#10
|
||||
PushConstant CP#4
|
||||
PushNull
|
||||
Push r0
|
||||
PushConstant CP#11
|
||||
AssertAssignable 0, CP#12
|
||||
PushConstant CP#5
|
||||
AssertAssignable 0, CP#6
|
||||
Drop1
|
||||
L3:
|
||||
PushNull
|
||||
@@ -488,69 +485,72 @@ Bytecode {
|
||||
Push r1
|
||||
Push FP[-5]
|
||||
StoreContextVar 0, 0
|
||||
PushConstant CP#0
|
||||
Push r1
|
||||
Push FP[-5]
|
||||
LoadTypeArgumentsField CP#13
|
||||
AllocateClosure
|
||||
AllocateClosure CP#34
|
||||
StoreLocal r4
|
||||
Push r4
|
||||
Push r0
|
||||
StoreFieldTOS CP#6
|
||||
PushConstant CP#1
|
||||
StoreClosureElement 0
|
||||
Push r4
|
||||
PushConstant CP#5
|
||||
StoreFieldTOS CP#3
|
||||
Push FP[-5]
|
||||
LoadTypeArgumentsField CP#7
|
||||
StoreClosureElement 1
|
||||
Push r4
|
||||
Push r0
|
||||
StoreClosureElement 2
|
||||
Push r4
|
||||
Push r1
|
||||
StoreClosureElement 3
|
||||
PopLocal r3
|
||||
PushConstant CP#36
|
||||
Push r3
|
||||
Push r3
|
||||
UncheckedClosureCall CP#33, 2
|
||||
UncheckedClosureCall CP#31, 2
|
||||
Drop1
|
||||
PushConstant CP#37
|
||||
Push r3
|
||||
Push r3
|
||||
UncheckedClosureCall CP#33, 2
|
||||
UncheckedClosureCall CP#31, 2
|
||||
Drop1
|
||||
PushNull
|
||||
ReturnTOS
|
||||
}
|
||||
ConstantPool {
|
||||
[0] = ClosureFunction 0
|
||||
[1] = InstanceField dart:core::_Closure::_context (field)
|
||||
[2] = Reserved
|
||||
[3] = InstanceField dart:core::_Closure::_delayed_type_arguments (field)
|
||||
[4] = Reserved
|
||||
[5] = EmptyTypeArguments
|
||||
[6] = InstanceField dart:core::_Closure::_function_type_arguments (field)
|
||||
[7] = Reserved
|
||||
[8] = DirectCall 'dart:_internal::_prependTypeArguments', ArgDesc num-args 4, num-type-args 0, names []
|
||||
[9] = Reserved
|
||||
[10] = ClosureFunction 1
|
||||
[11] = ClosureFunction 2
|
||||
[12] = ObjectRef < dart:core::Type >
|
||||
[13] = TypeArgumentsField DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A
|
||||
[14] = Type DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::TypeParam/0
|
||||
[15] = Type DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::TypeParam/1
|
||||
[16] = Type DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::TypeParam/0
|
||||
[17] = Type DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::TypeParam/1
|
||||
[18] = Type DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::Closure/0::TypeParam/0
|
||||
[19] = Type DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::Closure/0::TypeParam/1
|
||||
[20] = Type DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::Closure/1::TypeParam/0
|
||||
[21] = Type DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::Closure/1::TypeParam/1
|
||||
[22] = DirectCall 'dart:core::_GrowableList::_literal8 (constructor)', ArgDesc num-args 9, num-type-args 0, names []
|
||||
[23] = Reserved
|
||||
[24] = DirectCall 'dart:core::print', ArgDesc num-args 1, num-type-args 0, names []
|
||||
[1] = EmptyTypeArguments
|
||||
[2] = DirectCall 'dart:_internal::_prependTypeArguments', ArgDesc num-args 4, num-type-args 0, names []
|
||||
[3] = Reserved
|
||||
[4] = ClosureFunction 1
|
||||
[5] = ClosureFunction 2
|
||||
[6] = ObjectRef < dart:core::Type >
|
||||
[7] = TypeArgumentsField DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A
|
||||
[8] = Type DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::TypeParam/0
|
||||
[9] = Type DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::TypeParam/1
|
||||
[10] = Type DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::TypeParam/0
|
||||
[11] = Type DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::TypeParam/1
|
||||
[12] = Type DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::Closure/0::TypeParam/0
|
||||
[13] = Type DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::Closure/0::TypeParam/1
|
||||
[14] = Type DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::Closure/1::TypeParam/0
|
||||
[15] = Type DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::Closure/1::TypeParam/1
|
||||
[16] = DirectCall 'dart:core::_GrowableList::_literal8 (constructor)', ArgDesc num-args 9, num-type-args 0, names []
|
||||
[17] = Reserved
|
||||
[18] = DirectCall 'dart:core::print', ArgDesc num-args 1, num-type-args 0, names []
|
||||
[19] = Reserved
|
||||
[20] = ObjectRef < DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::TypeParam/0, DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::TypeParam/1, DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::TypeParam/0, DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::TypeParam/1, DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::Closure/0::TypeParam/0, DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::Closure/0::TypeParam/1, DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::Closure/1::TypeParam/0, DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::Closure/1::TypeParam/1 >
|
||||
[21] = DirectCall 'DART_SDK/pkg/dart2bytecode/testcases/closures.dart::callWithArgs', ArgDesc num-args 0, num-type-args 8, names []
|
||||
[22] = Reserved
|
||||
[23] = EndClosureFunctionScope
|
||||
[24] = AllocateClosure 2, num-elements: 3, flags: 6
|
||||
[25] = Reserved
|
||||
[26] = ObjectRef < DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::TypeParam/0, DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::TypeParam/1, DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::TypeParam/0, DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::TypeParam/1, DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::Closure/0::TypeParam/0, DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::Closure/0::TypeParam/1, DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::Closure/1::TypeParam/0, DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::Closure/1::TypeParam/1 >
|
||||
[27] = DirectCall 'DART_SDK/pkg/dart2bytecode/testcases/closures.dart::callWithArgs', ArgDesc num-args 0, num-type-args 8, names []
|
||||
[28] = Reserved
|
||||
[29] = EndClosureFunctionScope
|
||||
[30] = ObjectRef ArgDesc num-args 1, num-type-args 0, names []
|
||||
[31] = EndClosureFunctionScope
|
||||
[32] = ObjectRef < DART_SDK/pkg/dart2bytecode/testcases/closures.dart::C7, DART_SDK/pkg/dart2bytecode/testcases/closures.dart::C8 >
|
||||
[33] = ObjectRef ArgDesc num-args 1, num-type-args 2, names []
|
||||
[34] = ObjectRef < dart:core::List < DART_SDK/pkg/dart2bytecode/testcases/closures.dart::C7 >, dart:core::List < DART_SDK/pkg/dart2bytecode/testcases/closures.dart::C8 > >
|
||||
[35] = EndClosureFunctionScope
|
||||
[26] = ObjectRef ArgDesc num-args 1, num-type-args 0, names []
|
||||
[27] = EndClosureFunctionScope
|
||||
[28] = AllocateClosure 1, num-elements: 4, flags: 7
|
||||
[29] = Reserved
|
||||
[30] = ObjectRef < DART_SDK/pkg/dart2bytecode/testcases/closures.dart::C7, DART_SDK/pkg/dart2bytecode/testcases/closures.dart::C8 >
|
||||
[31] = ObjectRef ArgDesc num-args 1, num-type-args 2, names []
|
||||
[32] = ObjectRef < dart:core::List < DART_SDK/pkg/dart2bytecode/testcases/closures.dart::C7 >, dart:core::List < DART_SDK/pkg/dart2bytecode/testcases/closures.dart::C8 > >
|
||||
[33] = EndClosureFunctionScope
|
||||
[34] = AllocateClosure 0, num-elements: 4, flags: 7
|
||||
[35] = Reserved
|
||||
[36] = ObjectRef < DART_SDK/pkg/dart2bytecode/testcases/closures.dart::C5, DART_SDK/pkg/dart2bytecode/testcases/closures.dart::C6 >
|
||||
[37] = ObjectRef < dart:core::List < DART_SDK/pkg/dart2bytecode/testcases/closures.dart::C5 >, dart:core::List < DART_SDK/pkg/dart2bytecode/testcases/closures.dart::C6 > >
|
||||
}
|
||||
@@ -558,12 +558,12 @@ Closure DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::'nested1' ty
|
||||
ClosureCode {
|
||||
Entry 5
|
||||
Push FP[-5]
|
||||
LoadFieldTOS CP#1
|
||||
LoadClosureElement 3
|
||||
PopLocal r1
|
||||
Push FP[-5]
|
||||
LoadFieldTOS CP#3
|
||||
LoadClosureElement 0
|
||||
StoreLocal r0
|
||||
PushConstant CP#5
|
||||
PushConstant CP#1
|
||||
JumpIfEqStrict L1
|
||||
CheckFunctionTypeArgs 0, r2
|
||||
Jump L2
|
||||
@@ -573,34 +573,37 @@ L2:
|
||||
CheckStack 0
|
||||
Push r0
|
||||
Push FP[-5]
|
||||
LoadFieldTOS CP#6
|
||||
LoadClosureElement 2
|
||||
PushInt 2
|
||||
PushInt 4
|
||||
DirectCall CP#8, 4
|
||||
DirectCall CP#2, 4
|
||||
PopLocal r0
|
||||
PushConstant CP#10
|
||||
Push r1
|
||||
Push r1
|
||||
LoadContextVar 0, 0
|
||||
LoadTypeArgumentsField CP#13
|
||||
AllocateClosure
|
||||
AllocateClosure CP#28
|
||||
StoreLocal r4
|
||||
Push r4
|
||||
Push r0
|
||||
StoreFieldTOS CP#6
|
||||
PushConstant CP#1
|
||||
StoreClosureElement 0
|
||||
Push r4
|
||||
PushConstant CP#5
|
||||
StoreFieldTOS CP#3
|
||||
Push r1
|
||||
LoadContextVar 0, 0
|
||||
LoadTypeArgumentsField CP#7
|
||||
StoreClosureElement 1
|
||||
Push r4
|
||||
Push r0
|
||||
StoreClosureElement 2
|
||||
Push r4
|
||||
Push r1
|
||||
StoreClosureElement 3
|
||||
PopLocal r3
|
||||
PushConstant CP#30
|
||||
Push r3
|
||||
Push r3
|
||||
UncheckedClosureCall CP#31, 2
|
||||
Drop1
|
||||
PushConstant CP#32
|
||||
Push r3
|
||||
Push r3
|
||||
UncheckedClosureCall CP#33, 2
|
||||
Drop1
|
||||
PushConstant CP#34
|
||||
Push r3
|
||||
Push r3
|
||||
UncheckedClosureCall CP#33, 2
|
||||
UncheckedClosureCall CP#31, 2
|
||||
Drop1
|
||||
PushNull
|
||||
ReturnTOS
|
||||
@@ -610,12 +613,12 @@ Closure DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::Closure/0::'
|
||||
ClosureCode {
|
||||
Entry 5
|
||||
Push FP[-5]
|
||||
LoadFieldTOS CP#1
|
||||
LoadClosureElement 3
|
||||
PopLocal r1
|
||||
Push FP[-5]
|
||||
LoadFieldTOS CP#3
|
||||
LoadClosureElement 0
|
||||
StoreLocal r0
|
||||
PushConstant CP#5
|
||||
PushConstant CP#1
|
||||
JumpIfEqStrict L1
|
||||
CheckFunctionTypeArgs 0, r2
|
||||
Jump L2
|
||||
@@ -625,26 +628,29 @@ L2:
|
||||
CheckStack 0
|
||||
Push r0
|
||||
Push FP[-5]
|
||||
LoadFieldTOS CP#6
|
||||
LoadClosureElement 2
|
||||
PushInt 4
|
||||
PushInt 6
|
||||
DirectCall CP#8, 4
|
||||
DirectCall CP#2, 4
|
||||
PopLocal r0
|
||||
PushConstant CP#11
|
||||
Push r1
|
||||
Push r1
|
||||
LoadContextVar 0, 0
|
||||
LoadTypeArgumentsField CP#13
|
||||
AllocateClosure
|
||||
AllocateClosure CP#24
|
||||
StoreLocal r4
|
||||
Push r4
|
||||
Push r1
|
||||
LoadContextVar 0, 0
|
||||
LoadTypeArgumentsField CP#7
|
||||
StoreClosureElement 0
|
||||
Push r4
|
||||
Push r0
|
||||
StoreFieldTOS CP#6
|
||||
StoreClosureElement 1
|
||||
Push r4
|
||||
Push r1
|
||||
StoreClosureElement 2
|
||||
PopLocal r3
|
||||
Push r3
|
||||
StoreLocal r4
|
||||
Push r4
|
||||
UncheckedClosureCall CP#30, 1
|
||||
UncheckedClosureCall CP#26, 1
|
||||
Drop1
|
||||
PushNull
|
||||
ReturnTOS
|
||||
@@ -654,50 +660,50 @@ Closure DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::Closure/1::'
|
||||
ClosureCode {
|
||||
Entry 3
|
||||
Push FP[-5]
|
||||
LoadFieldTOS CP#1
|
||||
LoadClosureElement 2
|
||||
PopLocal r1
|
||||
CheckStack 0
|
||||
Push FP[-5]
|
||||
LoadFieldTOS CP#6
|
||||
LoadClosureElement 1
|
||||
PopLocal r0
|
||||
PushConstant CP#12
|
||||
PushConstant CP#6
|
||||
Push r1
|
||||
LoadContextVar 0, 0
|
||||
LoadTypeArgumentsField CP#13
|
||||
LoadTypeArgumentsField CP#7
|
||||
PushNull
|
||||
InstantiateType CP#8
|
||||
Push r1
|
||||
LoadContextVar 0, 0
|
||||
LoadTypeArgumentsField CP#7
|
||||
PushNull
|
||||
InstantiateType CP#9
|
||||
PushNull
|
||||
Push r0
|
||||
InstantiateType CP#10
|
||||
PushNull
|
||||
Push r0
|
||||
InstantiateType CP#11
|
||||
PushNull
|
||||
Push r0
|
||||
InstantiateType CP#12
|
||||
PushNull
|
||||
Push r0
|
||||
InstantiateType CP#13
|
||||
PushNull
|
||||
Push r0
|
||||
InstantiateType CP#14
|
||||
Push r1
|
||||
LoadContextVar 0, 0
|
||||
LoadTypeArgumentsField CP#13
|
||||
PushNull
|
||||
Push r0
|
||||
InstantiateType CP#15
|
||||
PushNull
|
||||
Push r0
|
||||
InstantiateType CP#16
|
||||
PushNull
|
||||
Push r0
|
||||
InstantiateType CP#17
|
||||
PushNull
|
||||
Push r0
|
||||
InstantiateType CP#18
|
||||
PushNull
|
||||
Push r0
|
||||
InstantiateType CP#19
|
||||
PushNull
|
||||
Push r0
|
||||
InstantiateType CP#20
|
||||
PushNull
|
||||
Push r0
|
||||
InstantiateType CP#21
|
||||
DirectCall CP#22, 9
|
||||
DirectCall CP#24, 1
|
||||
DirectCall CP#16, 9
|
||||
DirectCall CP#18, 1
|
||||
Drop1
|
||||
Push r1
|
||||
LoadContextVar 0, 0
|
||||
LoadTypeArgumentsField CP#13
|
||||
LoadTypeArgumentsField CP#7
|
||||
Push r0
|
||||
InstantiateTypeArgumentsTOS 0, CP#26
|
||||
DirectCall CP#27, 1
|
||||
InstantiateTypeArgumentsTOS 0, CP#20
|
||||
DirectCall CP#21, 1
|
||||
Drop1
|
||||
PushNull
|
||||
ReturnTOS
|
||||
@@ -753,22 +759,23 @@ Bytecode {
|
||||
Push r0
|
||||
PushInt 3
|
||||
StoreContextVar 0, 2
|
||||
PushConstant CP#0
|
||||
AllocateClosure CP#14
|
||||
StoreLocal r4
|
||||
Push r4
|
||||
Push r0
|
||||
PushNull
|
||||
AllocateClosure
|
||||
StoreClosureElement 0
|
||||
PopLocal r3
|
||||
Push r3
|
||||
StoreLocal r4
|
||||
PushInt 10
|
||||
Push r4
|
||||
UncheckedClosureCall CP#14, 2
|
||||
UncheckedClosureCall CP#16, 2
|
||||
Drop1
|
||||
Push r3
|
||||
StoreLocal r4
|
||||
PushInt 11
|
||||
Push r4
|
||||
UncheckedClosureCall CP#14, 2
|
||||
UncheckedClosureCall CP#16, 2
|
||||
Drop1
|
||||
Push r2
|
||||
DirectCall CP#11, 1
|
||||
@@ -784,10 +791,11 @@ Bytecode {
|
||||
Push r0
|
||||
PushInt 42
|
||||
StoreContextVar 0, 3
|
||||
PushConstant CP#15
|
||||
AllocateClosure CP#21
|
||||
StoreLocal r3
|
||||
Push r3
|
||||
Push r0
|
||||
PushNull
|
||||
AllocateClosure
|
||||
StoreClosureElement 0
|
||||
PopLocal r2
|
||||
Push r2
|
||||
StoreLocal r3
|
||||
@@ -799,30 +807,34 @@ Bytecode {
|
||||
}
|
||||
ConstantPool {
|
||||
[0] = ClosureFunction 0
|
||||
[1] = InstanceField dart:core::_Closure::_context (field)
|
||||
[2] = Reserved
|
||||
[3] = Type dart:core::int
|
||||
[4] = ObjectRef 'y'
|
||||
[5] = SubtypeTestCache
|
||||
[6] = ClosureFunction 1
|
||||
[7] = InterfaceCall 'DART_SDK/pkg/dart2bytecode/testcases/closures.dart::B::get:foo', ArgDesc num-args 1, num-type-args 0, names []
|
||||
[8] = Reserved
|
||||
[9] = EndClosureFunctionScope
|
||||
[1] = Type dart:core::int
|
||||
[2] = ObjectRef 'y'
|
||||
[3] = SubtypeTestCache
|
||||
[4] = ClosureFunction 1
|
||||
[5] = InterfaceCall 'DART_SDK/pkg/dart2bytecode/testcases/closures.dart::B::get:foo', ArgDesc num-args 1, num-type-args 0, names []
|
||||
[6] = Reserved
|
||||
[7] = EndClosureFunctionScope
|
||||
[8] = AllocateClosure 1, num-elements: 1, flags: 0
|
||||
[9] = Reserved
|
||||
[10] = ObjectRef ArgDesc num-args 1, num-type-args 0, names []
|
||||
[11] = DirectCall 'dart:core::print', ArgDesc num-args 1, num-type-args 0, names []
|
||||
[12] = Reserved
|
||||
[13] = EndClosureFunctionScope
|
||||
[14] = ObjectRef ArgDesc num-args 2, num-type-args 0, names []
|
||||
[15] = ClosureFunction 2
|
||||
[16] = InterfaceCall 'DART_SDK/pkg/dart2bytecode/testcases/closures.dart::B::set:foo', ArgDesc num-args 2, num-type-args 0, names []
|
||||
[17] = Reserved
|
||||
[18] = EndClosureFunctionScope
|
||||
[14] = AllocateClosure 0, num-elements: 1, flags: 0
|
||||
[15] = Reserved
|
||||
[16] = ObjectRef ArgDesc num-args 2, num-type-args 0, names []
|
||||
[17] = ClosureFunction 2
|
||||
[18] = InterfaceCall 'DART_SDK/pkg/dart2bytecode/testcases/closures.dart::B::set:foo', ArgDesc num-args 2, num-type-args 0, names []
|
||||
[19] = Reserved
|
||||
[20] = EndClosureFunctionScope
|
||||
[21] = AllocateClosure 2, num-elements: 1, flags: 0
|
||||
[22] = Reserved
|
||||
}
|
||||
Closure DART_SDK/pkg/dart2bytecode/testcases/closures.dart::B::topLevel::'<anonymous closure>' (dart:core::int y) -> Null
|
||||
ClosureCode {
|
||||
Entry 4
|
||||
Push FP[-6]
|
||||
LoadFieldTOS CP#1
|
||||
LoadClosureElement 0
|
||||
PopLocal r0
|
||||
CheckStack 0
|
||||
AllocateContext 1, 2
|
||||
@@ -836,11 +848,11 @@ ClosureCode {
|
||||
StoreContextVar 1, 0
|
||||
JumpIfUnchecked L1
|
||||
Push FP[-5]
|
||||
PushConstant CP#3
|
||||
PushConstant CP#1
|
||||
PushNull
|
||||
PushNull
|
||||
PushConstant CP#4
|
||||
AssertAssignable 1, CP#5
|
||||
PushConstant CP#2
|
||||
AssertAssignable 1, CP#3
|
||||
Drop1
|
||||
L1:
|
||||
Push r0
|
||||
@@ -859,10 +871,11 @@ L1:
|
||||
Push r0
|
||||
PushInt 4
|
||||
StoreContextVar 1, 1
|
||||
PushConstant CP#6
|
||||
AllocateClosure CP#8
|
||||
StoreLocal r3
|
||||
Push r3
|
||||
Push r0
|
||||
PushNull
|
||||
AllocateClosure
|
||||
StoreClosureElement 0
|
||||
PopLocal r2
|
||||
Push r2
|
||||
Push r2
|
||||
@@ -881,7 +894,7 @@ Closure DART_SDK/pkg/dart2bytecode/testcases/closures.dart::B::topLevel::Closure
|
||||
ClosureCode {
|
||||
Entry 2
|
||||
Push FP[-5]
|
||||
LoadFieldTOS CP#1
|
||||
LoadClosureElement 0
|
||||
PopLocal r0
|
||||
CheckStack 0
|
||||
Push r0
|
||||
@@ -896,7 +909,7 @@ ClosureCode {
|
||||
Push r0
|
||||
LoadContextParent
|
||||
LoadContextVar 0, 0
|
||||
InterfaceCall CP#7, 1
|
||||
InterfaceCall CP#5, 1
|
||||
Push r0
|
||||
LoadContextVar 1, 0
|
||||
AddInt
|
||||
@@ -909,14 +922,14 @@ Closure DART_SDK/pkg/dart2bytecode/testcases/closures.dart::B::topLevel::'<anony
|
||||
ClosureCode {
|
||||
Entry 3
|
||||
Push FP[-5]
|
||||
LoadFieldTOS CP#1
|
||||
LoadClosureElement 0
|
||||
PopLocal r0
|
||||
CheckStack 0
|
||||
Push r0
|
||||
LoadContextVar 0, 0
|
||||
Push r0
|
||||
LoadContextVar 0, 3
|
||||
InterfaceCall CP#16, 2
|
||||
InterfaceCall CP#18, 2
|
||||
Drop1
|
||||
PushNull
|
||||
ReturnTOS
|
||||
@@ -982,17 +995,19 @@ L2:
|
||||
CompareIntLt
|
||||
JumpIfFalse L1
|
||||
Push r2
|
||||
PushConstant CP#3
|
||||
AllocateClosure CP#5
|
||||
StoreLocal r4
|
||||
Push r4
|
||||
Push r0
|
||||
PushNull
|
||||
AllocateClosure
|
||||
StoreClosureElement 0
|
||||
InstantiatedInterfaceCall CP#7, 2
|
||||
Drop1
|
||||
Push r3
|
||||
PushConstant CP#10
|
||||
AllocateClosure CP#15
|
||||
StoreLocal r4
|
||||
Push r4
|
||||
Push r0
|
||||
PushNull
|
||||
AllocateClosure
|
||||
StoreClosureElement 0
|
||||
InstantiatedInterfaceCall CP#7, 2
|
||||
Drop1
|
||||
Push r0
|
||||
@@ -1023,9 +1038,9 @@ ConstantPool {
|
||||
[1] = DirectCall 'dart:core::_GrowableList:: (constructor)', ArgDesc num-args 2, num-type-args 0, names []
|
||||
[2] = Reserved
|
||||
[3] = ClosureFunction 0
|
||||
[4] = InstanceField dart:core::_Closure::_context (field)
|
||||
[5] = Reserved
|
||||
[6] = EndClosureFunctionScope
|
||||
[4] = EndClosureFunctionScope
|
||||
[5] = AllocateClosure 0, num-elements: 1, flags: 0
|
||||
[6] = Reserved
|
||||
[7] = InstantiatedInterfaceCall 'dart:core::List::add', ArgDesc num-args 2, num-type-args 0, names [], receiver dart:core::List < dart:core::Function >
|
||||
[8] = Reserved
|
||||
[9] = Reserved
|
||||
@@ -1034,12 +1049,14 @@ ConstantPool {
|
||||
[12] = ObjectRef 'ii'
|
||||
[13] = SubtypeTestCache
|
||||
[14] = EndClosureFunctionScope
|
||||
[15] = AllocateClosure 1, num-elements: 1, flags: 0
|
||||
[16] = Reserved
|
||||
}
|
||||
Closure DART_SDK/pkg/dart2bytecode/testcases/closures.dart::C::testForLoop::'<anonymous closure>' () -> dart:core::int
|
||||
ClosureCode {
|
||||
Entry 2
|
||||
Push FP[-5]
|
||||
LoadFieldTOS CP#4
|
||||
LoadClosureElement 0
|
||||
PopLocal r0
|
||||
CheckStack 0
|
||||
Push r0
|
||||
@@ -1055,7 +1072,7 @@ Closure DART_SDK/pkg/dart2bytecode/testcases/closures.dart::C::testForLoop::'<an
|
||||
ClosureCode {
|
||||
Entry 2
|
||||
Push FP[-6]
|
||||
LoadFieldTOS CP#4
|
||||
LoadClosureElement 0
|
||||
PopLocal r0
|
||||
CheckStack 0
|
||||
JumpIfUnchecked L1
|
||||
@@ -1100,10 +1117,11 @@ L2:
|
||||
Push r2
|
||||
InterfaceCall CP#4, 1
|
||||
StoreContextVar 0, 0
|
||||
PushConstant CP#6
|
||||
AllocateClosure CP#8
|
||||
StoreLocal r4
|
||||
Push r4
|
||||
Push r0
|
||||
PushNull
|
||||
AllocateClosure
|
||||
StoreClosureElement 0
|
||||
PopLocal r3
|
||||
Push r3
|
||||
StoreLocal r4
|
||||
@@ -1130,9 +1148,9 @@ ConstantPool {
|
||||
[4] = InterfaceCall 'dart:core::Iterator::get:current', ArgDesc num-args 1, num-type-args 0, names []
|
||||
[5] = Reserved
|
||||
[6] = ClosureFunction 0
|
||||
[7] = InstanceField dart:core::_Closure::_context (field)
|
||||
[8] = Reserved
|
||||
[9] = EndClosureFunctionScope
|
||||
[7] = EndClosureFunctionScope
|
||||
[8] = AllocateClosure 0, num-elements: 1, flags: 0
|
||||
[9] = Reserved
|
||||
[10] = ObjectRef ArgDesc num-args 1, num-type-args 0, names []
|
||||
[11] = DirectCall 'dart:core::print', ArgDesc num-args 1, num-type-args 0, names []
|
||||
[12] = Reserved
|
||||
@@ -1141,7 +1159,7 @@ Closure DART_SDK/pkg/dart2bytecode/testcases/closures.dart::C::testForInLoop::'<
|
||||
ClosureCode {
|
||||
Entry 2
|
||||
Push FP[-5]
|
||||
LoadFieldTOS CP#7
|
||||
LoadClosureElement 0
|
||||
PopLocal r0
|
||||
CheckStack 0
|
||||
Push r0
|
||||
@@ -1183,7 +1201,7 @@ Function 'foo', reflectable, debuggable
|
||||
return-type dynamic
|
||||
|
||||
Bytecode {
|
||||
Entry 2
|
||||
Entry 3
|
||||
CheckStack 0
|
||||
AllocateContext 0, 1
|
||||
PopLocal r0
|
||||
@@ -1200,11 +1218,15 @@ Bytecode {
|
||||
AssertAssignable 0, CP#3
|
||||
Drop1
|
||||
L1:
|
||||
PushConstant CP#4
|
||||
Push r0
|
||||
AllocateClosure CP#6
|
||||
StoreLocal r2
|
||||
Push r2
|
||||
Push FP[-6]
|
||||
LoadTypeArgumentsField CP#1
|
||||
AllocateClosure
|
||||
StoreClosureElement 0
|
||||
Push r2
|
||||
Push r0
|
||||
StoreClosureElement 1
|
||||
ReturnTOS
|
||||
}
|
||||
Parameter flags: [2]
|
||||
@@ -1214,15 +1236,15 @@ ConstantPool {
|
||||
[2] = ObjectRef 't'
|
||||
[3] = SubtypeTestCache
|
||||
[4] = ClosureFunction 0
|
||||
[5] = InstanceField dart:core::_Closure::_context (field)
|
||||
[6] = Reserved
|
||||
[7] = EndClosureFunctionScope
|
||||
[5] = EndClosureFunctionScope
|
||||
[6] = AllocateClosure 0, num-elements: 2, flags: 2
|
||||
[7] = Reserved
|
||||
}
|
||||
Closure DART_SDK/pkg/dart2bytecode/testcases/closures.dart::D::foo::'<anonymous closure>' () -> DART_SDK/pkg/dart2bytecode/testcases/closures.dart::D::TypeParam/0
|
||||
ClosureCode {
|
||||
Entry 2
|
||||
Push FP[-5]
|
||||
LoadFieldTOS CP#5
|
||||
LoadClosureElement 1
|
||||
PopLocal r0
|
||||
CheckStack 0
|
||||
Push r0
|
||||
@@ -1236,43 +1258,53 @@ Function 'bar', reflectable, debuggable
|
||||
return-type dynamic
|
||||
|
||||
Bytecode {
|
||||
Entry 2
|
||||
Entry 3
|
||||
CheckStack 0
|
||||
AllocateContext 0, 1
|
||||
PopLocal r0
|
||||
Push r0
|
||||
Push FP[-5]
|
||||
StoreContextVar 0, 0
|
||||
PushConstant CP#0
|
||||
Push r0
|
||||
AllocateClosure CP#8
|
||||
StoreLocal r2
|
||||
Push r2
|
||||
Push FP[-5]
|
||||
LoadTypeArgumentsField CP#5
|
||||
AllocateClosure
|
||||
StoreClosureElement 0
|
||||
Push r2
|
||||
Push r0
|
||||
StoreClosureElement 1
|
||||
ReturnTOS
|
||||
}
|
||||
ConstantPool {
|
||||
[0] = ClosureFunction 0
|
||||
[1] = InstanceField dart:core::_Closure::_context (field)
|
||||
[2] = Reserved
|
||||
[3] = ClosureFunction 1
|
||||
[4] = EndClosureFunctionScope
|
||||
[1] = ClosureFunction 1
|
||||
[2] = EndClosureFunctionScope
|
||||
[3] = AllocateClosure 1, num-elements: 2, flags: 2
|
||||
[4] = Reserved
|
||||
[5] = TypeArgumentsField DART_SDK/pkg/dart2bytecode/testcases/closures.dart::D
|
||||
[6] = ObjectRef ArgDesc num-args 1, num-type-args 0, names []
|
||||
[7] = EndClosureFunctionScope
|
||||
[8] = AllocateClosure 0, num-elements: 2, flags: 2
|
||||
[9] = Reserved
|
||||
}
|
||||
Closure DART_SDK/pkg/dart2bytecode/testcases/closures.dart::D::bar::'<anonymous closure>' () -> Null
|
||||
ClosureCode {
|
||||
Entry 4
|
||||
Push FP[-5]
|
||||
LoadFieldTOS CP#1
|
||||
LoadClosureElement 1
|
||||
PopLocal r0
|
||||
CheckStack 0
|
||||
PushConstant CP#3
|
||||
Push r0
|
||||
AllocateClosure CP#3
|
||||
StoreLocal r3
|
||||
Push r3
|
||||
Push r0
|
||||
LoadContextVar 0, 0
|
||||
LoadTypeArgumentsField CP#5
|
||||
AllocateClosure
|
||||
StoreClosureElement 0
|
||||
Push r3
|
||||
Push r0
|
||||
StoreClosureElement 1
|
||||
PopLocal r2
|
||||
Push r2
|
||||
Push r2
|
||||
@@ -1286,7 +1318,7 @@ Closure DART_SDK/pkg/dart2bytecode/testcases/closures.dart::D::bar::Closure/0::'
|
||||
ClosureCode {
|
||||
Entry 2
|
||||
Push FP[-5]
|
||||
LoadFieldTOS CP#1
|
||||
LoadClosureElement 1
|
||||
PopLocal r0
|
||||
CheckStack 0
|
||||
PushNull
|
||||
|
||||
@@ -13,7 +13,7 @@ Function 'testVoidNoArg', static, reflectable, debuggable
|
||||
return-type dynamic
|
||||
|
||||
Bytecode {
|
||||
Entry 5
|
||||
Entry 6
|
||||
CheckStack 0
|
||||
PushConstant CP#0
|
||||
PushConstant CP#1
|
||||
@@ -24,10 +24,11 @@ Bytecode {
|
||||
Push r0
|
||||
Push r2
|
||||
StoreContextVar 0, 0
|
||||
PushConstant CP#4
|
||||
AllocateClosure CP#7
|
||||
StoreLocal r5
|
||||
Push r5
|
||||
Push r0
|
||||
PushNull
|
||||
AllocateClosure
|
||||
StoreClosureElement 0
|
||||
PopLocal r4
|
||||
Push r4
|
||||
Push r0
|
||||
@@ -48,10 +49,10 @@ ConstantPool {
|
||||
[2] = DirectCall 'dart:ffi::Pointer::fromAddress (constructor)', ArgDesc num-args 2, num-type-args 0, names []
|
||||
[3] = Reserved
|
||||
[4] = ClosureFunction 0
|
||||
[5] = InstanceField dart:core::_Closure::_context (field)
|
||||
[6] = Reserved
|
||||
[7] = FfiCall
|
||||
[8] = EndClosureFunctionScope
|
||||
[5] = FfiCall
|
||||
[6] = EndClosureFunctionScope
|
||||
[7] = AllocateClosure 0, num-elements: 1, flags: 0
|
||||
[8] = Reserved
|
||||
[9] = ObjectRef ArgDesc num-args 1, num-type-args 0, names []
|
||||
}
|
||||
Closure DART_SDK/pkg/dart2bytecode/testcases/ffi.dart::testVoidNoArg::'#ffiClosure0' annotations const List<dynamic> [const dart:core::pragma {dart:core::pragma::name (field): 'vm:ffi:call-closure', dart:core::pragma::options (field): const dart:ffi::_FfiCall < FunctionType () -> dart:ffi::Void > {dart:ffi::_FfiCall::isLeaf (field): const false}}]
|
||||
@@ -59,12 +60,12 @@ Closure DART_SDK/pkg/dart2bytecode/testcases/ffi.dart::testVoidNoArg::'#ffiClosu
|
||||
ClosureCode {
|
||||
Entry 2
|
||||
Push FP[-5]
|
||||
LoadFieldTOS CP#5
|
||||
LoadClosureElement 0
|
||||
PopLocal r0
|
||||
CheckStack 0
|
||||
Push r0
|
||||
LoadContextVar 0, 0
|
||||
FfiCall CP#7
|
||||
FfiCall CP#5
|
||||
ReturnTOS
|
||||
}
|
||||
|
||||
@@ -74,7 +75,7 @@ Function 'testIntInt', static, reflectable, debuggable
|
||||
return-type dynamic
|
||||
|
||||
Bytecode {
|
||||
Entry 5
|
||||
Entry 6
|
||||
CheckStack 0
|
||||
PushConstant CP#0
|
||||
PushConstant CP#1
|
||||
@@ -85,10 +86,11 @@ Bytecode {
|
||||
Push r0
|
||||
Push r2
|
||||
StoreContextVar 0, 0
|
||||
PushConstant CP#4
|
||||
AllocateClosure CP#10
|
||||
StoreLocal r5
|
||||
Push r5
|
||||
Push r0
|
||||
PushNull
|
||||
AllocateClosure
|
||||
StoreClosureElement 0
|
||||
PopLocal r4
|
||||
Push r4
|
||||
Push r0
|
||||
@@ -108,13 +110,13 @@ ConstantPool {
|
||||
[2] = DirectCall 'dart:ffi::Pointer::fromAddress (constructor)', ArgDesc num-args 2, num-type-args 0, names []
|
||||
[3] = Reserved
|
||||
[4] = ClosureFunction 0
|
||||
[5] = InstanceField dart:core::_Closure::_context (field)
|
||||
[6] = Reserved
|
||||
[7] = Type dart:core::int
|
||||
[8] = ObjectRef 'arg1'
|
||||
[9] = SubtypeTestCache
|
||||
[10] = FfiCall
|
||||
[11] = EndClosureFunctionScope
|
||||
[5] = Type dart:core::int
|
||||
[6] = ObjectRef 'arg1'
|
||||
[7] = SubtypeTestCache
|
||||
[8] = FfiCall
|
||||
[9] = EndClosureFunctionScope
|
||||
[10] = AllocateClosure 0, num-elements: 1, flags: 0
|
||||
[11] = Reserved
|
||||
[12] = ObjectRef ArgDesc num-args 2, num-type-args 0, names []
|
||||
}
|
||||
Closure DART_SDK/pkg/dart2bytecode/testcases/ffi.dart::testIntInt::'#ffiClosure1' annotations const List<dynamic> [const dart:core::pragma {dart:core::pragma::name (field): 'vm:ffi:call-closure', dart:core::pragma::options (field): const dart:ffi::_FfiCall < FunctionType (dart:ffi::Int64) -> dart:ffi::Int32 > {dart:ffi::_FfiCall::isLeaf (field): const false}}]
|
||||
@@ -122,16 +124,16 @@ Closure DART_SDK/pkg/dart2bytecode/testcases/ffi.dart::testIntInt::'#ffiClosure1
|
||||
ClosureCode {
|
||||
Entry 2
|
||||
Push FP[-6]
|
||||
LoadFieldTOS CP#5
|
||||
LoadClosureElement 0
|
||||
PopLocal r0
|
||||
CheckStack 0
|
||||
JumpIfUnchecked L1
|
||||
Push FP[-5]
|
||||
PushConstant CP#7
|
||||
PushConstant CP#5
|
||||
PushNull
|
||||
PushNull
|
||||
PushConstant CP#8
|
||||
AssertAssignable 1, CP#9
|
||||
PushConstant CP#6
|
||||
AssertAssignable 1, CP#7
|
||||
Drop1
|
||||
L1:
|
||||
PushNull
|
||||
@@ -139,7 +141,7 @@ L1:
|
||||
Push FP[-5]
|
||||
Push r0
|
||||
LoadContextVar 0, 0
|
||||
FfiCall CP#10
|
||||
FfiCall CP#8
|
||||
ReturnTOS
|
||||
}
|
||||
|
||||
|
||||
@@ -72,43 +72,44 @@ Bytecode {
|
||||
Push r0
|
||||
Push FP[-5]
|
||||
StoreContextVar 0, 0
|
||||
PushConstant CP#0
|
||||
AllocateClosure CP#6
|
||||
StoreLocal r3
|
||||
Push r3
|
||||
Push r0
|
||||
PushNull
|
||||
AllocateClosure
|
||||
StoreClosureElement 0
|
||||
PopLocal r2
|
||||
Push r2
|
||||
Push r2
|
||||
UncheckedClosureCall CP#6, 1
|
||||
UncheckedClosureCall CP#4, 1
|
||||
Drop1
|
||||
PushNull
|
||||
ReturnTOS
|
||||
}
|
||||
ConstantPool {
|
||||
[0] = ClosureFunction 0
|
||||
[1] = InstanceField dart:core::_Closure::_context (field)
|
||||
[2] = Reserved
|
||||
[3] = ObjectRef 'visibleInner'
|
||||
[4] = DirectCall 'dart:core::print', ArgDesc num-args 1, num-type-args 0, names []
|
||||
[5] = Reserved
|
||||
[6] = ObjectRef ArgDesc num-args 1, num-type-args 0, names []
|
||||
[7] = EndClosureFunctionScope
|
||||
[1] = ObjectRef 'visibleInner'
|
||||
[2] = DirectCall 'dart:core::print', ArgDesc num-args 1, num-type-args 0, names []
|
||||
[3] = Reserved
|
||||
[4] = ObjectRef ArgDesc num-args 1, num-type-args 0, names []
|
||||
[5] = EndClosureFunctionScope
|
||||
[6] = AllocateClosure 0, num-elements: 1, flags: 0
|
||||
[7] = Reserved
|
||||
}
|
||||
Closure DART_SDK/pkg/dart2bytecode/testcases/invisible.dart::visibleClosure::'visibleInner' () -> Null
|
||||
ClosureCode {
|
||||
Entry 3
|
||||
Push FP[-5]
|
||||
LoadFieldTOS CP#1
|
||||
LoadClosureElement 0
|
||||
PopLocal r0
|
||||
CheckStack 0
|
||||
PushConstant CP#3
|
||||
DirectCall CP#4, 1
|
||||
PushConstant CP#1
|
||||
DirectCall CP#2, 1
|
||||
Drop1
|
||||
Push r0
|
||||
LoadContextVar 0, 0
|
||||
StoreLocal r2
|
||||
Push r2
|
||||
UncheckedClosureCall CP#6, 1
|
||||
UncheckedClosureCall CP#4, 1
|
||||
Drop1
|
||||
PushNull
|
||||
ReturnTOS
|
||||
@@ -127,44 +128,45 @@ Bytecode {
|
||||
Push r0
|
||||
Push FP[-5]
|
||||
StoreContextVar 0, 0
|
||||
PushConstant CP#0
|
||||
AllocateClosure CP#6
|
||||
StoreLocal r3
|
||||
Push r3
|
||||
Push r0
|
||||
PushNull
|
||||
AllocateClosure
|
||||
StoreClosureElement 0
|
||||
PopLocal r2
|
||||
Push r2
|
||||
Push r2
|
||||
UncheckedClosureCall CP#6, 1
|
||||
UncheckedClosureCall CP#4, 1
|
||||
Drop1
|
||||
PushNull
|
||||
ReturnTOS
|
||||
}
|
||||
ConstantPool {
|
||||
[0] = ClosureFunction 0
|
||||
[1] = InstanceField dart:core::_Closure::_context (field)
|
||||
[2] = Reserved
|
||||
[3] = ObjectRef 'invisibleInner'
|
||||
[4] = DirectCall 'dart:core::print', ArgDesc num-args 1, num-type-args 0, names []
|
||||
[5] = Reserved
|
||||
[6] = ObjectRef ArgDesc num-args 1, num-type-args 0, names []
|
||||
[7] = EndClosureFunctionScope
|
||||
[1] = ObjectRef 'invisibleInner'
|
||||
[2] = DirectCall 'dart:core::print', ArgDesc num-args 1, num-type-args 0, names []
|
||||
[3] = Reserved
|
||||
[4] = ObjectRef ArgDesc num-args 1, num-type-args 0, names []
|
||||
[5] = EndClosureFunctionScope
|
||||
[6] = AllocateClosure 0, num-elements: 1, flags: 0
|
||||
[7] = Reserved
|
||||
}
|
||||
Closure DART_SDK/pkg/dart2bytecode/testcases/invisible.dart::invisibleClosure::'invisibleInner' invisible annotations const List<dynamic> [const dart:core::pragma {dart:core::pragma::name (field): 'vm:invisible', dart:core::pragma::options (field): null}]
|
||||
() -> Null
|
||||
ClosureCode {
|
||||
Entry 3
|
||||
Push FP[-5]
|
||||
LoadFieldTOS CP#1
|
||||
LoadClosureElement 0
|
||||
PopLocal r0
|
||||
CheckStack 0
|
||||
PushConstant CP#3
|
||||
DirectCall CP#4, 1
|
||||
PushConstant CP#1
|
||||
DirectCall CP#2, 1
|
||||
Drop1
|
||||
Push r0
|
||||
LoadContextVar 0, 0
|
||||
StoreLocal r2
|
||||
Push r2
|
||||
UncheckedClosureCall CP#6, 1
|
||||
UncheckedClosureCall CP#4, 1
|
||||
Drop1
|
||||
PushNull
|
||||
ReturnTOS
|
||||
|
||||
@@ -215,10 +215,11 @@ Try #0 start:
|
||||
Push r0
|
||||
PushInt 2
|
||||
StoreContextVar 0, 1
|
||||
PushConstant CP#0
|
||||
AllocateClosure CP#6
|
||||
StoreLocal r5
|
||||
Push r5
|
||||
Push r0
|
||||
PushNull
|
||||
AllocateClosure
|
||||
StoreClosureElement 0
|
||||
PopLocal r4
|
||||
Push r4
|
||||
Push r4
|
||||
@@ -226,7 +227,7 @@ Try #0 start:
|
||||
Drop1
|
||||
Push r0
|
||||
LoadContextVar 0, 1
|
||||
DirectCall CP#4, 1
|
||||
DirectCall CP#2, 1
|
||||
Drop1
|
||||
Jump L1
|
||||
Try #0 end:
|
||||
@@ -263,12 +264,13 @@ Try #0 handler:
|
||||
LoadContextVar 0, 2
|
||||
StoreIndexedTOS
|
||||
DirectCall CP#11, 1
|
||||
DirectCall CP#4, 1
|
||||
DirectCall CP#2, 1
|
||||
Drop1
|
||||
PushConstant CP#13
|
||||
AllocateClosure CP#21
|
||||
StoreLocal r5
|
||||
Push r5
|
||||
Push r0
|
||||
PushNull
|
||||
AllocateClosure
|
||||
StoreClosureElement 0
|
||||
PopLocal r6
|
||||
Push r6
|
||||
ReturnTOS
|
||||
@@ -280,17 +282,17 @@ L1:
|
||||
ReturnTOS
|
||||
}
|
||||
ExceptionsTable {
|
||||
try-index 0, outer -1, start 20, end 56, handler 56, needs-stack-trace, types [CP#6]
|
||||
try-index 0, outer -1, start 20, end 60, handler 60, needs-stack-trace, types [CP#4]
|
||||
}
|
||||
ConstantPool {
|
||||
[0] = ClosureFunction 0
|
||||
[1] = InstanceField dart:core::_Closure::_context (field)
|
||||
[2] = Reserved
|
||||
[3] = ObjectRef 'danger foo'
|
||||
[4] = DirectCall 'dart:core::print', ArgDesc num-args 1, num-type-args 0, names []
|
||||
[5] = Reserved
|
||||
[6] = Type dart:core::Object
|
||||
[7] = EndClosureFunctionScope
|
||||
[1] = ObjectRef 'danger foo'
|
||||
[2] = DirectCall 'dart:core::print', ArgDesc num-args 1, num-type-args 0, names []
|
||||
[3] = Reserved
|
||||
[4] = Type dart:core::Object
|
||||
[5] = EndClosureFunctionScope
|
||||
[6] = AllocateClosure 0, num-elements: 1, flags: 0
|
||||
[7] = Reserved
|
||||
[8] = ObjectRef ArgDesc num-args 1, num-type-args 0, names []
|
||||
[9] = ObjectRef 'caught '
|
||||
[10] = ObjectRef ' '
|
||||
@@ -304,19 +306,21 @@ ConstantPool {
|
||||
[18] = ObjectRef 'error '
|
||||
[19] = ObjectRef ', captured stack trace: '
|
||||
[20] = EndClosureFunctionScope
|
||||
[21] = AllocateClosure 1, num-elements: 1, flags: 0
|
||||
[22] = Reserved
|
||||
}
|
||||
Closure DART_SDK/pkg/dart2bytecode/testcases/try_blocks.dart::testTryCatch3::'foo' () -> void
|
||||
ClosureCode {
|
||||
Entry 5
|
||||
Push FP[-5]
|
||||
LoadFieldTOS CP#1
|
||||
LoadClosureElement 0
|
||||
PopLocal r0
|
||||
CheckStack 0
|
||||
Push r0
|
||||
PopLocal r2
|
||||
Try #0 start:
|
||||
PushConstant CP#3
|
||||
DirectCall CP#4, 1
|
||||
PushConstant CP#1
|
||||
DirectCall CP#2, 1
|
||||
Drop1
|
||||
Jump L1
|
||||
Try #0 end:
|
||||
@@ -330,7 +334,7 @@ Try #0 handler:
|
||||
PopLocal r4
|
||||
Push r0
|
||||
LoadContextVar 0, 0
|
||||
DirectCall CP#4, 1
|
||||
DirectCall CP#2, 1
|
||||
Drop1
|
||||
Push r0
|
||||
PushInt 3
|
||||
@@ -345,14 +349,14 @@ Closure DART_SDK/pkg/dart2bytecode/testcases/try_blocks.dart::testTryCatch3::'ba
|
||||
ClosureCode {
|
||||
Entry 6
|
||||
Push FP[-5]
|
||||
LoadFieldTOS CP#1
|
||||
LoadClosureElement 0
|
||||
PopLocal r0
|
||||
CheckStack 0
|
||||
Push r0
|
||||
PopLocal r2
|
||||
Try #0 start:
|
||||
PushConstant CP#14
|
||||
DirectCall CP#4, 1
|
||||
DirectCall CP#2, 1
|
||||
Drop1
|
||||
Jump L1
|
||||
Try #0 end:
|
||||
@@ -390,7 +394,7 @@ Try #0 handler:
|
||||
LoadContextVar 0, 2
|
||||
StoreIndexedTOS
|
||||
DirectCall CP#11, 1
|
||||
DirectCall CP#4, 1
|
||||
DirectCall CP#2, 1
|
||||
Drop1
|
||||
Jump L1
|
||||
L2:
|
||||
@@ -588,10 +592,11 @@ Try #1 start:
|
||||
PushConstant CP#5
|
||||
DirectCall CP#3, 1
|
||||
Drop1
|
||||
PushConstant CP#6
|
||||
AllocateClosure CP#8
|
||||
StoreLocal r8
|
||||
Push r8
|
||||
Push r0
|
||||
PushNull
|
||||
AllocateClosure
|
||||
StoreClosureElement 0
|
||||
PopLocal r7
|
||||
Push r7
|
||||
Push r7
|
||||
@@ -648,8 +653,8 @@ L3:
|
||||
ReturnTOS
|
||||
}
|
||||
ExceptionsTable {
|
||||
try-index 0, outer -1, start 53, end 134, handler 134, needs-stack-trace, synthetic, types [CP#11]
|
||||
try-index 1, outer 0, start 70, end 96, handler 96, needs-stack-trace, synthetic, types [CP#11]
|
||||
try-index 0, outer -1, start 53, end 138, handler 138, needs-stack-trace, synthetic, types [CP#11]
|
||||
try-index 1, outer 0, start 70, end 100, handler 100, needs-stack-trace, synthetic, types [CP#11]
|
||||
}
|
||||
ConstantPool {
|
||||
[0] = InterfaceCall 'dart:core::Object::==', ArgDesc num-args 2, num-type-args 0, names []
|
||||
@@ -659,9 +664,9 @@ ConstantPool {
|
||||
[4] = Reserved
|
||||
[5] = ObjectRef 'try'
|
||||
[6] = ClosureFunction 0
|
||||
[7] = InstanceField dart:core::_Closure::_context (field)
|
||||
[8] = Reserved
|
||||
[9] = EndClosureFunctionScope
|
||||
[7] = EndClosureFunctionScope
|
||||
[8] = AllocateClosure 0, num-elements: 1, flags: 0
|
||||
[9] = Reserved
|
||||
[10] = ObjectRef ArgDesc num-args 1, num-type-args 0, names []
|
||||
[11] = Type dynamic
|
||||
[12] = ObjectRef 'finally 1'
|
||||
@@ -673,7 +678,7 @@ Closure DART_SDK/pkg/dart2bytecode/testcases/try_blocks.dart::testTryFinally2::'
|
||||
ClosureCode {
|
||||
Entry 2
|
||||
Push FP[-5]
|
||||
LoadFieldTOS CP#7
|
||||
LoadClosureElement 0
|
||||
PopLocal r0
|
||||
CheckStack 0
|
||||
Push r0
|
||||
@@ -694,7 +699,7 @@ Function 'testTryFinally3', static, reflectable, debuggable
|
||||
return-type dynamic
|
||||
|
||||
Bytecode {
|
||||
Entry 5
|
||||
Entry 6
|
||||
CheckStack 0
|
||||
AllocateContext 0, 1
|
||||
PopLocal r0
|
||||
@@ -706,22 +711,23 @@ Bytecode {
|
||||
Push r0
|
||||
PopLocal r3
|
||||
Try #0 start:
|
||||
PushConstant CP#0
|
||||
AllocateClosure CP#7
|
||||
StoreLocal r5
|
||||
Push r5
|
||||
Push r0
|
||||
PushNull
|
||||
AllocateClosure
|
||||
StoreClosureElement 0
|
||||
PopLocal r2
|
||||
Jump L1
|
||||
Try #0 end:
|
||||
Try #0 handler:
|
||||
SetFrame 5
|
||||
SetFrame 6
|
||||
Push r3
|
||||
PopLocal r0
|
||||
MoveSpecial exception, r3
|
||||
MoveSpecial stackTrace, r4
|
||||
Push r0
|
||||
LoadContextVar 0, 0
|
||||
DirectCall CP#3, 1
|
||||
DirectCall CP#1, 1
|
||||
Drop1
|
||||
Push r2
|
||||
DynamicCall CP#9, 1
|
||||
@@ -734,7 +740,7 @@ L1:
|
||||
PopLocal r0
|
||||
Push r0
|
||||
LoadContextVar 0, 0
|
||||
DirectCall CP#3, 1
|
||||
DirectCall CP#1, 1
|
||||
Drop1
|
||||
Push r2
|
||||
DynamicCall CP#9, 1
|
||||
@@ -746,18 +752,18 @@ L1:
|
||||
ReturnTOS
|
||||
}
|
||||
ExceptionsTable {
|
||||
try-index 0, outer -1, start 23, end 35, handler 35, needs-stack-trace, synthetic, types [CP#6]
|
||||
try-index 0, outer -1, start 23, end 39, handler 39, needs-stack-trace, synthetic, types [CP#4]
|
||||
}
|
||||
ConstantPool {
|
||||
[0] = ClosureFunction 0
|
||||
[1] = InstanceField dart:core::_Closure::_context (field)
|
||||
[1] = DirectCall 'dart:core::print', ArgDesc num-args 1, num-type-args 0, names []
|
||||
[2] = Reserved
|
||||
[3] = DirectCall 'dart:core::print', ArgDesc num-args 1, num-type-args 0, names []
|
||||
[4] = Reserved
|
||||
[5] = ObjectRef 'try 1'
|
||||
[6] = Type dynamic
|
||||
[7] = ObjectRef 'try 2'
|
||||
[8] = EndClosureFunctionScope
|
||||
[3] = ObjectRef 'try 1'
|
||||
[4] = Type dynamic
|
||||
[5] = ObjectRef 'try 2'
|
||||
[6] = EndClosureFunctionScope
|
||||
[7] = AllocateClosure 0, num-elements: 1, flags: 0
|
||||
[8] = Reserved
|
||||
[9] = DynamicCall 'implicit:call', ArgDesc num-args 1, num-type-args 0, names []
|
||||
[10] = Reserved
|
||||
}
|
||||
@@ -765,18 +771,18 @@ Closure DART_SDK/pkg/dart2bytecode/testcases/try_blocks.dart::testTryFinally3::'
|
||||
ClosureCode {
|
||||
Entry 6
|
||||
Push FP[-5]
|
||||
LoadFieldTOS CP#1
|
||||
LoadClosureElement 0
|
||||
PopLocal r0
|
||||
CheckStack 0
|
||||
Push r0
|
||||
LoadContextVar 0, 0
|
||||
DirectCall CP#3, 1
|
||||
DirectCall CP#1, 1
|
||||
Drop1
|
||||
Push r0
|
||||
PopLocal r2
|
||||
Try #0 start:
|
||||
PushConstant CP#5
|
||||
DirectCall CP#3, 1
|
||||
PushConstant CP#3
|
||||
DirectCall CP#1, 1
|
||||
Drop1
|
||||
Jump L1
|
||||
Try #0 end:
|
||||
@@ -789,8 +795,8 @@ Try #0 handler:
|
||||
Push r0
|
||||
PopLocal r4
|
||||
Try #1 start:
|
||||
PushConstant CP#7
|
||||
DirectCall CP#3, 1
|
||||
PushConstant CP#5
|
||||
DirectCall CP#1, 1
|
||||
Drop1
|
||||
Jump L2
|
||||
Try #1 end:
|
||||
@@ -802,7 +808,7 @@ Try #1 handler:
|
||||
MoveSpecial stackTrace, r5
|
||||
Push r0
|
||||
LoadContextVar 0, 0
|
||||
DirectCall CP#3, 1
|
||||
DirectCall CP#1, 1
|
||||
Drop1
|
||||
Push r4
|
||||
Push r5
|
||||
@@ -812,7 +818,7 @@ L2:
|
||||
PopLocal r0
|
||||
Push r0
|
||||
LoadContextVar 0, 0
|
||||
DirectCall CP#3, 1
|
||||
DirectCall CP#1, 1
|
||||
Drop1
|
||||
PushInt 43
|
||||
ReturnTOS
|
||||
@@ -822,8 +828,8 @@ L1:
|
||||
Push r0
|
||||
PopLocal r4
|
||||
Try #2 start:
|
||||
PushConstant CP#7
|
||||
DirectCall CP#3, 1
|
||||
PushConstant CP#5
|
||||
DirectCall CP#1, 1
|
||||
Drop1
|
||||
Jump L3
|
||||
Try #2 end:
|
||||
@@ -835,7 +841,7 @@ Try #2 handler:
|
||||
MoveSpecial stackTrace, r5
|
||||
Push r0
|
||||
LoadContextVar 0, 0
|
||||
DirectCall CP#3, 1
|
||||
DirectCall CP#1, 1
|
||||
Drop1
|
||||
Push r4
|
||||
Push r5
|
||||
@@ -845,7 +851,7 @@ L3:
|
||||
PopLocal r0
|
||||
Push r0
|
||||
LoadContextVar 0, 0
|
||||
DirectCall CP#3, 1
|
||||
DirectCall CP#1, 1
|
||||
Drop1
|
||||
PushInt 43
|
||||
ReturnTOS
|
||||
|
||||
@@ -7,7 +7,6 @@ import 'dart:math' as math;
|
||||
import 'package:cfg/ir/constant_value.dart';
|
||||
import 'package:cfg/ir/field.dart';
|
||||
import 'package:cfg/ir/functions.dart';
|
||||
import 'package:cfg/ir/global_context.dart';
|
||||
import 'package:cfg/ir/instructions.dart';
|
||||
import 'package:cfg/ir/types.dart';
|
||||
import 'package:cfg/utils/misc.dart';
|
||||
@@ -1203,18 +1202,50 @@ final class Arm64CodeGenerator extends CodeGenerator {
|
||||
|
||||
@override
|
||||
void visitAllocateClosure(AllocateClosure instr) {
|
||||
final cls = GlobalContext.instance.coreTypes.index.getClass(
|
||||
'dart:core',
|
||||
'_Closure',
|
||||
final function = instr.function;
|
||||
final hasDelayedTypeArgs = function.hasFunctionTypeParameters;
|
||||
final hasInstantiatorTypeArgs = switch (function) {
|
||||
LocalFunction() => containsClassTypeParameters(
|
||||
function.functionNode!.computeFunctionType(ast.Nullability.nonNullable),
|
||||
),
|
||||
TearOffFunction() =>
|
||||
function.member.isInstanceMember &&
|
||||
containsClassTypeParameters(
|
||||
function.member.function!.computeFunctionType(
|
||||
ast.Nullability.nonNullable,
|
||||
),
|
||||
),
|
||||
};
|
||||
final hasFunctionTypeArgs = switch (function) {
|
||||
LocalFunction() => hasGenericEnclosingFunction(function.localFunction),
|
||||
TearOffFunction() => false,
|
||||
};
|
||||
final numElements =
|
||||
(hasDelayedTypeArgs ? 1 : 0) +
|
||||
(hasInstantiatorTypeArgs ? 1 : 0) +
|
||||
(hasFunctionTypeArgs ? 1 : 0) +
|
||||
1 /* context */;
|
||||
final lengthAndFlags = vmOffsets.encodeClosureLengthAndFlags(
|
||||
numElements,
|
||||
hasDelayedTypeArgs: hasDelayedTypeArgs,
|
||||
hasInstantiatorTypeArgs: hasInstantiatorTypeArgs,
|
||||
hasFunctionTypeArgs: hasFunctionTypeArgs,
|
||||
);
|
||||
final instanceSize = objectLayout.getInstanceSize(cls);
|
||||
final instanceSize = roundUp(
|
||||
vmOffsets.Closure_elementsStartOffset +
|
||||
numElements * objectLayout.compressedWordSize,
|
||||
objectAlignment(wordSize),
|
||||
);
|
||||
|
||||
final resultReg = AllocationStub.resultReg;
|
||||
assert(outputReg(instr) == resultReg);
|
||||
|
||||
final initializeObject = Label();
|
||||
final done = Label();
|
||||
Label slowPath = addSlowPath(() {
|
||||
_asm.callStub(backEndState.stubFactory.getAllocationStub(cls));
|
||||
_asm.b(initializeObject);
|
||||
_asm.unimplemented(
|
||||
'Unimplemented: code generation for AllocateClosure slow path',
|
||||
);
|
||||
_asm.b(done);
|
||||
});
|
||||
|
||||
_asm.loadImmediate(
|
||||
@@ -1234,16 +1265,21 @@ final class Arm64CodeGenerator extends CodeGenerator {
|
||||
slowPath,
|
||||
initializeFields: true,
|
||||
);
|
||||
|
||||
_asm.bind(initializeObject);
|
||||
final fieldReg = AllocationStub.scratch1Reg;
|
||||
_asm.loadFromPool(fieldReg, instr.function);
|
||||
_asm.loadFromPool(fieldReg, function);
|
||||
_asm.str(
|
||||
fieldReg,
|
||||
_asm.fieldAddress(resultReg, vmOffsets.Closure_function_offset),
|
||||
);
|
||||
_asm.loadImmediate(fieldReg, lengthAndFlags << smiShift);
|
||||
_asm.str(
|
||||
fieldReg,
|
||||
_asm.fieldAddress(resultReg, vmOffsets.Closure_length_and_flags_offset),
|
||||
);
|
||||
_asm.str(ZR, _asm.fieldAddress(resultReg, vmOffsets.Closure_hash_offset));
|
||||
// TODO: initialize the rest of the fields.
|
||||
assert(instr.inputCount == 0);
|
||||
_asm.bind(done);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -150,3 +150,35 @@ bool hasNonTrivialInitializer(ast.Field field) {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if [type] references class type parameters.
|
||||
bool containsClassTypeParameters(ast.DartType type) {
|
||||
final visitor = _FindClassTypeParameters();
|
||||
type.accept(visitor);
|
||||
return visitor.containsClassTypeParams;
|
||||
}
|
||||
|
||||
class _FindClassTypeParameters extends ast.RecursiveVisitor {
|
||||
bool containsClassTypeParams = false;
|
||||
|
||||
_FindClassTypeParameters();
|
||||
|
||||
@override
|
||||
void visitTypeParameterType(ast.TypeParameterType node) {
|
||||
if (node.parameter.declaration is ast.Class) {
|
||||
containsClassTypeParams = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool hasGenericEnclosingFunction(ast.TreeNode node) {
|
||||
for (;;) {
|
||||
node = node.parent!;
|
||||
if (node is ast.Member) {
|
||||
return false;
|
||||
}
|
||||
if (node is ast.FunctionNode && node.typeParameters.isNotEmpty) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,6 +111,36 @@ extension ComputedOffsets on VMOffsets {
|
||||
(encodedSize << UntaggedObject_kSizeTagPos) |
|
||||
(cid.index << UntaggedObject_kClassIdTagPos);
|
||||
}
|
||||
|
||||
int encodeClosureLengthAndFlags(
|
||||
int numElements, {
|
||||
required bool hasDelayedTypeArgs,
|
||||
required bool hasInstantiatorTypeArgs,
|
||||
required bool hasFunctionTypeArgs,
|
||||
}) {
|
||||
assert(
|
||||
(0 <= numElements) &&
|
||||
(numElements < (1 << UntaggedClosure_kLengthBitsSize)),
|
||||
);
|
||||
final functionTypeArgsIndex =
|
||||
(hasDelayedTypeArgs ? 1 : 0) + (hasInstantiatorTypeArgs ? 1 : 0);
|
||||
assert(
|
||||
functionTypeArgsIndex <
|
||||
(1 << UntaggedClosure_kFunctionTypeArgumentsIndexBitsSize),
|
||||
);
|
||||
return (hasDelayedTypeArgs
|
||||
? (1 << UntaggedClosure_kHasDelayedTypeArgumentsBit)
|
||||
: 0) |
|
||||
(hasInstantiatorTypeArgs
|
||||
? (1 << UntaggedClosure_kHasInstantiatorTypeArgumentsBit)
|
||||
: 0) |
|
||||
(hasFunctionTypeArgs
|
||||
? ((1 << UntaggedClosure_kHasFunctionTypeArgumentsBit) |
|
||||
(functionTypeArgsIndex <<
|
||||
UntaggedClosure_kFunctionTypeArgumentsIndexBitsPos))
|
||||
: 0) |
|
||||
(numElements << UntaggedClosure_kLengthBitsPos);
|
||||
}
|
||||
}
|
||||
|
||||
// Symbol names used in Dart snapshots.
|
||||
|
||||
@@ -48,6 +48,14 @@ base class VMOffsets {
|
||||
int get SubtypeTestCache_kMaxInputs => throw 'Unknown';
|
||||
int get SubtypeTestCache_kTestResult => throw 'Unknown';
|
||||
int get TypeArguments_kMaxElements => throw 'Unknown';
|
||||
int get UntaggedClosure_kHasDelayedTypeArgumentsBit => throw 'Unknown';
|
||||
int get UntaggedClosure_kHasInstantiatorTypeArgumentsBit => throw 'Unknown';
|
||||
int get UntaggedClosure_kHasFunctionTypeArgumentsBit => throw 'Unknown';
|
||||
int get UntaggedClosure_kFunctionTypeArgumentsIndexBitsPos => throw 'Unknown';
|
||||
int get UntaggedClosure_kFunctionTypeArgumentsIndexBitsSize =>
|
||||
throw 'Unknown';
|
||||
int get UntaggedClosure_kLengthBitsPos => throw 'Unknown';
|
||||
int get UntaggedClosure_kLengthBitsSize => throw 'Unknown';
|
||||
int get UntaggedObject_kCardRememberedBit => throw 'Unknown';
|
||||
int get UntaggedObject_kCanonicalBit => throw 'Unknown';
|
||||
int get UntaggedObject_kNotMarkedBit => throw 'Unknown';
|
||||
@@ -86,12 +94,9 @@ base class VMOffsets {
|
||||
int get Class_host_type_arguments_field_offset_in_words_offset =>
|
||||
throw 'Unknown';
|
||||
int get ClassTable_allocation_tracing_state_table_offset => throw 'Unknown';
|
||||
int get Closure_context_offset => throw 'Unknown';
|
||||
int get Closure_delayed_type_arguments_offset => throw 'Unknown';
|
||||
int get Closure_function_offset => throw 'Unknown';
|
||||
int get Closure_function_type_arguments_offset => throw 'Unknown';
|
||||
int get Closure_hash_offset => throw 'Unknown';
|
||||
int get Closure_instantiator_type_arguments_offset => throw 'Unknown';
|
||||
int get Closure_length_and_flags_offset => throw 'Unknown';
|
||||
int get ClosureData_packed_fields_offset => throw 'Unknown';
|
||||
int get Code_instructions_offset => throw 'Unknown';
|
||||
int get Code_object_pool_offset => throw 'Unknown';
|
||||
@@ -405,7 +410,6 @@ base class VMOffsets {
|
||||
int get Bytecode_InstanceSize => throw 'Unknown';
|
||||
int get Capability_InstanceSize => throw 'Unknown';
|
||||
int get Class_InstanceSize => throw 'Unknown';
|
||||
int get Closure_InstanceSize => throw 'Unknown';
|
||||
int get ClosureData_InstanceSize => throw 'Unknown';
|
||||
int get CodeSourceMap_HeaderSize => throw 'Unknown';
|
||||
int get CompressedStackMaps_ObjectHeaderSize => throw 'Unknown';
|
||||
@@ -484,6 +488,10 @@ base class VMOffsets {
|
||||
int get ClassTable_elementSize => throw 'Unknown';
|
||||
int ClassTable_elementOffset(int index) =>
|
||||
ClassTable_elementsStartOffset + index * ClassTable_elementSize;
|
||||
int get Closure_elementsStartOffset => throw 'Unknown';
|
||||
int get Closure_elementSize => throw 'Unknown';
|
||||
int Closure_elementOffset(int index) =>
|
||||
Closure_elementsStartOffset + index * Closure_elementSize;
|
||||
int get Code_elementsStartOffset => throw 'Unknown';
|
||||
int get Code_elementSize => throw 'Unknown';
|
||||
int Code_elementOffset(int index) =>
|
||||
@@ -548,6 +556,10 @@ final class Arm64VMOffsets extends VMOffsets {
|
||||
@override
|
||||
int get ClassTable_elementSize => 0x1;
|
||||
@override
|
||||
int get Closure_elementsStartOffset => 0x20;
|
||||
@override
|
||||
int get Closure_elementSize => 0x8;
|
||||
@override
|
||||
int get Code_elementsStartOffset => 0xb0;
|
||||
@override
|
||||
int get Code_elementSize => 0x4;
|
||||
@@ -654,6 +666,20 @@ final class Arm64VMOffsets extends VMOffsets {
|
||||
@override
|
||||
int get TypeArguments_kMaxElements => 0x7ffffffffffffff;
|
||||
@override
|
||||
int get UntaggedClosure_kHasDelayedTypeArgumentsBit => 0x0;
|
||||
@override
|
||||
int get UntaggedClosure_kHasInstantiatorTypeArgumentsBit => 0x1;
|
||||
@override
|
||||
int get UntaggedClosure_kHasFunctionTypeArgumentsBit => 0x2;
|
||||
@override
|
||||
int get UntaggedClosure_kFunctionTypeArgumentsIndexBitsPos => 0x3;
|
||||
@override
|
||||
int get UntaggedClosure_kFunctionTypeArgumentsIndexBitsSize => 0x2;
|
||||
@override
|
||||
int get UntaggedClosure_kLengthBitsPos => 0x5;
|
||||
@override
|
||||
int get UntaggedClosure_kLengthBitsSize => 0x39;
|
||||
@override
|
||||
int get UntaggedObject_kCardRememberedBit => 0x0;
|
||||
@override
|
||||
int get UntaggedObject_kCanonicalBit => 0x1;
|
||||
@@ -728,17 +754,11 @@ final class Arm64VMOffsets extends VMOffsets {
|
||||
@override
|
||||
int get ClassTable_allocation_tracing_state_table_offset => 0x8;
|
||||
@override
|
||||
int get Closure_context_offset => 0x28;
|
||||
int get Closure_function_offset => 0x18;
|
||||
@override
|
||||
int get Closure_delayed_type_arguments_offset => 0x18;
|
||||
int get Closure_hash_offset => 0x10;
|
||||
@override
|
||||
int get Closure_function_offset => 0x20;
|
||||
@override
|
||||
int get Closure_function_type_arguments_offset => 0x10;
|
||||
@override
|
||||
int get Closure_hash_offset => 0x30;
|
||||
@override
|
||||
int get Closure_instantiator_type_arguments_offset => 0x8;
|
||||
int get Closure_length_and_flags_offset => 0x8;
|
||||
@override
|
||||
int get ClosureData_packed_fields_offset => 0x20;
|
||||
@override
|
||||
@@ -1342,8 +1362,6 @@ final class Arm64VMOffsets extends VMOffsets {
|
||||
@override
|
||||
int get Class_InstanceSize => 0xc8;
|
||||
@override
|
||||
int get Closure_InstanceSize => 0x38;
|
||||
@override
|
||||
int get ClosureData_InstanceSize => 0x28;
|
||||
@override
|
||||
int get CodeSourceMap_HeaderSize => 0x10;
|
||||
@@ -1499,6 +1517,10 @@ final class Arm64ProductVMOffsets extends VMOffsets {
|
||||
@override
|
||||
int get Array_elementSize => 0x8;
|
||||
@override
|
||||
int get Closure_elementsStartOffset => 0x20;
|
||||
@override
|
||||
int get Closure_elementSize => 0x8;
|
||||
@override
|
||||
int get Code_elementsStartOffset => 0x90;
|
||||
@override
|
||||
int get Code_elementSize => 0x4;
|
||||
@@ -1605,6 +1627,20 @@ final class Arm64ProductVMOffsets extends VMOffsets {
|
||||
@override
|
||||
int get TypeArguments_kMaxElements => 0x7ffffffffffffff;
|
||||
@override
|
||||
int get UntaggedClosure_kHasDelayedTypeArgumentsBit => 0x0;
|
||||
@override
|
||||
int get UntaggedClosure_kHasInstantiatorTypeArgumentsBit => 0x1;
|
||||
@override
|
||||
int get UntaggedClosure_kHasFunctionTypeArgumentsBit => 0x2;
|
||||
@override
|
||||
int get UntaggedClosure_kFunctionTypeArgumentsIndexBitsPos => 0x3;
|
||||
@override
|
||||
int get UntaggedClosure_kFunctionTypeArgumentsIndexBitsSize => 0x2;
|
||||
@override
|
||||
int get UntaggedClosure_kLengthBitsPos => 0x5;
|
||||
@override
|
||||
int get UntaggedClosure_kLengthBitsSize => 0x39;
|
||||
@override
|
||||
int get UntaggedObject_kCardRememberedBit => 0x0;
|
||||
@override
|
||||
int get UntaggedObject_kCanonicalBit => 0x1;
|
||||
@@ -1677,17 +1713,11 @@ final class Arm64ProductVMOffsets extends VMOffsets {
|
||||
@override
|
||||
int get Class_host_type_arguments_field_offset_in_words_offset => 0xb4;
|
||||
@override
|
||||
int get Closure_context_offset => 0x28;
|
||||
int get Closure_function_offset => 0x18;
|
||||
@override
|
||||
int get Closure_delayed_type_arguments_offset => 0x18;
|
||||
int get Closure_hash_offset => 0x10;
|
||||
@override
|
||||
int get Closure_function_offset => 0x20;
|
||||
@override
|
||||
int get Closure_function_type_arguments_offset => 0x10;
|
||||
@override
|
||||
int get Closure_hash_offset => 0x30;
|
||||
@override
|
||||
int get Closure_instantiator_type_arguments_offset => 0x8;
|
||||
int get Closure_length_and_flags_offset => 0x8;
|
||||
@override
|
||||
int get ClosureData_packed_fields_offset => 0x20;
|
||||
@override
|
||||
@@ -2287,8 +2317,6 @@ final class Arm64ProductVMOffsets extends VMOffsets {
|
||||
@override
|
||||
int get Class_InstanceSize => 0xc0;
|
||||
@override
|
||||
int get Closure_InstanceSize => 0x38;
|
||||
@override
|
||||
int get ClosureData_InstanceSize => 0x28;
|
||||
@override
|
||||
int get CodeSourceMap_HeaderSize => 0x10;
|
||||
@@ -2431,7 +2459,6 @@ final class Arm64ProductVMOffsets extends VMOffsets {
|
||||
}
|
||||
|
||||
enum StubCode {
|
||||
GetCStackPointer,
|
||||
JumpToFrame,
|
||||
RunExceptionHandler,
|
||||
RunExceptionHandlerUnbox,
|
||||
@@ -2462,10 +2489,10 @@ enum StubCode {
|
||||
AllocateFloat64x2Array,
|
||||
AllocateMintSharedWithFPURegs,
|
||||
AllocateMintSharedWithoutFPURegs,
|
||||
AllocateClosure,
|
||||
AllocateClosureGeneric,
|
||||
AllocateClosureTA,
|
||||
AllocateClosureTAGeneric,
|
||||
AllocateClosure1,
|
||||
AllocateClosure2,
|
||||
AllocateClosure3,
|
||||
AllocateClosure4,
|
||||
AllocateContext,
|
||||
AllocateGrowableArray,
|
||||
AllocateObject,
|
||||
|
||||
@@ -806,6 +806,7 @@ void main() async {
|
||||
'#type': 'class',
|
||||
'makeSomeClosures': {
|
||||
'#type': 'function',
|
||||
'#size': lessThan(0),
|
||||
'<anonymous closure>': {
|
||||
'#type': 'function',
|
||||
'#size': lessThan(0),
|
||||
@@ -886,7 +887,7 @@ Map<String, dynamic>? diffToJson(ProgramInfo diff,
|
||||
keepOnlyInputPackage ? key != 'package:input' : key.startsWith('file:'));
|
||||
|
||||
// Rebuild the diff JSON discarding all nodes with size below threshold.
|
||||
const smallChangeThreshold = 16;
|
||||
const smallChangeThreshold = 7;
|
||||
Map<String, dynamic>? discardSmallChanges(Map<String, dynamic> map) {
|
||||
final result = <String, dynamic>{};
|
||||
|
||||
|
||||
@@ -59,7 +59,12 @@ main() async {
|
||||
if (uri == '') {
|
||||
// We don't verify non-user-visible objects.
|
||||
} else if (uri.startsWith('dart') &&
|
||||
['Array', 'List', 'Record'].any((p) => klass.name.contains(p))) {
|
||||
[
|
||||
'Array',
|
||||
'Closure',
|
||||
'List',
|
||||
'Record',
|
||||
].any((p) => klass.name.contains(p))) {
|
||||
Expect.isTrue(fields.length <= object.references.length);
|
||||
} else {
|
||||
// For objects with vm-defined layouts, this fails if a new field is
|
||||
|
||||
@@ -34,7 +34,7 @@ void matchIL$main_testForIn(FlowGraph graph) {
|
||||
match.block('Graph'),
|
||||
match.block('Function', [
|
||||
'v2' << match.Parameter(index: 0),
|
||||
'v3' << match.LoadField('v2', slot: 'Closure.context'),
|
||||
'v3' << match.LoadField('v2', slot: ':closure_element[0]'),
|
||||
'v4' << match.LoadField('v3', slot: 'list'),
|
||||
'v92' << match.LoadField('v4', slot: 'GrowableObjectArray.length'),
|
||||
if (!is32BitConfiguration) 'v112' << match.UnboxInt64('v92'),
|
||||
|
||||
@@ -116,7 +116,8 @@ void matchIL$testCSE2(FlowGraph graph) {
|
||||
'b' << match.Parameter(index: 0),
|
||||
match.CheckStackOverflow(),
|
||||
'b_type_args' << match.LoadField('b'),
|
||||
'b_bar' << match.AllocateClosure(match.any, 'b', 'b_type_args'),
|
||||
'b_bar' << match.AllocateClosure(match.any, 'b'),
|
||||
match.StoreField('b_bar', 'b_type_args', slot: ':closure_element[1]'),
|
||||
match.MoveArgument('b_bar'),
|
||||
match.StaticCall(),
|
||||
'cond' << match.LoadStaticField(),
|
||||
@@ -145,7 +146,8 @@ void matchIL$testCSE3(FlowGraph graph) {
|
||||
'b' << match.Parameter(index: 0),
|
||||
match.CheckStackOverflow(),
|
||||
'b_type_args' << match.LoadField('b'),
|
||||
'b_bar' << match.AllocateClosure(match.any, 'b', 'b_type_args'),
|
||||
'b_bar' << match.AllocateClosure(match.any, 'b'),
|
||||
match.StoreField('b_bar', 'b_type_args', slot: ':closure_element[1]'),
|
||||
match.MoveArgument('b_bar'),
|
||||
match.MoveArgument(match.any),
|
||||
'b_bar_int' << match.StaticCall(), // _instantiateClosure
|
||||
@@ -212,7 +214,8 @@ void matchIL$testLICM2(FlowGraph graph) {
|
||||
'b' << match.Parameter(index: 0),
|
||||
match.CheckStackOverflow(),
|
||||
'b_type_args' << match.LoadField('b'),
|
||||
'b_bar' << match.AllocateClosure(match.any, 'b', 'b_type_args'),
|
||||
'b_bar' << match.AllocateClosure(match.any, 'b'),
|
||||
match.StoreField('b_bar', 'b_type_args', slot: ':closure_element[1]'),
|
||||
match.Goto('B5'),
|
||||
]),
|
||||
'B5' <<
|
||||
|
||||
@@ -5257,7 +5257,7 @@ class ClosureSerializationCluster : public SerializationCluster {
|
||||
ClosureSerializationCluster(bool is_canonical, bool is_deeply_immutable)
|
||||
: SerializationCluster("Closure",
|
||||
kClosureCid,
|
||||
compiler::target::Closure::InstanceSize(),
|
||||
kSizeVaries,
|
||||
is_canonical,
|
||||
is_deeply_immutable) {}
|
||||
~ClosureSerializationCluster() {}
|
||||
@@ -5265,7 +5265,9 @@ class ClosureSerializationCluster : public SerializationCluster {
|
||||
void Trace(Serializer* s, ObjectPtr object) {
|
||||
ClosurePtr closure = Closure::RawCast(object);
|
||||
objects_.Add(closure);
|
||||
PushFromTo(closure);
|
||||
const intptr_t length = UntaggedClosure::LengthBits::decode(
|
||||
Smi::Value(closure->untag()->length_and_flags()));
|
||||
PushFromTo(closure, length);
|
||||
}
|
||||
|
||||
void WriteAlloc(Serializer* s) {
|
||||
@@ -5274,6 +5276,11 @@ class ClosureSerializationCluster : public SerializationCluster {
|
||||
for (intptr_t i = 0; i < count; i++) {
|
||||
ClosurePtr closure = objects_[i];
|
||||
s->AssignRef(closure);
|
||||
AutoTraceObject(closure);
|
||||
const intptr_t length = UntaggedClosure::LengthBits::decode(
|
||||
Smi::Value(closure->untag()->length_and_flags()));
|
||||
s->WriteUnsigned(length);
|
||||
target_memory_size_ += compiler::target::Closure::InstanceSize(length);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5281,8 +5288,11 @@ class ClosureSerializationCluster : public SerializationCluster {
|
||||
const intptr_t count = objects_.length();
|
||||
for (intptr_t i = 0; i < count; i++) {
|
||||
ClosurePtr closure = objects_[i];
|
||||
const intptr_t length = UntaggedClosure::LengthBits::decode(
|
||||
Smi::Value(closure->untag()->length_and_flags()));
|
||||
AutoTraceObject(closure);
|
||||
WriteFromTo(closure);
|
||||
s->WriteUnsigned(length);
|
||||
WriteFromTo(closure, length);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5304,7 +5314,13 @@ class ClosureDeserializationCluster
|
||||
~ClosureDeserializationCluster() {}
|
||||
|
||||
void ReadAlloc(Deserializer* d) override {
|
||||
ReadAllocFixedSize(d, Closure::InstanceSize());
|
||||
start_index_ = d->next_index();
|
||||
const intptr_t count = d->ReadUnsigned();
|
||||
for (intptr_t i = 0; i < count; i++) {
|
||||
const intptr_t length = d->ReadUnsigned();
|
||||
d->AssignRef(d->Allocate(Closure::InstanceSize(length)));
|
||||
}
|
||||
stop_index_ = d->next_index();
|
||||
}
|
||||
|
||||
void ReadFill(Deserializer* d_) override {
|
||||
@@ -5313,10 +5329,11 @@ class ClosureDeserializationCluster
|
||||
const bool mark_canonical = is_root_unit_ && is_canonical();
|
||||
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
||||
ClosurePtr closure = static_cast<ClosurePtr>(d.Ref(id));
|
||||
const intptr_t length = d.ReadUnsigned();
|
||||
Deserializer::InitializeHeader(closure, kClosureCid,
|
||||
Closure::InstanceSize(), mark_canonical,
|
||||
is_deeply_immutable());
|
||||
d.ReadFromTo(closure);
|
||||
Closure::InstanceSize(length),
|
||||
mark_canonical, is_deeply_immutable());
|
||||
d.ReadFromTo(closure, length);
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
closure->untag()->entry_point_ = 0;
|
||||
#endif
|
||||
|
||||
@@ -55,40 +55,6 @@ static void Finish(Thread* thread) {
|
||||
Class& cls = Class::Handle(zone, object_store->closure_class());
|
||||
cls.EnsureIsFinalized(thread);
|
||||
|
||||
// Make sure _Closure fields are not marked as unboxed as they are accessed
|
||||
// with plain loads.
|
||||
const Array& fields = Array::Handle(zone, cls.fields());
|
||||
Field& field = Field::Handle(zone);
|
||||
for (intptr_t i = 0; i < fields.Length(); ++i) {
|
||||
field ^= fields.At(i);
|
||||
field.set_is_unboxed(false);
|
||||
}
|
||||
// _Closure._hash field should be explicitly marked as nullable because
|
||||
// VM creates instances of _Closure without compiling its constructors,
|
||||
// so it won't get nullability info from a constructor.
|
||||
field ^= fields.At(fields.Length() - 1);
|
||||
// Note that UserVisibleName depends on --show-internal-names.
|
||||
ASSERT(strncmp(field.UserVisibleNameCString(), "_hash", 5) == 0);
|
||||
field.RecordStore(Object::null_object());
|
||||
|
||||
#if defined(DEBUG)
|
||||
// Verify that closure field offsets are identical in Dart and C++.
|
||||
ASSERT_EQUAL(fields.Length(), 6);
|
||||
field ^= fields.At(0);
|
||||
ASSERT_EQUAL(field.HostOffset(),
|
||||
Closure::instantiator_type_arguments_offset());
|
||||
field ^= fields.At(1);
|
||||
ASSERT_EQUAL(field.HostOffset(), Closure::function_type_arguments_offset());
|
||||
field ^= fields.At(2);
|
||||
ASSERT_EQUAL(field.HostOffset(), Closure::delayed_type_arguments_offset());
|
||||
field ^= fields.At(3);
|
||||
ASSERT_EQUAL(field.HostOffset(), Closure::function_offset());
|
||||
field ^= fields.At(4);
|
||||
ASSERT_EQUAL(field.HostOffset(), Closure::context_offset());
|
||||
field ^= fields.At(5);
|
||||
ASSERT_EQUAL(field.HostOffset(), Closure::hash_offset());
|
||||
#endif // defined(DEBUG)
|
||||
|
||||
// Eagerly compile to avoid repeated checks when loading constants or
|
||||
// serializing.
|
||||
cls = object_store->null_class();
|
||||
|
||||
@@ -574,6 +574,7 @@ intptr_t BytecodeReaderHelper::ReadConstantPool(const Function& function,
|
||||
kExternalCall,
|
||||
kFfiCall,
|
||||
kDeferredLibraryPrefix,
|
||||
kAllocateClosure,
|
||||
};
|
||||
|
||||
Object& obj = Object::Handle(Z);
|
||||
@@ -766,6 +767,32 @@ intptr_t BytecodeReaderHelper::ReadConstantPool(const Function& function,
|
||||
}
|
||||
ASSERT(LibraryPrefix::Cast(obj).GetLibrary(0) == target_library.ptr());
|
||||
} break;
|
||||
case ConstantPoolTag::kAllocateClosure: {
|
||||
const intptr_t closure_index = reader_.ReadUInt();
|
||||
const intptr_t num_elements = reader_.ReadUInt();
|
||||
const intptr_t flags = reader_.ReadUInt();
|
||||
// AllocateClosure flags, must be in sync with ConstantAllocateClosure
|
||||
// constants in pkg/dart2bytecode/lib/constant_pool.dart.
|
||||
const intptr_t kHasDelayedTypeArguments = 1 << 0;
|
||||
const intptr_t kHasInstantiatorTypeArguments = 1 << 1;
|
||||
const intptr_t kHasFunctionTypeArguments = 1 << 2;
|
||||
// AllocateClosure constant occupies 2 entries:
|
||||
// function and length_and_flags.
|
||||
obj = closures_->At(closure_index);
|
||||
ASSERT(obj.IsFunction());
|
||||
// Set current entry.
|
||||
pool.SetTypeAt(i, ObjectPool::EntryType::kTaggedObject,
|
||||
ObjectPool::Patchability::kNotPatchable,
|
||||
ObjectPool::SnapshotBehavior::kNotSnapshotable);
|
||||
pool.SetObjectAt(i, obj);
|
||||
++i;
|
||||
ASSERT(i < obj_count);
|
||||
// The second entry is used for encoded length and flags.
|
||||
obj = Smi::New(UntaggedClosure::EncodeLengthAndFlags(
|
||||
(flags & kHasDelayedTypeArguments) != 0,
|
||||
(flags & kHasInstantiatorTypeArguments) != 0,
|
||||
(flags & kHasFunctionTypeArguments) != 0, num_elements));
|
||||
} break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
@@ -1098,6 +1098,13 @@ class Assembler : public AssemblerBase {
|
||||
// Stores a Smi value into a heap object field that always contains a Smi.
|
||||
void StoreIntoSmiField(const Address& dest, Register value);
|
||||
|
||||
void ExtractBitField(Register dst,
|
||||
Register src,
|
||||
intptr_t low_bit,
|
||||
intptr_t width) override {
|
||||
ubfx(dst, src, low_bit, width);
|
||||
}
|
||||
|
||||
void ExtractClassIdFromTags(Register result,
|
||||
Register tags,
|
||||
Condition cond = AL);
|
||||
|
||||
@@ -2103,6 +2103,13 @@ class Assembler : public AssemblerBase {
|
||||
void PushImmediate(Immediate immediate) { PushImmediate(immediate.value()); }
|
||||
void CompareObject(Register reg, const Object& object);
|
||||
|
||||
void ExtractBitField(Register dst,
|
||||
Register src,
|
||||
intptr_t low_bit,
|
||||
intptr_t width) override {
|
||||
ubfx(dst, src, low_bit, width);
|
||||
}
|
||||
|
||||
void ExtractClassIdFromTags(Register result, Register tags);
|
||||
void ExtractInstanceSizeFromTags(Register result, Register tags);
|
||||
|
||||
|
||||
@@ -1174,6 +1174,11 @@ class AssemblerBase : public StackResource {
|
||||
// or the architecture must define a TMP register, which is clobbered.
|
||||
virtual void LslRegister(Register dst, Register shift) = 0;
|
||||
|
||||
virtual void ExtractBitField(Register dst,
|
||||
Register src,
|
||||
intptr_t low_bit,
|
||||
intptr_t width) = 0;
|
||||
|
||||
// Performs CombineHashes from runtime/vm/hash.h on the hashes contained in
|
||||
// dst and other. Puts the result in dst. Clobbers other.
|
||||
//
|
||||
|
||||
@@ -3032,6 +3032,17 @@ void Assembler::EmitGenericShift(int rm,
|
||||
EmitOperand(rm, Operand(operand));
|
||||
}
|
||||
|
||||
void Assembler::ExtractBitField(Register dst,
|
||||
Register src,
|
||||
intptr_t low_bit,
|
||||
intptr_t width) {
|
||||
MoveRegister(dst, src);
|
||||
if (low_bit > 0) {
|
||||
LsrImmediate(dst, low_bit);
|
||||
}
|
||||
AndImmediate(dst, (1 << width) - 1);
|
||||
}
|
||||
|
||||
void Assembler::LoadClassId(Register result, Register object) {
|
||||
ASSERT(target::UntaggedObject::kClassIdTagPos == 12);
|
||||
ASSERT(target::UntaggedObject::kClassIdTagSize == 20);
|
||||
|
||||
@@ -942,6 +942,11 @@ class Assembler : public AssemblerBase {
|
||||
RangeCheckCondition condition,
|
||||
Label* target) override;
|
||||
|
||||
void ExtractBitField(Register dst,
|
||||
Register src,
|
||||
intptr_t low_bit,
|
||||
intptr_t width) override;
|
||||
|
||||
/*
|
||||
* Loading and comparing classes of objects.
|
||||
*/
|
||||
|
||||
@@ -4799,6 +4799,26 @@ void Assembler::CompareObject(Register reg, const Object& object) {
|
||||
}
|
||||
}
|
||||
|
||||
void Assembler::ExtractBitField(Register dst,
|
||||
Register src,
|
||||
intptr_t low_bit,
|
||||
intptr_t width) {
|
||||
ASSERT((0 <= low_bit) && (low_bit + width <= XLEN));
|
||||
if (width == 1) {
|
||||
if (low_bit == 0) {
|
||||
andi(dst, src, 1);
|
||||
return;
|
||||
} else if (Supports(RV_Zbs)) {
|
||||
bexti(dst, src, low_bit);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (low_bit + width < XLEN) {
|
||||
slli(dst, src, XLEN - (low_bit + width));
|
||||
}
|
||||
srli(dst, dst, XLEN - width);
|
||||
}
|
||||
|
||||
void Assembler::ExtractClassIdFromTags(Register result, Register tags) {
|
||||
ASSERT(target::UntaggedObject::kClassIdTagPos == 12);
|
||||
ASSERT(target::UntaggedObject::kClassIdTagSize == 20);
|
||||
|
||||
@@ -1494,6 +1494,11 @@ class Assembler : public MicroAssembler {
|
||||
}
|
||||
void CompareObject(Register reg, const Object& object);
|
||||
|
||||
void ExtractBitField(Register dst,
|
||||
Register src,
|
||||
intptr_t low_bit,
|
||||
intptr_t width) override;
|
||||
|
||||
void ExtractClassIdFromTags(Register result, Register tags);
|
||||
void ExtractInstanceSizeFromTags(Register result, Register tags);
|
||||
|
||||
|
||||
@@ -2770,6 +2770,17 @@ void Assembler::EmitGenericShift(bool wide,
|
||||
EmitOperand(rm, Operand(operand));
|
||||
}
|
||||
|
||||
void Assembler::ExtractBitField(Register dst,
|
||||
Register src,
|
||||
intptr_t low_bit,
|
||||
intptr_t width) {
|
||||
MoveRegister(dst, src);
|
||||
if (low_bit > 0) {
|
||||
LsrImmediate(dst, low_bit);
|
||||
}
|
||||
AndImmediate(dst, Immediate((1 << width) - 1));
|
||||
}
|
||||
|
||||
void Assembler::ExtractClassIdFromTags(Register result, Register tags) {
|
||||
ASSERT(target::UntaggedObject::kClassIdTagPos == 12);
|
||||
ASSERT(target::UntaggedObject::kClassIdTagSize == 20);
|
||||
|
||||
@@ -979,6 +979,11 @@ class Assembler : public AssemblerBase {
|
||||
void CallCFunction(Register reg, bool restore_rsp = false);
|
||||
void CallCFunction(Address address, bool restore_rsp = false);
|
||||
|
||||
void ExtractBitField(Register dst,
|
||||
Register src,
|
||||
intptr_t low_bit,
|
||||
intptr_t width) override;
|
||||
|
||||
void ExtractClassIdFromTags(Register result, Register tags);
|
||||
void ExtractInstanceSizeFromTags(Register result, Register tags);
|
||||
|
||||
|
||||
@@ -61,6 +61,12 @@ DEFINE_FLAG(bool,
|
||||
DECLARE_FLAG(bool, inline_alloc);
|
||||
DECLARE_FLAG(bool, use_slow_path);
|
||||
|
||||
// Macro for shared code generation methods (EmitNativeCode and
|
||||
// MakeLocationSummary). Only assembly code that can be shared across all
|
||||
// architectures can be used. Machine specific register allocation and code
|
||||
// generation is located in il_<arch>.cc
|
||||
#define __ compiler->assembler()->
|
||||
|
||||
class SubtypeFinder {
|
||||
public:
|
||||
SubtypeFinder(Zone* zone,
|
||||
@@ -961,11 +967,6 @@ LocationSummary* AllocateClosureInstr::MakeLocationSummary(Zone* zone,
|
||||
Location::RegisterLocation(AllocateClosureABI::kFunctionReg));
|
||||
locs->set_in(kContextPos,
|
||||
Location::RegisterLocation(AllocateClosureABI::kContextReg));
|
||||
if (has_instantiator_type_args()) {
|
||||
locs->set_in(kInstantiatorTypeArgsPos,
|
||||
Location::RegisterLocation(
|
||||
AllocateClosureABI::kInstantiatorTypeArgsReg));
|
||||
}
|
||||
locs->set_out(0, Location::RegisterLocation(AllocateClosureABI::kResultReg));
|
||||
return locs;
|
||||
}
|
||||
@@ -973,19 +974,25 @@ LocationSummary* AllocateClosureInstr::MakeLocationSummary(Zone* zone,
|
||||
void AllocateClosureInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
auto object_store = compiler->isolate_group()->object_store();
|
||||
Code& stub = Code::ZoneHandle(compiler->zone());
|
||||
if (has_instantiator_type_args()) {
|
||||
if (is_generic()) {
|
||||
stub = object_store->allocate_closure_ta_generic_stub();
|
||||
} else {
|
||||
stub = object_store->allocate_closure_ta_stub();
|
||||
}
|
||||
} else {
|
||||
if (is_generic()) {
|
||||
stub = object_store->allocate_closure_generic_stub();
|
||||
} else {
|
||||
stub = object_store->allocate_closure_stub();
|
||||
}
|
||||
const intptr_t num_elements = NumElements();
|
||||
switch (num_elements) {
|
||||
case 1:
|
||||
stub = object_store->allocate_closure1_stub();
|
||||
break;
|
||||
case 2:
|
||||
stub = object_store->allocate_closure2_stub();
|
||||
break;
|
||||
case 3:
|
||||
stub = object_store->allocate_closure3_stub();
|
||||
break;
|
||||
case 4:
|
||||
stub = object_store->allocate_closure4_stub();
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
__ LoadImmediate(AllocateClosureABI::kLengthAndFlagsReg,
|
||||
compiler::target::ToRawSmi(EncodedLengthAndFlags()));
|
||||
compiler->GenerateStubCall(source(), stub, UntaggedPcDescriptors::kOther,
|
||||
locs(), deopt_id(), env());
|
||||
}
|
||||
@@ -2754,8 +2761,21 @@ bool LoadFieldInstr::TryEvaluateLoad(const Object& instance,
|
||||
const Record& record = Record::Cast(instance);
|
||||
if (index < record.num_fields()) {
|
||||
*result = record.FieldAt(index);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
case Slot::Kind::kClosureElement:
|
||||
if (instance.IsClosure()) {
|
||||
const intptr_t index =
|
||||
compiler::target::Closure::element_index_at_offset(
|
||||
field.offset_in_bytes());
|
||||
const Closure& closure = Closure::Cast(instance);
|
||||
if (index < closure.length()) {
|
||||
*result = closure.ElementAt(index);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -4247,13 +4267,6 @@ void CallTargets::Print() const {
|
||||
}
|
||||
}
|
||||
|
||||
// Shared code generation methods (EmitNativeCode and
|
||||
// MakeLocationSummary). Only assembly code that can be shared across all
|
||||
// architectures can be used. Machine specific register allocation and code
|
||||
// generation is located in intermediate_language_<arch>.cc
|
||||
|
||||
#define __ compiler->assembler()->
|
||||
|
||||
LocationSummary* GraphEntryInstr::MakeLocationSummary(Zone* zone,
|
||||
bool optimizing) const {
|
||||
UNREACHABLE();
|
||||
|
||||
@@ -7584,49 +7584,53 @@ class AllocateObjectInstr : public AllocationInstr {
|
||||
DISALLOW_COPY_AND_ASSIGN(AllocateObjectInstr);
|
||||
};
|
||||
|
||||
// Allocates and null initializes a closure object, given the closure function
|
||||
// and the context as values.
|
||||
class AllocateClosureInstr : public TemplateAllocation<3> {
|
||||
// Allocates and null initializes a closure object.
|
||||
class AllocateClosureInstr : public TemplateAllocation<2> {
|
||||
public:
|
||||
enum Inputs {
|
||||
kFunctionPos = 0,
|
||||
kContextPos = 1,
|
||||
kInstantiatorTypeArgsPos = 2,
|
||||
};
|
||||
AllocateClosureInstr(const InstructionSource& source,
|
||||
Value* closure_function,
|
||||
Value* context,
|
||||
Value* instantiator_type_args, // Optional.
|
||||
bool is_generic,
|
||||
bool has_delayed_type_args,
|
||||
bool has_instantiator_type_args,
|
||||
bool has_function_type_args,
|
||||
bool is_tear_off,
|
||||
intptr_t deopt_id)
|
||||
: TemplateAllocation(source, deopt_id),
|
||||
has_instantiator_type_args_(instantiator_type_args != nullptr),
|
||||
is_generic_(is_generic),
|
||||
has_delayed_type_args_(has_delayed_type_args),
|
||||
has_instantiator_type_args_(has_instantiator_type_args),
|
||||
has_function_type_args_(has_function_type_args),
|
||||
is_tear_off_(is_tear_off) {
|
||||
SetInputAt(kFunctionPos, closure_function);
|
||||
SetInputAt(kContextPos, context);
|
||||
if (has_instantiator_type_args_) {
|
||||
SetInputAt(kInstantiatorTypeArgsPos, instantiator_type_args);
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_INSTRUCTION(AllocateClosure)
|
||||
virtual CompileType ComputeType() const;
|
||||
|
||||
virtual intptr_t InputCount() const {
|
||||
return has_instantiator_type_args() ? 3 : 2;
|
||||
}
|
||||
virtual intptr_t InputCount() const { return 2; }
|
||||
|
||||
Value* closure_function() const { return inputs_[kFunctionPos]; }
|
||||
Value* context() const { return inputs_[kContextPos]; }
|
||||
|
||||
bool has_instantiator_type_args() const {
|
||||
return has_instantiator_type_args_;
|
||||
}
|
||||
bool is_generic() const { return is_generic_; }
|
||||
bool is_tear_off() const { return is_tear_off_; }
|
||||
|
||||
intptr_t NumElements() const {
|
||||
return UntaggedClosure::ContextIndex(has_delayed_type_args_,
|
||||
has_instantiator_type_args_,
|
||||
has_function_type_args_) +
|
||||
1;
|
||||
}
|
||||
|
||||
intptr_t EncodedLengthAndFlags() const {
|
||||
return UntaggedClosure::EncodeLengthAndFlags(
|
||||
has_delayed_type_args_, has_instantiator_type_args_,
|
||||
has_function_type_args_, NumElements());
|
||||
}
|
||||
|
||||
const Function& known_function() const {
|
||||
Value* const value = closure_function();
|
||||
if (value->BindsToConstant()) {
|
||||
@@ -7641,11 +7645,12 @@ class AllocateClosureInstr : public TemplateAllocation<3> {
|
||||
case kFunctionPos:
|
||||
return &Slot::Closure_function();
|
||||
case kContextPos:
|
||||
return &Slot::Closure_context();
|
||||
case kInstantiatorTypeArgsPos:
|
||||
return has_instantiator_type_args()
|
||||
? &Slot::Closure_instantiator_type_arguments()
|
||||
: nullptr;
|
||||
return &Slot::GetClosureElementSlot(
|
||||
Thread::Current(),
|
||||
compiler::target::Closure::element_offset(
|
||||
UntaggedClosure::ContextIndex(has_delayed_type_args_,
|
||||
has_instantiator_type_args_,
|
||||
has_function_type_args_)));
|
||||
default:
|
||||
return TemplateAllocation::SlotForInput(pos);
|
||||
}
|
||||
@@ -7659,20 +7664,22 @@ class AllocateClosureInstr : public TemplateAllocation<3> {
|
||||
|
||||
virtual bool AttributesEqual(const Instruction& other) const {
|
||||
const auto other_ac = other.AsAllocateClosure();
|
||||
return (other_ac->has_instantiator_type_args() ==
|
||||
has_instantiator_type_args()) &&
|
||||
(other_ac->is_generic() == is_generic()) &&
|
||||
return (other_ac->has_delayed_type_args_ == has_delayed_type_args_) &&
|
||||
(other_ac->has_instantiator_type_args_ ==
|
||||
has_instantiator_type_args_) &&
|
||||
(other_ac->has_function_type_args_ == has_function_type_args_) &&
|
||||
(other_ac->is_tear_off() == is_tear_off());
|
||||
}
|
||||
|
||||
virtual bool WillAllocateNewOrRemembered() const {
|
||||
return compiler::target::Heap::IsAllocatableInNewSpace(
|
||||
compiler::target::Closure::InstanceSize());
|
||||
compiler::target::Closure::InstanceSize(NumElements()));
|
||||
}
|
||||
|
||||
#define FIELD_LIST(F) \
|
||||
F(const bool, has_delayed_type_args_) \
|
||||
F(const bool, has_instantiator_type_args_) \
|
||||
F(const bool, is_generic_) \
|
||||
F(const bool, has_function_type_args_) \
|
||||
F(const bool, is_tear_off_)
|
||||
|
||||
DECLARE_INSTRUCTION_SERIALIZABLE_FIELDS(AllocateClosureInstr,
|
||||
|
||||
@@ -2265,7 +2265,8 @@ void LoadIndexedInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
const Register result = locs()->out(0).reg();
|
||||
ASSERT(rep == kTagged);
|
||||
ASSERT((class_id() == kArrayCid) || (class_id() == kImmutableArrayCid) ||
|
||||
(class_id() == kTypeArgumentsCid) || (class_id() == kRecordCid));
|
||||
(class_id() == kTypeArgumentsCid) || (class_id() == kClosureCid) ||
|
||||
(class_id() == kRecordCid));
|
||||
__ ldr(result, element_address);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1978,7 +1978,8 @@ void LoadIndexedInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
const Register result = locs()->out(0).reg();
|
||||
ASSERT(representation() == kTagged);
|
||||
ASSERT((class_id() == kArrayCid) || (class_id() == kImmutableArrayCid) ||
|
||||
(class_id() == kTypeArgumentsCid) || (class_id() == kRecordCid));
|
||||
(class_id() == kTypeArgumentsCid) || (class_id() == kClosureCid) ||
|
||||
(class_id() == kRecordCid));
|
||||
__ LoadCompressed(result, element_address);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1673,7 +1673,8 @@ void LoadIndexedInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
const Register result = locs()->out(0).reg();
|
||||
ASSERT(representation() == kTagged);
|
||||
ASSERT((class_id() == kArrayCid) || (class_id() == kImmutableArrayCid) ||
|
||||
(class_id() == kTypeArgumentsCid) || (class_id() == kRecordCid));
|
||||
(class_id() == kTypeArgumentsCid) || (class_id() == kClosureCid) ||
|
||||
(class_id() == kRecordCid));
|
||||
__ movl(result, element_address);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2067,7 +2067,8 @@ void LoadIndexedInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
} else {
|
||||
ASSERT(rep == kTagged);
|
||||
ASSERT((class_id() == kArrayCid) || (class_id() == kImmutableArrayCid) ||
|
||||
(class_id() == kTypeArgumentsCid) || (class_id() == kRecordCid));
|
||||
(class_id() == kTypeArgumentsCid) || (class_id() == kClosureCid) ||
|
||||
(class_id() == kRecordCid));
|
||||
const Register result = locs()->out(0).reg();
|
||||
__ Load(result, element_address);
|
||||
}
|
||||
|
||||
@@ -2413,6 +2413,9 @@ void Slot::Write(FlowGraphSerializer* s) const {
|
||||
case Kind::kRecordField:
|
||||
s->Write<intptr_t>(offset_in_bytes_);
|
||||
break;
|
||||
case Kind::kClosureElement:
|
||||
s->Write<intptr_t>(offset_in_bytes_);
|
||||
break;
|
||||
case Kind::kCapturedVariable:
|
||||
s->Write<int8_t>(flags_);
|
||||
s->Write<intptr_t>(offset_in_bytes_);
|
||||
@@ -2463,6 +2466,14 @@ const Slot& Slot::Read(FlowGraphDeserializer* d) {
|
||||
data = ":record_field";
|
||||
type = CompileType::Dynamic();
|
||||
break;
|
||||
case Kind::kClosureElement:
|
||||
flags = IsCompressedBit::encode(Closure::ContainsCompressedPointers());
|
||||
offset = d->Read<intptr_t>();
|
||||
data = OS::SCreate(
|
||||
d->zone(), ":closure_element[%" Pd "]",
|
||||
compiler::target::Closure::element_index_at_offset(offset));
|
||||
type = CompileType::Dynamic();
|
||||
break;
|
||||
case Kind::kCapturedVariable:
|
||||
flags = d->Read<int8_t>();
|
||||
offset = d->Read<intptr_t>();
|
||||
|
||||
@@ -182,10 +182,10 @@ ISOLATE_UNIT_TEST_CASE(IRTest_InitializingStores) {
|
||||
expected_stores_jit.insert(
|
||||
expected_stores_jit.end(),
|
||||
{"value", "Context.parent", "Context.parent", "value",
|
||||
"Closure.function_type_arguments", "Closure.context"});
|
||||
":closure_element[0]", ":closure_element[0]"});
|
||||
expected_stores_aot.insert(
|
||||
expected_stores_aot.end(),
|
||||
{"value", "Closure.function_type_arguments", "Closure.context"});
|
||||
{"value", ":closure_element[0]", ":closure_element[0]"});
|
||||
|
||||
RunInitializingStoresTest(root_library, "f4", CompilerPass::kJIT,
|
||||
expected_stores_jit);
|
||||
|
||||
@@ -1885,7 +1885,8 @@ void LoadIndexedInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
} else {
|
||||
ASSERT(rep == kTagged);
|
||||
ASSERT((class_id() == kArrayCid) || (class_id() == kImmutableArrayCid) ||
|
||||
(class_id() == kTypeArgumentsCid) || (class_id() == kRecordCid));
|
||||
(class_id() == kTypeArgumentsCid) || (class_id() == kClosureCid) ||
|
||||
(class_id() == kRecordCid));
|
||||
Register result = locs()->out(0).reg();
|
||||
__ LoadCompressed(result, element_address);
|
||||
}
|
||||
|
||||
@@ -93,6 +93,7 @@ Representation RepresentationUtils::RepresentationOfArrayElement(
|
||||
#define ARRAY_CASE(Name) case k##Name##Cid:
|
||||
CLASS_LIST_ARRAYS(ARRAY_CASE)
|
||||
#undef ARRAY_CASE
|
||||
case kClosureCid:
|
||||
case kRecordCid:
|
||||
case kTypeArgumentsCid:
|
||||
return kTagged;
|
||||
|
||||
@@ -2843,6 +2843,7 @@ void LoadFieldInstr::InferRange(RangeAnalysis* analysis, Range* range) {
|
||||
break;
|
||||
|
||||
case Slot::Kind::kDartField:
|
||||
case Slot::Kind::kClosureElement:
|
||||
case Slot::Kind::kCapturedVariable:
|
||||
case Slot::Kind::kRecordField:
|
||||
// Use default value.
|
||||
@@ -2872,6 +2873,7 @@ void LoadFieldInstr::InferRange(RangeAnalysis* analysis, Range* range) {
|
||||
break;
|
||||
|
||||
case Slot::Kind::kClosure_hash:
|
||||
case Slot::Kind::kClosure_length_and_flags:
|
||||
case Slot::Kind::kLinkedHashBase_hash_mask:
|
||||
case Slot::Kind::kLinkedHashBase_used_data:
|
||||
case Slot::Kind::kLinkedHashBase_deleted_keys:
|
||||
|
||||
@@ -1478,6 +1478,13 @@ static bool IsLoopInvariantLoad(ZoneGrowableArray<BitVector*>* sets,
|
||||
(*sets)[loop_header_index]->Contains(GetPlaceId(instr));
|
||||
}
|
||||
|
||||
static bool IsInitializingStore(Instruction* instr) {
|
||||
if (auto store = instr->AsStoreField()) {
|
||||
return store->is_initialization();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
LICM::LICM(FlowGraph* flow_graph) : flow_graph_(flow_graph) {
|
||||
ASSERT(flow_graph->is_licm_allowed());
|
||||
}
|
||||
@@ -1647,9 +1654,10 @@ void LICM::Optimize() {
|
||||
// instructions can be hoisted as long as its exception is still
|
||||
// the very first "visible" effect of the loop.
|
||||
bool is_loop_invariant = false;
|
||||
if ((current->AllowsCSE() ||
|
||||
IsLoopInvariantLoad(loop_invariant_loads, i, current)) &&
|
||||
(!seen_visible_effect || !current->MayHaveVisibleEffect())) {
|
||||
if (((current->AllowsCSE() ||
|
||||
IsLoopInvariantLoad(loop_invariant_loads, i, current)) &&
|
||||
(!seen_visible_effect || !current->MayHaveVisibleEffect())) ||
|
||||
IsInitializingStore(current)) {
|
||||
is_loop_invariant = true;
|
||||
for (intptr_t i = 0; i < current->InputCount(); ++i) {
|
||||
Definition* input_def = current->InputAt(i)->definition();
|
||||
@@ -3991,9 +3999,10 @@ void AllocationSinking::CreateMaterializationAt(
|
||||
intptr_t length_or_shape = -1;
|
||||
if (auto instr = alloc->AsAllocateObject()) {
|
||||
cls = &(instr->cls());
|
||||
} else if (alloc->IsAllocateClosure()) {
|
||||
} else if (auto instr = alloc->AsAllocateClosure()) {
|
||||
cls = &Class::ZoneHandle(
|
||||
flow_graph_->isolate_group()->object_store()->closure_class());
|
||||
length_or_shape = instr->EncodedLengthAndFlags();
|
||||
} else if (auto instr = alloc->AsAllocateContext()) {
|
||||
cls = &Class::ZoneHandle(Object::context_class());
|
||||
length_or_shape = instr->num_context_variables();
|
||||
|
||||
@@ -317,6 +317,34 @@ const Slot& Slot::GetRecordFieldSlot(Thread* thread, intptr_t offset_in_bytes) {
|
||||
offset_in_bytes, ":record_field", CompileType::Dynamic(), kTagged);
|
||||
}
|
||||
|
||||
const Slot& Slot::GetClosureElementSlot(Thread* thread,
|
||||
intptr_t offset_in_bytes) {
|
||||
const char* name = OS::SCreate(
|
||||
thread->zone(), ":closure_element[%" Pd "]",
|
||||
compiler::target::Closure::element_index_at_offset(offset_in_bytes));
|
||||
return GetCanonicalSlot(
|
||||
thread, Kind::kClosureElement,
|
||||
IsImmutableBit::encode(true) |
|
||||
IsCompressedBit::encode(Closure::ContainsCompressedPointers()),
|
||||
offset_in_bytes, name, CompileType::Dynamic(), kTagged);
|
||||
}
|
||||
|
||||
const Slot& Slot::GetClosureContextSlot(Thread* thread,
|
||||
const Function& function) {
|
||||
ASSERT(function.IsClosureFunction());
|
||||
const bool has_delayed_type_args =
|
||||
Closure::HasDelayedTypeArgumentsField(function);
|
||||
const bool has_instantiator_type_args =
|
||||
Closure::HasInstantiatorTypeArgumentsField(function);
|
||||
const bool has_function_type_args =
|
||||
Closure::HasFunctionTypeArgumentsField(function);
|
||||
return Slot::GetClosureElementSlot(
|
||||
thread,
|
||||
compiler::target::Closure::element_offset(UntaggedClosure::ContextIndex(
|
||||
has_delayed_type_args, has_instantiator_type_args,
|
||||
has_function_type_args)));
|
||||
}
|
||||
|
||||
const Slot& Slot::GetCanonicalSlot(Thread* thread,
|
||||
Slot::Kind kind,
|
||||
int8_t flags,
|
||||
@@ -487,6 +515,7 @@ bool Slot::Equals(const Slot& other) const {
|
||||
case Kind::kTypeArgumentsIndex:
|
||||
case Kind::kArrayElement:
|
||||
case Kind::kRecordField:
|
||||
case Kind::kClosureElement:
|
||||
return true;
|
||||
|
||||
case Kind::kCapturedVariable: {
|
||||
|
||||
@@ -64,10 +64,6 @@ class ParsedFunction;
|
||||
V(FinalizerEntry, UntaggedFinalizerEntry, next, FinalizerEntry, VAR) \
|
||||
V(Function, UntaggedFunction, signature, FunctionType, FINAL) \
|
||||
V(Context, UntaggedContext, parent, Context, FINAL) \
|
||||
V(Closure, UntaggedClosure, instantiator_type_arguments, TypeArguments, \
|
||||
FINAL) \
|
||||
V(Closure, UntaggedClosure, delayed_type_arguments, TypeArguments, FINAL) \
|
||||
V(Closure, UntaggedClosure, function_type_arguments, TypeArguments, FINAL) \
|
||||
V(FunctionType, UntaggedFunctionType, type_parameters, TypeParameters, \
|
||||
FINAL) \
|
||||
V(ReceivePort, UntaggedReceivePort, send_port, SendPort, FINAL) \
|
||||
@@ -101,6 +97,7 @@ class ParsedFunction;
|
||||
// that) or like a non-final field.
|
||||
#define NONNULLABLE_INT_TAGGED_NATIVE_DART_SLOTS_LIST(V) \
|
||||
V(Array, UntaggedArray, length, Smi, FINAL) \
|
||||
V(Closure, UntaggedClosure, length_and_flags, Smi, FINAL) \
|
||||
V(Closure, UntaggedClosure, hash, Smi, VAR_NOSANITIZETHREAD) \
|
||||
V(GrowableObjectArray, UntaggedGrowableObjectArray, length, Smi, VAR) \
|
||||
V(TypedDataBase, UntaggedTypedDataBase, length, Smi, FINAL) \
|
||||
@@ -133,7 +130,6 @@ class ParsedFunction;
|
||||
// that) or like a non-final field.
|
||||
#define NONNULLABLE_NONINT_TAGGED_NATIVE_DART_SLOTS_LIST(V) \
|
||||
V(Closure, UntaggedClosure, function, Function, FINAL) \
|
||||
V(Closure, UntaggedClosure, context, Dynamic, FINAL) \
|
||||
V(Finalizer, UntaggedFinalizer, callback, Closure, FINAL) \
|
||||
V(NativeFinalizer, UntaggedFinalizer, callback, Pointer, FINAL) \
|
||||
V(Function, UntaggedFunction, data, Dynamic, FINAL) \
|
||||
@@ -455,6 +451,9 @@ class Slot : public ZoneObject {
|
||||
// A slot corresponding to a record field at the given offset.
|
||||
kRecordField,
|
||||
|
||||
// A slot corresponding to a Closure element at given offset.
|
||||
kClosureElement,
|
||||
|
||||
// A slot within a Context object that contains a value of a captured
|
||||
// local variable.
|
||||
kCapturedVariable,
|
||||
@@ -486,6 +485,14 @@ class Slot : public ZoneObject {
|
||||
static const Slot& GetRecordFieldSlot(Thread* thread,
|
||||
intptr_t offset_in_bytes);
|
||||
|
||||
// Returns a slot corresponding to a Closure element at [offset_in_bytes].
|
||||
static const Slot& GetClosureElementSlot(Thread* thread,
|
||||
intptr_t offset_in_bytes);
|
||||
|
||||
// Returns 'Closure.context' slot for the given closure [function].
|
||||
static const Slot& GetClosureContextSlot(Thread* thread,
|
||||
const Function& function);
|
||||
|
||||
// Returns a slot that represents the given captured local variable.
|
||||
static const Slot& GetContextVariableSlotFor(Thread* thread,
|
||||
const LocalVariable& var);
|
||||
@@ -510,6 +517,7 @@ class Slot : public ZoneObject {
|
||||
bool IsArgumentOfType() const { return kind() == Kind::kTypeArgumentsIndex; }
|
||||
bool IsArrayElement() const { return kind() == Kind::kArrayElement; }
|
||||
bool IsRecordField() const { return kind() == Kind::kRecordField; }
|
||||
bool IsClosureElement() const { return kind() == Kind::kClosureElement; }
|
||||
bool IsLengthSlot() const;
|
||||
bool IsImmutableLengthSlot() const;
|
||||
|
||||
|
||||
@@ -1881,6 +1881,7 @@ CompileType LoadIndexedInstr::ComputeType() const {
|
||||
CompileType::kCannotBeNull,
|
||||
CompileType::kCannotBeSentinel);
|
||||
|
||||
case kClosureCid:
|
||||
case kRecordCid:
|
||||
return CompileType::Dynamic();
|
||||
|
||||
|
||||
@@ -389,13 +389,20 @@ Fragment BaseFlowGraphBuilder::TestTypeArgsLen(Fragment eq_branch,
|
||||
Fragment BaseFlowGraphBuilder::TestDelayedTypeArgs(LocalVariable* closure,
|
||||
Fragment present,
|
||||
Fragment absent) {
|
||||
Fragment test;
|
||||
const auto& function = parsed_function_->function();
|
||||
ASSERT(function.IsClosureFunction());
|
||||
|
||||
if (!function.IsGeneric()) {
|
||||
return absent;
|
||||
}
|
||||
|
||||
Fragment test;
|
||||
TargetEntryInstr* absent_entry;
|
||||
TargetEntryInstr* present_entry;
|
||||
|
||||
test += LoadLocal(closure);
|
||||
test += LoadNativeField(Slot::Closure_delayed_type_arguments());
|
||||
test += LoadNativeField(Slot::GetClosureElementSlot(
|
||||
thread_, compiler::target::Closure::element_offset(
|
||||
UntaggedClosure::kDelayedTypeArgumentsIndex)));
|
||||
test += Constant(Object::empty_type_arguments());
|
||||
test += BranchIfEqual(&absent_entry, &present_entry);
|
||||
|
||||
@@ -997,16 +1004,16 @@ Fragment BaseFlowGraphBuilder::AllocateContext(
|
||||
}
|
||||
|
||||
Fragment BaseFlowGraphBuilder::AllocateClosure(TokenPosition position,
|
||||
bool has_delayed_type_args,
|
||||
bool has_instantiator_type_args,
|
||||
bool is_generic,
|
||||
bool has_function_type_args,
|
||||
bool is_tear_off) {
|
||||
Value* instantiator_type_args =
|
||||
(has_instantiator_type_args ? Pop() : nullptr);
|
||||
auto const context = Pop();
|
||||
auto const function = Pop();
|
||||
auto* allocate = new (Z) AllocateClosureInstr(
|
||||
InstructionSource(position), function, context, instantiator_type_args,
|
||||
is_generic, is_tear_off, GetNextDeoptId());
|
||||
InstructionSource(position), function, context, has_delayed_type_args,
|
||||
has_instantiator_type_args, has_function_type_args, is_tear_off,
|
||||
GetNextDeoptId());
|
||||
Push(allocate);
|
||||
return Fragment(allocate);
|
||||
}
|
||||
|
||||
@@ -399,8 +399,9 @@ class BaseFlowGraphBuilder {
|
||||
Fragment AllocateContext(const ZoneGrowableArray<const Slot*>& scope);
|
||||
// Top of the stack should be the closure function.
|
||||
Fragment AllocateClosure(TokenPosition position,
|
||||
bool has_delayed_type_args,
|
||||
bool has_instantiator_type_args,
|
||||
bool is_generic,
|
||||
bool has_function_type_args,
|
||||
bool is_tear_off);
|
||||
Fragment CreateArray();
|
||||
Fragment AllocateRecord(TokenPosition position, RecordShape shape);
|
||||
|
||||
@@ -502,12 +502,22 @@ Fragment StreamingFlowGraphBuilder::TypeArgumentsHandling(
|
||||
LocalVariable* closure = parsed_function()->ParameterVariable(0);
|
||||
LocalVariable* fn_type_args = parsed_function()->function_type_arguments();
|
||||
ASSERT(fn_type_args != nullptr && closure != nullptr);
|
||||
ASSERT(Closure::HasFunctionTypeArgumentsField(dart_function));
|
||||
|
||||
const bool has_instantiator_type_args =
|
||||
Closure::HasInstantiatorTypeArgumentsField(dart_function);
|
||||
|
||||
if (dart_function.IsGeneric()) {
|
||||
ASSERT(Closure::HasDelayedTypeArgumentsField(dart_function));
|
||||
const intptr_t function_type_args_index =
|
||||
UntaggedClosure::FunctionTypeArgumentsIndex(
|
||||
/*has_delayed_type_args=*/true, has_instantiator_type_args);
|
||||
prologue += LoadLocal(fn_type_args);
|
||||
|
||||
prologue += LoadLocal(closure);
|
||||
prologue += LoadNativeField(Slot::Closure_function_type_arguments());
|
||||
prologue += LoadNativeField(Slot::GetClosureElementSlot(
|
||||
thread(),
|
||||
compiler::target::Closure::element_offset(function_type_args_index)));
|
||||
|
||||
prologue += IntConstant(dart_function.NumParentTypeArguments());
|
||||
|
||||
@@ -521,8 +531,14 @@ Fragment StreamingFlowGraphBuilder::TypeArgumentsHandling(
|
||||
prologue += StoreLocal(TokenPosition::kNoSource, fn_type_args);
|
||||
prologue += Drop();
|
||||
} else {
|
||||
ASSERT(!Closure::HasDelayedTypeArgumentsField(dart_function));
|
||||
const intptr_t function_type_args_index =
|
||||
UntaggedClosure::FunctionTypeArgumentsIndex(
|
||||
/*has_delayed_type_args=*/false, has_instantiator_type_args);
|
||||
prologue += LoadLocal(closure);
|
||||
prologue += LoadNativeField(Slot::Closure_function_type_arguments());
|
||||
prologue += LoadNativeField(Slot::GetClosureElementSlot(
|
||||
thread(),
|
||||
compiler::target::Closure::element_offset(function_type_args_index)));
|
||||
prologue += StoreLocal(TokenPosition::kNoSource, fn_type_args);
|
||||
prologue += Drop();
|
||||
}
|
||||
@@ -5938,6 +5954,13 @@ Fragment StreamingFlowGraphBuilder::BuildFunctionNode(
|
||||
ASSERT(function.kernel_offset() == func_node_offset);
|
||||
SkipFunctionNode();
|
||||
|
||||
const bool has_delayed_type_args =
|
||||
Closure::HasDelayedTypeArgumentsField(function);
|
||||
const bool has_instantiator_type_args =
|
||||
Closure::HasInstantiatorTypeArgumentsField(function);
|
||||
const bool has_function_type_args =
|
||||
Closure::HasFunctionTypeArgumentsField(function);
|
||||
|
||||
Fragment instructions;
|
||||
instructions += Constant(function);
|
||||
if (scopes()->IsClosureWithEmptyContext(func_node_offset)) {
|
||||
@@ -5945,24 +5968,37 @@ Fragment StreamingFlowGraphBuilder::BuildFunctionNode(
|
||||
} else {
|
||||
instructions += LoadLocal(parsed_function()->current_context_var());
|
||||
}
|
||||
// The function signature can have uninstantiated class type parameters.
|
||||
const bool has_instantiator_type_args =
|
||||
!function.HasInstantiatedSignature(kCurrentClass);
|
||||
if (has_instantiator_type_args) {
|
||||
instructions += LoadInstantiatorTypeArguments();
|
||||
}
|
||||
instructions += flow_graph_builder_->AllocateClosure(
|
||||
function.token_pos(), has_instantiator_type_args, function.IsGeneric(),
|
||||
function.token_pos(), has_delayed_type_args, has_instantiator_type_args,
|
||||
has_function_type_args,
|
||||
/*is_tear_off=*/false);
|
||||
LocalVariable* closure = MakeTemporary();
|
||||
|
||||
// TODO(30455): We only need to save these if the closure uses any captured
|
||||
// type parameters.
|
||||
instructions += LoadLocal(closure);
|
||||
instructions += LoadFunctionTypeArguments();
|
||||
instructions += flow_graph_builder_->StoreNativeField(
|
||||
Slot::Closure_function_type_arguments(),
|
||||
StoreFieldInstr::Kind::kInitializing);
|
||||
// The function signature can have uninstantiated class type parameters.
|
||||
if (has_instantiator_type_args) {
|
||||
instructions += LoadLocal(closure);
|
||||
instructions += LoadInstantiatorTypeArguments();
|
||||
instructions += flow_graph_builder_->StoreNativeField(
|
||||
Slot::GetClosureElementSlot(
|
||||
thread(), compiler::target::Closure::element_offset(
|
||||
UntaggedClosure::InstantiatorTypeArgumentsIndex(
|
||||
has_delayed_type_args))),
|
||||
StoreFieldInstr::Kind::kInitializing);
|
||||
}
|
||||
|
||||
if (has_function_type_args) {
|
||||
// TODO(30455): We only need to save these if the closure uses any captured
|
||||
// type parameters.
|
||||
instructions += LoadLocal(closure);
|
||||
instructions += LoadFunctionTypeArguments();
|
||||
instructions += flow_graph_builder_->StoreNativeField(
|
||||
Slot::GetClosureElementSlot(
|
||||
thread(),
|
||||
compiler::target::Closure::element_offset(
|
||||
UntaggedClosure::FunctionTypeArgumentsIndex(
|
||||
has_delayed_type_args, has_instantiator_type_args))),
|
||||
StoreFieldInstr::Kind::kInitializing);
|
||||
}
|
||||
|
||||
return instructions;
|
||||
}
|
||||
|
||||
@@ -278,12 +278,14 @@ Fragment FlowGraphBuilder::CatchBlockEntry(const Array& handler_types,
|
||||
CurrentException()->is_captured() || CurrentCatchContext()->is_captured();
|
||||
LocalVariable* context_variable = parsed_function_->current_context_var();
|
||||
if (should_restore_closure_context) {
|
||||
ASSERT(parsed_function_->function().IsClosureFunction());
|
||||
const auto& function = parsed_function_->function();
|
||||
ASSERT(function.IsClosureFunction());
|
||||
|
||||
LocalVariable* closure_parameter = parsed_function_->ParameterVariable(0);
|
||||
ASSERT(!closure_parameter->is_captured());
|
||||
instructions += LoadLocal(closure_parameter);
|
||||
instructions += LoadNativeField(Slot::Closure_context());
|
||||
instructions +=
|
||||
LoadNativeField(Slot::GetClosureContextSlot(thread_, function));
|
||||
instructions += StoreLocal(TokenPosition::kNoSource, context_variable);
|
||||
instructions += Drop();
|
||||
}
|
||||
@@ -895,6 +897,7 @@ const Function& TypedListGetNativeFunction(Thread* thread, classid_t cid) {
|
||||
V(ByteDataViewLength, TypedDataBase_length) \
|
||||
V(ByteDataViewOffsetInBytes, TypedDataView_offset_in_bytes) \
|
||||
V(ByteDataViewTypedData, TypedDataView_typed_data) \
|
||||
V(Closure_hash, Closure_hash) \
|
||||
V(Finalizer_getCallback, Finalizer_callback) \
|
||||
V(FinalizerBase_getAllEntries, FinalizerBase_all_entries) \
|
||||
V(FinalizerBase_getDetachments, FinalizerBase_detachments) \
|
||||
@@ -2280,17 +2283,31 @@ Fragment FlowGraphBuilder::BuildImplicitClosureCreation(
|
||||
ASSERT(!target.HasGenericParent());
|
||||
ASSERT(target.IsImplicitInstanceClosureFunction());
|
||||
|
||||
const bool has_delayed_type_args =
|
||||
Closure::HasDelayedTypeArgumentsField(target);
|
||||
const bool has_instantiator_type_args =
|
||||
Closure::HasInstantiatorTypeArgumentsField(target);
|
||||
ASSERT(!Closure::HasFunctionTypeArgumentsField(target));
|
||||
|
||||
Fragment fragment;
|
||||
fragment += Constant(target);
|
||||
fragment += LoadLocal(parsed_function_->receiver_var());
|
||||
fragment += AllocateClosure(
|
||||
position, has_delayed_type_args, has_instantiator_type_args,
|
||||
/*has_function_type_args=*/false, /*is_tear_off=*/true);
|
||||
|
||||
// The function signature can have uninstantiated class type parameters.
|
||||
const bool has_instantiator_type_args =
|
||||
!target.HasInstantiatedSignature(kCurrentClass);
|
||||
if (has_instantiator_type_args) {
|
||||
LocalVariable* closure = MakeTemporary();
|
||||
fragment += LoadLocal(closure);
|
||||
fragment += LoadInstantiatorTypeArguments();
|
||||
fragment += StoreNativeField(
|
||||
Slot::GetClosureElementSlot(
|
||||
thread_, compiler::target::Closure::element_offset(
|
||||
UntaggedClosure::InstantiatorTypeArgumentsIndex(
|
||||
has_delayed_type_args))),
|
||||
StoreFieldInstr::Kind::kInitializing);
|
||||
}
|
||||
fragment += AllocateClosure(position, has_instantiator_type_args,
|
||||
target.IsGeneric(), /*is_tear_off=*/true);
|
||||
|
||||
return fragment;
|
||||
}
|
||||
@@ -2890,10 +2907,9 @@ struct FlowGraphBuilder::ClosureCallInfo {
|
||||
LocalVariable* named_parameter_names = nullptr;
|
||||
LocalVariable* parameter_types = nullptr;
|
||||
LocalVariable* type_parameters = nullptr;
|
||||
LocalVariable* closure_length_and_flags = nullptr;
|
||||
LocalVariable* num_type_parameters = nullptr;
|
||||
LocalVariable* type_parameter_flags = nullptr;
|
||||
LocalVariable* instantiator_type_args = nullptr;
|
||||
LocalVariable* parent_function_type_args = nullptr;
|
||||
LocalVariable* num_parent_type_args = nullptr;
|
||||
};
|
||||
|
||||
@@ -3002,9 +3018,17 @@ Fragment FlowGraphBuilder::BuildClosureCallDefaultTypeHandling(
|
||||
return store_provided;
|
||||
}
|
||||
|
||||
Fragment instructions;
|
||||
JoinEntryInstr* end = BuildJoinEntry();
|
||||
TargetEntryInstr *has_delayed_type_args, *no_delayed_type_args;
|
||||
|
||||
instructions += LoadLocal(info.vars->delayed_type_args);
|
||||
instructions += Constant(Object::empty_type_arguments());
|
||||
instructions += BranchIfEqual(&no_delayed_type_args, &has_delayed_type_args);
|
||||
|
||||
// Load the defaults, instantiating or replacing them with the other type
|
||||
// arguments as appropriate.
|
||||
Fragment store_default;
|
||||
Fragment store_default(no_delayed_type_args);
|
||||
store_default += LoadLocal(info.closure);
|
||||
store_default += LoadNativeField(Slot::Closure_function());
|
||||
store_default += LoadNativeField(Slot::Function_data());
|
||||
@@ -3048,10 +3072,10 @@ Fragment FlowGraphBuilder::BuildClosureCallDefaultTypeHandling(
|
||||
|
||||
Fragment do_instantiation(needs_instantiation);
|
||||
// Load the instantiator type arguments.
|
||||
do_instantiation += LoadLocal(info.instantiator_type_args);
|
||||
do_instantiation += LoadLocal(info.vars->instantiator_type_args);
|
||||
// Load the parent function type arguments. (No local function type arguments
|
||||
// can be used within the defaults).
|
||||
do_instantiation += LoadLocal(info.parent_function_type_args);
|
||||
do_instantiation += LoadLocal(info.vars->parent_function_type_args);
|
||||
// Load the default type arguments to instantiate.
|
||||
do_instantiation += LoadLocal(info.type_parameters);
|
||||
do_instantiation += LoadNativeField(Slot::TypeParameters_defaults());
|
||||
@@ -3061,7 +3085,7 @@ Fragment FlowGraphBuilder::BuildClosureCallDefaultTypeHandling(
|
||||
do_instantiation += Goto(done);
|
||||
|
||||
Fragment share_instantiator(can_share_instantiator);
|
||||
share_instantiator += LoadLocal(info.instantiator_type_args);
|
||||
share_instantiator += LoadLocal(info.vars->instantiator_type_args);
|
||||
share_instantiator += StoreLocal(info.vars->function_type_args);
|
||||
share_instantiator += Drop();
|
||||
share_instantiator += Goto(done);
|
||||
@@ -3069,7 +3093,7 @@ Fragment FlowGraphBuilder::BuildClosureCallDefaultTypeHandling(
|
||||
Fragment share_function(can_share_function);
|
||||
// Since the defaults won't have local type parameters, these must all be
|
||||
// from the parent function type arguments, so we can just use it.
|
||||
share_function += LoadLocal(info.parent_function_type_args);
|
||||
share_function += LoadLocal(info.vars->parent_function_type_args);
|
||||
share_function += StoreLocal(info.vars->function_type_args);
|
||||
share_function += Drop();
|
||||
share_function += Goto(done);
|
||||
@@ -3077,15 +3101,16 @@ Fragment FlowGraphBuilder::BuildClosureCallDefaultTypeHandling(
|
||||
store_default.current = done; // Return here after branching.
|
||||
store_default += DropTemporary(&default_tav_kind);
|
||||
store_default += DropTemporary(&closure_data);
|
||||
store_default += Goto(end);
|
||||
|
||||
Fragment store_delayed;
|
||||
store_delayed += LoadLocal(info.closure);
|
||||
store_delayed += LoadNativeField(Slot::Closure_delayed_type_arguments());
|
||||
Fragment store_delayed(has_delayed_type_args);
|
||||
store_delayed += LoadLocal(info.vars->delayed_type_args);
|
||||
store_delayed += StoreLocal(info.vars->function_type_args);
|
||||
store_delayed += Drop();
|
||||
store_delayed += Goto(end);
|
||||
|
||||
// Use the delayed type args if present, else the default ones.
|
||||
return TestDelayedTypeArgs(info.closure, store_delayed, store_default);
|
||||
instructions.current = end;
|
||||
return instructions;
|
||||
}
|
||||
|
||||
Fragment FlowGraphBuilder::BuildClosureCallNamedArgumentsCheck(
|
||||
@@ -3230,12 +3255,15 @@ Fragment FlowGraphBuilder::BuildClosureCallArgumentsValidCheck(
|
||||
Fragment check_entry;
|
||||
// We only need to check the length of any explicitly provided type arguments.
|
||||
if (info.descriptor.TypeArgsLen() > 0) {
|
||||
Fragment check_type_args_length;
|
||||
check_type_args_length += LoadLocal(info.type_parameters);
|
||||
TargetEntryInstr* null;
|
||||
TargetEntryInstr* not_null;
|
||||
check_type_args_length += BranchIfNull(&null, ¬_null);
|
||||
check_type_args_length.current = not_null; // Continue in non-error case.
|
||||
TargetEntryInstr *has_delayed_type_args, *no_delayed_type_args;
|
||||
|
||||
check_entry += LoadLocal(info.vars->delayed_type_args);
|
||||
check_entry += Constant(Object::empty_type_arguments());
|
||||
check_entry += BranchIfEqual(&no_delayed_type_args, &has_delayed_type_args);
|
||||
|
||||
Fragment(has_delayed_type_args) + Goto(info.throw_no_such_method);
|
||||
|
||||
Fragment check_type_args_length(no_delayed_type_args);
|
||||
check_type_args_length += LoadLocal(info.signature);
|
||||
check_type_args_length += BuildExtractUnboxedSlotBitFieldIntoSmi<
|
||||
UntaggedFunctionType::PackedNumTypeParameters>(
|
||||
@@ -3244,19 +3272,11 @@ Fragment FlowGraphBuilder::BuildClosureCallArgumentsValidCheck(
|
||||
TargetEntryInstr* equal;
|
||||
TargetEntryInstr* not_equal;
|
||||
check_type_args_length += BranchIfEqual(&equal, ¬_equal);
|
||||
check_type_args_length.current = equal; // Continue in non-error case.
|
||||
|
||||
// The function is not generic.
|
||||
Fragment(null) + Goto(info.throw_no_such_method);
|
||||
|
||||
// An incorrect number of type arguments were passed.
|
||||
Fragment(not_equal) + Goto(info.throw_no_such_method);
|
||||
|
||||
// Type arguments should not be provided if there are delayed type
|
||||
// arguments, as then the closure itself is not generic.
|
||||
check_entry += TestDelayedTypeArgs(
|
||||
info.closure, /*present=*/Goto(info.throw_no_such_method),
|
||||
/*absent=*/check_type_args_length);
|
||||
check_entry.current = equal; // Continue in non-error case.
|
||||
}
|
||||
|
||||
check_entry += LoadLocal(info.has_named_params);
|
||||
@@ -3424,7 +3444,7 @@ Fragment FlowGraphBuilder::BuildClosureCallTypeArgumentsTypeCheck(
|
||||
|
||||
Fragment loop_call_check(call);
|
||||
// Load instantiators.
|
||||
loop_call_check += LoadLocal(info.instantiator_type_args);
|
||||
loop_call_check += LoadLocal(info.vars->instantiator_type_args);
|
||||
loop_call_check += LoadLocal(info.vars->function_type_args);
|
||||
// Load instantiated type parameter.
|
||||
loop_call_check += LoadLocal(info.vars->current_type_param);
|
||||
@@ -3471,7 +3491,7 @@ Fragment FlowGraphBuilder::BuildClosureCallArgumentTypeCheck(
|
||||
instructions += LoadIndexed(
|
||||
kArrayCid, /*index_scale*/ compiler::target::kCompressedWordSize);
|
||||
// Load instantiator type arguments.
|
||||
instructions += LoadLocal(info.instantiator_type_args);
|
||||
instructions += LoadLocal(info.vars->instantiator_type_args);
|
||||
// Load the full set of function type arguments.
|
||||
instructions += LoadLocal(info.vars->function_type_args);
|
||||
// Check that the value has the right type.
|
||||
@@ -3513,6 +3533,51 @@ Fragment FlowGraphBuilder::BuildClosureCallArgumentTypeChecks(
|
||||
return instructions;
|
||||
}
|
||||
|
||||
Fragment FlowGraphBuilder::BuildLoadDynamicClosureElement(
|
||||
LocalVariable* closure,
|
||||
LocalVariable* length_and_flags,
|
||||
LocalVariable* result,
|
||||
intptr_t present_mask,
|
||||
intptr_t element_offset,
|
||||
intptr_t index_mask,
|
||||
intptr_t index_shift) {
|
||||
Fragment instructions;
|
||||
TargetEntryInstr *present, *absent;
|
||||
JoinEntryInstr* done = BuildJoinEntry();
|
||||
|
||||
instructions += LoadLocal(length_and_flags);
|
||||
instructions += IntConstant(present_mask);
|
||||
instructions += SmiBinaryOp(Token::kBIT_AND);
|
||||
instructions += IntConstant(0);
|
||||
instructions += BranchIfEqual(&absent, &present);
|
||||
|
||||
Fragment load(present);
|
||||
load += LoadLocal(closure);
|
||||
if (element_offset >= 0) {
|
||||
load +=
|
||||
LoadNativeField(Slot::GetClosureElementSlot(thread_, element_offset));
|
||||
} else {
|
||||
load += LoadLocal(length_and_flags);
|
||||
load += IntConstant(index_mask);
|
||||
load += SmiBinaryOp(Token::kBIT_AND);
|
||||
load += IntConstant(index_shift);
|
||||
load += SmiBinaryOp(Token::kSHR);
|
||||
load += LoadIndexed(kClosureCid, compiler::target::kCompressedWordSize);
|
||||
}
|
||||
load += StoreLocal(result);
|
||||
load += Drop();
|
||||
load += Goto(done);
|
||||
|
||||
Fragment store_null(absent);
|
||||
store_null += NullConstant();
|
||||
store_null += StoreLocal(result);
|
||||
store_null += Drop();
|
||||
store_null += Goto(done);
|
||||
|
||||
instructions.current = done;
|
||||
return instructions;
|
||||
}
|
||||
|
||||
Fragment FlowGraphBuilder::BuildDynamicClosureCallChecks(
|
||||
LocalVariable* closure) {
|
||||
ClosureCallInfo info(closure, BuildThrowNoSuchMethod(),
|
||||
@@ -3564,12 +3629,29 @@ Fragment FlowGraphBuilder::BuildDynamicClosureCallChecks(
|
||||
info.type_parameters = MakeTemporary("type_parameters");
|
||||
|
||||
body += LoadLocal(info.closure);
|
||||
body += LoadNativeField(Slot::Closure_instantiator_type_arguments());
|
||||
info.instantiator_type_args = MakeTemporary("instantiator_type_args");
|
||||
body += LoadNativeField(Slot::Closure_length_and_flags());
|
||||
info.closure_length_and_flags = MakeTemporary("closure_length_and_flags");
|
||||
|
||||
body += LoadLocal(info.closure);
|
||||
body += LoadNativeField(Slot::Closure_function_type_arguments());
|
||||
info.parent_function_type_args = MakeTemporary("parent_function_type_args");
|
||||
body += BuildLoadDynamicClosureElement(
|
||||
info.closure, info.closure_length_and_flags,
|
||||
info.vars->instantiator_type_args,
|
||||
UntaggedClosure::HasInstantiatorTypeArgumentsBit::mask_in_place(), -1,
|
||||
UntaggedClosure::InstantiatorTypeArgumentsIndexBits::mask_in_place(),
|
||||
UntaggedClosure::InstantiatorTypeArgumentsIndexBits::shift());
|
||||
|
||||
body += BuildLoadDynamicClosureElement(
|
||||
info.closure, info.closure_length_and_flags,
|
||||
info.vars->parent_function_type_args,
|
||||
UntaggedClosure::HasFunctionTypeArgumentsBit::mask_in_place(), -1,
|
||||
UntaggedClosure::FunctionTypeArgumentsIndexBits::mask_in_place(),
|
||||
UntaggedClosure::FunctionTypeArgumentsIndexBits::shift());
|
||||
|
||||
body += BuildLoadDynamicClosureElement(
|
||||
info.closure, info.closure_length_and_flags, info.vars->delayed_type_args,
|
||||
UntaggedClosure::HasDelayedTypeArgumentsBit::mask_in_place(),
|
||||
compiler::target::Closure::element_offset(
|
||||
UntaggedClosure::kDelayedTypeArgumentsIndex),
|
||||
0, 0);
|
||||
|
||||
// At this point, all the read-only temporaries stored in the ClosureCallInfo
|
||||
// should be either loaded or still nullptr, if not needed for this function.
|
||||
@@ -3580,7 +3662,7 @@ Fragment FlowGraphBuilder::BuildDynamicClosureCallChecks(
|
||||
// args. Thus, use whatever was stored for the parent function type arguments,
|
||||
// which has already been checked against any parent type parameter bounds.
|
||||
Fragment not_generic;
|
||||
not_generic += LoadLocal(info.parent_function_type_args);
|
||||
not_generic += LoadLocal(info.vars->parent_function_type_args);
|
||||
not_generic += StoreLocal(info.vars->function_type_args);
|
||||
not_generic += Drop();
|
||||
|
||||
@@ -3615,7 +3697,7 @@ Fragment FlowGraphBuilder::BuildDynamicClosureCallChecks(
|
||||
// Load the local function type args.
|
||||
generic += LoadLocal(info.vars->function_type_args);
|
||||
// Load the parent function type args.
|
||||
generic += LoadLocal(info.parent_function_type_args);
|
||||
generic += LoadLocal(info.vars->parent_function_type_args);
|
||||
// Load the number of parent type parameters.
|
||||
generic += LoadLocal(info.num_parent_type_args);
|
||||
// Load the number of total type parameters.
|
||||
@@ -3635,8 +3717,21 @@ Fragment FlowGraphBuilder::BuildDynamicClosureCallChecks(
|
||||
// the type system and need not be checked again at the call site.
|
||||
auto const check_bounds = BuildClosureCallTypeArgumentsTypeCheck(info);
|
||||
if (FLAG_eliminate_type_checks) {
|
||||
generic += TestDelayedTypeArgs(info.closure, /*present=*/{},
|
||||
/*absent=*/check_bounds);
|
||||
JoinEntryInstr* done = BuildJoinEntry();
|
||||
TargetEntryInstr *has_delayed_type_args, *no_delayed_type_args;
|
||||
|
||||
generic += LoadLocal(info.vars->delayed_type_args);
|
||||
generic += Constant(Object::empty_type_arguments());
|
||||
generic += BranchIfEqual(&no_delayed_type_args, &has_delayed_type_args);
|
||||
|
||||
Fragment present(has_delayed_type_args);
|
||||
present += Goto(done);
|
||||
|
||||
Fragment absent(no_delayed_type_args);
|
||||
absent += check_bounds;
|
||||
absent += Goto(done);
|
||||
|
||||
generic.current = done;
|
||||
} else {
|
||||
generic += check_bounds;
|
||||
}
|
||||
@@ -3653,8 +3748,7 @@ Fragment FlowGraphBuilder::BuildDynamicClosureCallChecks(
|
||||
body += BuildClosureCallArgumentTypeChecks(info);
|
||||
|
||||
// Drop all the read-only temporaries at the end of the fragment.
|
||||
body += DropTemporary(&info.parent_function_type_args);
|
||||
body += DropTemporary(&info.instantiator_type_args);
|
||||
body += DropTemporary(&info.closure_length_and_flags);
|
||||
body += DropTemporary(&info.type_parameters);
|
||||
body += DropTemporary(&info.parameter_types);
|
||||
body += DropTemporary(&info.named_parameter_names);
|
||||
@@ -4074,35 +4168,55 @@ Fragment FlowGraphBuilder::BuildDefaultTypeHandling(const Function& function) {
|
||||
// the closure object instead.
|
||||
LocalVariable* const closure = parsed_function_->ParameterVariable(0);
|
||||
auto const mode = function.default_type_arguments_instantiation_mode();
|
||||
const bool has_delayed_type_args =
|
||||
Closure::HasDelayedTypeArgumentsField(function);
|
||||
const bool has_instantiator_type_args =
|
||||
Closure::HasInstantiatorTypeArgumentsField(function);
|
||||
const bool has_function_type_args =
|
||||
Closure::HasFunctionTypeArgumentsField(function);
|
||||
|
||||
switch (mode) {
|
||||
case InstantiationMode::kIsInstantiated:
|
||||
use_defaults += Constant(default_types);
|
||||
break;
|
||||
case InstantiationMode::kSharesInstantiatorTypeArguments:
|
||||
ASSERT(has_instantiator_type_args);
|
||||
use_defaults += LoadLocal(closure);
|
||||
use_defaults +=
|
||||
LoadNativeField(Slot::Closure_instantiator_type_arguments());
|
||||
use_defaults += LoadNativeField(Slot::GetClosureElementSlot(
|
||||
thread_, compiler::target::Closure::element_offset(
|
||||
UntaggedClosure::InstantiatorTypeArgumentsIndex(
|
||||
has_delayed_type_args))));
|
||||
break;
|
||||
case InstantiationMode::kSharesFunctionTypeArguments:
|
||||
ASSERT(has_function_type_args);
|
||||
use_defaults += LoadLocal(closure);
|
||||
use_defaults +=
|
||||
LoadNativeField(Slot::Closure_function_type_arguments());
|
||||
use_defaults += LoadNativeField(Slot::GetClosureElementSlot(
|
||||
thread_,
|
||||
compiler::target::Closure::element_offset(
|
||||
UntaggedClosure::FunctionTypeArgumentsIndex(
|
||||
has_delayed_type_args, has_instantiator_type_args))));
|
||||
break;
|
||||
case InstantiationMode::kNeedsInstantiation:
|
||||
// Only load the instantiator or function type arguments from the
|
||||
// closure if they're needed for instantiation.
|
||||
if (!default_types.IsInstantiated(kCurrentClass)) {
|
||||
ASSERT(has_instantiator_type_args);
|
||||
use_defaults += LoadLocal(closure);
|
||||
use_defaults +=
|
||||
LoadNativeField(Slot::Closure_instantiator_type_arguments());
|
||||
use_defaults += LoadNativeField(Slot::GetClosureElementSlot(
|
||||
thread_, compiler::target::Closure::element_offset(
|
||||
UntaggedClosure::InstantiatorTypeArgumentsIndex(
|
||||
has_delayed_type_args))));
|
||||
} else {
|
||||
use_defaults += NullConstant();
|
||||
}
|
||||
if (!default_types.IsInstantiated(kFunctions)) {
|
||||
if (has_function_type_args &&
|
||||
!default_types.IsInstantiated(kFunctions)) {
|
||||
use_defaults += LoadLocal(closure);
|
||||
use_defaults +=
|
||||
LoadNativeField(Slot::Closure_function_type_arguments());
|
||||
use_defaults += LoadNativeField(Slot::GetClosureElementSlot(
|
||||
thread_,
|
||||
compiler::target::Closure::element_offset(
|
||||
UntaggedClosure::FunctionTypeArgumentsIndex(
|
||||
has_delayed_type_args, has_instantiator_type_args))));
|
||||
} else {
|
||||
use_defaults += NullConstant();
|
||||
}
|
||||
@@ -4278,7 +4392,7 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfImplicitClosureFunction(
|
||||
} else if (!target.is_static()) {
|
||||
// The closure context is the receiver.
|
||||
closure += LoadLocal(parsed_function_->ParameterVariable(0));
|
||||
closure += LoadNativeField(Slot::Closure_context());
|
||||
closure += LoadNativeField(Slot::GetClosureContextSlot(thread_, function));
|
||||
}
|
||||
|
||||
closure += PushExplicitParameters(function);
|
||||
|
||||
@@ -145,6 +145,21 @@ class FlowGraphBuilder : public BaseFlowGraphBuilder {
|
||||
// information for the function retrieved at runtime from the closure.
|
||||
Fragment BuildClosureCallArgumentTypeChecks(const ClosureCallInfo& info);
|
||||
|
||||
// Generate fragment which loads an optional element from an unknown closure.
|
||||
//
|
||||
// 1) Test if element is present: ([length_and_flags] & [present_mask]) != 0.
|
||||
// 2a) If present and [element_offset] >= 0, then load element using offset.
|
||||
// 2b) If present and [element_offset] < 0, then load element using
|
||||
// ([length_and_flags] & [index_mask] >> [index_shift]) index.
|
||||
// 3) If element is absent, the [result] is set to null.
|
||||
Fragment BuildLoadDynamicClosureElement(LocalVariable* closure,
|
||||
LocalVariable* length_and_flags,
|
||||
LocalVariable* result,
|
||||
intptr_t present_mask,
|
||||
intptr_t element_offset,
|
||||
intptr_t index_mask,
|
||||
intptr_t index_shift);
|
||||
|
||||
// Main entry point for building checks.
|
||||
Fragment BuildDynamicClosureCallChecks(LocalVariable* closure);
|
||||
|
||||
|
||||
@@ -326,6 +326,8 @@ Fragment PrologueBuilder::BuildParameterHandling() {
|
||||
}
|
||||
|
||||
Fragment PrologueBuilder::BuildClosureContextHandling() {
|
||||
const auto& function = parsed_function_->function();
|
||||
ASSERT(function.IsClosureFunction());
|
||||
LocalVariable* closure_parameter = parsed_function_->ParameterVariable(0);
|
||||
LocalVariable* context = parsed_function_->current_context_var();
|
||||
|
||||
@@ -333,7 +335,8 @@ Fragment PrologueBuilder::BuildClosureContextHandling() {
|
||||
// (both load/store happen on the copied-down places).
|
||||
Fragment populate_context;
|
||||
populate_context += LoadLocal(closure_parameter);
|
||||
populate_context += LoadNativeField(Slot::Closure_context());
|
||||
populate_context +=
|
||||
LoadNativeField(Slot::GetClosureContextSlot(thread_, function));
|
||||
populate_context += StoreLocal(TokenPosition::kNoSource, context);
|
||||
populate_context += Drop();
|
||||
return populate_context;
|
||||
@@ -368,7 +371,7 @@ Fragment PrologueBuilder::BuildTypeArgumentsHandling() {
|
||||
|
||||
Fragment PrologueBuilder::BuildClosureDelayedTypeArgumentsHandling() {
|
||||
const auto& function = parsed_function_->function();
|
||||
ASSERT(function.IsClosureFunction());
|
||||
ASSERT(function.IsClosureFunction() && function.IsGeneric());
|
||||
LocalVariable* const type_args_var =
|
||||
parsed_function_->RawTypeArgumentsVariable();
|
||||
ASSERT(type_args_var != nullptr);
|
||||
@@ -380,8 +383,9 @@ Fragment PrologueBuilder::BuildClosureDelayedTypeArgumentsHandling() {
|
||||
// correct in number and bound.
|
||||
Fragment use_delayed_type_args;
|
||||
use_delayed_type_args += LoadLocal(closure);
|
||||
use_delayed_type_args +=
|
||||
LoadNativeField(Slot::Closure_delayed_type_arguments());
|
||||
use_delayed_type_args += LoadNativeField(Slot::GetClosureElementSlot(
|
||||
thread_, compiler::target::Closure::element_offset(
|
||||
UntaggedClosure::kDelayedTypeArgumentsIndex)));
|
||||
use_delayed_type_args += StoreLocal(TokenPosition::kNoSource, type_args_var);
|
||||
use_delayed_type_args += Drop();
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ namespace dart {
|
||||
V(CoreLibrary, _GrowableList, []=, GrowableArraySetIndexed, 0x3a23c6fa) \
|
||||
V(CoreLibrary, _GrowableList, get:_emptyList, GrowableArrayGetEmptyList, \
|
||||
0x735785f0) \
|
||||
V(CoreLibrary, _Closure, get:_hash, Closure_hash, 0x6e92d1d5) \
|
||||
V(CoreLibrary, _Record, get:_fieldNames, Record_fieldNames, 0x68c8319e) \
|
||||
V(CoreLibrary, _Record, get:_numFields, Record_numFields, 0x7ba4f393) \
|
||||
V(CoreLibrary, _Record, get:_shape, Record_shape, 0x70c40933) \
|
||||
|
||||
@@ -422,8 +422,6 @@ static uword GetInstanceSizeImpl(const dart::Class& handle) {
|
||||
return Instance::InstanceSize();
|
||||
case kGrowableObjectArrayCid:
|
||||
return GrowableObjectArray::InstanceSize();
|
||||
case kClosureCid:
|
||||
return Closure::InstanceSize();
|
||||
case kTypedDataBaseCid:
|
||||
return TypedDataBase::InstanceSize();
|
||||
case kMapCid:
|
||||
@@ -517,6 +515,8 @@ word Instance::DataOffsetFor(intptr_t cid) {
|
||||
case kArrayCid:
|
||||
case kImmutableArrayCid:
|
||||
return Array::data_offset();
|
||||
case kClosureCid:
|
||||
return Closure::element_offset(0);
|
||||
case kTypeArgumentsCid:
|
||||
return TypeArguments::types_offset();
|
||||
case kOneByteStringCid:
|
||||
@@ -1020,6 +1020,15 @@ intptr_t Array::index_at_offset(intptr_t offset_in_bytes) {
|
||||
TranslateOffsetInWordsToHost(offset_in_bytes));
|
||||
}
|
||||
|
||||
intptr_t Closure::element_index_at_offset(intptr_t offset_in_bytes) {
|
||||
// Note: cannot delegate to dart::Closure::element_index_at_offset as
|
||||
// Closure layout is different between AOT and precompiler.
|
||||
const intptr_t index =
|
||||
(offset_in_bytes - Closure::element_offset(0)) / kCompressedWordSize;
|
||||
ASSERT(index >= 0);
|
||||
return index;
|
||||
}
|
||||
|
||||
intptr_t Record::field_index_at_offset(intptr_t offset_in_bytes) {
|
||||
return dart::Record::field_index_at_offset(
|
||||
TranslateOffsetInWordsToHost(offset_in_bytes));
|
||||
|
||||
@@ -429,6 +429,16 @@ class UntaggedObject : public AllStatic {
|
||||
static bool IsTypedDataClassId(intptr_t cid);
|
||||
};
|
||||
|
||||
class UntaggedClosure : public AllStatic {
|
||||
static const word kHasDelayedTypeArgumentsBit;
|
||||
static const word kHasInstantiatorTypeArgumentsBit;
|
||||
static const word kHasFunctionTypeArgumentsBit;
|
||||
static const word kFunctionTypeArgumentsIndexBitsPos;
|
||||
static const word kFunctionTypeArgumentsIndexBitsSize;
|
||||
static const word kLengthBitsPos;
|
||||
static const word kLengthBitsSize;
|
||||
};
|
||||
|
||||
class UntaggedAbstractType : public AllStatic {
|
||||
public:
|
||||
static const word kTypeStateFinalizedInstantiated;
|
||||
@@ -1472,13 +1482,13 @@ class Context : public AllStatic {
|
||||
|
||||
class Closure : public AllStatic {
|
||||
public:
|
||||
static word context_offset();
|
||||
static word delayed_type_arguments_offset();
|
||||
static word entry_point_offset();
|
||||
static word function_offset();
|
||||
static word function_type_arguments_offset();
|
||||
static word instantiator_type_arguments_offset();
|
||||
static word hash_offset();
|
||||
static word length_and_flags_offset();
|
||||
static word element_offset(intptr_t index);
|
||||
static intptr_t element_index_at_offset(intptr_t offset_in_bytes);
|
||||
static word InstanceSize(intptr_t length);
|
||||
static word InstanceSize();
|
||||
FINAL_CLASS();
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -60,6 +60,7 @@
|
||||
PAYLOAD_SIZEOF, RANGE, CONSTANT, ENUM) \
|
||||
ARRAY(Array, element_offset) \
|
||||
NOT_IN_PRODUCT(ARRAY(ClassTable, AllocationTracingStateSlotOffsetFor)) \
|
||||
ARRAY(Closure, element_offset) \
|
||||
ARRAY(Code, element_offset) \
|
||||
ARRAY(Context, variable_offset) \
|
||||
ARRAY(ContextScope, element_offset) \
|
||||
@@ -71,6 +72,7 @@
|
||||
ARRAY(TwoByteString, element_offset) \
|
||||
ARRAY(WeakArray, element_offset) \
|
||||
ARRAY_SIZEOF(Array, InstanceSize, element_offset) \
|
||||
ARRAY_SIZEOF(Closure, InstanceSize, element_offset) \
|
||||
ARRAY_SIZEOF(Code, InstanceSize, element_offset) \
|
||||
ARRAY_SIZEOF(Context, InstanceSize, variable_offset) \
|
||||
ARRAY_SIZEOF(ContextScope, InstanceSize, element_offset) \
|
||||
@@ -114,6 +116,13 @@
|
||||
CONSTANT(SubtypeTestCache, kMaxInputs) \
|
||||
CONSTANT(SubtypeTestCache, kTestResult) \
|
||||
CONSTANT(TypeArguments, kMaxElements) \
|
||||
CONSTANT(UntaggedClosure, kHasDelayedTypeArgumentsBit) \
|
||||
CONSTANT(UntaggedClosure, kHasInstantiatorTypeArgumentsBit) \
|
||||
CONSTANT(UntaggedClosure, kHasFunctionTypeArgumentsBit) \
|
||||
CONSTANT(UntaggedClosure, kFunctionTypeArgumentsIndexBitsPos) \
|
||||
CONSTANT(UntaggedClosure, kFunctionTypeArgumentsIndexBitsSize) \
|
||||
CONSTANT(UntaggedClosure, kLengthBitsPos) \
|
||||
CONSTANT(UntaggedClosure, kLengthBitsSize) \
|
||||
CONSTANT(UntaggedObject, kCardRememberedBit) \
|
||||
CONSTANT(UntaggedObject, kCanonicalBit) \
|
||||
CONSTANT(UntaggedObject, kNotMarkedBit) \
|
||||
@@ -151,12 +160,9 @@
|
||||
FIELD(Class, super_type_offset) \
|
||||
FIELD(Class, host_type_arguments_field_offset_in_words_offset) \
|
||||
NOT_IN_PRODUCT(FIELD(ClassTable, allocation_tracing_state_table_offset)) \
|
||||
FIELD(Closure, context_offset) \
|
||||
FIELD(Closure, delayed_type_arguments_offset) \
|
||||
FIELD(Closure, function_offset) \
|
||||
FIELD(Closure, function_type_arguments_offset) \
|
||||
FIELD(Closure, hash_offset) \
|
||||
FIELD(Closure, instantiator_type_arguments_offset) \
|
||||
FIELD(Closure, length_and_flags_offset) \
|
||||
FIELD(ClosureData, packed_fields_offset) \
|
||||
FIELD(Code, instructions_offset) \
|
||||
FIELD(Code, object_pool_offset) \
|
||||
@@ -444,7 +450,6 @@
|
||||
SIZEOF(Bytecode, InstanceSize, UntaggedBytecode) \
|
||||
SIZEOF(Capability, InstanceSize, UntaggedCapability) \
|
||||
SIZEOF(Class, InstanceSize, UntaggedClass) \
|
||||
SIZEOF(Closure, InstanceSize, UntaggedClosure) \
|
||||
SIZEOF(ClosureData, InstanceSize, UntaggedClosureData) \
|
||||
SIZEOF(CodeSourceMap, HeaderSize, UntaggedCodeSourceMap) \
|
||||
SIZEOF(CompressedStackMaps, ObjectHeaderSize, UntaggedCompressedStackMaps) \
|
||||
|
||||
@@ -1234,19 +1234,21 @@ VM_TYPE_TESTING_STUB_CODE_LIST(GENERATE_BREAKPOINT_STUB)
|
||||
// Called for inline allocation of closure.
|
||||
// Input (preserved):
|
||||
// AllocateClosureABI::kFunctionReg: closure function.
|
||||
// AllocateClosureABI::kLengthAndFlagsReg: encoded length_and_flags.
|
||||
// AllocateClosureABI::kContextReg: closure context.
|
||||
// AllocateClosureABI::kInstantiatorTypeArgs: instantiator type arguments.
|
||||
// Output:
|
||||
// AllocateClosureABI::kResultReg: new allocated Closure object.
|
||||
// Clobbered:
|
||||
// AllocateClosureABI::kScratchReg
|
||||
void StubCodeCompiler::GenerateAllocateClosureStub(
|
||||
bool has_instantiator_type_args,
|
||||
bool is_generic) {
|
||||
const intptr_t instance_size =
|
||||
target::RoundedAllocationSize(target::Closure::InstanceSize());
|
||||
void StubCodeCompiler::GenerateAllocateClosureStub(intptr_t num_elements) {
|
||||
const intptr_t instance_size = target::RoundedAllocationSize(
|
||||
target::Closure::InstanceSize(num_elements));
|
||||
const Register result_reg = AllocateClosureABI::kResultReg;
|
||||
const Register scratch_reg = AllocateClosureABI::kScratchReg;
|
||||
|
||||
__ EnsureHasClassIdInDEBUG(kFunctionCid, AllocateClosureABI::kFunctionReg,
|
||||
AllocateClosureABI::kScratchReg);
|
||||
scratch_reg);
|
||||
|
||||
if (!FLAG_use_slow_path && FLAG_inline_alloc) {
|
||||
Label slow_case;
|
||||
__ Comment("Inline allocation of uninitialized closure");
|
||||
@@ -1257,63 +1259,59 @@ void StubCodeCompiler::GenerateAllocateClosureStub(
|
||||
const auto distance = Assembler::kNearJump;
|
||||
#endif
|
||||
__ TryAllocateObject(kClosureCid, instance_size, &slow_case, distance,
|
||||
AllocateClosureABI::kResultReg,
|
||||
AllocateClosureABI::kScratchReg);
|
||||
result_reg, scratch_reg);
|
||||
|
||||
__ Comment("Inline initialization of allocated closure");
|
||||
// Put null in the scratch register for initializing most boxed fields.
|
||||
// We initialize the fields in offset order below.
|
||||
// Since the TryAllocateObject above did not go to the slow path, we're
|
||||
// guaranteed an object in new space here, and thus no barriers are needed.
|
||||
__ LoadObject(AllocateClosureABI::kScratchReg, NullObject());
|
||||
if (has_instantiator_type_args) {
|
||||
__ StoreToSlotNoBarrier(AllocateClosureABI::kInstantiatorTypeArgsReg,
|
||||
AllocateClosureABI::kResultReg,
|
||||
Slot::Closure_instantiator_type_arguments());
|
||||
} else {
|
||||
__ StoreToSlotNoBarrier(AllocateClosureABI::kScratchReg,
|
||||
AllocateClosureABI::kResultReg,
|
||||
Slot::Closure_instantiator_type_arguments());
|
||||
}
|
||||
__ StoreToSlotNoBarrier(AllocateClosureABI::kScratchReg,
|
||||
AllocateClosureABI::kResultReg,
|
||||
Slot::Closure_function_type_arguments());
|
||||
if (!is_generic) {
|
||||
__ StoreToSlotNoBarrier(AllocateClosureABI::kScratchReg,
|
||||
AllocateClosureABI::kResultReg,
|
||||
Slot::Closure_delayed_type_arguments());
|
||||
}
|
||||
__ StoreToSlotNoBarrier(AllocateClosureABI::kFunctionReg,
|
||||
AllocateClosureABI::kResultReg,
|
||||
__ LoadObject(scratch_reg, NullObject());
|
||||
__ StoreToSlotNoBarrier(AllocateClosureABI::kFunctionReg, result_reg,
|
||||
Slot::Closure_function());
|
||||
__ StoreToSlotNoBarrier(AllocateClosureABI::kContextReg,
|
||||
AllocateClosureABI::kResultReg,
|
||||
Slot::Closure_context());
|
||||
__ StoreToSlotNoBarrier(AllocateClosureABI::kScratchReg,
|
||||
AllocateClosureABI::kResultReg,
|
||||
Slot::Closure_hash());
|
||||
if (is_generic) {
|
||||
__ LoadObject(AllocateClosureABI::kScratchReg, EmptyTypeArguments());
|
||||
__ StoreToSlotNoBarrier(AllocateClosureABI::kScratchReg,
|
||||
AllocateClosureABI::kResultReg,
|
||||
Slot::Closure_delayed_type_arguments());
|
||||
__ StoreToSlotNoBarrier(AllocateClosureABI::kLengthAndFlagsReg, result_reg,
|
||||
Slot::Closure_length_and_flags());
|
||||
for (intptr_t i = 0; i < num_elements - 1; ++i) {
|
||||
__ StoreCompressedIntoObjectNoBarrier(
|
||||
result_reg,
|
||||
FieldAddress(result_reg, target::Closure::element_offset(i)),
|
||||
scratch_reg);
|
||||
}
|
||||
__ StoreCompressedIntoObjectNoBarrier(
|
||||
result_reg,
|
||||
FieldAddress(result_reg,
|
||||
target::Closure::element_offset(num_elements - 1)),
|
||||
AllocateClosureABI::kContextReg);
|
||||
if (num_elements >= 2) {
|
||||
Label initialized;
|
||||
__ BranchIfBit(
|
||||
AllocateClosureABI::kLengthAndFlagsReg,
|
||||
UntaggedClosure::kHasDelayedTypeArgumentsBit + kSmiTagShift, ZERO,
|
||||
&initialized);
|
||||
__ LoadObject(scratch_reg, EmptyTypeArguments());
|
||||
__ StoreCompressedIntoObjectNoBarrier(
|
||||
result_reg,
|
||||
FieldAddress(result_reg,
|
||||
target::Closure::element_offset(
|
||||
UntaggedClosure::kDelayedTypeArgumentsIndex)),
|
||||
scratch_reg);
|
||||
__ Bind(&initialized);
|
||||
}
|
||||
__ LoadImmediate(scratch_reg, target::ToRawSmi(0));
|
||||
__ StoreToSlotNoBarrier(scratch_reg, result_reg, Slot::Closure_hash());
|
||||
#if defined(DART_PRECOMPILER) && !defined(TARGET_ARCH_IA32)
|
||||
if (FLAG_precompiled_mode) {
|
||||
// Set the closure entry point in precompiled mode, either to the function
|
||||
// entry point in bare instructions mode or to 0 otherwise (to catch
|
||||
// misuse). This overwrites the scratch register, but there are no more
|
||||
// boxed fields.
|
||||
__ LoadFromSlot(AllocateClosureABI::kScratchReg,
|
||||
AllocateClosureABI::kFunctionReg,
|
||||
// misuse).
|
||||
__ LoadFromSlot(scratch_reg, AllocateClosureABI::kFunctionReg,
|
||||
Slot::Function_entry_point());
|
||||
__ StoreToSlotNoBarrier(AllocateClosureABI::kScratchReg,
|
||||
AllocateClosureABI::kResultReg,
|
||||
__ StoreToSlotNoBarrier(scratch_reg, result_reg,
|
||||
Slot::Closure_entry_point());
|
||||
}
|
||||
#endif
|
||||
|
||||
// AllocateClosureABI::kResultReg: new object.
|
||||
// result_reg: new object.
|
||||
__ Ret();
|
||||
|
||||
__ Bind(&slow_case);
|
||||
@@ -1322,26 +1320,12 @@ void StubCodeCompiler::GenerateAllocateClosureStub(
|
||||
__ Comment("Closure allocation via runtime");
|
||||
__ EnterStubFrame();
|
||||
__ PushObject(NullObject()); // Space on the stack for the return value.
|
||||
__ PushRegistersInOrder(
|
||||
{AllocateClosureABI::kFunctionReg, AllocateClosureABI::kContextReg});
|
||||
if (has_instantiator_type_args) {
|
||||
__ PushRegister(AllocateClosureABI::kInstantiatorTypeArgsReg);
|
||||
} else {
|
||||
__ PushObject(NullObject());
|
||||
}
|
||||
if (is_generic) {
|
||||
__ PushObject(EmptyTypeArguments());
|
||||
} else {
|
||||
__ PushObject(NullObject());
|
||||
}
|
||||
__ CallRuntime(kAllocateClosureRuntimeEntry, 4);
|
||||
if (has_instantiator_type_args) {
|
||||
__ Drop(1);
|
||||
__ PopRegister(AllocateClosureABI::kInstantiatorTypeArgsReg);
|
||||
} else {
|
||||
__ Drop(2);
|
||||
}
|
||||
__ PushRegistersInOrder({AllocateClosureABI::kFunctionReg,
|
||||
AllocateClosureABI::kLengthAndFlagsReg,
|
||||
AllocateClosureABI::kContextReg});
|
||||
__ CallRuntime(kAllocateClosureRuntimeEntry, 3);
|
||||
__ PopRegister(AllocateClosureABI::kContextReg);
|
||||
__ PopRegister(AllocateClosureABI::kLengthAndFlagsReg);
|
||||
__ PopRegister(AllocateClosureABI::kFunctionReg);
|
||||
__ PopRegister(AllocateClosureABI::kResultReg);
|
||||
ASSERT(target::WillAllocateNewOrRememberedObject(instance_size));
|
||||
@@ -1350,26 +1334,26 @@ void StubCodeCompiler::GenerateAllocateClosureStub(
|
||||
|
||||
// AllocateClosureABI::kResultReg: new object
|
||||
__ Ret();
|
||||
|
||||
if (FLAG_use_slow_path || !FLAG_inline_alloc) {
|
||||
// Make sure AllocateClosureN stubs have different code as
|
||||
// precompiler chokes on distinct stub Code objects with the same
|
||||
// (de-duplicated) instructions.
|
||||
__ LoadImmediate(scratch_reg, num_elements);
|
||||
}
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateAllocateClosureStub() {
|
||||
GenerateAllocateClosureStub(/*has_instantiator_type_args=*/false,
|
||||
/*is_generic=*/false);
|
||||
void StubCodeCompiler::GenerateAllocateClosure1Stub() {
|
||||
GenerateAllocateClosureStub(1);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateAllocateClosureGenericStub() {
|
||||
GenerateAllocateClosureStub(/*has_instantiator_type_args=*/false,
|
||||
/*is_generic=*/true);
|
||||
void StubCodeCompiler::GenerateAllocateClosure2Stub() {
|
||||
GenerateAllocateClosureStub(2);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateAllocateClosureTAStub() {
|
||||
GenerateAllocateClosureStub(/*has_instantiator_type_args=*/true,
|
||||
/*is_generic=*/false);
|
||||
void StubCodeCompiler::GenerateAllocateClosure3Stub() {
|
||||
GenerateAllocateClosureStub(3);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateAllocateClosureTAGenericStub() {
|
||||
GenerateAllocateClosureStub(/*has_instantiator_type_args=*/true,
|
||||
/*is_generic=*/true);
|
||||
void StubCodeCompiler::GenerateAllocateClosure4Stub() {
|
||||
GenerateAllocateClosureStub(4);
|
||||
}
|
||||
|
||||
// Generates allocation stub for _GrowableList class.
|
||||
@@ -3348,22 +3332,55 @@ void StubCodeCompiler::GenerateSubtypeTestCacheSearch(
|
||||
FieldAddress(instance_cid_or_sig_reg,
|
||||
target::Function::signature_offset()));
|
||||
if (n >= 2) {
|
||||
__ LoadCompressed(
|
||||
instance_type_args_reg,
|
||||
__ LoadCompressedSmi(
|
||||
TypeTestABI::kScratchReg,
|
||||
FieldAddress(TypeTestABI::kInstanceReg,
|
||||
target::Closure::instantiator_type_arguments_offset()));
|
||||
}
|
||||
if (n >= 5) {
|
||||
__ LoadCompressed(
|
||||
parent_fun_type_args_reg,
|
||||
FieldAddress(TypeTestABI::kInstanceReg,
|
||||
target::Closure::function_type_arguments_offset()));
|
||||
}
|
||||
if (n >= 6) {
|
||||
__ LoadCompressed(
|
||||
delayed_type_args_reg,
|
||||
FieldAddress(TypeTestABI::kInstanceReg,
|
||||
target::Closure::delayed_type_arguments_offset()));
|
||||
target::Closure::length_and_flags_offset()));
|
||||
|
||||
Label load_function_type_arguments, load_delayed_type_arguments;
|
||||
__ MoveRegister(instance_type_args_reg, null_reg);
|
||||
__ BranchIfBit(
|
||||
TypeTestABI::kScratchReg,
|
||||
UntaggedClosure::kHasInstantiatorTypeArgumentsBit + kSmiTagShift,
|
||||
ZERO, (n >= 5) ? &load_function_type_arguments : &initialized);
|
||||
__ ExtractBitField(
|
||||
instance_type_args_reg, TypeTestABI::kScratchReg,
|
||||
UntaggedClosure::InstantiatorTypeArgumentsIndexBits::shift(),
|
||||
UntaggedClosure::InstantiatorTypeArgumentsIndexBits::bitsize());
|
||||
__ LoadIndexedCompressed(
|
||||
instance_type_args_reg, TypeTestABI::kInstanceReg,
|
||||
target::Closure::element_offset(0), instance_type_args_reg);
|
||||
if (n >= 5) {
|
||||
__ Bind(&load_function_type_arguments);
|
||||
|
||||
__ MoveRegister(parent_fun_type_args_reg, null_reg);
|
||||
__ BranchIfBit(
|
||||
TypeTestABI::kScratchReg,
|
||||
UntaggedClosure::kHasFunctionTypeArgumentsBit + kSmiTagShift, ZERO,
|
||||
(n >= 6) ? &load_delayed_type_arguments : &initialized);
|
||||
__ ExtractBitField(
|
||||
parent_fun_type_args_reg, TypeTestABI::kScratchReg,
|
||||
UntaggedClosure::FunctionTypeArgumentsIndexBits::shift(),
|
||||
UntaggedClosure::FunctionTypeArgumentsIndexBits::bitsize());
|
||||
__ LoadIndexedCompressed(
|
||||
parent_fun_type_args_reg, TypeTestABI::kInstanceReg,
|
||||
target::Closure::element_offset(0), parent_fun_type_args_reg);
|
||||
}
|
||||
|
||||
if (n >= 6) {
|
||||
__ Bind(&load_delayed_type_arguments);
|
||||
|
||||
__ MoveRegister(delayed_type_args_reg, null_reg);
|
||||
__ BranchIfBit(
|
||||
TypeTestABI::kScratchReg,
|
||||
UntaggedClosure::kHasDelayedTypeArgumentsBit + kSmiTagShift, ZERO,
|
||||
&initialized);
|
||||
__ LoadCompressed(
|
||||
delayed_type_args_reg,
|
||||
FieldAddress(TypeTestABI::kInstanceReg,
|
||||
target::Closure::element_offset(
|
||||
UntaggedClosure::kDelayedTypeArgumentsIndex)));
|
||||
}
|
||||
}
|
||||
|
||||
__ Jump(&initialized, Assembler::kNearJump);
|
||||
|
||||
@@ -196,9 +196,8 @@ class StubCodeCompiler {
|
||||
// InitLateFinalInstanceField stubs.
|
||||
void GenerateInitLateInstanceFieldStub(bool is_final);
|
||||
|
||||
// Common function for generating AllocateClosure[TA][Generic] stubs.
|
||||
void GenerateAllocateClosureStub(bool has_instantiator_type_args,
|
||||
bool is_generic);
|
||||
// Common function for generating AllocateClosure<N> stubs.
|
||||
void GenerateAllocateClosureStub(intptr_t num_elements);
|
||||
|
||||
// Common function for generating Allocate<TypedData>Array stubs.
|
||||
void GenerateAllocateTypedDataArrayStub(intptr_t cid);
|
||||
|
||||
@@ -2780,20 +2780,70 @@ void StubCodeCompiler::GenerateSubtypeNTestCacheStub(Assembler* assembler,
|
||||
__ movl(STCInternal::kInstanceCidOrSignatureReg,
|
||||
FieldAddress(STCInternal::kInstanceCidOrSignatureReg,
|
||||
target::Function::signature_offset()));
|
||||
|
||||
if (n >= 2) {
|
||||
__ movl(
|
||||
__ movl(STCInternal::kScratchReg,
|
||||
FieldAddress(TypeTestABI::kInstanceReg,
|
||||
target::Closure::length_and_flags_offset()));
|
||||
|
||||
Label load_function_type_arguments, load_delayed_type_arguments;
|
||||
__ movl(STCInternal::kInstanceInstantiatorTypeArgumentsReg, raw_null);
|
||||
__ BranchIfBit(
|
||||
STCInternal::kScratchReg,
|
||||
UntaggedClosure::kHasInstantiatorTypeArgumentsBit + kSmiTagShift,
|
||||
ZERO, (n >= 5) ? &load_function_type_arguments : &loop);
|
||||
__ ExtractBitField(
|
||||
STCInternal::kInstanceInstantiatorTypeArgumentsReg,
|
||||
STCInternal::kScratchReg,
|
||||
UntaggedClosure::InstantiatorTypeArgumentsIndexBits::shift(),
|
||||
UntaggedClosure::InstantiatorTypeArgumentsIndexBits::bitsize());
|
||||
__ Load(
|
||||
STCInternal::kInstanceInstantiatorTypeArgumentsReg,
|
||||
FieldAddress(TypeTestABI::kInstanceReg,
|
||||
target::Closure::instantiator_type_arguments_offset()));
|
||||
}
|
||||
if (n >= 5) {
|
||||
__ pushl(FieldAddress(TypeTestABI::kInstanceReg,
|
||||
target::Closure::function_type_arguments_offset()));
|
||||
}
|
||||
if (n >= 6) {
|
||||
__ pushl(FieldAddress(TypeTestABI::kInstanceReg,
|
||||
target::Closure::delayed_type_arguments_offset()));
|
||||
STCInternal::kInstanceInstantiatorTypeArgumentsReg,
|
||||
TIMES_WORD_SIZE, target::Closure::element_offset(0)));
|
||||
if (n >= 5) {
|
||||
Label no_function_type_arguments;
|
||||
__ Bind(&load_function_type_arguments);
|
||||
|
||||
__ BranchIfBit(
|
||||
STCInternal::kScratchReg,
|
||||
UntaggedClosure::kHasFunctionTypeArgumentsBit + kSmiTagShift, ZERO,
|
||||
&no_function_type_arguments);
|
||||
__ ExtractBitField(
|
||||
STCInternal::kScratchReg, STCInternal::kScratchReg,
|
||||
UntaggedClosure::FunctionTypeArgumentsIndexBits::shift(),
|
||||
UntaggedClosure::FunctionTypeArgumentsIndexBits::bitsize());
|
||||
__ pushl(FieldAddress(TypeTestABI::kInstanceReg,
|
||||
STCInternal::kScratchReg, TIMES_WORD_SIZE,
|
||||
target::Closure::element_offset(0)));
|
||||
__ jmp((n >= 6) ? &load_delayed_type_arguments : &loop,
|
||||
Assembler::kNearJump);
|
||||
|
||||
__ Bind(&no_function_type_arguments);
|
||||
__ pushl(raw_null);
|
||||
}
|
||||
|
||||
if (n >= 6) {
|
||||
Label no_delayed_type_arguments;
|
||||
__ Bind(&load_delayed_type_arguments);
|
||||
|
||||
__ testl(FieldAddress(TypeTestABI::kInstanceReg,
|
||||
target::Closure::length_and_flags_offset()),
|
||||
Immediate(UntaggedClosure::kHasDelayedTypeArgumentsBit +
|
||||
kSmiTagShift));
|
||||
__ j(ZERO, &no_delayed_type_arguments, Assembler::kNearJump);
|
||||
__ pushl(
|
||||
FieldAddress(TypeTestABI::kInstanceReg,
|
||||
target::Closure::element_offset(
|
||||
UntaggedClosure::kDelayedTypeArgumentsIndex)));
|
||||
__ jmp(&loop, Assembler::kNearJump);
|
||||
|
||||
__ Bind(&no_delayed_type_arguments);
|
||||
__ pushl(raw_null);
|
||||
}
|
||||
}
|
||||
|
||||
__ jmp(&loop, Assembler::kNearJump);
|
||||
}
|
||||
|
||||
|
||||
@@ -357,6 +357,7 @@ bool WriteBarrierElimination::SlotEligibleForWBE(const Slot& slot) {
|
||||
// RestoreWriteBarrierInvariantVisitor::VisitPointers.
|
||||
|
||||
switch (slot.kind()) {
|
||||
case Slot::Kind::kClosureElement: // Closure
|
||||
case Slot::Kind::kCapturedVariable: // Context
|
||||
case Slot::Kind::kDartField: // Instance
|
||||
case Slot::Kind::kRecordField: // Instance
|
||||
|
||||
@@ -503,8 +503,8 @@ struct AllocateObjectABI {
|
||||
struct AllocateClosureABI {
|
||||
static constexpr Register kResultReg = AllocateObjectABI::kResultReg;
|
||||
static constexpr Register kFunctionReg = R1;
|
||||
static constexpr Register kContextReg = R2;
|
||||
static constexpr Register kInstantiatorTypeArgsReg = R3;
|
||||
static constexpr Register kLengthAndFlagsReg = R2;
|
||||
static constexpr Register kContextReg = R3;
|
||||
static constexpr Register kScratchReg = R4;
|
||||
};
|
||||
|
||||
|
||||
@@ -341,8 +341,8 @@ struct AllocateObjectABI {
|
||||
struct AllocateClosureABI {
|
||||
static constexpr Register kResultReg = AllocateObjectABI::kResultReg;
|
||||
static constexpr Register kFunctionReg = R1;
|
||||
static constexpr Register kContextReg = R2;
|
||||
static constexpr Register kInstantiatorTypeArgsReg = R3;
|
||||
static constexpr Register kLengthAndFlagsReg = R2;
|
||||
static constexpr Register kContextReg = R3;
|
||||
static constexpr Register kScratchReg = R4;
|
||||
};
|
||||
|
||||
|
||||
@@ -243,8 +243,8 @@ struct AllocateBoxABI {
|
||||
struct AllocateClosureABI {
|
||||
static constexpr Register kResultReg = AllocateObjectABI::kResultReg;
|
||||
static constexpr Register kFunctionReg = EBX;
|
||||
static constexpr Register kContextReg = ECX;
|
||||
static constexpr Register kInstantiatorTypeArgsReg = EDI;
|
||||
static constexpr Register kLengthAndFlagsReg = ECX;
|
||||
static constexpr Register kContextReg = EDI;
|
||||
static constexpr Register kScratchReg = EDX;
|
||||
};
|
||||
|
||||
|
||||
@@ -55,8 +55,12 @@ namespace dart {
|
||||
V(Allocate_Wide, D, WIDE, lit, ___, ___) \
|
||||
V(AllocateT, 0, ORDN, ___, ___, ___) \
|
||||
V(CreateArrayTOS, 0, ORDN, ___, ___, ___) \
|
||||
V(AllocateClosure, 0, ORDN, ___, ___, ___) \
|
||||
V(Unused03, 0, RESV, ___, ___, ___) \
|
||||
V(AllocateClosure, D, ORDN, lit, ___, ___) \
|
||||
V(AllocateClosure_Wide, D, WIDE, lit, ___, ___) \
|
||||
V(LoadClosureElement, D, ORDN, num, ___, ___) \
|
||||
V(LoadClosureElement_Wide, D, WIDE, num, ___, ___) \
|
||||
V(StoreClosureElement, D, ORDN, num, ___, ___) \
|
||||
V(StoreClosureElement_Wide, D, WIDE, num, ___, ___) \
|
||||
V(AllocateContext, A_E, ORDN, num, num, ___) \
|
||||
V(AllocateContext_Wide, A_E, WIDE, num, num, ___) \
|
||||
V(CloneContext, A_E, ORDN, num, num, ___) \
|
||||
@@ -223,7 +227,8 @@ namespace dart {
|
||||
V(VMInternal_ImplicitSharedStaticGetter, 0, ORDN, ___, ___, ___) \
|
||||
V(VMInternal_ImplicitStaticSetter, 0, ORDN, ___, ___, ___) \
|
||||
V(VMInternal_ImplicitSharedStaticSetter, 0, ORDN, ___, ___, ___) \
|
||||
V(VMInternal_MethodExtractor, 0, ORDN, ___, ___, ___) \
|
||||
V(VMInternal_MethodExtractorWithITA, 0, ORDN, ___, ___, ___) \
|
||||
V(VMInternal_MethodExtractorWithoutITA, 0, ORDN, ___, ___, ___) \
|
||||
V(VMInternal_InvokeClosure, 0, ORDN, ___, ___, ___) \
|
||||
V(VMInternal_InvokeField, 0, ORDN, ___, ___, ___) \
|
||||
V(VMInternal_ForwardDynamicInvocation, 0, ORDN, ___, ___, ___) \
|
||||
@@ -265,7 +270,7 @@ class KernelBytecode {
|
||||
static const intptr_t kMagicValue = 0x44424333; // 'DBC3'
|
||||
// Bytecode format version supported by the VM
|
||||
// (should match pkg/dart2bytecode/lib/dbc.dart).
|
||||
static const intptr_t kBytecodeFormatVersion = 1;
|
||||
static const intptr_t kBytecodeFormatVersion = 2;
|
||||
|
||||
enum Opcode {
|
||||
#define DECLARE_BYTECODE(name, encoding, kind, op1, op2, op3) k##name,
|
||||
|
||||
@@ -385,8 +385,8 @@ struct AllocateObjectABI {
|
||||
struct AllocateClosureABI {
|
||||
static constexpr Register kResultReg = AllocateObjectABI::kResultReg;
|
||||
static constexpr Register kFunctionReg = T1;
|
||||
static constexpr Register kContextReg = T2;
|
||||
static constexpr Register kInstantiatorTypeArgsReg = T3;
|
||||
static constexpr Register kLengthAndFlagsReg = T2;
|
||||
static constexpr Register kContextReg = T3;
|
||||
static constexpr Register kScratchReg = T4;
|
||||
};
|
||||
|
||||
|
||||
@@ -307,8 +307,8 @@ struct AllocateObjectABI {
|
||||
struct AllocateClosureABI {
|
||||
static constexpr Register kResultReg = AllocateObjectABI::kResultReg;
|
||||
static constexpr Register kFunctionReg = RBX;
|
||||
static constexpr Register kContextReg = RDX;
|
||||
static constexpr Register kInstantiatorTypeArgsReg = RCX;
|
||||
static constexpr Register kLengthAndFlagsReg = RDX;
|
||||
static constexpr Register kContextReg = RCX;
|
||||
static constexpr Register kScratchReg = R13;
|
||||
};
|
||||
|
||||
|
||||
@@ -227,6 +227,17 @@ void DeferredObject::Create() {
|
||||
cls ^= GetClass();
|
||||
|
||||
switch (cls.id()) {
|
||||
case kClosureCid: {
|
||||
const intptr_t length_and_flags =
|
||||
Smi::Cast(Object::Handle(GetLengthOrShape())).Value();
|
||||
if (FLAG_trace_deoptimization_verbose) {
|
||||
OS::PrintErr("materializing closure with length and flags %" Px " (%" Px
|
||||
", %" Pd " fields)\n",
|
||||
length_and_flags, reinterpret_cast<uword>(args_),
|
||||
field_count_);
|
||||
}
|
||||
object_ = &Closure::ZoneHandle(Closure::New(length_and_flags));
|
||||
} break;
|
||||
case kContextCid: {
|
||||
const intptr_t num_variables =
|
||||
Smi::Cast(Object::Handle(GetLengthOrShape())).Value();
|
||||
@@ -296,6 +307,35 @@ void DeferredObject::Fill() {
|
||||
cls ^= GetClass();
|
||||
|
||||
switch (cls.id()) {
|
||||
case kClosureCid: {
|
||||
const Closure& closure = Closure::Cast(*object_);
|
||||
|
||||
Smi& offset = Smi::Handle();
|
||||
Object& value = Object::Handle();
|
||||
|
||||
for (intptr_t i = 0; i < field_count_; i++) {
|
||||
offset ^= GetFieldOffset(i);
|
||||
if (offset.Value() == Closure::function_offset()) {
|
||||
Function& function = Function::Handle();
|
||||
function ^= GetValue(i);
|
||||
closure.set_function(function);
|
||||
if (FLAG_trace_deoptimization_verbose) {
|
||||
OS::PrintErr(" closure@function (offset %" Pd ") <- %s\n",
|
||||
offset.Value(), function.ToCString());
|
||||
}
|
||||
} else {
|
||||
ASSERT(offset.Value() >= Closure::element_offset(0));
|
||||
const intptr_t index =
|
||||
Closure::element_index_at_offset(offset.Value());
|
||||
value = GetValue(i);
|
||||
closure.SetElementAt(index, value);
|
||||
if (FLAG_trace_deoptimization_verbose) {
|
||||
OS::PrintErr(" closure@%" Pd " (offset %" Pd ") <- %s\n", index,
|
||||
offset.Value(), value.ToCString());
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case kContextCid: {
|
||||
const Context& context = Context::Cast(*object_);
|
||||
|
||||
|
||||
@@ -202,6 +202,7 @@ class DeferredObject {
|
||||
enum {
|
||||
kClassIndex = 0,
|
||||
|
||||
// For closure: encoded length and flags.
|
||||
// For contexts: number of context variables.
|
||||
// For arrays and typed data objects: number of elements.
|
||||
// For records: shape.
|
||||
|
||||
@@ -288,9 +288,9 @@ VM_UNIT_TEST_CASE(FfiCallbackMetadata_CreateIsolateLocalFfiCallback) {
|
||||
const Code& code = Code::Handle(func.EnsureHasCode());
|
||||
EXPECT(!code.IsNull());
|
||||
|
||||
// Using a FfiCallbackKind::kSync function as a dummy closure.
|
||||
// Using a tear-off of target of the callback function as a dummy closure.
|
||||
const Function& closure_func = Function::Handle(
|
||||
CreateTestFunction(FfiCallbackKind::kIsolateLocalStaticCallback));
|
||||
Function::Handle(func.FfiCallbackTarget()).ImplicitClosureFunction());
|
||||
const Context& context = Context::Handle(Context::null());
|
||||
const Closure& closure1 = Closure::Handle(
|
||||
Closure::New(Object::null_type_arguments(),
|
||||
|
||||
+118
-51
@@ -1377,11 +1377,11 @@ bool Interpreter::AssertAssignable(Thread* thread,
|
||||
TypeArgumentsPtr delayed_function_type_arguments;
|
||||
if (cid == kClosureCid) {
|
||||
ClosurePtr closure = static_cast<ClosurePtr>(instance);
|
||||
instance_type_arguments = closure->untag()->instantiator_type_arguments();
|
||||
instance_type_arguments = Closure::instantiator_type_arguments(closure);
|
||||
parent_function_type_arguments =
|
||||
closure->untag()->function_type_arguments();
|
||||
Closure::function_type_arguments(closure);
|
||||
delayed_function_type_arguments =
|
||||
closure->untag()->delayed_type_arguments();
|
||||
Closure::delayed_type_arguments(closure);
|
||||
instance_cid_or_function =
|
||||
closure->untag()->function()->untag()->signature();
|
||||
} else {
|
||||
@@ -1770,10 +1770,14 @@ bool Interpreter::AllocateContext(Thread* thread,
|
||||
// Allocate a _Closure and put it into SP[0].
|
||||
// Returns false on exception.
|
||||
bool Interpreter::AllocateClosure(Thread* thread,
|
||||
FunctionPtr function,
|
||||
SmiPtr length_and_flags,
|
||||
const KBCInstr* pc,
|
||||
ObjectPtr* FP,
|
||||
ObjectPtr* SP) {
|
||||
const intptr_t instance_size = Closure::InstanceSize();
|
||||
const intptr_t length =
|
||||
UntaggedClosure::LengthBits::decode(Smi::Value(length_and_flags));
|
||||
const intptr_t instance_size = Closure::InstanceSize(length);
|
||||
ClosurePtr result;
|
||||
if (TryAllocate(thread, kClosureCid, instance_size,
|
||||
reinterpret_cast<ObjectPtr*>(&result))) {
|
||||
@@ -1782,15 +1786,21 @@ bool Interpreter::AllocateClosure(Thread* thread,
|
||||
Closure::ContainsCompressedPointers(),
|
||||
Object::from_offset<Closure>(),
|
||||
Object::to_offset<Closure>());
|
||||
result->untag()->set_function(function);
|
||||
ONLY_IN_PRECOMPILED(result->untag()->entry_point_ =
|
||||
function->untag()->entry_point_);
|
||||
result->untag()->set_length_and_flags(length_and_flags);
|
||||
result->untag()->set_hash(Smi::New(0));
|
||||
SP[0] = result;
|
||||
return true;
|
||||
} else {
|
||||
SP[0] = 0; // Space for the result.
|
||||
SP[1] = thread->isolate_group()->object_store()->closure_class();
|
||||
SP[2] = Object::null(); // Type arguments.
|
||||
Exit(thread, FP, SP + 3, pc);
|
||||
NativeArguments args(thread, 2, SP + 1, SP);
|
||||
return InvokeRuntime(thread, this, DRT_AllocateObject, args);
|
||||
SP[1] = function;
|
||||
SP[2] = length_and_flags;
|
||||
SP[3] = Object::null(); // Context.
|
||||
Exit(thread, FP, SP + 4, pc);
|
||||
NativeArguments args(thread, 3, SP + 1, SP);
|
||||
return InvokeRuntime(thread, this, DRT_AllocateClosure, args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3485,28 +3495,36 @@ SwitchDispatchNoSingleStep:
|
||||
}
|
||||
|
||||
{
|
||||
BYTECODE(AllocateClosure, 0);
|
||||
++SP;
|
||||
if (!AllocateClosure(thread, pc, FP, SP)) {
|
||||
HANDLE_EXCEPTION;
|
||||
}
|
||||
ClosurePtr closure = Closure::RawCast(SP[0]);
|
||||
FunctionPtr function = Function::RawCast(SP[-3]);
|
||||
ObjectPtr context = SP[-2];
|
||||
TypeArgumentsPtr instantiator_type_arguments =
|
||||
TypeArguments::RawCast(SP[-1]);
|
||||
|
||||
BYTECODE(AllocateClosure, D);
|
||||
FunctionPtr function = Function::RawCast(LOAD_CONSTANT(rD));
|
||||
ASSERT((Function::KindOf(function) == UntaggedFunction::kClosureFunction) ||
|
||||
(Function::KindOf(function) ==
|
||||
UntaggedFunction::kImplicitClosureFunction));
|
||||
closure->untag()->set_function(function);
|
||||
ONLY_IN_PRECOMPILED(closure->untag()->entry_point_ =
|
||||
function->untag()->entry_point_);
|
||||
closure->untag()->set_context(context);
|
||||
closure->untag()->set_instantiator_type_arguments(
|
||||
instantiator_type_arguments);
|
||||
SP -= 3;
|
||||
SP[0] = closure;
|
||||
SmiPtr length_and_flags = Smi::RawCast(LOAD_CONSTANT(rD + 1));
|
||||
++SP;
|
||||
if (!AllocateClosure(thread, function, length_and_flags, pc, FP, SP)) {
|
||||
HANDLE_EXCEPTION;
|
||||
}
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
{
|
||||
BYTECODE(LoadClosureElement, D);
|
||||
ClosurePtr instance = Closure::RawCast(SP[0]);
|
||||
ASSERT((0 <= rD) && (rD < UntaggedClosure::LengthBits::decode(Smi::Value(
|
||||
instance->untag()->length_and_flags()))));
|
||||
SP[0] = instance->untag()->element(rD);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
{
|
||||
BYTECODE(StoreClosureElement, D);
|
||||
ClosurePtr instance = Closure::RawCast(SP[-1]);
|
||||
ObjectPtr value = static_cast<ObjectPtr>(SP[0]);
|
||||
ASSERT((0 <= rD) && (rD < UntaggedClosure::LengthBits::decode(Smi::Value(
|
||||
instance->untag()->length_and_flags()))));
|
||||
instance->untag()->set_element(rD, value);
|
||||
SP -= 2;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@@ -3827,34 +3845,87 @@ SwitchDispatchNoSingleStep:
|
||||
}
|
||||
|
||||
{
|
||||
BYTECODE(VMInternal_MethodExtractor, 0);
|
||||
BYTECODE(VMInternal_MethodExtractorWithITA, 0);
|
||||
|
||||
FunctionPtr function = FrameFunction(FP);
|
||||
ASSERT(Function::KindOf(function) == UntaggedFunction::kMethodExtractor);
|
||||
function = Function::RawCast(function->untag()->data());
|
||||
ASSERT(Function::KindOf(function) ==
|
||||
UntaggedFunction::kImplicitClosureFunction);
|
||||
|
||||
ASSERT(InterpreterHelpers::ArgDescTypeArgsLen(argdesc_) == 0);
|
||||
const bool has_delayed_type_args =
|
||||
FunctionType::RawCast(function->untag()->signature())
|
||||
->untag()
|
||||
->type_parameters() != TypeParameters::null();
|
||||
const bool has_instantiator_type_args = true;
|
||||
const bool has_function_type_args = false;
|
||||
const intptr_t length =
|
||||
UntaggedClosure::ContextIndex(has_delayed_type_args,
|
||||
has_instantiator_type_args,
|
||||
has_function_type_args) +
|
||||
1;
|
||||
SmiPtr length_and_flags = Smi::New(UntaggedClosure::EncodeLengthAndFlags(
|
||||
has_delayed_type_args, has_instantiator_type_args,
|
||||
has_function_type_args, length));
|
||||
|
||||
++SP;
|
||||
if (!AllocateClosure(thread, pc, FP, SP)) {
|
||||
if (!AllocateClosure(thread, function, length_and_flags, pc, FP, SP)) {
|
||||
HANDLE_EXCEPTION;
|
||||
}
|
||||
|
||||
ClosurePtr closure = Closure::RawCast(SP[0]);
|
||||
InstancePtr instance = Instance::RawCast(FrameArguments(FP, 1)[0]);
|
||||
intptr_t index = 0;
|
||||
if (has_delayed_type_args) {
|
||||
closure->untag()->set_element(index++,
|
||||
Object::empty_type_arguments().ptr());
|
||||
}
|
||||
closure->untag()->set_element(
|
||||
index++, InterpreterHelpers::GetTypeArguments(thread, instance));
|
||||
closure->untag()->set_element(index++, instance);
|
||||
ASSERT(index == length);
|
||||
|
||||
ClosurePtr closure = Closure::RawCast(*SP);
|
||||
closure->untag()->set_instantiator_type_arguments(
|
||||
InterpreterHelpers::GetTypeArguments(thread, instance));
|
||||
// function_type_arguments is already null
|
||||
closure->untag()->set_delayed_type_arguments(
|
||||
Object::empty_type_arguments().ptr());
|
||||
closure->untag()->set_function(function);
|
||||
ONLY_IN_PRECOMPILED(closure->untag()->entry_point_ =
|
||||
function->untag()->entry_point_);
|
||||
closure->untag()->set_context(instance);
|
||||
// hash is already null
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
{
|
||||
BYTECODE(VMInternal_MethodExtractorWithoutITA, 0);
|
||||
|
||||
FunctionPtr function = FrameFunction(FP);
|
||||
ASSERT(Function::KindOf(function) == UntaggedFunction::kMethodExtractor);
|
||||
function = Function::RawCast(function->untag()->data());
|
||||
ASSERT(Function::KindOf(function) ==
|
||||
UntaggedFunction::kImplicitClosureFunction);
|
||||
ASSERT(InterpreterHelpers::ArgDescTypeArgsLen(argdesc_) == 0);
|
||||
const bool has_delayed_type_args =
|
||||
FunctionType::RawCast(function->untag()->signature())
|
||||
->untag()
|
||||
->type_parameters() != TypeParameters::null();
|
||||
const bool has_instantiator_type_args = false;
|
||||
const bool has_function_type_args = false;
|
||||
const intptr_t length =
|
||||
UntaggedClosure::ContextIndex(has_delayed_type_args,
|
||||
has_instantiator_type_args,
|
||||
has_function_type_args) +
|
||||
1;
|
||||
SmiPtr length_and_flags = Smi::New(UntaggedClosure::EncodeLengthAndFlags(
|
||||
has_delayed_type_args, has_instantiator_type_args,
|
||||
has_function_type_args, length));
|
||||
|
||||
++SP;
|
||||
if (!AllocateClosure(thread, function, length_and_flags, pc, FP, SP)) {
|
||||
HANDLE_EXCEPTION;
|
||||
}
|
||||
|
||||
ClosurePtr closure = Closure::RawCast(SP[0]);
|
||||
InstancePtr instance = Instance::RawCast(FrameArguments(FP, 1)[0]);
|
||||
intptr_t index = 0;
|
||||
if (has_delayed_type_args) {
|
||||
closure->untag()->set_element(index++,
|
||||
Object::empty_type_arguments().ptr());
|
||||
}
|
||||
closure->untag()->set_element(index++, instance);
|
||||
ASSERT(index == length);
|
||||
|
||||
DISPATCH();
|
||||
}
|
||||
@@ -4130,9 +4201,7 @@ SwitchDispatchNoSingleStep:
|
||||
}
|
||||
} else {
|
||||
TypeArgumentsPtr delayed_type_arguments =
|
||||
Closure::RawCast(argv[receiver_idx])
|
||||
->untag()
|
||||
->delayed_type_arguments();
|
||||
Closure::delayed_type_arguments(Closure::RawCast(argv[receiver_idx]));
|
||||
if (delayed_type_arguments != Object::empty_type_arguments().ptr()) {
|
||||
if (type_args_len > 0) {
|
||||
SP[1] = function;
|
||||
@@ -4194,7 +4263,7 @@ SwitchDispatchNoSingleStep:
|
||||
}
|
||||
} else {
|
||||
TypeArgumentsPtr delayed_type_arguments =
|
||||
closure->untag()->delayed_type_arguments();
|
||||
Closure::delayed_type_arguments(closure);
|
||||
if (delayed_type_arguments != Object::empty_type_arguments().ptr()) {
|
||||
if (type_args_len > 0) {
|
||||
SP[1] = function;
|
||||
@@ -4205,7 +4274,7 @@ SwitchDispatchNoSingleStep:
|
||||
*++SP = delayed_type_arguments;
|
||||
ObjectPtr* call_base = SP;
|
||||
// Captured receiver.
|
||||
*++SP = closure->untag()->context();
|
||||
*++SP = Closure::RawContextOf(closure);
|
||||
// Copy the rest of the arguments.
|
||||
for (intptr_t i = receiver_idx + 1; i < argc; i++) {
|
||||
*++SP = argv[i];
|
||||
@@ -4235,7 +4304,7 @@ SwitchDispatchNoSingleStep:
|
||||
|
||||
// Replace closure receiver with captured receiver
|
||||
// and call target function.
|
||||
argv[receiver_idx] = closure->untag()->context();
|
||||
argv[receiver_idx] = Closure::RawContextOf(closure);
|
||||
SP[1] = target;
|
||||
|
||||
goto TailCallSP1;
|
||||
@@ -4281,9 +4350,7 @@ SwitchDispatchNoSingleStep:
|
||||
type_args = TypeArguments::null();
|
||||
} else {
|
||||
TypeArgumentsPtr delayed_type_arguments =
|
||||
Closure::RawCast(argv[receiver_idx])
|
||||
->untag()
|
||||
->delayed_type_arguments();
|
||||
Closure::delayed_type_arguments(Closure::RawCast(argv[receiver_idx]));
|
||||
if (delayed_type_arguments != Object::empty_type_arguments().ptr()) {
|
||||
if (type_args_len > 0) {
|
||||
SP[1] = function;
|
||||
|
||||
@@ -243,6 +243,8 @@ class Interpreter {
|
||||
ObjectPtr* FP,
|
||||
ObjectPtr* SP);
|
||||
bool AllocateClosure(Thread* thread,
|
||||
FunctionPtr function,
|
||||
SmiPtr length_and_flags,
|
||||
const KBCInstr* pc,
|
||||
ObjectPtr* FP,
|
||||
ObjectPtr* SP);
|
||||
|
||||
+80
-24
@@ -1229,8 +1229,11 @@ void Object::Init(IsolateGroup* isolate_group) {
|
||||
Roots::implicit_shared_static_setter_bytecode().initRO(
|
||||
CreateVMInternalBytecode(
|
||||
KernelBytecode::kVMInternal_ImplicitSharedStaticSetter));
|
||||
Roots::method_extractor_bytecode().initRO(
|
||||
CreateVMInternalBytecode(KernelBytecode::kVMInternal_MethodExtractor));
|
||||
Roots::method_extractor_with_ita_bytecode().initRO(CreateVMInternalBytecode(
|
||||
KernelBytecode::kVMInternal_MethodExtractorWithITA));
|
||||
Roots::method_extractor_without_ita_bytecode().initRO(
|
||||
CreateVMInternalBytecode(
|
||||
KernelBytecode::kVMInternal_MethodExtractorWithoutITA));
|
||||
Roots::invoke_closure_bytecode().initRO(
|
||||
CreateVMInternalBytecode(KernelBytecode::kVMInternal_InvokeClosure));
|
||||
Roots::invoke_field_bytecode().initRO(
|
||||
@@ -1254,7 +1257,8 @@ void Object::Init(IsolateGroup* isolate_group) {
|
||||
Roots::implicit_shared_static_getter_bytecode().initRO(Bytecode::null());
|
||||
Roots::implicit_static_setter_bytecode().initRO(Bytecode::null());
|
||||
Roots::implicit_shared_static_setter_bytecode().initRO(Bytecode::null());
|
||||
Roots::method_extractor_bytecode().initRO(Bytecode::null());
|
||||
Roots::method_extractor_with_ita_bytecode().initRO(Bytecode::null());
|
||||
Roots::method_extractor_without_ita_bytecode().initRO(Bytecode::null());
|
||||
Roots::invoke_closure_bytecode().initRO(Bytecode::null());
|
||||
Roots::invoke_field_bytecode().initRO(Bytecode::null());
|
||||
Roots::nsm_dispatcher_bytecode().initRO(Bytecode::null());
|
||||
@@ -1356,8 +1360,10 @@ void Object::Init(IsolateGroup* isolate_group) {
|
||||
ASSERT(Roots::implicit_static_getter_bytecode().IsBytecode());
|
||||
ASSERT(!Roots::implicit_static_setter_bytecode().IsSmi());
|
||||
ASSERT(Roots::implicit_static_setter_bytecode().IsBytecode());
|
||||
ASSERT(!Roots::method_extractor_bytecode().IsSmi());
|
||||
ASSERT(Roots::method_extractor_bytecode().IsBytecode());
|
||||
ASSERT(!Roots::method_extractor_with_ita_bytecode().IsSmi());
|
||||
ASSERT(Roots::method_extractor_with_ita_bytecode().IsBytecode());
|
||||
ASSERT(!Roots::method_extractor_without_ita_bytecode().IsSmi());
|
||||
ASSERT(Roots::method_extractor_without_ita_bytecode().IsBytecode());
|
||||
ASSERT(!Roots::invoke_closure_bytecode().IsSmi());
|
||||
ASSERT(Roots::invoke_closure_bytecode().IsBytecode());
|
||||
ASSERT(!Roots::invoke_field_bytecode().IsSmi());
|
||||
@@ -4151,7 +4157,18 @@ FunctionPtr Function::CreateMethodExtractor(const String& getter_name) const {
|
||||
const bool attach_bytecode = is_declared_in_bytecode();
|
||||
#endif
|
||||
if (attach_bytecode) {
|
||||
extractor.AttachBytecode(Object::method_extractor_bytecode());
|
||||
// Bytecode method extractor may be used to create a closure instance
|
||||
// with a compiled closure function, so layout of the closure object
|
||||
// has to be compatible between them.
|
||||
// 'Closure::HasInstantiatorTypeArgumentsField' involves a heavy signature
|
||||
// type inspection which is not feasible to perform in the method
|
||||
// extractor. So it is performed here to select a variant of the bytecode
|
||||
// method extractor.
|
||||
if (Closure::HasInstantiatorTypeArgumentsField(closure_function)) {
|
||||
extractor.AttachBytecode(Object::method_extractor_with_ita_bytecode());
|
||||
} else {
|
||||
extractor.AttachBytecode(Object::method_extractor_without_ita_bytecode());
|
||||
}
|
||||
}
|
||||
#endif // defined(DART_DYNAMIC_MODULES)
|
||||
|
||||
@@ -19365,8 +19382,12 @@ static const char* BytecodeStubName(const Bytecode& bytecode) {
|
||||
} else if (bytecode.ptr() ==
|
||||
Object::implicit_shared_static_setter_bytecode().ptr()) {
|
||||
return "[Bytecode Stub] VMInternal_ImplicitSharedStaticSetter";
|
||||
} else if (bytecode.ptr() == Object::method_extractor_bytecode().ptr()) {
|
||||
return "[Bytecode Stub] VMInternal_MethodExtractor";
|
||||
} else if (bytecode.ptr() ==
|
||||
Object::method_extractor_with_ita_bytecode().ptr()) {
|
||||
return "[Bytecode Stub] VMInternal_MethodExtractorWithITA";
|
||||
} else if (bytecode.ptr() ==
|
||||
Object::method_extractor_without_ita_bytecode().ptr()) {
|
||||
return "[Bytecode Stub] VMInternal_MethodExtractorWithoutITA";
|
||||
} else if (bytecode.ptr() == Object::invoke_closure_bytecode().ptr()) {
|
||||
return "[Bytecode Stub] VMInternal_InvokeClosure";
|
||||
} else if (bytecode.ptr() == Object::invoke_field_bytecode().ptr()) {
|
||||
@@ -26763,18 +26784,35 @@ uword Closure::ComputeHash() const {
|
||||
return FinalizeHash(result, String::kHashBits);
|
||||
}
|
||||
|
||||
ClosurePtr Closure::New(intptr_t length_and_flags, Heap::Space space) {
|
||||
const intptr_t num_elements =
|
||||
UntaggedClosure::LengthBits::decode(length_and_flags);
|
||||
ASSERT(num_elements >= 0);
|
||||
if (!IsValidLength(num_elements)) {
|
||||
// This should be caught before we reach here.
|
||||
FATAL("Fatal error in Closure::New: invalid num_elements %" Pd "\n",
|
||||
num_elements);
|
||||
}
|
||||
auto raw = Object::Allocate<Closure>(space, num_elements);
|
||||
NoSafepointScope no_safepoint;
|
||||
raw->untag()->set_length_and_flags(Smi::New(length_and_flags));
|
||||
raw->untag()->set_hash(Smi::New(0));
|
||||
if (UntaggedClosure::HasDelayedTypeArgumentsBit::decode(length_and_flags)) {
|
||||
raw->untag()->set_element(UntaggedClosure::kDelayedTypeArgumentsIndex,
|
||||
Object::empty_type_arguments().ptr());
|
||||
}
|
||||
return raw;
|
||||
}
|
||||
|
||||
ClosurePtr Closure::New(const TypeArguments& instantiator_type_arguments,
|
||||
const TypeArguments& function_type_arguments,
|
||||
const Function& function,
|
||||
const Object& context,
|
||||
Heap::Space space) {
|
||||
// We store null delayed type arguments, not empty ones, in closures with
|
||||
// non-generic functions a) to make method extraction slightly faster and
|
||||
// b) to make the Closure::IsGeneric check fast.
|
||||
// Keep in sync with StubCodeCompiler::GenerateAllocateClosureStub.
|
||||
return Closure::New(instantiator_type_arguments, function_type_arguments,
|
||||
function.IsGeneric() ? Object::empty_type_arguments()
|
||||
: Object::null_type_arguments(),
|
||||
Closure::HasDelayedTypeArgumentsField(function)
|
||||
? Object::empty_type_arguments()
|
||||
: Object::null_type_arguments(),
|
||||
function, context, space);
|
||||
}
|
||||
|
||||
@@ -26787,21 +26825,39 @@ ClosurePtr Closure::New(const TypeArguments& instantiator_type_arguments,
|
||||
ASSERT(instantiator_type_arguments.IsCanonical());
|
||||
ASSERT(function_type_arguments.IsCanonical());
|
||||
ASSERT(delayed_type_arguments.IsCanonical());
|
||||
ASSERT(function.IsClosureFunction());
|
||||
ASSERT(FunctionType::Handle(function.signature()).IsCanonical());
|
||||
ASSERT(
|
||||
(function.IsImplicitInstanceClosureFunction() && context.IsInstance()) ||
|
||||
(function.IsNonImplicitClosureFunction() && context.IsContext()) ||
|
||||
context.IsNull());
|
||||
const auto& result = Closure::Handle(Object::Allocate<Closure>(space));
|
||||
result.untag()->set_instantiator_type_arguments(
|
||||
instantiator_type_arguments.ptr());
|
||||
result.untag()->set_function_type_arguments(function_type_arguments.ptr());
|
||||
result.untag()->set_delayed_type_arguments(delayed_type_arguments.ptr());
|
||||
result.untag()->set_function(function.ptr());
|
||||
result.untag()->set_context(context.ptr());
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
result.set_entry_point(function.entry_point());
|
||||
#endif
|
||||
|
||||
const bool has_delayed_type_args =
|
||||
Closure::HasDelayedTypeArgumentsField(function);
|
||||
const bool has_instantiator_type_args =
|
||||
Closure::HasInstantiatorTypeArgumentsField(function);
|
||||
const bool has_function_type_args =
|
||||
Closure::HasFunctionTypeArgumentsField(function);
|
||||
const intptr_t context_index = UntaggedClosure::ContextIndex(
|
||||
has_delayed_type_args, has_instantiator_type_args,
|
||||
has_function_type_args);
|
||||
const intptr_t num_elements = context_index + 1;
|
||||
const intptr_t length_and_flags = UntaggedClosure::EncodeLengthAndFlags(
|
||||
has_delayed_type_args, has_instantiator_type_args, has_function_type_args,
|
||||
num_elements);
|
||||
|
||||
const auto& result = Closure::Handle(Closure::New(length_and_flags, space));
|
||||
result.set_function(function);
|
||||
if (has_delayed_type_args) {
|
||||
result.set_delayed_type_arguments(delayed_type_arguments);
|
||||
}
|
||||
if (has_instantiator_type_args) {
|
||||
result.set_instantiator_type_arguments(instantiator_type_arguments);
|
||||
}
|
||||
if (has_function_type_args) {
|
||||
result.set_function_type_arguments(function_type_arguments);
|
||||
}
|
||||
result.SetElementAt(context_index, context);
|
||||
return result.ptr();
|
||||
}
|
||||
|
||||
|
||||
+172
-40
@@ -548,7 +548,8 @@ class Object {
|
||||
V(Bytecode, implicit_shared_static_getter_bytecode) \
|
||||
V(Bytecode, implicit_static_setter_bytecode) \
|
||||
V(Bytecode, implicit_shared_static_setter_bytecode) \
|
||||
V(Bytecode, method_extractor_bytecode) \
|
||||
V(Bytecode, method_extractor_with_ita_bytecode) \
|
||||
V(Bytecode, method_extractor_without_ita_bytecode) \
|
||||
V(Bytecode, invoke_closure_bytecode) \
|
||||
V(Bytecode, invoke_field_bytecode) \
|
||||
V(Bytecode, nsm_dispatcher_bytecode) \
|
||||
@@ -12652,35 +12653,17 @@ class Closure : public Instance {
|
||||
}
|
||||
#endif
|
||||
|
||||
TypeArgumentsPtr instantiator_type_arguments() const {
|
||||
return untag()->instantiator_type_arguments();
|
||||
static intptr_t LengthOf(ClosurePtr ptr) {
|
||||
return UntaggedClosure::LengthBits::decode(
|
||||
Smi::Value(ptr->untag()->length_and_flags()));
|
||||
}
|
||||
void set_instantiator_type_arguments(const TypeArguments& args) const {
|
||||
untag()->set_instantiator_type_arguments(args.ptr());
|
||||
}
|
||||
static intptr_t instantiator_type_arguments_offset() {
|
||||
return OFFSET_OF(UntaggedClosure, instantiator_type_arguments_);
|
||||
intptr_t length() const { return LengthOf(ptr()); }
|
||||
static intptr_t length_and_flags_offset() {
|
||||
return OFFSET_OF(UntaggedClosure, length_and_flags_);
|
||||
}
|
||||
|
||||
TypeArgumentsPtr function_type_arguments() const {
|
||||
return untag()->function_type_arguments();
|
||||
}
|
||||
void set_function_type_arguments(const TypeArguments& args) const {
|
||||
untag()->set_function_type_arguments(args.ptr());
|
||||
}
|
||||
static intptr_t function_type_arguments_offset() {
|
||||
return OFFSET_OF(UntaggedClosure, function_type_arguments_);
|
||||
}
|
||||
|
||||
TypeArgumentsPtr delayed_type_arguments() const {
|
||||
return untag()->delayed_type_arguments();
|
||||
}
|
||||
void set_delayed_type_arguments(const TypeArguments& args) const {
|
||||
untag()->set_delayed_type_arguments(args.ptr());
|
||||
}
|
||||
static intptr_t delayed_type_arguments_offset() {
|
||||
return OFFSET_OF(UntaggedClosure, delayed_type_arguments_);
|
||||
}
|
||||
SmiPtr hash() const { return untag()->hash(); }
|
||||
static intptr_t hash_offset() { return OFFSET_OF(UntaggedClosure, hash_); }
|
||||
|
||||
FunctionPtr function() const { return untag()->function(); }
|
||||
static intptr_t function_offset() {
|
||||
@@ -12689,8 +12672,149 @@ class Closure : public Instance {
|
||||
static FunctionPtr FunctionOf(ClosurePtr closure) {
|
||||
return closure.untag()->function();
|
||||
}
|
||||
void set_function(const Function& function) const {
|
||||
untag()->set_function(function.ptr());
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
untag()->entry_point_ = function.entry_point();
|
||||
#endif
|
||||
}
|
||||
|
||||
ObjectPtr RawContext() const { return untag()->context(); }
|
||||
ObjectPtr ElementAt(intptr_t index) const {
|
||||
ASSERT((0 <= index) && (index < length()));
|
||||
return untag()->element(index);
|
||||
}
|
||||
void SetElementAt(intptr_t index, const Object& value) const {
|
||||
ASSERT((0 <= index) && (index < length()));
|
||||
untag()->set_element(index, value.ptr());
|
||||
}
|
||||
|
||||
static constexpr intptr_t kBytesPerElement = kCompressedWordSize;
|
||||
static constexpr intptr_t kMaxElements =
|
||||
UntaggedClosure::LengthBits::max() / kBytesPerElement;
|
||||
|
||||
static constexpr bool IsValidLength(intptr_t length) {
|
||||
return 0 <= length && length <= kMaxElements;
|
||||
}
|
||||
|
||||
struct ArrayTraits {
|
||||
static intptr_t elements_start_offset() { return sizeof(UntaggedClosure); }
|
||||
static constexpr intptr_t kElementSize = kBytesPerElement;
|
||||
};
|
||||
|
||||
static intptr_t element_offset(intptr_t index) {
|
||||
return OFFSET_OF_RETURNED_VALUE(UntaggedClosure, data) +
|
||||
kBytesPerElement * index;
|
||||
}
|
||||
static intptr_t element_index_at_offset(intptr_t offset_in_bytes) {
|
||||
const intptr_t index =
|
||||
(offset_in_bytes - OFFSET_OF_RETURNED_VALUE(UntaggedClosure, data)) /
|
||||
kBytesPerElement;
|
||||
ASSERT(index >= 0);
|
||||
return index;
|
||||
}
|
||||
|
||||
static intptr_t InstanceSize() {
|
||||
ASSERT(sizeof(UntaggedClosure) ==
|
||||
OFFSET_OF_RETURNED_VALUE(UntaggedClosure, data));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static intptr_t InstanceSize(intptr_t num_elements) {
|
||||
return RoundedAllocationSize(sizeof(UntaggedClosure) +
|
||||
(num_elements * kBytesPerElement));
|
||||
}
|
||||
|
||||
bool has_delayed_type_arguments() const {
|
||||
return UntaggedClosure::HasDelayedTypeArgumentsBit::decode(
|
||||
Smi::Value(untag()->length_and_flags()));
|
||||
}
|
||||
bool has_instantiator_type_arguments() const {
|
||||
return UntaggedClosure::HasInstantiatorTypeArgumentsBit::decode(
|
||||
Smi::Value(untag()->length_and_flags()));
|
||||
}
|
||||
bool has_function_type_arguments() const {
|
||||
return UntaggedClosure::HasFunctionTypeArgumentsBit::decode(
|
||||
Smi::Value(untag()->length_and_flags()));
|
||||
}
|
||||
|
||||
intptr_t delayed_type_arguments_index() const {
|
||||
ASSERT(has_delayed_type_arguments());
|
||||
return UntaggedClosure::kDelayedTypeArgumentsIndex;
|
||||
}
|
||||
intptr_t instantiator_type_arguments_index() const {
|
||||
ASSERT(has_instantiator_type_arguments());
|
||||
return UntaggedClosure::InstantiatorTypeArgumentsIndexBits::decode(
|
||||
Smi::Value(untag()->length_and_flags()));
|
||||
}
|
||||
intptr_t function_type_arguments_index() const {
|
||||
ASSERT(has_function_type_arguments());
|
||||
return UntaggedClosure::FunctionTypeArgumentsIndexBits::decode(
|
||||
Smi::Value(untag()->length_and_flags()));
|
||||
}
|
||||
|
||||
TypeArgumentsPtr delayed_type_arguments() const {
|
||||
return has_delayed_type_arguments() ? TypeArguments::RawCast(ElementAt(
|
||||
delayed_type_arguments_index()))
|
||||
: TypeArguments::null();
|
||||
}
|
||||
static TypeArgumentsPtr delayed_type_arguments(ClosurePtr ptr) {
|
||||
return UntaggedClosure::HasDelayedTypeArgumentsBit::decode(
|
||||
Smi::Value(ptr.untag()->length_and_flags()))
|
||||
? TypeArguments::RawCast(ptr.untag()->element(
|
||||
UntaggedClosure::kDelayedTypeArgumentsIndex))
|
||||
: TypeArguments::null();
|
||||
}
|
||||
void set_delayed_type_arguments(const TypeArguments& args) const {
|
||||
SetElementAt(delayed_type_arguments_index(), args);
|
||||
}
|
||||
|
||||
TypeArgumentsPtr instantiator_type_arguments() const {
|
||||
return has_instantiator_type_arguments()
|
||||
? TypeArguments::RawCast(
|
||||
ElementAt(instantiator_type_arguments_index()))
|
||||
: TypeArguments::null();
|
||||
}
|
||||
static TypeArgumentsPtr instantiator_type_arguments(ClosurePtr ptr) {
|
||||
const intptr_t length_and_flags =
|
||||
Smi::Value(ptr.untag()->length_and_flags());
|
||||
return UntaggedClosure::HasInstantiatorTypeArgumentsBit::decode(
|
||||
length_and_flags)
|
||||
? TypeArguments::RawCast(ptr.untag()->element(
|
||||
UntaggedClosure::InstantiatorTypeArgumentsIndexBits::
|
||||
decode(length_and_flags)))
|
||||
: TypeArguments::null();
|
||||
}
|
||||
void set_instantiator_type_arguments(const TypeArguments& args) const {
|
||||
SetElementAt(instantiator_type_arguments_index(), args);
|
||||
}
|
||||
|
||||
TypeArgumentsPtr function_type_arguments() const {
|
||||
return has_function_type_arguments() ? TypeArguments::RawCast(ElementAt(
|
||||
function_type_arguments_index()))
|
||||
: TypeArguments::null();
|
||||
}
|
||||
static TypeArgumentsPtr function_type_arguments(ClosurePtr ptr) {
|
||||
const intptr_t length_and_flags =
|
||||
Smi::Value(ptr.untag()->length_and_flags());
|
||||
return UntaggedClosure::HasFunctionTypeArgumentsBit::decode(
|
||||
length_and_flags)
|
||||
? TypeArguments::RawCast(ptr.untag()->element(
|
||||
UntaggedClosure::FunctionTypeArgumentsIndexBits::decode(
|
||||
length_and_flags)))
|
||||
: TypeArguments::null();
|
||||
}
|
||||
void set_function_type_arguments(const TypeArguments& args) const {
|
||||
SetElementAt(function_type_arguments_index(), args);
|
||||
}
|
||||
|
||||
static ObjectPtr RawContextOf(ClosurePtr ptr) {
|
||||
return ptr->untag()->element(LengthOf(ptr) - 1);
|
||||
}
|
||||
ObjectPtr RawContext() const { return RawContextOf(ptr()); }
|
||||
|
||||
void SetRawContext(const Object& context) const {
|
||||
SetElementAt(length() - 1, context);
|
||||
}
|
||||
|
||||
ContextPtr GetContext() const {
|
||||
ASSERT(!Function::IsImplicitClosureFunction(function()));
|
||||
@@ -12702,21 +12826,11 @@ class Closure : public Instance {
|
||||
return Instance::RawCast(RawContext());
|
||||
}
|
||||
|
||||
static intptr_t context_offset() {
|
||||
return OFFSET_OF(UntaggedClosure, context_);
|
||||
}
|
||||
|
||||
// Returns whether the closure is generic, that is, it has a generic closure
|
||||
// function and no delayed type arguments.
|
||||
bool IsGeneric() const {
|
||||
return delayed_type_arguments() == Object::empty_type_arguments().ptr();
|
||||
}
|
||||
|
||||
SmiPtr hash() const { return untag()->hash(); }
|
||||
static intptr_t hash_offset() { return OFFSET_OF(UntaggedClosure, hash_); }
|
||||
|
||||
static intptr_t InstanceSize() {
|
||||
return RoundedAllocationSize(sizeof(UntaggedClosure));
|
||||
return has_delayed_type_arguments() &&
|
||||
(delayed_type_arguments() == Object::empty_type_arguments().ptr());
|
||||
}
|
||||
|
||||
virtual void CanonicalizeFieldsLocked(Thread* thread) const;
|
||||
@@ -12726,6 +12840,24 @@ class Closure : public Instance {
|
||||
}
|
||||
uword ComputeHash() const;
|
||||
|
||||
static bool HasDelayedTypeArgumentsField(const Function& function) {
|
||||
ASSERT(function.IsClosureFunction());
|
||||
return function.IsGeneric();
|
||||
}
|
||||
|
||||
static bool HasInstantiatorTypeArgumentsField(const Function& function) {
|
||||
ASSERT(function.IsClosureFunction());
|
||||
return !function.HasInstantiatedSignature(kCurrentClass);
|
||||
}
|
||||
|
||||
static bool HasFunctionTypeArgumentsField(const Function& function) {
|
||||
ASSERT(function.IsClosureFunction());
|
||||
return function.HasGenericParent();
|
||||
}
|
||||
|
||||
static ClosurePtr New(intptr_t length_and_flags,
|
||||
Heap::Space space = Heap::kNew);
|
||||
|
||||
static ClosurePtr New(const TypeArguments& instantiator_type_arguments,
|
||||
const TypeArguments& function_type_arguments,
|
||||
const Function& function,
|
||||
|
||||
@@ -167,7 +167,7 @@ static bool CanShareObject(ObjectPtr obj, uword tags) {
|
||||
|
||||
if (cid == kClosureCid) {
|
||||
// We can share a closure iff it doesn't close over any state.
|
||||
return Closure::RawCast(obj)->untag()->context() == Object::null();
|
||||
return Closure::RawContextOf(Closure::RawCast(obj)) == Object::null();
|
||||
}
|
||||
|
||||
// All other objects that have immutability bit set are deeply immutable.
|
||||
@@ -290,6 +290,9 @@ void UpdateLengthField(intptr_t cid, ObjectPtr from, ObjectPtr to) {
|
||||
if (cid == kArrayCid || cid == kImmutableArrayCid) {
|
||||
static_cast<UntaggedArray*>(to.untag())->length_ =
|
||||
static_cast<UntaggedArray*>(from.untag())->length_;
|
||||
} else if (cid == kClosureCid) {
|
||||
static_cast<UntaggedClosure*>(to.untag())->length_and_flags_ =
|
||||
static_cast<UntaggedClosure*>(from.untag())->length_and_flags_;
|
||||
} else if (cid == kContextCid) {
|
||||
static_cast<UntaggedContext*>(to.untag())->num_variables_ =
|
||||
static_cast<UntaggedContext*>(from.untag())->num_variables_;
|
||||
@@ -1779,14 +1782,16 @@ class ObjectCopy : public Base {
|
||||
Base::ForwardCompressedPointers(from, to, kWordSize, instance_size);
|
||||
}
|
||||
void CopyClosure(typename Types::Closure from, typename Types::Closure to) {
|
||||
Base::StoreCompressedPointers(
|
||||
from, to, OFFSET_OF(UntaggedClosure, instantiator_type_arguments_),
|
||||
OFFSET_OF(UntaggedClosure, function_));
|
||||
Base::ForwardCompressedPointer(from, to,
|
||||
OFFSET_OF(UntaggedClosure, context_));
|
||||
Base::StoreCompressedPointersNoBarrier(from, to,
|
||||
OFFSET_OF(UntaggedClosure, hash_),
|
||||
OFFSET_OF(UntaggedClosure, hash_));
|
||||
const intptr_t length = Closure::LengthOf(Types::GetClosurePtr(from));
|
||||
Base::StoreCompressedPointersNoBarrier(
|
||||
from, to, OFFSET_OF(UntaggedClosure, length_and_flags_),
|
||||
OFFSET_OF(UntaggedClosure, hash_));
|
||||
Base::StoreCompressedPointers(from, to,
|
||||
OFFSET_OF(UntaggedClosure, function_),
|
||||
OFFSET_OF(UntaggedClosure, function_));
|
||||
Base::ForwardCompressedPointers(
|
||||
from, to, Closure::element_offset(0),
|
||||
Closure::element_offset(0) + Closure::kBytesPerElement * length);
|
||||
ONLY_IN_PRECOMPILED(UntagClosure(to)->entry_point_ =
|
||||
UntagClosure(from)->entry_point_);
|
||||
}
|
||||
|
||||
@@ -250,10 +250,10 @@ class ObjectPointerVisitor;
|
||||
RW(Code, allocate_float32x4_array_stub) \
|
||||
RW(Code, allocate_int32x4_array_stub) \
|
||||
RW(Code, allocate_float64x2_array_stub) \
|
||||
RW(Code, allocate_closure_stub) \
|
||||
RW(Code, allocate_closure_generic_stub) \
|
||||
RW(Code, allocate_closure_ta_stub) \
|
||||
RW(Code, allocate_closure_ta_generic_stub) \
|
||||
RW(Code, allocate_closure1_stub) \
|
||||
RW(Code, allocate_closure2_stub) \
|
||||
RW(Code, allocate_closure3_stub) \
|
||||
RW(Code, allocate_closure4_stub) \
|
||||
RW(Code, allocate_context_stub) \
|
||||
RW(Code, allocate_growable_array_stub) \
|
||||
RW(Code, allocate_object_stub) \
|
||||
@@ -358,10 +358,10 @@ class ObjectPointerVisitor;
|
||||
DO(allocate_float32x4_array_stub, AllocateFloat32x4Array) \
|
||||
DO(allocate_int32x4_array_stub, AllocateInt32x4Array) \
|
||||
DO(allocate_float64x2_array_stub, AllocateFloat64x2Array) \
|
||||
DO(allocate_closure_stub, AllocateClosure) \
|
||||
DO(allocate_closure_generic_stub, AllocateClosureGeneric) \
|
||||
DO(allocate_closure_ta_stub, AllocateClosureTA) \
|
||||
DO(allocate_closure_ta_generic_stub, AllocateClosureTAGeneric) \
|
||||
DO(allocate_closure1_stub, AllocateClosure1) \
|
||||
DO(allocate_closure2_stub, AllocateClosure2) \
|
||||
DO(allocate_closure3_stub, AllocateClosure3) \
|
||||
DO(allocate_closure4_stub, AllocateClosure4) \
|
||||
DO(allocate_context_stub, AllocateContext) \
|
||||
DO(allocate_growable_array_stub, AllocateGrowableArray) \
|
||||
DO(allocate_object_stub, AllocateObject) \
|
||||
|
||||
+4
-1
@@ -256,7 +256,10 @@ class ParsedFunction : public ZoneObject {
|
||||
V(current_num_processed, Smi, CurrentNumProcessed) \
|
||||
V(current_param_index, Smi, CurrentParamIndex) \
|
||||
V(current_type_param, Dynamic, CurrentTypeParam) \
|
||||
V(function_type_args, Dynamic, FunctionTypeArgs)
|
||||
V(function_type_args, Dynamic, FunctionTypeArgs) \
|
||||
V(instantiator_type_args, Dynamic, InstantiatorTypeArgs) \
|
||||
V(parent_function_type_args, Dynamic, ParentFunctionTypeArgs) \
|
||||
V(delayed_type_args, Dynamic, DelayedTypeArgs)
|
||||
|
||||
#define DEFINE_FIELD(Name, _, __) LocalVariable* Name = nullptr;
|
||||
FOR_EACH_DYNAMIC_CLOSURE_CALL_VARIABLE(DEFINE_FIELD)
|
||||
|
||||
@@ -1020,7 +1020,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_ClosureAllocation) {
|
||||
ProfileStackWalker walker(&profile);
|
||||
|
||||
EXPECT_SUBSTRING("DRT_AllocateClosure", walker.VMTagName());
|
||||
EXPECT_STREQ("[Stub] AllocateClosure", walker.CurrentName());
|
||||
EXPECT_STREQ("[Stub] AllocateClosure1", walker.CurrentName());
|
||||
EXPECT(walker.Down());
|
||||
EXPECT_SUBSTRING("foo", walker.CurrentName());
|
||||
EXPECT(!walker.Down());
|
||||
|
||||
@@ -103,6 +103,13 @@ intptr_t UntaggedObject::HeapSizeFromClass(uword tags) const {
|
||||
instance_size = InstructionsSection::InstanceSize(section_size);
|
||||
break;
|
||||
}
|
||||
case kClosureCid: {
|
||||
const ClosurePtr raw_closure = static_cast<const ClosurePtr>(this);
|
||||
intptr_t num_elements = UntaggedClosure::LengthBits::decode(
|
||||
Smi::Value(raw_closure->untag()->length_and_flags()));
|
||||
instance_size = Closure::InstanceSize(num_elements);
|
||||
break;
|
||||
}
|
||||
case kContextCid: {
|
||||
const ContextPtr raw_context = static_cast<const ContextPtr>(this);
|
||||
intptr_t num_variables = raw_context->untag()->num_variables_;
|
||||
@@ -526,7 +533,9 @@ COMPRESSED_VISITOR(FunctionType)
|
||||
COMPRESSED_VISITOR(RecordType)
|
||||
COMPRESSED_VISITOR(TypeParameter)
|
||||
COMPRESSED_VISITOR(Function)
|
||||
COMPRESSED_VISITOR(Closure)
|
||||
VARIABLE_COMPRESSED_VISITOR(Closure,
|
||||
UntaggedClosure::LengthBits::decode(Smi::Value(
|
||||
raw_obj->untag()->length_and_flags())))
|
||||
COMPRESSED_VISITOR(LibraryPrefix)
|
||||
COMPRESSED_VISITOR(Bytecode)
|
||||
REGULAR_VISITOR(SingleTargetCache)
|
||||
|
||||
+103
-42
@@ -3100,53 +3100,114 @@ class UntaggedTypeParameter : public UntaggedAbstractType {
|
||||
};
|
||||
|
||||
class UntaggedClosure : public UntaggedInstance {
|
||||
public:
|
||||
using HasDelayedTypeArgumentsBit = BitField<intptr_t, bool, 0, 1>;
|
||||
static constexpr intptr_t kHasDelayedTypeArgumentsBit =
|
||||
HasDelayedTypeArgumentsBit::shift();
|
||||
|
||||
using HasInstantiatorTypeArgumentsBit =
|
||||
BitField<intptr_t, bool, HasDelayedTypeArgumentsBit::kNextBit, 1>;
|
||||
static constexpr intptr_t kHasInstantiatorTypeArgumentsBit =
|
||||
HasInstantiatorTypeArgumentsBit::shift();
|
||||
|
||||
using HasFunctionTypeArgumentsBit =
|
||||
BitField<intptr_t, bool, HasInstantiatorTypeArgumentsBit::kNextBit, 1>;
|
||||
static constexpr intptr_t kHasFunctionTypeArgumentsBit =
|
||||
HasFunctionTypeArgumentsBit::shift();
|
||||
|
||||
// Same as HasDelayedTypeArgumentsBit.
|
||||
using InstantiatorTypeArgumentsIndexBits =
|
||||
BitField<intptr_t,
|
||||
uint8_t,
|
||||
HasDelayedTypeArgumentsBit::shift(),
|
||||
HasDelayedTypeArgumentsBit::bitsize()>;
|
||||
|
||||
using FunctionTypeArgumentsIndexBits =
|
||||
BitField<intptr_t, uint8_t, HasFunctionTypeArgumentsBit::kNextBit, 2>;
|
||||
static constexpr intptr_t kFunctionTypeArgumentsIndexBitsPos =
|
||||
FunctionTypeArgumentsIndexBits::shift();
|
||||
static constexpr intptr_t kFunctionTypeArgumentsIndexBitsSize =
|
||||
FunctionTypeArgumentsIndexBits::bitsize();
|
||||
|
||||
using LengthBits = BitField<intptr_t,
|
||||
intptr_t,
|
||||
FunctionTypeArgumentsIndexBits::kNextBit,
|
||||
compiler::target::kSmiBits -
|
||||
FunctionTypeArgumentsIndexBits::kNextBit>;
|
||||
static_assert(LengthBits::kNextBit <= compiler::target::kSmiBits,
|
||||
"Length and flags should fit into a Smi");
|
||||
static constexpr intptr_t kLengthBitsPos = LengthBits::shift();
|
||||
static constexpr intptr_t kLengthBitsSize = LengthBits::bitsize();
|
||||
|
||||
static constexpr intptr_t kDelayedTypeArgumentsIndex = 0;
|
||||
|
||||
static intptr_t InstantiatorTypeArgumentsIndex(bool has_delayed_type_args) {
|
||||
return static_cast<intptr_t>(has_delayed_type_args);
|
||||
}
|
||||
static intptr_t FunctionTypeArgumentsIndex(bool has_delayed_type_args,
|
||||
bool has_instantiator_type_args) {
|
||||
return static_cast<intptr_t>(has_delayed_type_args) +
|
||||
static_cast<intptr_t>(has_instantiator_type_args);
|
||||
}
|
||||
static intptr_t ContextIndex(bool has_delayed_type_args,
|
||||
bool has_instantiator_type_args,
|
||||
bool has_function_type_args) {
|
||||
return static_cast<intptr_t>(has_delayed_type_args) +
|
||||
static_cast<intptr_t>(has_instantiator_type_args) +
|
||||
static_cast<intptr_t>(has_function_type_args);
|
||||
}
|
||||
|
||||
static intptr_t EncodeLengthAndFlags(bool has_delayed_type_args,
|
||||
bool has_instantiator_type_args,
|
||||
bool has_function_type_args,
|
||||
intptr_t num_elements) {
|
||||
return HasDelayedTypeArgumentsBit::encode(has_delayed_type_args) |
|
||||
HasInstantiatorTypeArgumentsBit::encode(has_instantiator_type_args) |
|
||||
HasFunctionTypeArgumentsBit::encode(has_function_type_args) |
|
||||
FunctionTypeArgumentsIndexBits::encode(
|
||||
has_function_type_args
|
||||
? FunctionTypeArgumentsIndex(has_delayed_type_args,
|
||||
has_instantiator_type_args)
|
||||
: 0) |
|
||||
LengthBits::encode(num_elements);
|
||||
}
|
||||
|
||||
private:
|
||||
RAW_HEAP_OBJECT_IMPLEMENTATION(Closure);
|
||||
|
||||
// The following fields are also declared in the Dart source of class
|
||||
// _Closure, and so must be the first fields in the object and must appear
|
||||
// in the same order, so the offsets are identical in Dart and C++.
|
||||
//
|
||||
// Note that the type of a closure is defined by instantiating the
|
||||
// signature of the closure function with the instantiator, function, and
|
||||
// delayed (if non-empty) type arguments stored in the closure value.
|
||||
|
||||
// Stores the instantiator type arguments provided when the closure was
|
||||
// created.
|
||||
COMPRESSED_POINTER_FIELD(TypeArgumentsPtr, instantiator_type_arguments)
|
||||
VISIT_FROM(instantiator_type_arguments)
|
||||
// Stores the function type arguments provided for any generic parent
|
||||
// functions when the closure was created.
|
||||
COMPRESSED_POINTER_FIELD(TypeArgumentsPtr, function_type_arguments)
|
||||
// If this field contains the empty type argument vector, then the closure
|
||||
// value is generic.
|
||||
//
|
||||
// To create a new closure that is a specific type instantiation of a generic
|
||||
// closure, a copy of the closure is created where the empty type argument
|
||||
// vector in this field is replaced with the vector of local type arguments.
|
||||
// The resulting closure value is not generic, and so an attempt to provide
|
||||
// type arguments when invoking the new closure value is treated the same as
|
||||
// calling any other non-generic function with unneeded type arguments.
|
||||
//
|
||||
// If the signature for the closure function has no local type parameters,
|
||||
// the only guarantee about this field is that it never contains the empty
|
||||
// type arguments vector. Thus, only this field need be inspected to
|
||||
// determine whether a given closure value is generic.
|
||||
COMPRESSED_POINTER_FIELD(TypeArgumentsPtr, delayed_type_arguments)
|
||||
COMPRESSED_POINTER_FIELD(FunctionPtr, function)
|
||||
// For tear-offs - captured receiver.
|
||||
// For ordinary closures - Context object with captured variables.
|
||||
COMPRESSED_POINTER_FIELD(ObjectPtr, context)
|
||||
COMPRESSED_POINTER_FIELD(SmiPtr, hash)
|
||||
VISIT_TO(hash)
|
||||
|
||||
// We have an extra word in the object due to alignment rounding, so use it in
|
||||
// bare instructions mode to cache the entry point from the closure function
|
||||
// to avoid an extra redirection on call. Closure functions only have
|
||||
// one entry point, as dynamic calls use dynamic closure call dispatchers.
|
||||
// Cached entry point from the closure function to avoid an extra
|
||||
// indirection on call. Closure functions only have one entry point,
|
||||
// as dynamic calls use dynamic closure call dispatchers.
|
||||
ONLY_IN_PRECOMPILED(uword entry_point_);
|
||||
|
||||
CompressedObjectPtr* to_snapshot(Snapshot::Kind kind) { return to(); }
|
||||
#if defined(DART_COMPRESSED_POINTERS)
|
||||
// This explicit padding avoids implicit padding between [function] and
|
||||
// [data]. Closure allocation doesn't initialize the implicit padding but
|
||||
// GC scans everything between 'from' (length_and_flags) and 'to'
|
||||
// (end of data), so it would see garbage if implicit padding is inserted.
|
||||
uint32_t padding_;
|
||||
#endif
|
||||
|
||||
COMPRESSED_SMI_FIELD(SmiPtr, length_and_flags)
|
||||
VISIT_FROM(length_and_flags)
|
||||
COMPRESSED_SMI_FIELD(SmiPtr, hash)
|
||||
COMPRESSED_POINTER_FIELD(FunctionPtr, function)
|
||||
|
||||
// Variable length data follows here.
|
||||
// It contains (in order):
|
||||
// - delayed type arguments (if function is generic);
|
||||
// - instantiator type arguments (if enclosing class is generic);
|
||||
// - parent function type arguments (if enclosing function has type args);
|
||||
// - captured values and contexts.
|
||||
COMPRESSED_VARIABLE_POINTER_FIELDS(ObjectPtr, element, data)
|
||||
|
||||
CompressedObjectPtr* to_snapshot(Snapshot::Kind kind, intptr_t num_elements) {
|
||||
return to(num_elements);
|
||||
}
|
||||
|
||||
friend void UpdateLengthField(intptr_t,
|
||||
ObjectPtr,
|
||||
ObjectPtr); // length_and_flags
|
||||
|
||||
friend class Interpreter;
|
||||
friend class UnitDeserializationRoots;
|
||||
|
||||
@@ -151,11 +151,8 @@ namespace dart {
|
||||
F(TypeParameters, flags_) \
|
||||
F(TypeParameters, bounds_) \
|
||||
F(TypeParameters, defaults_) \
|
||||
F(Closure, instantiator_type_arguments_) \
|
||||
F(Closure, function_type_arguments_) \
|
||||
F(Closure, delayed_type_arguments_) \
|
||||
F(Closure, function_) \
|
||||
F(Closure, context_) \
|
||||
F(Closure, length_and_flags_) \
|
||||
F(Closure, hash_) \
|
||||
F(String, length_) \
|
||||
F(Array, type_arguments_) \
|
||||
|
||||
+2
-1
@@ -89,7 +89,8 @@ namespace dart {
|
||||
V(Bytecode, implicit_shared_static_getter_bytecode) \
|
||||
V(Bytecode, implicit_static_setter_bytecode) \
|
||||
V(Bytecode, implicit_shared_static_setter_bytecode) \
|
||||
V(Bytecode, method_extractor_bytecode) \
|
||||
V(Bytecode, method_extractor_with_ita_bytecode) \
|
||||
V(Bytecode, method_extractor_without_ita_bytecode) \
|
||||
V(Bytecode, invoke_closure_bytecode) \
|
||||
V(Bytecode, invoke_field_bytecode) \
|
||||
V(Bytecode, nsm_dispatcher_bytecode) \
|
||||
|
||||
+11
-14
@@ -852,24 +852,21 @@ DEFINE_RUNTIME_ENTRY(SubtypeCheck, 5) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
// Allocate a new closure and initializes its function, context,
|
||||
// instantiator type arguments and delayed type arguments fields.
|
||||
// Allocate a new closure and initializes its function, length,
|
||||
// flags, context, hash and entry point.
|
||||
// Arg0: function.
|
||||
// Arg1: context.
|
||||
// Arg2: instantiator type arguments.
|
||||
// Arg3: delayed type arguments.
|
||||
// Arg1: length and flags.
|
||||
// Arg2: context.
|
||||
// Return value: newly allocated closure.
|
||||
DEFINE_RUNTIME_ENTRY(AllocateClosure, 4) {
|
||||
DEFINE_RUNTIME_ENTRY(AllocateClosure, 3) {
|
||||
const auto& function = Function::CheckedHandle(zone, arguments.ArgAt(0));
|
||||
const auto& context = Object::Handle(zone, arguments.ArgAt(1));
|
||||
const auto& instantiator_type_args =
|
||||
TypeArguments::CheckedHandle(zone, arguments.ArgAt(2));
|
||||
const auto& delayed_type_args =
|
||||
TypeArguments::CheckedHandle(zone, arguments.ArgAt(3));
|
||||
const intptr_t length_and_flags =
|
||||
Smi::CheckedHandle(zone, arguments.ArgAt(1)).Value();
|
||||
const auto& context = Object::Handle(zone, arguments.ArgAt(2));
|
||||
const Closure& closure = Closure::Handle(
|
||||
zone, Closure::New(instantiator_type_args, Object::null_type_arguments(),
|
||||
delayed_type_args, function, context,
|
||||
SpaceForRuntimeAllocation()));
|
||||
zone, Closure::New(length_and_flags, SpaceForRuntimeAllocation()));
|
||||
closure.set_function(function);
|
||||
closure.SetRawContext(context);
|
||||
arguments.SetReturn(closure);
|
||||
RuntimeAllocationEpilogue(thread);
|
||||
}
|
||||
|
||||
@@ -226,7 +226,7 @@ CodePtr StubCode::GetAllocationStubForClass(const Class& cls) {
|
||||
case kInt32x4Cid:
|
||||
return object_store->allocate_int32x4_stub();
|
||||
case kClosureCid:
|
||||
return object_store->allocate_closure_stub();
|
||||
return object_store->allocate_closure1_stub();
|
||||
case kRecordCid:
|
||||
return object_store->allocate_record_stub();
|
||||
}
|
||||
|
||||
@@ -59,10 +59,10 @@ namespace dart {
|
||||
V(AllocateFloat64x2Array) \
|
||||
V(AllocateMintSharedWithFPURegs) \
|
||||
V(AllocateMintSharedWithoutFPURegs) \
|
||||
V(AllocateClosure) \
|
||||
V(AllocateClosureGeneric) \
|
||||
V(AllocateClosureTA) \
|
||||
V(AllocateClosureTAGeneric) \
|
||||
V(AllocateClosure1) \
|
||||
V(AllocateClosure2) \
|
||||
V(AllocateClosure3) \
|
||||
V(AllocateClosure4) \
|
||||
V(AllocateContext) \
|
||||
V(AllocateGrowableArray) \
|
||||
V(AllocateObject) \
|
||||
|
||||
@@ -97,7 +97,11 @@ namespace dart {
|
||||
V(DynamicCallCurrentNumProcessedVar, ":dyn_call_current_num_processed") \
|
||||
V(DynamicCallCurrentParamIndexVar, ":dyn_call_current_param_index") \
|
||||
V(DynamicCallCurrentTypeParamVar, ":dyn_call_current_type_param") \
|
||||
V(DynamicCallDelayedTypeArgsVar, ":dyn_call_delayed_type_args") \
|
||||
V(DynamicCallFunctionTypeArgsVar, ":dyn_call_function_type_args") \
|
||||
V(DynamicCallInstantiatorTypeArgsVar, ":dyn_call_instantiator_type_args") \
|
||||
V(DynamicCallParentFunctionTypeArgsVar, \
|
||||
":dyn_call_parent_function_type_args") \
|
||||
V(DynamicImplicitCall, "dyn:implicit:call") \
|
||||
V(DynamicPrefix, "dyn:") \
|
||||
V(EntryPointsTemp, ":entry_points_temp") \
|
||||
|
||||
@@ -14,44 +14,21 @@ final class _Closure implements Function {
|
||||
external bool operator ==(Object other);
|
||||
|
||||
int get hashCode {
|
||||
_hash ??= _computeHash();
|
||||
return _hash;
|
||||
int hash = _hash;
|
||||
if (hash == 0) {
|
||||
hash = _computeHash();
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
@pragma("vm:entry-point")
|
||||
_Closure get call => this;
|
||||
|
||||
@pragma("vm:recognized", "other")
|
||||
@pragma("vm:prefer-inline")
|
||||
external int get _hash;
|
||||
|
||||
@pragma("vm:external-name", "Closure_computeHash")
|
||||
@pragma("vm:exact-result-type", "dart:core#_Smi")
|
||||
external int _computeHash();
|
||||
|
||||
// No instance fields should be declared before the following fields whose
|
||||
// offsets must be identical in Dart and C++.
|
||||
|
||||
// The following fields are declared both in raw_object.h (for direct access
|
||||
// from C++ code) and also here so that the offset-to-field map used by
|
||||
// deferred objects is properly initialized.
|
||||
// Caution: These fields are not Dart instances, but VM objects. Their Dart
|
||||
// names do not need to match the C++ names, but they must be private.
|
||||
@pragma("vm:entry-point")
|
||||
var _instantiator_type_arguments;
|
||||
@pragma("vm:entry-point")
|
||||
var _function_type_arguments;
|
||||
@pragma("vm:entry-point")
|
||||
var _delayed_type_arguments;
|
||||
@pragma("vm:entry-point")
|
||||
var _function;
|
||||
@pragma("vm:entry-point")
|
||||
var _context;
|
||||
|
||||
// Note: _Closure objects are created by VM "magically", without invoking
|
||||
// constructor. So, _Closure default constructor is never compiled and
|
||||
// detection of default-initialized fields is not performed.
|
||||
// As a consequence, VM incorrectly assumes that _hash field is not
|
||||
// nullable and may incorrectly remove 'if (_hash == null)' in get:hashCode.
|
||||
// This initializer makes _hash field nullable even without constructor
|
||||
// compilation.
|
||||
@pragma("vm:entry-point")
|
||||
// Harmless race lazily computing the hash.
|
||||
@pragma("vm:no-sanitize-thread")
|
||||
var _hash = null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user