[modular_aot] Initial code generation for TypeCast, TypeTest
Also: * Revise code generation for cycles in parallel moves using temporary register. * Support splitting of live intervals both at even and odd positions. TEST=ci Issue: https://github.com/dart-lang/sdk/issues/61635 Change-Id: I900edc275b59daf93604668eada1421dd125b217 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/487982 Reviewed-by: Slava Egorov <vegorov@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
committed by
Commit Queue
parent
5bc9ebab68
commit
d611d205e3
@@ -366,12 +366,12 @@ class AstToIr extends ast.RecursiveVisitor {
|
||||
}
|
||||
return switch (typeParametersStyle) {
|
||||
.separateFunctionAndClassTypeParameters => [
|
||||
visitor.containsFunctionTypeParams
|
||||
? functionTypeParameters!
|
||||
: builder.graph.getConstant(ConstantValue.fromNull()),
|
||||
visitor.containsClassTypeParams
|
||||
? classTypeParameters!
|
||||
: builder.graph.getConstant(ConstantValue.fromNull()),
|
||||
visitor.containsFunctionTypeParams
|
||||
? functionTypeParameters!
|
||||
: builder.graph.getConstant(ConstantValue.fromNull()),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1727,8 +1727,10 @@ enum ParallelMoveStage {
|
||||
output,
|
||||
// Spill output of the instruction.
|
||||
spill,
|
||||
// Split live ranges.
|
||||
// Split live ranges between instructions.
|
||||
split,
|
||||
// Split live ranges at the next instruction.
|
||||
splitLate,
|
||||
// Moves at control flow edges (including phi moves).
|
||||
control,
|
||||
// Move instruction inputs to their fixed locations.
|
||||
|
||||
@@ -18,7 +18,7 @@ B0 = EntryBlock()
|
||||
v4 = TypeParameters(v1)
|
||||
v7 = DirectCall _GrowableList.(v5, v6)
|
||||
DirectCall print(v7)
|
||||
v10 = TypeArguments(v4, v9, <listLiterals.T%>)
|
||||
v10 = TypeArguments(v9, v4, <listLiterals.T%>)
|
||||
v11 = DirectCall _GrowableList.(v10, v6)
|
||||
DirectCall print(v11)
|
||||
v17 = DirectCall _GrowableList._literal3(v13, v14, v15, v16)
|
||||
@@ -46,7 +46,7 @@ B0 = EntryBlock()
|
||||
v7 = TypeParameters(v1)
|
||||
v9 = AllocateMapLiteral(v8)
|
||||
DirectCall print(v9)
|
||||
v12 = TypeArguments(v7, v11, <mapLiterals.S%, mapLiterals.T%>)
|
||||
v12 = TypeArguments(v11, v7, <mapLiterals.S%, mapLiterals.T%>)
|
||||
v13 = AllocateMapLiteral(v12)
|
||||
DirectCall print(v13)
|
||||
v20 = AllocateMapLiteral(v15, v16, v17, v18, v19)
|
||||
|
||||
@@ -15,7 +15,7 @@ B0 = EntryBlock()
|
||||
v1 = Parameter(this)
|
||||
v3 = TypeParameters(v1)
|
||||
InterfaceCall A.foo(v4, v1, v6)
|
||||
v9 = TypeArguments(v8, v3, <Map<String, A.T%>>)
|
||||
v9 = TypeArguments(v3, v8, <Map<String, A.T%>>)
|
||||
InterfaceCall A.foo(v9, v1, v11)
|
||||
Return(v8)
|
||||
|
||||
@@ -27,10 +27,10 @@ B0 = EntryBlock() dominates:(B12, B14, B11)
|
||||
v3 = Parameter(o)
|
||||
TypeParameters(v1)
|
||||
v7 = TypeParameters(v2)
|
||||
v10 = TypeTest(v3, v9, v7, List<A.T%>)
|
||||
v10 = TypeTest(v3, v7, v9, List<A.T%>)
|
||||
Branch(v10, true: B11, false: B12)
|
||||
B11 = TargetBlock() idom:B0
|
||||
v17 = TypeCast(v3, v9, v7, List<A.T%>, unchecked)
|
||||
v17 = TypeCast(v3, v7, v9, List<A.T%>, unchecked)
|
||||
TypeCast(v17, Map<dynamic, dynamic>)
|
||||
Goto(B14)
|
||||
B12 = TargetBlock() idom:B0
|
||||
@@ -47,9 +47,9 @@ B0 = EntryBlock()
|
||||
v4 = TypeParameters(v1)
|
||||
v6 = TypeParameters(v2)
|
||||
DirectCall print(v7)
|
||||
v10 = TypeLiteral(A.T%, v9, v6)
|
||||
v10 = TypeLiteral(A.T%, v6, v9)
|
||||
DirectCall print(v10)
|
||||
v12 = TypeLiteral(Map<A.baz.S%, A.T%>, v4, v6)
|
||||
v12 = TypeLiteral(Map<A.baz.S%, A.T%>, v6, v4)
|
||||
DirectCall print(v12)
|
||||
Return(v9)
|
||||
|
||||
@@ -58,7 +58,7 @@ B0 = EntryBlock()
|
||||
v4 = Constant(null)
|
||||
v1 = Parameter(#functionTypeParameters)
|
||||
v3 = TypeParameters(v1)
|
||||
v5 = TypeArguments(v3, v4, <A..T%>)
|
||||
v5 = TypeArguments(v4, v3, <A..T%>)
|
||||
v6 = AllocateObject A<A..T%>(v5)
|
||||
DirectCall A._(v6)
|
||||
Return(v6)
|
||||
|
||||
@@ -685,6 +685,26 @@ final class Arm64Assembler extends Assembler with Uint32OutputBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void cmpImmediate(
|
||||
Register reg,
|
||||
int value, [
|
||||
OperandSize sz = OperandSize.s64,
|
||||
Register scratch = temp2Reg,
|
||||
]) {
|
||||
assert(sz.is32or64);
|
||||
assert(_isInt(sz.bitWidth, value) || _isUint(sz.bitWidth, value));
|
||||
if (canEncodeImm12(value)) {
|
||||
cmp(reg, Immediate(value), sz);
|
||||
} else if (canEncodeImm12(-value)) {
|
||||
cmn(reg, Immediate(-value), sz);
|
||||
} else {
|
||||
assert(reg != scratch);
|
||||
loadImmediate(scratch, value);
|
||||
cmp(reg, scratch, sz);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void andImmediate(
|
||||
Register dst,
|
||||
@@ -810,6 +830,16 @@ final class Arm64Assembler extends Assembler with Uint32OutputBuffer {
|
||||
addImmediate(resultReg, resultReg, heapObjectTag);
|
||||
}
|
||||
|
||||
void loadClassId(Register result, Register object) {
|
||||
ldr(result, fieldAddress(object, vmOffsets.Object_tags_offset));
|
||||
ubfx(
|
||||
result,
|
||||
result,
|
||||
vmOffsets.UntaggedObject_kClassIdTagPos,
|
||||
vmOffsets.UntaggedObject_kClassIdTagSize,
|
||||
);
|
||||
}
|
||||
|
||||
// [rd] and [rn] can be SP if [o] is Immediate or ExtRegOperand.
|
||||
// For an unmodified rm in this case, use ExtRegOperand(rm, Extend.UXTX, 0).
|
||||
void add(
|
||||
|
||||
@@ -814,14 +814,253 @@ final class Arm64CodeGenerator extends CodeGenerator {
|
||||
_asm.unimplemented('Unimplemented: code generation for NullCheck');
|
||||
}
|
||||
|
||||
int _getNumberOfInputsForSubtypeTestCache(
|
||||
ast.DartType type, {
|
||||
required bool hasInstantiatorTypeArgs,
|
||||
required bool hasFunctionTypeArgs,
|
||||
}) {
|
||||
if (type is ast.ExtensionType) {
|
||||
type = type.extensionTypeErasure;
|
||||
}
|
||||
switch (type) {
|
||||
case ast.NullType():
|
||||
case ast.NeverType():
|
||||
case ast.InterfaceType() when type.classNode.typeParameters.isEmpty:
|
||||
return 1;
|
||||
case ast.InterfaceType():
|
||||
case ast.FutureOrType():
|
||||
if (hasFunctionTypeArgs) {
|
||||
return 4;
|
||||
}
|
||||
if (hasInstantiatorTypeArgs) {
|
||||
return 3;
|
||||
}
|
||||
return 2;
|
||||
case ast.FunctionType():
|
||||
case ast.RecordType():
|
||||
case ast.TypeParameterType():
|
||||
return 6;
|
||||
case ast.ExtensionType():
|
||||
case ast.DynamicType():
|
||||
case ast.VoidType():
|
||||
case ast.StructuralParameterType():
|
||||
case ast.IntersectionType():
|
||||
case ast.TypedefType():
|
||||
case ast.InvalidType():
|
||||
case ast.AuxiliaryType():
|
||||
case ast.ExperimentalType():
|
||||
throw 'Unexpected type ${type.runtimeType} $type';
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void visitTypeCast(TypeCast instr) {
|
||||
_asm.unimplemented('Unimplemented: code generation for TypeCast');
|
||||
final operandReg = inputReg(instr, 0);
|
||||
final resultReg = outputReg(instr);
|
||||
if (operandReg != resultReg) {
|
||||
_asm.mov(resultReg, operandReg);
|
||||
}
|
||||
|
||||
if (!instr.isChecked) {
|
||||
return;
|
||||
}
|
||||
|
||||
final done = Label();
|
||||
late final Label slowPath = addSlowPath(() {
|
||||
_asm.unimplemented(
|
||||
'Unimplemented: code generation for TypeCast slow path',
|
||||
);
|
||||
_asm.b(done);
|
||||
});
|
||||
|
||||
// Handle a few built-in types, use TTS for other types.
|
||||
final type = instr.testedType;
|
||||
switch (type) {
|
||||
case ObjectType():
|
||||
_asm.cmp(resultReg, nullReg);
|
||||
_asm.b(slowPath, .equal);
|
||||
case NullType():
|
||||
_asm.cmp(resultReg, nullReg);
|
||||
_asm.b(slowPath, .notEqual);
|
||||
case IntType():
|
||||
_asm.tbz(resultReg, smiBit, done);
|
||||
_asm.loadClassId(tempReg, resultReg);
|
||||
_asm.cmpImmediate(tempReg, ClassId.MintCid.index);
|
||||
_asm.b(slowPath, .notEqual);
|
||||
case DoubleType():
|
||||
_asm.tbz(resultReg, smiBit, slowPath);
|
||||
_asm.loadClassId(tempReg, resultReg);
|
||||
_asm.cmpImmediate(tempReg, ClassId.DoubleCid.index);
|
||||
_asm.b(slowPath, .notEqual);
|
||||
case BoolType():
|
||||
_asm.tbz(resultReg, smiBit, slowPath);
|
||||
_asm.loadClassId(tempReg, resultReg);
|
||||
_asm.cmpImmediate(tempReg, ClassId.BoolCid.index);
|
||||
_asm.b(slowPath, .notEqual);
|
||||
case StringType():
|
||||
_asm.tbz(resultReg, smiBit, slowPath);
|
||||
_asm.loadClassId(tempReg, resultReg);
|
||||
_asm.cmpImmediate(tempReg, ClassId.OneByteStringCid.index);
|
||||
_asm.b(done, .equal);
|
||||
_asm.cmpImmediate(tempReg, ClassId.TwoByteStringCid.index);
|
||||
_asm.b(slowPath, .notEqual);
|
||||
default:
|
||||
_asm.tbz(
|
||||
resultReg,
|
||||
smiBit,
|
||||
const IntType().isSubtypeOf(type) ? done : slowPath,
|
||||
);
|
||||
if (type.isNullable) {
|
||||
_asm.cmp(resultReg, nullReg);
|
||||
_asm.b(done, .equal);
|
||||
}
|
||||
final dartType = type.dartType;
|
||||
if (dartType is ast.TypeParameterType) {
|
||||
final declaration = dartType.parameter.declaration;
|
||||
assert(instr.inputCount == 3);
|
||||
final instantiatorTypeArgsReg = inputReg(instr, 1);
|
||||
final functionTypeArgsReg = inputReg(instr, 2);
|
||||
final typeArgsReg = (declaration is ast.Class)
|
||||
? instantiatorTypeArgsReg
|
||||
: functionTypeArgsReg;
|
||||
final index = computeIndexOfTypeParameter(dartType.parameter);
|
||||
_asm.cmp(typeArgsReg, nullReg);
|
||||
_asm.b(done, .equal);
|
||||
_asm.ldr(
|
||||
TypeTestingStub.dstTypeReg,
|
||||
_asm.address(
|
||||
typeArgsReg,
|
||||
vmOffsets.TypeArguments_types_offset +
|
||||
index * objectLayout.compressedWordSize,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
_asm.loadFromPool(TypeTestingStub.dstTypeReg, dartType);
|
||||
}
|
||||
_asm.ldr(
|
||||
tempReg,
|
||||
_asm.address(
|
||||
TypeTestingStub.dstTypeReg,
|
||||
vmOffsets.AbstractType_type_test_stub_entry_point_offset,
|
||||
),
|
||||
);
|
||||
bool isNullConstant(Definition def) =>
|
||||
def is Constant && def.value.isNull;
|
||||
final hasInstantiatorTypeArgs =
|
||||
instr.inputCount > 1 && !isNullConstant(instr.inputDefAt(1));
|
||||
final hasFunctionTypeArgs =
|
||||
instr.inputCount > 1 && !isNullConstant(instr.inputDefAt(2));
|
||||
final stc = SubtypeTestCache(
|
||||
_getNumberOfInputsForSubtypeTestCache(
|
||||
dartType,
|
||||
hasInstantiatorTypeArgs: hasInstantiatorTypeArgs,
|
||||
hasFunctionTypeArgs: hasFunctionTypeArgs,
|
||||
),
|
||||
);
|
||||
_asm.loadFromPool(TypeTestingStub.subtypeTestCacheReg, stc);
|
||||
_asm.blr(tempReg);
|
||||
}
|
||||
|
||||
_asm.bind(done);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitTypeTest(TypeTest instr) {
|
||||
_asm.unimplemented('Unimplemented: code generation for TypeTest');
|
||||
final operandReg = inputReg(instr, 0);
|
||||
final resultReg = outputReg(instr);
|
||||
final doneFalse = Label();
|
||||
final doneTrue = Label();
|
||||
final done = Label();
|
||||
|
||||
late final Label slowPath = addSlowPath(() {
|
||||
_asm.unimplemented(
|
||||
'Unimplemented: code generation for TypeTest slow path',
|
||||
);
|
||||
_asm.b(done);
|
||||
});
|
||||
|
||||
// Handle a few built-in types, use STC for other types.
|
||||
final type = instr.testedType;
|
||||
switch (type) {
|
||||
case ObjectType():
|
||||
_asm.cmp(operandReg, nullReg);
|
||||
_asm.b(doneTrue, .notEqual);
|
||||
case NullType():
|
||||
_asm.cmp(resultReg, nullReg);
|
||||
_asm.b(doneTrue, .equal);
|
||||
case IntType():
|
||||
_asm.tbz(resultReg, smiBit, doneTrue);
|
||||
_asm.loadClassId(tempReg, resultReg);
|
||||
_asm.cmpImmediate(tempReg, ClassId.MintCid.index);
|
||||
_asm.b(doneTrue, .equal);
|
||||
case DoubleType():
|
||||
_asm.tbz(resultReg, smiBit, doneFalse);
|
||||
_asm.loadClassId(tempReg, resultReg);
|
||||
_asm.cmpImmediate(tempReg, ClassId.DoubleCid.index);
|
||||
_asm.b(doneTrue, .equal);
|
||||
case BoolType():
|
||||
_asm.tbz(resultReg, smiBit, doneFalse);
|
||||
_asm.loadClassId(tempReg, resultReg);
|
||||
_asm.cmpImmediate(tempReg, ClassId.BoolCid.index);
|
||||
_asm.b(doneTrue, .equal);
|
||||
case StringType():
|
||||
_asm.tbz(resultReg, smiBit, doneFalse);
|
||||
_asm.loadClassId(tempReg, resultReg);
|
||||
_asm.cmpImmediate(tempReg, ClassId.OneByteStringCid.index);
|
||||
_asm.b(doneTrue, .equal);
|
||||
_asm.cmpImmediate(tempReg, ClassId.TwoByteStringCid.index);
|
||||
_asm.b(doneTrue, .equal);
|
||||
default:
|
||||
_asm.tbz(
|
||||
resultReg,
|
||||
smiBit,
|
||||
const IntType().isSubtypeOf(type) ? doneTrue : doneFalse,
|
||||
);
|
||||
if (type.isNullable) {
|
||||
_asm.cmp(resultReg, nullReg);
|
||||
_asm.b(doneTrue, .equal);
|
||||
}
|
||||
bool isNullConstant(Definition def) =>
|
||||
def is Constant && def.value.isNull;
|
||||
final hasInstantiatorTypeArgs =
|
||||
instr.inputCount > 1 && !isNullConstant(instr.inputDefAt(1));
|
||||
final hasFunctionTypeArgs =
|
||||
instr.inputCount > 1 && !isNullConstant(instr.inputDefAt(2));
|
||||
final stc = SubtypeTestCache(
|
||||
_getNumberOfInputsForSubtypeTestCache(
|
||||
type.dartType,
|
||||
hasInstantiatorTypeArgs: hasInstantiatorTypeArgs,
|
||||
hasFunctionTypeArgs: hasFunctionTypeArgs,
|
||||
),
|
||||
);
|
||||
final stub = switch (stc.numInputs) {
|
||||
1 => StubCode.Subtype1TestCache,
|
||||
2 => StubCode.Subtype2TestCache,
|
||||
3 => StubCode.Subtype3TestCache,
|
||||
4 => StubCode.Subtype4TestCache,
|
||||
6 => StubCode.Subtype6TestCache,
|
||||
_ =>
|
||||
throw 'Unexpected number of SubtypeTestCache inputs ${stc.numInputs} (type $type)',
|
||||
};
|
||||
_asm.loadFromPool(TypeTestingStub.subtypeTestCacheReg, stc);
|
||||
_asm.loadFromPool(codeReg, stub);
|
||||
_asm.ldr(
|
||||
tempReg,
|
||||
_asm.fieldAddress(codeReg, vmOffsets.Code_entry_point_offset.first),
|
||||
);
|
||||
_asm.blr(tempReg);
|
||||
_asm.cmp(TypeTestingStub.subtypeTestCacheResultReg, nullReg);
|
||||
_asm.b(slowPath, .equal);
|
||||
_asm.mov(resultReg, TypeTestingStub.subtypeTestCacheResultReg);
|
||||
_asm.b(done);
|
||||
}
|
||||
|
||||
_asm.bind(doneFalse);
|
||||
_asm.loadConstant(resultReg, ConstantValue.fromBool(false));
|
||||
_asm.b(done);
|
||||
_asm.bind(doneTrue);
|
||||
_asm.loadConstant(resultReg, ConstantValue.fromBool(true));
|
||||
_asm.bind(done);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1225,6 +1464,13 @@ final class Arm64CodeGenerator extends CodeGenerator {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Location getMoveTempRegister(RegisterClass registerClass) =>
|
||||
switch (registerClass) {
|
||||
.cpu => tempReg,
|
||||
.fpu => fpTempReg,
|
||||
};
|
||||
|
||||
@override
|
||||
void generateMove(Location from, Location to) {
|
||||
switch (from) {
|
||||
@@ -1265,15 +1511,6 @@ final class Arm64CodeGenerator extends CodeGenerator {
|
||||
'Unimplemented: code generation for generateLoadConstant',
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void generatePush(Location loc) {
|
||||
_asm.unimplemented('Unimplemented: code generation for generatePush');
|
||||
}
|
||||
|
||||
void generatePop(Location loc) {
|
||||
_asm.unimplemented('Unimplemented: code generation for generatePop');
|
||||
}
|
||||
}
|
||||
|
||||
extension on ComparisonOpcode {
|
||||
|
||||
@@ -204,24 +204,80 @@ final class Arm64Constraints extends Constraints {
|
||||
const InstructionConstraints(anyCpuRegister, [anyCpuRegister]);
|
||||
|
||||
@override
|
||||
InstructionConstraints? visitTypeCast(TypeCast instr) =>
|
||||
InstructionConstraints(anyCpuRegister, [
|
||||
anyCpuRegister,
|
||||
if (instr.inputCount > 1) ...[
|
||||
anyRegisterOrImmediate(instr.inputDefAt(1)),
|
||||
anyRegisterOrImmediate(instr.inputDefAt(2)),
|
||||
InstructionConstraints? visitTypeCast(TypeCast instr) {
|
||||
final callsTypeTestingStub =
|
||||
instr.isChecked &&
|
||||
switch (instr.testedType) {
|
||||
ObjectType() ||
|
||||
NullType() ||
|
||||
IntType() ||
|
||||
DoubleType() ||
|
||||
BoolType() ||
|
||||
StringType() => false,
|
||||
_ => true,
|
||||
};
|
||||
if (callsTypeTestingStub) {
|
||||
return InstructionConstraints(
|
||||
TypeTestingStub.instanceReg,
|
||||
[
|
||||
TypeTestingStub.instanceReg,
|
||||
if (instr.inputCount > 1) ...const [
|
||||
TypeTestingStub.instantiatorTypeArgumentsReg,
|
||||
TypeTestingStub.functionTypeArgumentsReg,
|
||||
],
|
||||
],
|
||||
]);
|
||||
const [
|
||||
TypeTestingStub.dstTypeReg,
|
||||
TypeTestingStub.subtypeTestCacheReg,
|
||||
TypeTestingStub.scratchReg,
|
||||
],
|
||||
);
|
||||
}
|
||||
return InstructionConstraints(anyCpuRegister, [
|
||||
anyCpuRegister,
|
||||
if (instr.inputCount > 1) ...[
|
||||
anyRegisterOrImmediate(instr.inputDefAt(1)),
|
||||
anyRegisterOrImmediate(instr.inputDefAt(2)),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
@override
|
||||
InstructionConstraints? visitTypeTest(TypeTest instr) =>
|
||||
InstructionConstraints(anyCpuRegister, [
|
||||
anyCpuRegister,
|
||||
if (instr.inputCount > 1) ...[
|
||||
anyRegisterOrImmediate(instr.inputDefAt(1)),
|
||||
anyRegisterOrImmediate(instr.inputDefAt(2)),
|
||||
InstructionConstraints? visitTypeTest(TypeTest instr) {
|
||||
final callsSubtypeTestCacheStub = switch (instr.testedType) {
|
||||
ObjectType() ||
|
||||
NullType() ||
|
||||
IntType() ||
|
||||
DoubleType() ||
|
||||
BoolType() ||
|
||||
StringType() => false,
|
||||
_ => true,
|
||||
};
|
||||
if (callsSubtypeTestCacheStub) {
|
||||
return InstructionConstraints(
|
||||
TypeTestingStub.subtypeTestCacheResultReg,
|
||||
[
|
||||
TypeTestingStub.instanceReg,
|
||||
if (instr.inputCount > 1) ...const [
|
||||
TypeTestingStub.instantiatorTypeArgumentsReg,
|
||||
TypeTestingStub.functionTypeArgumentsReg,
|
||||
],
|
||||
],
|
||||
]);
|
||||
const [
|
||||
TypeTestingStub.dstTypeReg,
|
||||
TypeTestingStub.subtypeTestCacheReg,
|
||||
TypeTestingStub.scratchReg,
|
||||
],
|
||||
);
|
||||
}
|
||||
return InstructionConstraints(anyCpuRegister, [
|
||||
anyCpuRegister,
|
||||
if (instr.inputCount > 1) ...[
|
||||
anyRegisterOrImmediate(instr.inputDefAt(1)),
|
||||
anyRegisterOrImmediate(instr.inputDefAt(2)),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
@override
|
||||
InstructionConstraints? visitTypeArguments(TypeArguments instr) =>
|
||||
|
||||
@@ -108,6 +108,16 @@ final class WriteBarrierStub extends Arm64StubCodeGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
final class TypeTestingStub {
|
||||
static const Register instanceReg = R0;
|
||||
static const Register dstTypeReg = R8;
|
||||
static const Register instantiatorTypeArgumentsReg = R2;
|
||||
static const Register functionTypeArgumentsReg = R1;
|
||||
static const Register subtypeTestCacheReg = R3;
|
||||
static const Register scratchReg = R4;
|
||||
static const Register subtypeTestCacheResultReg = R7;
|
||||
}
|
||||
|
||||
final class Arm64StubFactory extends StubFactory {
|
||||
final VMOffsets vmOffsets;
|
||||
final ObjectLayout objectLayout;
|
||||
|
||||
@@ -174,6 +174,13 @@ abstract base class Assembler {
|
||||
OperandSize sz = OperandSize.s64,
|
||||
]);
|
||||
|
||||
/// compare [reg] with arbitrary integer [value].
|
||||
void cmpImmediate(
|
||||
Register reg,
|
||||
int value, [
|
||||
OperandSize sz = OperandSize.s64,
|
||||
]);
|
||||
|
||||
/// [dst] = bitwise and ([src], arbitrary integer [value]).
|
||||
void andImmediate(
|
||||
Register dst,
|
||||
|
||||
@@ -220,19 +220,14 @@ abstract base class CodeGenerator extends Pass
|
||||
}
|
||||
}
|
||||
}
|
||||
for (final move in instr.moves) {
|
||||
if (move is Move) {
|
||||
final from = move.from.physicalLocation;
|
||||
final to = move.to.physicalLocation;
|
||||
|
||||
if (map.containsKey(from)) {
|
||||
if (map.containsKey(to)) {
|
||||
_generateDependentMoves(from, to, map);
|
||||
} else {
|
||||
generateMove(from, to);
|
||||
map.remove(from);
|
||||
}
|
||||
}
|
||||
while (map.isNotEmpty) {
|
||||
final from = map.keys.first;
|
||||
final to = map[from]!;
|
||||
if (map.containsKey(to)) {
|
||||
_generateDependentMoves(from, to, map);
|
||||
} else {
|
||||
generateMove(from, to);
|
||||
map.remove(from);
|
||||
}
|
||||
}
|
||||
for (final move in instr.moves) {
|
||||
@@ -248,23 +243,28 @@ abstract base class CodeGenerator extends Pass
|
||||
Map<Location, Location> moves,
|
||||
) {
|
||||
assert(from != to);
|
||||
assert(moves[from] == to);
|
||||
final pendingList = <Location>[from];
|
||||
final pendingSet = <Location>{from};
|
||||
// Visit the chain of dependent moves until it ends or cycle is found.
|
||||
while (moves.containsKey(to)) {
|
||||
if (pendingSet.contains(to)) {
|
||||
// Moves form a cycle. Save value on the stack to generate moves.
|
||||
// Moves form a cycle. Save value to the temporary register to generate moves.
|
||||
// TODO: regalloc should provide scratch register(s) for
|
||||
// ParallelMove instructions if there are available registers.
|
||||
// TODO: we can also allocate a scratch register from ParallelMove
|
||||
// itself, resusing source registers which are already moved out or
|
||||
// destination registers which are not moved in yet.
|
||||
// TODO: as a last resort, allocate a scratch space on the stack and
|
||||
// avoid any push/pop.
|
||||
generatePush(to);
|
||||
for (final from in pendingList.reversed) {
|
||||
final temp = getMoveTempRegister(
|
||||
(to is FPRegister || moves[to] is FPRegister)
|
||||
? RegisterClass.fpu
|
||||
: RegisterClass.cpu,
|
||||
);
|
||||
generateMove(to, temp);
|
||||
while (pendingList.isNotEmpty) {
|
||||
from = pendingList.removeLast();
|
||||
if (from == to) {
|
||||
generatePop(moves.remove(from)!);
|
||||
generateMove(temp, moves.remove(from)!);
|
||||
break;
|
||||
}
|
||||
generateMove(from, moves.remove(from)!);
|
||||
@@ -281,10 +281,9 @@ abstract base class CodeGenerator extends Pass
|
||||
}
|
||||
}
|
||||
|
||||
Location getMoveTempRegister(RegisterClass registerClass);
|
||||
void generateMove(Location from, Location to);
|
||||
void generateLoadConstant(ConstantValue value, Location to);
|
||||
void generatePush(Location loc);
|
||||
void generatePop(Location loc);
|
||||
|
||||
@override
|
||||
void visitTypeParameters(TypeParameters instr) =>
|
||||
|
||||
@@ -122,3 +122,13 @@ final class StaticFieldOffset extends SpecializedEntry {
|
||||
bool operator ==(Object other) =>
|
||||
other is StaticFieldOffset && this.field == other.field;
|
||||
}
|
||||
|
||||
/// Object pool entry representing a subtype test cache.
|
||||
/// This is not a specialized entry, it is encoded as a regular object reference.
|
||||
final class SubtypeTestCache {
|
||||
final int numInputs;
|
||||
SubtypeTestCache(this.numInputs);
|
||||
|
||||
// Use identity hashCode and == as separate subtype test caches are
|
||||
// used for each type check.
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ final class LinearScanRegisterAllocator extends RegisterAllocator {
|
||||
}
|
||||
|
||||
errorContext.annotator = (Instruction instr) =>
|
||||
'[${instructionPos(instr)}]';
|
||||
(instr is ParallelMove) ? null : '[${instructionPos(instr)}]';
|
||||
}
|
||||
|
||||
void buildLiveRanges(SSALivenessAnalysis liveness) {
|
||||
@@ -170,6 +170,7 @@ final class LinearScanRegisterAllocator extends RegisterAllocator {
|
||||
_fpuRegLiveRanges = List.filled(constraints.getNumberOfFPRegisters(), null);
|
||||
|
||||
for (final block in backEndState.codeGenBlockOrder.reversed) {
|
||||
currentBlock = block;
|
||||
final blockStart = blockStartPos(block);
|
||||
final blockEnd = blockEndPos(block);
|
||||
|
||||
@@ -203,6 +204,7 @@ final class LinearScanRegisterAllocator extends RegisterAllocator {
|
||||
}
|
||||
|
||||
for (final instr in block.reversed) {
|
||||
currentInstruction = instr;
|
||||
if (instr is CallInstruction) {
|
||||
backEndState.stackFrame.allocateArgumentsSlots(instr);
|
||||
}
|
||||
@@ -811,15 +813,25 @@ final class LinearScanRegisterAllocator extends RegisterAllocator {
|
||||
if (instr is JoinBlock && instr.hasPhis) {
|
||||
instr = instr.phis.last;
|
||||
}
|
||||
ParallelMoveStage stage;
|
||||
if (instr is! Goto && next.start.isOdd) {
|
||||
instr = _nextInstruction(instr);
|
||||
stage = ParallelMoveStage.split;
|
||||
} else {
|
||||
stage = ParallelMoveStage.splitLate;
|
||||
}
|
||||
_insertMoveBefore(
|
||||
instr,
|
||||
ParallelMoveStage.split,
|
||||
stage,
|
||||
liveRange.allocatedLocation!,
|
||||
next.allocatedLocation!,
|
||||
);
|
||||
if (trace) {
|
||||
print(
|
||||
'Insert split move at ${next.start} (before ${IrToText.instruction(instr)})'
|
||||
' ${liveRange} ${liveRange.allocatedLocation} => $next ${next.allocatedLocation}',
|
||||
);
|
||||
}
|
||||
}
|
||||
liveRange = next;
|
||||
}
|
||||
|
||||
@@ -100,6 +100,23 @@ bool isAllDynamic(List<ast.DartType> typeArgs) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Calculate index of [tp] in the type arguments vector.
|
||||
int computeIndexOfTypeParameter(ast.TypeParameter tp) {
|
||||
final decl = tp.declaration!;
|
||||
int index = decl.typeParameters.indexOf(tp);
|
||||
assert(index >= 0);
|
||||
if (decl is ast.LocalFunction) {
|
||||
ast.TreeNode node = decl.parent!;
|
||||
while (node is! ast.Member) {
|
||||
if (node is ast.FunctionNode) {
|
||||
index += node.typeParameters.length;
|
||||
}
|
||||
node = node.parent!;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
/// Returns true if [field] has a non-trivial initializer.
|
||||
///
|
||||
/// VM does not allow field initializer functions for fields
|
||||
|
||||
@@ -21,6 +21,7 @@ import 'package:native_compiler/configuration.dart';
|
||||
import 'package:native_compiler/runtime/names.dart';
|
||||
import 'package:native_compiler/runtime/object_layout.dart';
|
||||
import 'package:native_compiler/runtime/type_utils.dart';
|
||||
import 'package:native_compiler/runtime/vm_defs.dart';
|
||||
|
||||
/// Kinds of Dart snapshots.
|
||||
/// Should match Snapshot::Kind enum in runtime/vm/snapshot.h.
|
||||
@@ -77,6 +78,7 @@ enum PredefinedClusters {
|
||||
typeArguments,
|
||||
codes,
|
||||
icDatas,
|
||||
subtypeTestCaches,
|
||||
objectPools,
|
||||
instances, // Separate cluster for every class.
|
||||
}
|
||||
@@ -160,6 +162,12 @@ class SnapshotSerializer {
|
||||
addBaseObject(const ast.NullType());
|
||||
addBaseObject(const ast.NeverType.nonNullable());
|
||||
addBaseObject(ast.ListConstant(const ast.DynamicType(), const []));
|
||||
// TODO: generate these stubs instead of referencig them from the VM.
|
||||
addBaseObject(StubCode.Subtype1TestCache);
|
||||
addBaseObject(StubCode.Subtype2TestCache);
|
||||
addBaseObject(StubCode.Subtype3TestCache);
|
||||
addBaseObject(StubCode.Subtype4TestCache);
|
||||
addBaseObject(StubCode.Subtype6TestCache);
|
||||
numObjects = numBaseObjects;
|
||||
}
|
||||
|
||||
@@ -342,6 +350,9 @@ class SnapshotSerializer {
|
||||
// Generated code and object pool
|
||||
Code() => getPredefinedCluster(PredefinedClusters.codes),
|
||||
ICData() => getPredefinedCluster(PredefinedClusters.icDatas),
|
||||
SubtypeTestCache() => getPredefinedCluster(
|
||||
PredefinedClusters.subtypeTestCaches,
|
||||
),
|
||||
ObjectPool() => getPredefinedCluster(PredefinedClusters.objectPools),
|
||||
_ => throw 'Unxpected ${obj.runtimeType} $obj',
|
||||
};
|
||||
@@ -381,6 +392,7 @@ class SnapshotSerializer {
|
||||
.typeParameterTypes => throw 'Unimplemented cluster $clusterId',
|
||||
.codes => CodeSerializationCluster(),
|
||||
.icDatas => ICDataSerializationCluster(),
|
||||
.subtypeTestCaches => SubtypeTestCacheSerializationCluster(),
|
||||
.objectPools => ObjectPoolSerializationCluster(),
|
||||
.instances => throw 'Each class has a separate instance cluster',
|
||||
};
|
||||
@@ -1270,6 +1282,36 @@ final class ICDataSerializationCluster extends SerializationCluster {
|
||||
}
|
||||
}
|
||||
|
||||
final class SubtypeTestCacheSerializationCluster extends SerializationCluster {
|
||||
final List<SubtypeTestCache> _objects = [];
|
||||
|
||||
@override
|
||||
void trace(SnapshotSerializer serializer, Object object) {
|
||||
final obj = object as SubtypeTestCache;
|
||||
_objects.add(obj);
|
||||
}
|
||||
|
||||
@override
|
||||
void writePreLoad(SnapshotSerializer serializer) {
|
||||
serializer.writeUint(PredefinedClusters.subtypeTestCaches.index);
|
||||
}
|
||||
|
||||
@override
|
||||
void writeAlloc(SnapshotSerializer serializer) {
|
||||
serializer.writeUint(_objects.length);
|
||||
for (final obj in _objects) {
|
||||
serializer.assignRef(obj);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void writeFill(SnapshotSerializer serializer) {
|
||||
for (final obj in _objects) {
|
||||
serializer.writeUint(obj.numInputs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class ObjectPoolSerializationCluster extends SerializationCluster {
|
||||
final List<ObjectPool> _objects = [];
|
||||
final Map<ICDataCallEntry, ICData> icDatas = {};
|
||||
|
||||
@@ -58,7 +58,7 @@ class IntervalList {
|
||||
assert(start < end);
|
||||
if (!isEmpty) {
|
||||
// Intervals should be added in descending order.
|
||||
assert(end < endAt(0));
|
||||
assert(end <= endAt(0));
|
||||
if (start == startAt(0)) {
|
||||
// Ignore nested interval.
|
||||
return;
|
||||
|
||||
@@ -311,6 +311,34 @@ void main() {
|
||||
'sub csp, fp, r17 uxtx 0\n',
|
||||
);
|
||||
});
|
||||
test('cmpImmediate', () {
|
||||
asm.cmpImmediate(R1, 0);
|
||||
asm.cmpImmediate(R3, 0xabc);
|
||||
asm.cmpImmediate(R4, 0xabc000);
|
||||
asm.cmpImmediate(R0, 0x1001);
|
||||
asm.cmpImmediate(R6, 0x11223344_55667788);
|
||||
asm.cmpImmediate(FP, -1);
|
||||
asm.cmpImmediate(R4, -0xabc);
|
||||
asm.cmpImmediate(R5, -0xabc000);
|
||||
asm.cmpImmediate(R7, -0x1001);
|
||||
expectDisassembly(
|
||||
'cmp r1, #0x0\n'
|
||||
'cmp r3, #0xabc\n'
|
||||
'cmp r4, #0xabc000\n'
|
||||
'movz r17, #0x1001\n'
|
||||
'cmp r0, r17\n'
|
||||
'movz r17, #0x7788\n'
|
||||
'movk r17, #0x5566 lsl 16\n'
|
||||
'movk r17, #0x3344 lsl 32\n'
|
||||
'movk r17, #0x1122 lsl 48\n'
|
||||
'cmp r6, r17\n'
|
||||
'cmn fp, #0x1\n'
|
||||
'cmn r4, #0xabc\n'
|
||||
'cmn r5, #0xabc000\n'
|
||||
'movn r17, #0x1000\n'
|
||||
'cmp r7, r17\n',
|
||||
);
|
||||
});
|
||||
test('andImmediate', () {
|
||||
asm.andImmediate(R1, R2, 0);
|
||||
asm.andImmediate(R1, R2, 0, .u32);
|
||||
@@ -444,6 +472,21 @@ void main() {
|
||||
'add r0, r0, #0x1\n',
|
||||
);
|
||||
});
|
||||
test('loadClassId', () {
|
||||
asm.loadClassId(R0, R0);
|
||||
asm.loadClassId(R1, R5);
|
||||
final lowBit = vmOffsets.UntaggedObject_kClassIdTagPos;
|
||||
final highBit =
|
||||
vmOffsets.UntaggedObject_kClassIdTagPos +
|
||||
vmOffsets.UntaggedObject_kClassIdTagSize -
|
||||
1;
|
||||
expectDisassembly(
|
||||
'ldr r0, [r0, #${vmOffsets.Object_tags_offset - heapObjectTag}]\n'
|
||||
'ubfm r0, r0, #$lowBit, #$highBit\n'
|
||||
'ldr r1, [r5, #${vmOffsets.Object_tags_offset - heapObjectTag}]\n'
|
||||
'ubfm r1, r1, #$lowBit, #$highBit\n',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('instruction', () {
|
||||
|
||||
@@ -11,20 +11,28 @@ B0 = EntryBlock()
|
||||
B0 = EntryBlock()
|
||||
v9 = Constant(null)
|
||||
v1 = Parameter(#functionTypeParameters) # RA: param[0] <- ()
|
||||
ParallelMove output(param[0] -> vloc:R0)
|
||||
ParallelMove output(param[0] -> vloc:R5)
|
||||
v2 = Parameter(this) # RA: param[1] <- ()
|
||||
ParallelMove output(param[1] -> vloc:R1)
|
||||
ParallelMove output(param[1] -> vloc:R0)
|
||||
v3 = Parameter(x) # RA: param[2] <- ()
|
||||
ParallelMove output(param[2] -> vloc:R2)
|
||||
v20 = LoadInstanceField(C.#typeArguments, v2) # RA: R1 <- (R1)
|
||||
ParallelMove spill(R1 -> stack[0])
|
||||
v10 = TypeTest(v3, v9, v20, List<C.T%>) # RA: R3 <- (R2, -, R1)
|
||||
DirectCall print(v10) # RA: R0 <- (R3) temps: [R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R19, R20, R23, R25, V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, V30]
|
||||
ParallelMove split(param[0] -> R1, param[2] -> R2)
|
||||
v13 = TypeTest(v3, v1, v9, List<C.typeParameters.U%>) # RA: R0 <- (R2, R1, -)
|
||||
DirectCall print(v13) # RA: R0 <- (R0) temps: [R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R19, R20, R23, R25, V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, V30]
|
||||
ParallelMove split(param[0] -> R2, param[2] -> R3, stack[0] -> R1)
|
||||
v16 = TypeCast(v3, v1, v20, Map<C.T%, C.typeParameters.U%>) # RA: R0 <- (R3, R2, R1)
|
||||
ParallelMove output(param[2] -> vloc:R6)
|
||||
v20 = LoadInstanceField(C.#typeArguments, v2) # RA: R2 <- (R0)
|
||||
ParallelMove spill(R2 -> stack[0])
|
||||
ParallelMove splitLate(R2 -> R9)
|
||||
ParallelMove input(vloc:R6 -> R0, vloc:R2 -> R2, NullConstant(null) -> R1)
|
||||
v10 = TypeTest(v3, v20, v9, List<C.T%>) # RA: R7 <- (R0, R2, R1) temps: [R8, R3, R4]
|
||||
ParallelMove output(R7 -> vloc:R7)
|
||||
DirectCall print(v10) # RA: R0 <- (R7) temps: [R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R19, R20, R23, R25, V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, V30]
|
||||
ParallelMove split(param[0] -> R1, param[2] -> R5)
|
||||
ParallelMove splitLate(R1 -> R6)
|
||||
ParallelMove input(vloc:R5 -> R0, NullConstant(null) -> R2, vloc:R1 -> R1)
|
||||
v13 = TypeTest(v3, v9, v1, List<C.typeParameters.U%>) # RA: R7 <- (R0, R2, R1) temps: [R8, R3, R4]
|
||||
ParallelMove output(R7 -> vloc:R7)
|
||||
DirectCall print(v13) # RA: R0 <- (R7) temps: [R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R19, R20, R23, R25, V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, V30]
|
||||
ParallelMove split(param[0] -> R3, param[2] -> R1, stack[0] -> R2)
|
||||
ParallelMove input(vloc:R1 -> R0, vloc:R2 -> R2, vloc:R3 -> R1)
|
||||
v16 = TypeCast(v3, v20, v1, Map<C.T%, C.typeParameters.U%>) # RA: R0 <- (R0, R2, R1) temps: [R8, R3, R4]
|
||||
ParallelMove output(R0 -> vloc:R0)
|
||||
DirectCall print(v16) # RA: R0 <- (R0) temps: [R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R19, R20, R23, R25, V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, V30]
|
||||
ParallelMove input(NullConstant(null) -> R0)
|
||||
Return(v9) # RA: (R0)
|
||||
@@ -215,7 +223,7 @@ B0 = EntryBlock()
|
||||
ParallelMove output(R0 -> vloc:R0)
|
||||
DirectCall print(v7) # RA: R0 <- (R0) temps: [R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R19, R20, R23, R25, V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, V30]
|
||||
ParallelMove split(param[0] -> R1)
|
||||
v10 = TypeArguments(v1, v9, <listLiterals.T%>) # RA: R0 <- (R1, -)
|
||||
v10 = TypeArguments(v9, v1, <listLiterals.T%>) # RA: R0 <- (-, R1)
|
||||
ParallelMove spill(R0 -> stack[0])
|
||||
v11 = DirectCall _GrowableList.(v10, v6) # RA: R0 <- (R0, -) temps: [R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R19, R20, R23, R25, V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, V30]
|
||||
ParallelMove output(R0 -> vloc:R0)
|
||||
@@ -292,7 +300,7 @@ B0 = EntryBlock()
|
||||
ParallelMove output(R0 -> vloc:R0)
|
||||
DirectCall print(v32) # RA: R0 <- (R0) temps: [R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R19, R20, R23, R25, V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, V30]
|
||||
ParallelMove split(param[0] -> R1)
|
||||
v12 = TypeArguments(v1, v11, <mapLiterals.S%, mapLiterals.T%>) # RA: R0 <- (R1, -)
|
||||
v12 = TypeArguments(v11, v1, <mapLiterals.S%, mapLiterals.T%>) # RA: R0 <- (-, R1)
|
||||
ParallelMove spill(R0 -> stack[0])
|
||||
v33 = DirectCall Map._fromLiteral(v12, v31) # RA: R0 <- (R0, -) temps: [R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R19, R20, R23, R25, V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, V20, V21, V22, V23, V24, V25, V26, V27, V28, V29, V30]
|
||||
ParallelMove output(R0 -> vloc:R0)
|
||||
|
||||
@@ -73,6 +73,7 @@ class ModuleSnapshot : public AllStatic {
|
||||
kTypeArguments,
|
||||
kCodes,
|
||||
kICDatas,
|
||||
kSubtypeTestCaches,
|
||||
kObjectPools,
|
||||
kInstances,
|
||||
};
|
||||
@@ -1127,6 +1128,30 @@ class ICDataDeserializationCluster : public DeserializationCluster {
|
||||
}
|
||||
};
|
||||
|
||||
class SubtypeTestCacheDeserializationCluster : public DeserializationCluster {
|
||||
public:
|
||||
SubtypeTestCacheDeserializationCluster()
|
||||
: DeserializationCluster("SubtypeTestCache") {}
|
||||
~SubtypeTestCacheDeserializationCluster() {}
|
||||
|
||||
void ReadAlloc(Deserializer* d) override {
|
||||
ReadAllocFixedSize(d, SubtypeTestCache::InstanceSize());
|
||||
}
|
||||
|
||||
void ReadFill(Deserializer* d_) override {
|
||||
Deserializer::Local d(d_);
|
||||
|
||||
for (intptr_t id = start_index_, n = stop_index_; id < n; id++) {
|
||||
SubtypeTestCachePtr stc = static_cast<SubtypeTestCachePtr>(d.Ref(id));
|
||||
Deserializer::InitializeHeader(stc, kSubtypeTestCacheCid,
|
||||
SubtypeTestCache::InstanceSize());
|
||||
stc->untag()->cache_ = Object::empty_subtype_test_cache_array().ptr();
|
||||
stc->untag()->num_inputs_ = d.ReadUnsigned();
|
||||
stc->untag()->num_occupied_ = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class ObjectPoolDeserializationCluster : public DeserializationCluster {
|
||||
public:
|
||||
ObjectPoolDeserializationCluster() : DeserializationCluster("ObjectPool") {}
|
||||
@@ -1350,6 +1375,8 @@ DeserializationCluster* Deserializer::ReadCluster() {
|
||||
return new (Z) CodeDeserializationCluster(Z);
|
||||
case ModuleSnapshot::kICDatas:
|
||||
return new (Z) ICDataDeserializationCluster();
|
||||
case ModuleSnapshot::kSubtypeTestCaches:
|
||||
return new (Z) SubtypeTestCacheDeserializationCluster();
|
||||
case ModuleSnapshot::kObjectPools:
|
||||
return new (Z) ObjectPoolDeserializationCluster();
|
||||
case ModuleSnapshot::kInstances: {
|
||||
@@ -1406,6 +1433,11 @@ void Deserializer::Deserialize() {
|
||||
AddBaseObject(Type::Handle(zone(), object_store->null_type()));
|
||||
AddBaseObject(Type::Handle(zone(), object_store->never_type()));
|
||||
AddBaseObject(Object::empty_array());
|
||||
AddBaseObject(StubCode::Subtype1TestCache());
|
||||
AddBaseObject(StubCode::Subtype2TestCache());
|
||||
AddBaseObject(StubCode::Subtype3TestCache());
|
||||
AddBaseObject(StubCode::Subtype4TestCache());
|
||||
AddBaseObject(StubCode::Subtype6TestCache());
|
||||
|
||||
if (num_base_objects_ != (next_ref_index_ - kFirstReference)) {
|
||||
FATAL("Snapshot expects %" Pd
|
||||
|
||||
@@ -58,6 +58,7 @@ class ListDeserializationCluster;
|
||||
class MapDeserializationCluster;
|
||||
class ObjectPoolDeserializationCluster;
|
||||
class SetDeserializationCluster;
|
||||
class SubtypeTestCacheDeserializationCluster;
|
||||
class TypeArgumentsDeserializationCluster;
|
||||
} // namespace module_snapshot
|
||||
|
||||
@@ -2789,6 +2790,7 @@ class UntaggedSubtypeTestCache : public UntaggedObject {
|
||||
uint32_t num_occupied_;
|
||||
|
||||
friend class Interpreter;
|
||||
friend class module_snapshot::SubtypeTestCacheDeserializationCluster;
|
||||
};
|
||||
|
||||
class UntaggedLoadingUnit : public UntaggedObject {
|
||||
|
||||
Reference in New Issue
Block a user