[modular_aot] Fixes for code generation of TypeCast

* Use exact code pattern for calling TTS which is expected by
  the VM at runtime.
* Always set instantiator/function type arguments registers for TTS.
* List all registers potentially clobbered by TTS.

Issue: https://github.com/dart-lang/sdk/issues/61635
Change-Id: I011bcfc8b755864271877cc36d365ba2b5f10648
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/509480
Reviewed-by: Slava Egorov <vegorov@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Alexander Markov
2026-06-08 07:17:10 -07:00
committed by dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent 19db32f041
commit ea1360fbaf
7 changed files with 66 additions and 23 deletions
@@ -19,6 +19,7 @@ import 'package:native_compiler/back_end/assembler.dart';
import 'package:native_compiler/back_end/code_generator.dart';
import 'package:native_compiler/back_end/locations.dart';
import 'package:native_compiler/back_end/object_pool.dart';
import 'package:native_compiler/runtime/names.dart';
import 'package:native_compiler/runtime/type_utils.dart';
import 'package:native_compiler/runtime/vm_defs.dart';
@@ -1042,7 +1043,7 @@ final class Arm64CodeGenerator extends CodeGenerator {
_asm.loadFromPool(TypeTestingStub.dstTypeReg, dartType);
}
_asm.ldr(
tempReg,
TypeTestingStub.entryPointReg,
_asm.fieldAddress(
TypeTestingStub.dstTypeReg,
vmOffsets.AbstractType_type_test_stub_entry_point_offset,
@@ -1061,8 +1062,16 @@ final class Arm64CodeGenerator extends CodeGenerator {
hasFunctionTypeArgs: hasFunctionTypeArgs,
),
);
_asm.loadFromPool(TypeTestingStub.subtypeTestCacheReg, stc);
_asm.blr(tempReg);
if (instr.inputCount == 1) {
_asm.mov(TypeTestingStub.instantiatorTypeArgumentsReg, nullReg);
_asm.mov(TypeTestingStub.functionTypeArgumentsReg, nullReg);
}
// VM expects exact code pattern for type testing stub calling sequence.
_asm.loadFromPool(
TypeTestingStub.subtypeTestCacheReg,
SubtypeTestCacheWithName(stc, Name('', null)),
);
_asm.blr(TypeTestingStub.entryPointReg);
}
_asm.bind(done);
@@ -241,10 +241,15 @@ final class Arm64Constraints extends Constraints {
TypeTestingStub.functionTypeArgumentsReg,
],
],
const [
TypeTestingStub.dstTypeReg,
TypeTestingStub.subtypeTestCacheReg,
TypeTestingStub.scratchReg,
// Type testing stub can call runtime without preserving registers.
[
for (final r in allocatableRegisters)
if (r != TypeTestingStub.instanceReg &&
((instr.inputCount == 1) ||
(r != TypeTestingStub.instantiatorTypeArgumentsReg &&
r != TypeTestingStub.functionTypeArgumentsReg)))
r,
...allocatableFPRegisters,
],
);
}
@@ -136,6 +136,7 @@ final class TypeTestingStub {
static const Register subtypeTestCacheReg = R3;
static const Register scratchReg = R4;
static const Register subtypeTestCacheResultReg = R7;
static const Register entryPointReg = R9;
}
final class InstantiateTypeArgumentsStub {
@@ -132,3 +132,22 @@ final class SubtypeTestCache {
// Use identity hashCode and == as separate subtype test caches are
// used for each type check.
}
/// A pair (subtype test cache, name). VM decodes type testing stub
/// calling sequence and reads name from object pool immediately
/// after subtype test cache when throwing type errors.
final class SubtypeTestCacheWithName extends PairSpecializedEntry {
final SubtypeTestCache stc;
final Name name;
SubtypeTestCacheWithName(this.stc, this.name);
@override
int get hashCode => finalizeHash(combineHash(stc.hashCode, name.hashCode));
@override
bool operator ==(Object other) =>
other is SubtypeTestCacheWithName &&
this.stc == other.stc &&
this.name == other.name;
}
@@ -1631,6 +1631,9 @@ final class ObjectPoolSerializationCluster extends SerializationCluster {
entry.selector,
);
serializer.push(icData);
case SubtypeTestCacheWithName():
serializer.push(entry.stc);
serializer.push(entry.name);
case ReservedEntry():
break;
}
@@ -1673,6 +1676,11 @@ final class ObjectPoolSerializationCluster extends SerializationCluster {
case DynamicCallEntry():
serializer.writeUint(ObjectPoolEntryKind.dynamicCall.index);
serializer.writeRefId(icDatas[entry]);
case SubtypeTestCacheWithName():
serializer.writeUint(ObjectPoolEntryKind.objectRef.index);
serializer.writeRefId(entry.stc);
serializer.writeUint(ObjectPoolEntryKind.objectRef.index);
serializer.writeRefId(entry.name);
case ReservedEntry():
}
} else if (entry is UnboxedIntConstant) {
@@ -30,9 +30,9 @@ B0 = EntryBlock()
v16 = TypeTest(v3, v12, v1, List<C.typeParameters.U%>) # RA: R7 <- (R0, R2, R1) temps: [R8, R3, R4]
ParallelMove output(R7 -> vloc:R7)
DirectCall print(v16) # 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 output(param[0] -> R5, param[2] -> R1, stack[0] -> R2)
ParallelMove input(vloc:R1 -> R0, vloc:R2 -> R2, vloc:R5 -> R1)
v19 = TypeCast(v3, v37, v1, Map<C.T%, C.typeParameters.U%>) # RA: R0 <- (R0, R2, R1) temps: [R8, R3, R4]
ParallelMove output(param[0] -> R0, param[2] -> R1, stack[0] -> R2)
ParallelMove input(vloc:R1 -> R0, vloc:R2 -> R2, vloc:R0 -> R1)
v19 = TypeCast(v3, v37, v1, Map<C.T%, C.typeParameters.U%>) # RA: R0 <- (R0, R2, R1) temps: [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)
DirectCall print(v19) # 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]
v21 = AllocateClosure() # RA: R0 <- () temps: [R2, R3, R4]
@@ -401,15 +401,15 @@ B6 = TargetBlock() idom:B0
ParallelMove output(param[0] -> R0)
v17 = InterfaceCall getter StreamController._varData(v1) # 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, param[0] -> R1)
v25 = LoadInstanceField(StreamController.#typeArguments, v1) # RA: R5 <- (R1)
ParallelMove output(R5 -> stack[0])
ParallelMove input(vloc:R0 -> R0, vloc:R5 -> R2, NullConstant(null) -> R1)
v18 = TypeCast(v17, v25, v13, _StreamControllerAddStreamState<StreamController.T%>) # RA: R0 <- (R0, R2, R1) temps: [R8, R3, R4]
v25 = LoadInstanceField(StreamController.#typeArguments, v1) # RA: R1 <- (R1)
ParallelMove output(R1 -> stack[0])
ParallelMove input(vloc:R0 -> R0, vloc:R1 -> R2, NullConstant(null) -> R1)
v18 = TypeCast(v17, v25, v13, _StreamControllerAddStreamState<StreamController.T%>) # RA: R0 <- (R0, R2, R1) temps: [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)
v21 = InterfaceCall getter _StreamControllerAddStreamState._varData(v18) # 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, stack[0] -> R2)
ParallelMove input(vloc:R0 -> R0, vloc:R2 -> R2, NullConstant(null) -> R1)
v22 = TypeCast(v21, v25, v13, _PendingEvents<StreamController.T%>?) # RA: R0 <- (R0, R2, R1) temps: [R8, R3, R4]
v22 = TypeCast(v21, v25, v13, _PendingEvents<StreamController.T%>?) # RA: R0 <- (R0, R2, R1) temps: [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)
ParallelMove input(vloc:R0 -> R0)
Return(v22) # RA: (R0)
@@ -420,7 +420,7 @@ B7 = TargetBlock() idom:B0
ParallelMove output(R0 -> vloc:R0, param[0] -> R1)
v26 = LoadInstanceField(StreamController.#typeArguments, v1) # RA: R2 <- (R1)
ParallelMove input(vloc:R0 -> R0, vloc:R2 -> R2, NullConstant(null) -> R1)
v14 = TypeCast(v12, v26, v13, _PendingEvents<StreamController.T%>?) # RA: R0 <- (R0, R2, R1) temps: [R8, R3, R4]
v14 = TypeCast(v12, v26, v13, _PendingEvents<StreamController.T%>?) # RA: R0 <- (R0, R2, R1) temps: [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)
ParallelMove input(vloc:R0 -> R0)
Return(v14) # RA: (R0)
@@ -1198,7 +1198,7 @@ B577 = TargetBlock() idom:B570
v588 = InterfaceCall List.[](v585, v788) # RA: R0 <- (-, R1) 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)
ParallelMove input(vloc:R0 -> R0)
v589 = TypeCast(v588, num) # RA: R0 <- (R0) temps: [R8, R3, R4]
v589 = TypeCast(v588, num) # 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:R1, stack[20] -> V0)
v789 = BoxDouble(v564) # RA: R4 <- (V0) temps: [R0, R3, R2]
v590 = InterfaceCall double./(v789, v589) # RA: R0 <- (R4, R1) 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]
@@ -1230,7 +1230,7 @@ B609 = TargetBlock() idom:B597
v618 = InterfaceCall List.[](v585, v794) # RA: R0 <- (-, R4) 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)
ParallelMove input(vloc:R0 -> R0)
v619 = TypeCast(v618, num) # RA: R0 <- (R0) temps: [R8, R3, R4]
v619 = TypeCast(v618, num) # 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:R2, stack[20] -> V0)
v795 = BoxDouble(v564) # RA: R4 <- (V0) temps: [R1, R3, R0]
v620 = InterfaceCall double.*(v795, v619) # RA: R0 <- (R4, R2) 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]
@@ -1675,12 +1675,13 @@ B0 = EntryBlock()
v2 = Parameter(b) # RA: param[1] <- ()
ParallelMove output(param[1] -> vloc:R1, param[0] -> vloc:R0)
ParallelMove input(vloc:R0 -> R0)
v4 = TypeCast(v1, Comparable<dynamic>) # RA: R0 <- (R0) temps: [R8, R3, R4]
ParallelMove output(R0 -> vloc:R2)
v4 = TypeCast(v1, Comparable<dynamic>) # 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, param[1] -> R1)
ParallelMove spill(R0 -> stack[0])
ParallelMove input(vloc:R1 -> R0)
v6 = TypeCast(v2, Comparable<dynamic>) # RA: R0 <- (R0) temps: [R8, R3, R4]
ParallelMove output(R0 -> vloc:R0)
v7 = DirectCall Comparable.compare(v4, v6) # RA: R0 <- (R2, 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]
v6 = TypeCast(v2, Comparable<dynamic>) # 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, stack[0] -> R1)
v7 = DirectCall Comparable.compare(v4, v6) # RA: R0 <- (R1, 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)
ParallelMove input(vloc:R0 -> R0)
Return(v7) # RA: (R0)