[vm,modular_aot] Boxing/unboxing of int and double values
TEST=ci Issue: https://github.com/dart-lang/sdk/issues/61635 Change-Id: I9fe47364bf772ccedf21f95aa5b16b92b0215b3e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/486240 Commit-Queue: Alexander Markov <alexmarkov@google.com> Reviewed-by: Slava Egorov <vegorov@google.com>
This commit is contained in:
committed by
Commit Queue
parent
f02b704216
commit
d97566551b
@@ -27,20 +27,31 @@ extension type ConstantValue(ast.Constant constant) {
|
||||
factory ConstantValue.fromString(String value) =>
|
||||
ConstantValue(ast.StringConstant(value));
|
||||
|
||||
int get intValue => (constant as ast.IntConstant).value;
|
||||
double get doubleValue => (constant as ast.DoubleConstant).value;
|
||||
int get intValue => switch (constant) {
|
||||
ast.IntConstant(:var value) => value,
|
||||
UnboxedIntConstant(:var value) => value,
|
||||
_ => throw 'Unexpected int constant ${constant.runtimeType}',
|
||||
};
|
||||
double get doubleValue => switch (constant) {
|
||||
ast.DoubleConstant(:var value) => value,
|
||||
UnboxedDoubleConstant(:var value) => value,
|
||||
_ => throw 'Unexpected double constant ${constant.runtimeType}',
|
||||
};
|
||||
bool get boolValue => (constant as ast.BoolConstant).value;
|
||||
String get stringValue => (constant as ast.StringConstant).value;
|
||||
|
||||
bool get isInt => constant is ast.IntConstant;
|
||||
bool get isDouble => constant is ast.DoubleConstant;
|
||||
bool get isInt =>
|
||||
constant is ast.IntConstant || constant is UnboxedIntConstant;
|
||||
bool get isDouble =>
|
||||
constant is ast.DoubleConstant || constant is UnboxedDoubleConstant;
|
||||
bool get isBool => constant is ast.BoolConstant;
|
||||
bool get isNull => constant is ast.NullConstant;
|
||||
bool get isString => constant is ast.StringConstant;
|
||||
bool get isUnboxed => constant is UnboxedConstant;
|
||||
|
||||
CType get type => switch (constant) {
|
||||
ast.IntConstant() => const IntType(),
|
||||
ast.DoubleConstant() => const DoubleType(),
|
||||
ast.IntConstant() || UnboxedIntConstant() => const IntType(),
|
||||
ast.DoubleConstant() || UnboxedDoubleConstant() => const DoubleType(),
|
||||
ast.BoolConstant() => const BoolType(),
|
||||
ast.NullConstant() => const NullType(),
|
||||
ast.StringConstant() => const StringType(),
|
||||
@@ -52,13 +63,17 @@ extension type ConstantValue(ast.Constant constant) {
|
||||
|
||||
bool get isZero => switch (constant) {
|
||||
ast.IntConstant(:var value) => value == 0,
|
||||
UnboxedIntConstant(:var value) => value == 0,
|
||||
ast.DoubleConstant(:var value) => value == 0.0,
|
||||
UnboxedDoubleConstant(:var value) => value == 0.0,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
bool get isNegative => switch (constant) {
|
||||
ast.IntConstant(:var value) => value < 0,
|
||||
UnboxedIntConstant(:var value) => value < 0,
|
||||
ast.DoubleConstant(:var value) => value.isNegative,
|
||||
UnboxedDoubleConstant(:var value) => value.isNegative,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
@@ -296,7 +311,9 @@ class SentinelConstant extends ast.AuxiliaryConstant {
|
||||
void visitChildren(ast.Visitor v) {}
|
||||
|
||||
@override
|
||||
void toTextInternal(ast_printer.AstPrinter printer) => '#sentinel';
|
||||
void toTextInternal(ast_printer.AstPrinter printer) {
|
||||
printer.write('#sentinel');
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => toStringInternal();
|
||||
@@ -310,3 +327,73 @@ class SentinelConstant extends ast.AuxiliaryConstant {
|
||||
@override
|
||||
ast.DartType getType(StaticTypeContext context) => const ast.DynamicType();
|
||||
}
|
||||
|
||||
/// Base class for unboxed constant values.
|
||||
///
|
||||
/// Used by certain back-ends to distinguish raw unboxed values
|
||||
/// (incompatible with Dart objects) from regular constants.
|
||||
abstract base class UnboxedConstant extends ast.AuxiliaryConstant {
|
||||
UnboxedConstant();
|
||||
}
|
||||
|
||||
/// Unboxed int constant.
|
||||
///
|
||||
/// Used by certain back-ends to distinguish raw unboxed values
|
||||
/// (incompatible with Dart objects) from regular constants.
|
||||
final class UnboxedIntConstant extends UnboxedConstant {
|
||||
final int value;
|
||||
UnboxedIntConstant(this.value);
|
||||
|
||||
@override
|
||||
void visitChildren(ast.Visitor v) {}
|
||||
|
||||
@override
|
||||
void toTextInternal(ast_printer.AstPrinter printer) {
|
||||
printer.write('#unboxed $value');
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => 'UnboxedIntConstant($value)';
|
||||
|
||||
@override
|
||||
int get hashCode => value.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
other is UnboxedIntConstant && other.value == value;
|
||||
|
||||
@override
|
||||
ast.DartType getType(StaticTypeContext context) =>
|
||||
context.typeEnvironment.coreTypes.intNonNullableRawType;
|
||||
}
|
||||
|
||||
/// Unboxed double constant.
|
||||
///
|
||||
/// Used by certain back-ends to distinguish raw unboxed values
|
||||
/// (incompatible with Dart objects) from regular constants.
|
||||
final class UnboxedDoubleConstant extends UnboxedConstant {
|
||||
final double value;
|
||||
UnboxedDoubleConstant(this.value);
|
||||
|
||||
@override
|
||||
void visitChildren(ast.Visitor v) {}
|
||||
|
||||
@override
|
||||
void toTextInternal(ast_printer.AstPrinter printer) {
|
||||
printer.write('#unboxed $value');
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => 'UnboxedDoubleConstant($value)';
|
||||
|
||||
@override
|
||||
int get hashCode => value.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
other is UnboxedDoubleConstant && identical(value, other.value);
|
||||
|
||||
@override
|
||||
ast.DartType getType(StaticTypeContext context) =>
|
||||
context.typeEnvironment.coreTypes.doubleNonNullableRawType;
|
||||
}
|
||||
|
||||
@@ -433,6 +433,26 @@ final class FlowGraphChecker extends Pass implements InstructionVisitor<void> {
|
||||
@override
|
||||
void visitSetListElement(SetListElement instr) {}
|
||||
|
||||
@override
|
||||
void visitBoxInt(BoxInt instr) {
|
||||
assert(instr.operand.type is IntType);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitBoxDouble(BoxDouble instr) {
|
||||
assert(instr.operand.type is DoubleType);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitUnboxInt(UnboxInt instr) {
|
||||
assert(instr.operand.type is IntType);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitUnboxDouble(UnboxDouble instr) {
|
||||
assert(instr.operand.type is DoubleType);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitParallelMove(ParallelMove instr) {}
|
||||
}
|
||||
|
||||
@@ -1647,6 +1647,72 @@ final class SetListElement extends Instruction
|
||||
R accept<R>(InstructionVisitor<R> v) => v.visitSetListElement(this);
|
||||
}
|
||||
|
||||
/// Base class for boxing instructions.
|
||||
abstract base class Box extends Definition
|
||||
with CanThrow, Pure, BackendInstruction {
|
||||
Box(super.graph, super.sourcePosition, Definition operand)
|
||||
: super(inputCount: 1) {
|
||||
setInputAt(0, operand);
|
||||
}
|
||||
|
||||
Definition get operand => inputDefAt(0);
|
||||
}
|
||||
|
||||
/// Create a box out of raw int value.
|
||||
final class BoxInt extends Box {
|
||||
BoxInt(super.graph, super.sourcePosition, super.operand);
|
||||
|
||||
@override
|
||||
CType get type => const IntType();
|
||||
|
||||
@override
|
||||
R accept<R>(InstructionVisitor<R> v) => v.visitBoxInt(this);
|
||||
}
|
||||
|
||||
/// Create a box out of raw double value.
|
||||
final class BoxDouble extends Box {
|
||||
BoxDouble(super.graph, super.sourcePosition, super.operand);
|
||||
|
||||
@override
|
||||
CType get type => const DoubleType();
|
||||
|
||||
@override
|
||||
R accept<R>(InstructionVisitor<R> v) => v.visitBoxDouble(this);
|
||||
}
|
||||
|
||||
/// Base class for unboxing instructions.
|
||||
abstract base class Unbox extends Definition
|
||||
with NoThrow, Pure, BackendInstruction {
|
||||
Unbox(super.graph, super.sourcePosition, Definition operand)
|
||||
: super(inputCount: 1) {
|
||||
setInputAt(0, operand);
|
||||
}
|
||||
|
||||
Definition get operand => inputDefAt(0);
|
||||
}
|
||||
|
||||
/// Get raw int value out of the box.
|
||||
final class UnboxInt extends Unbox {
|
||||
UnboxInt(super.graph, super.sourcePosition, super.operand);
|
||||
|
||||
@override
|
||||
CType get type => const IntType();
|
||||
|
||||
@override
|
||||
R accept<R>(InstructionVisitor<R> v) => v.visitUnboxInt(this);
|
||||
}
|
||||
|
||||
/// Get raw double value out of the box.
|
||||
final class UnboxDouble extends Unbox {
|
||||
UnboxDouble(super.graph, super.sourcePosition, super.operand);
|
||||
|
||||
@override
|
||||
CType get type => const DoubleType();
|
||||
|
||||
@override
|
||||
R accept<R>(InstructionVisitor<R> v) => v.visitUnboxDouble(this);
|
||||
}
|
||||
|
||||
/// Base class for move operations, part of [ParallelMove].
|
||||
abstract base class MoveOp {}
|
||||
|
||||
|
||||
@@ -51,6 +51,10 @@ abstract interface class InstructionVisitor<R> {
|
||||
R visitCompareAndBranch(CompareAndBranch instr);
|
||||
R visitAllocateList(AllocateList instr);
|
||||
R visitSetListElement(SetListElement instr);
|
||||
R visitBoxInt(BoxInt instr);
|
||||
R visitBoxDouble(BoxDouble instr);
|
||||
R visitUnboxInt(UnboxInt instr);
|
||||
R visitUnboxDouble(UnboxDouble instr);
|
||||
R visitParallelMove(ParallelMove instr);
|
||||
}
|
||||
|
||||
@@ -120,6 +124,10 @@ abstract mixin class DefaultInstructionVisitor<R>
|
||||
R visitAllocateList(AllocateList instr) => defaultBackendInstruction(instr);
|
||||
R visitSetListElement(SetListElement instr) =>
|
||||
defaultBackendInstruction(instr);
|
||||
R visitBoxInt(BoxInt instr) => defaultBackendInstruction(instr);
|
||||
R visitBoxDouble(BoxDouble instr) => defaultBackendInstruction(instr);
|
||||
R visitUnboxInt(UnboxInt instr) => defaultBackendInstruction(instr);
|
||||
R visitUnboxDouble(UnboxDouble instr) => defaultBackendInstruction(instr);
|
||||
R visitParallelMove(ParallelMove instr) => defaultBackendInstruction(instr);
|
||||
}
|
||||
|
||||
|
||||
@@ -550,6 +550,26 @@ final class ConstantPropagation extends Pass
|
||||
@override
|
||||
void visitSetListElement(SetListElement instr) {}
|
||||
|
||||
@override
|
||||
void visitBoxInt(BoxInt instr) {
|
||||
_setNonConstant(instr);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitBoxDouble(BoxDouble instr) {
|
||||
_setNonConstant(instr);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitUnboxInt(UnboxInt instr) {
|
||||
_setNonConstant(instr);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitUnboxDouble(UnboxDouble instr) {
|
||||
_setNonConstant(instr);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitParallelMove(ParallelMove instr) {}
|
||||
|
||||
|
||||
@@ -279,6 +279,18 @@ final class Simplification extends Pass
|
||||
@override
|
||||
Instruction visitSetListElement(SetListElement instr) => instr;
|
||||
|
||||
@override
|
||||
Instruction visitBoxInt(BoxInt instr) => instr;
|
||||
|
||||
@override
|
||||
Instruction visitBoxDouble(BoxDouble instr) => instr;
|
||||
|
||||
@override
|
||||
Instruction visitUnboxInt(UnboxInt instr) => instr;
|
||||
|
||||
@override
|
||||
Instruction visitUnboxDouble(UnboxDouble instr) => instr;
|
||||
|
||||
@override
|
||||
Instruction visitParallelMove(ParallelMove instr) => instr;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:native_compiler/back_end/assembler.dart';
|
||||
import 'package:native_compiler/back_end/code.dart';
|
||||
import 'package:native_compiler/back_end/locations.dart';
|
||||
import 'package:native_compiler/back_end/object_pool.dart';
|
||||
import 'package:native_compiler/runtime/object_layout.dart';
|
||||
import 'package:native_compiler/runtime/vm_defs.dart';
|
||||
import 'package:cfg/ir/constant_value.dart';
|
||||
|
||||
@@ -322,7 +323,9 @@ const int B31 = (1 << 31);
|
||||
/// TODO: support long branches, large offsets and floating-point instructions.
|
||||
/// TODO: measure performance overhead of always checking encoding constraints.
|
||||
final class Arm64Assembler extends Assembler with Uint32OutputBuffer {
|
||||
Arm64Assembler(super.vmOffsets);
|
||||
final ObjectLayout objectLayout;
|
||||
|
||||
Arm64Assembler(super.vmOffsets, this.objectLayout);
|
||||
|
||||
/// Create a [base + offset] address for arbitrary offset,
|
||||
/// generating extra code if necessary.
|
||||
@@ -503,7 +506,13 @@ final class Arm64Assembler extends Assembler with Uint32OutputBuffer {
|
||||
assert(reg != SP);
|
||||
|
||||
if (value.isInt) {
|
||||
loadImmediate(reg, value.intValue);
|
||||
if (value.isUnboxed) {
|
||||
loadImmediate(reg, value.intValue);
|
||||
} else if (objectLayout.isSmi(value.intValue)) {
|
||||
loadImmediate(reg, value.intValue << smiShift);
|
||||
} else {
|
||||
loadFromPool(reg, value as Object);
|
||||
}
|
||||
} else {
|
||||
loadFromPool(reg, value as Object);
|
||||
}
|
||||
@@ -697,8 +706,9 @@ final class Arm64Assembler extends Assembler with Uint32OutputBuffer {
|
||||
Register scratch1Reg,
|
||||
Register scratch2Reg,
|
||||
int instanceSize,
|
||||
Label slowPath,
|
||||
) {
|
||||
Label slowPath, {
|
||||
required bool initializeFields,
|
||||
}) {
|
||||
final endReg = scratch1Reg;
|
||||
final newTopReg = scratch2Reg;
|
||||
// Load Thread.top_ and Thread.end_.
|
||||
@@ -712,34 +722,44 @@ final class Arm64Assembler extends Assembler with Uint32OutputBuffer {
|
||||
str(tagsReg, address(resultReg, vmOffsets.Object_tags_offset));
|
||||
// TODO: figure out if we need store-store barrier here.
|
||||
|
||||
// TODO: support compressed pointers.
|
||||
const maxUnrolledSize = 16 * wordSize;
|
||||
if (instanceSize <= maxUnrolledSize) {
|
||||
int offset = vmOffsets.Instance_first_field_offset;
|
||||
for (; offset + 2 * wordSize <= instanceSize; offset += 2 * wordSize) {
|
||||
stp(nullReg, nullReg, pairAddress(resultReg, offset));
|
||||
}
|
||||
if (offset < instanceSize) {
|
||||
str(nullReg, address(resultReg, offset));
|
||||
offset += wordSize;
|
||||
}
|
||||
assert(offset == instanceSize);
|
||||
} else {
|
||||
final fieldReg = scratch1Reg;
|
||||
addImmediate(fieldReg, resultReg, vmOffsets.Instance_first_field_offset);
|
||||
if (initializeFields) {
|
||||
// TODO: support compressed pointers.
|
||||
const maxUnrolledSize = 16 * wordSize;
|
||||
if (instanceSize <= maxUnrolledSize) {
|
||||
int offset = vmOffsets.Instance_first_field_offset;
|
||||
for (; offset + 2 * wordSize <= instanceSize; offset += 2 * wordSize) {
|
||||
stp(nullReg, nullReg, pairAddress(resultReg, offset));
|
||||
}
|
||||
if (offset < instanceSize) {
|
||||
str(nullReg, address(resultReg, offset));
|
||||
offset += wordSize;
|
||||
}
|
||||
assert(offset == instanceSize);
|
||||
} else {
|
||||
final fieldReg = scratch1Reg;
|
||||
addImmediate(
|
||||
fieldReg,
|
||||
resultReg,
|
||||
vmOffsets.Instance_first_field_offset,
|
||||
);
|
||||
|
||||
final loop = Label();
|
||||
bind(loop);
|
||||
stp(
|
||||
nullReg,
|
||||
nullReg,
|
||||
WritebackRegOffsetAddress(fieldReg, 2 * wordSize, isPostIndexed: true),
|
||||
);
|
||||
// There is at least two word (kAllocationRedZoneSize) gap at the end of page
|
||||
// which makes it possible to initialize objects by two words at once and
|
||||
// write slightly beyond the end.
|
||||
cmp(fieldReg, newTopReg);
|
||||
b(loop, Condition.unsignedLess);
|
||||
final loop = Label();
|
||||
bind(loop);
|
||||
stp(
|
||||
nullReg,
|
||||
nullReg,
|
||||
WritebackRegOffsetAddress(
|
||||
fieldReg,
|
||||
2 * wordSize,
|
||||
isPostIndexed: true,
|
||||
),
|
||||
);
|
||||
// There is at least two word (kAllocationRedZoneSize) gap at the end of page
|
||||
// which makes it possible to initialize objects by two words at once and
|
||||
// write slightly beyond the end.
|
||||
cmp(fieldReg, newTopReg);
|
||||
b(loop, Condition.unsignedLess);
|
||||
}
|
||||
}
|
||||
|
||||
addImmediate(resultReg, resultReg, heapObjectTag);
|
||||
@@ -1055,6 +1075,19 @@ final class Arm64Assembler extends Assembler with Uint32OutputBuffer {
|
||||
ubfm(rd, rn, 0, 15, sz);
|
||||
}
|
||||
|
||||
void asr(
|
||||
Register rd,
|
||||
Register rn,
|
||||
int shift, [
|
||||
OperandSize sz = OperandSize.s64,
|
||||
]) {
|
||||
if (shift == 0) {
|
||||
mov(rd, rn, sz);
|
||||
} else {
|
||||
sbfm(rd, rn, shift, sz.bitWidth - 1, sz);
|
||||
}
|
||||
}
|
||||
|
||||
void _emitBitfieldMove(
|
||||
int opcode,
|
||||
Register rd,
|
||||
@@ -1300,6 +1333,7 @@ final class Arm64Assembler extends Assembler with Uint32OutputBuffer {
|
||||
}
|
||||
|
||||
void _emitLoadStore(int opcode, Register rt, Address a, OperandSize sz) {
|
||||
assert(!sz.is128);
|
||||
switch (a) {
|
||||
case RegOffsetAddress():
|
||||
emit(
|
||||
@@ -1323,6 +1357,37 @@ final class Arm64Assembler extends Assembler with Uint32OutputBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
void fldr(FPRegister rt, Address a, [OperandSize sz = OperandSize.s64]) {
|
||||
_emitFPLoadStore(B22 | B26 | B27 | B28 | B29, rt, a, sz);
|
||||
}
|
||||
|
||||
void fstr(FPRegister rt, Address a, [OperandSize sz = OperandSize.s64]) {
|
||||
_emitFPLoadStore(B26 | B27 | B28 | B29, rt, a, sz);
|
||||
}
|
||||
|
||||
void _emitFPLoadStore(int opcode, FPRegister rt, Address a, OperandSize sz) {
|
||||
switch (a) {
|
||||
case RegOffsetAddress():
|
||||
emit(
|
||||
opcode |
|
||||
rt.encodingRt |
|
||||
a.encoding(sz) |
|
||||
(sz.is128 ? B23 : 0) |
|
||||
((sz.log2sizeInBytes & 3) << 30),
|
||||
);
|
||||
case WritebackRegOffsetAddress():
|
||||
emit(
|
||||
opcode |
|
||||
rt.encodingRt |
|
||||
a.encoding(sz) |
|
||||
(sz.is128 ? B23 : 0) |
|
||||
((sz.log2sizeInBytes & 3) << 30),
|
||||
);
|
||||
default:
|
||||
throw 'Unexpect address ${a.runtimeType}';
|
||||
}
|
||||
}
|
||||
|
||||
void ldp(
|
||||
Register low,
|
||||
Register high,
|
||||
@@ -1530,6 +1595,10 @@ extension on Register {
|
||||
int encodingRt2({bool allowSP = false}) => encoding(allowSP: allowSP) << 10;
|
||||
}
|
||||
|
||||
extension on FPRegister {
|
||||
int get encodingRt => index;
|
||||
}
|
||||
|
||||
extension on Immediate {
|
||||
int get encodingImm12 {
|
||||
if (_isUint(12, value)) {
|
||||
|
||||
@@ -29,7 +29,8 @@ final class Arm64CodeGenerator extends CodeGenerator {
|
||||
Arm64CodeGenerator(super.backEndState, this.functionRegistry);
|
||||
|
||||
@override
|
||||
Assembler createAssembler() => _asm = Arm64Assembler(backEndState.vmOffsets);
|
||||
Assembler createAssembler() =>
|
||||
_asm = Arm64Assembler(backEndState.vmOffsets, backEndState.objectLayout);
|
||||
|
||||
@override
|
||||
void enterFrame() {
|
||||
@@ -856,6 +857,7 @@ final class Arm64CodeGenerator extends CodeGenerator {
|
||||
AllocationStub.scratch2Reg,
|
||||
instanceSize,
|
||||
slowPath,
|
||||
initializeFields: true,
|
||||
);
|
||||
|
||||
if (typeArgsField != null) {
|
||||
@@ -914,6 +916,7 @@ final class Arm64CodeGenerator extends CodeGenerator {
|
||||
AllocationStub.scratch2Reg,
|
||||
instanceSize,
|
||||
slowPath,
|
||||
initializeFields: true,
|
||||
);
|
||||
|
||||
_asm.bind(initializeObject);
|
||||
@@ -937,6 +940,115 @@ final class Arm64CodeGenerator extends CodeGenerator {
|
||||
_asm.unimplemented('Unimplemented: code generation for SetListElement');
|
||||
}
|
||||
|
||||
@override
|
||||
void visitBoxInt(BoxInt instr) {
|
||||
final operandReg = inputReg(instr, 0);
|
||||
final tagsReg = temporaryReg(instr, 0);
|
||||
final scratch1Reg = temporaryReg(instr, 1);
|
||||
final scratch2Reg = temporaryReg(instr, 2);
|
||||
final resultReg = outputReg(instr);
|
||||
final done = Label();
|
||||
final cls = GlobalContext.instance.coreTypes.index.getClass(
|
||||
'dart:core',
|
||||
'_Mint',
|
||||
);
|
||||
final instanceSize = vmOffsets.Mint_InstanceSize;
|
||||
|
||||
Label slowPath = addSlowPath(() {
|
||||
_asm.unimplemented('Unimplemented: code generation for BoxInt slow path');
|
||||
_asm.b(done);
|
||||
});
|
||||
|
||||
_asm.adds(resultReg, operandReg, operandReg);
|
||||
_asm.b(done, .noOverflow);
|
||||
|
||||
// TODO: compute tags at compile time.
|
||||
_asm.loadFromPool(tagsReg, NewObjectTags(cls));
|
||||
_asm.inlineAllocation(
|
||||
resultReg,
|
||||
tagsReg,
|
||||
scratch1Reg,
|
||||
scratch2Reg,
|
||||
instanceSize,
|
||||
slowPath,
|
||||
initializeFields: false,
|
||||
);
|
||||
_asm.str(
|
||||
operandReg,
|
||||
_asm.fieldAddress(resultReg, vmOffsets.Mint_value_offset),
|
||||
);
|
||||
_asm.bind(done);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitBoxDouble(BoxDouble instr) {
|
||||
final operandReg = inputFPReg(instr, 0);
|
||||
final tagsReg = temporaryReg(instr, 0);
|
||||
final scratch1Reg = temporaryReg(instr, 1);
|
||||
final scratch2Reg = temporaryReg(instr, 2);
|
||||
final resultReg = outputReg(instr);
|
||||
final done = Label();
|
||||
final cls = GlobalContext.instance.coreTypes.index.getClass(
|
||||
'dart:core',
|
||||
'_Double',
|
||||
);
|
||||
final instanceSize = vmOffsets.Double_InstanceSize;
|
||||
|
||||
Label slowPath = addSlowPath(() {
|
||||
_asm.unimplemented(
|
||||
'Unimplemented: code generation for BoxDouble slow path',
|
||||
);
|
||||
_asm.b(done);
|
||||
});
|
||||
|
||||
// TODO: compute tags at compile time.
|
||||
_asm.loadFromPool(tagsReg, NewObjectTags(cls));
|
||||
_asm.inlineAllocation(
|
||||
resultReg,
|
||||
tagsReg,
|
||||
scratch1Reg,
|
||||
scratch2Reg,
|
||||
instanceSize,
|
||||
slowPath,
|
||||
initializeFields: false,
|
||||
);
|
||||
_asm.fstr(
|
||||
operandReg,
|
||||
_asm.fieldAddress(resultReg, vmOffsets.Double_value_offset),
|
||||
);
|
||||
_asm.bind(done);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitUnboxInt(UnboxInt instr) {
|
||||
var operandReg = inputReg(instr, 0);
|
||||
final resultReg = outputReg(instr);
|
||||
final done = Label();
|
||||
|
||||
if (operandReg == resultReg) {
|
||||
_asm.mov(tempReg, operandReg);
|
||||
operandReg = tempReg;
|
||||
}
|
||||
|
||||
_asm.asr(resultReg, operandReg, smiShift);
|
||||
_asm.tbz(operandReg, smiBit, done);
|
||||
_asm.ldr(
|
||||
resultReg,
|
||||
_asm.fieldAddress(operandReg, vmOffsets.Mint_value_offset),
|
||||
);
|
||||
_asm.bind(done);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitUnboxDouble(UnboxDouble instr) {
|
||||
final operandReg = inputReg(instr, 0);
|
||||
final resultReg = outputFPReg(instr);
|
||||
_asm.fldr(
|
||||
resultReg,
|
||||
_asm.fieldAddress(operandReg, vmOffsets.Double_value_offset),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitBinaryIntOp(BinaryIntOp instr) {
|
||||
_asm.unimplemented('Unimplemented: code generation for BinaryIntOp');
|
||||
|
||||
@@ -270,6 +270,30 @@ final class Arm64Constraints extends Constraints {
|
||||
anyCpuRegister,
|
||||
]);
|
||||
|
||||
@override
|
||||
InstructionConstraints? visitBoxInt(BoxInt instr) =>
|
||||
const InstructionConstraints(
|
||||
anyCpuRegister,
|
||||
[anyCpuRegister],
|
||||
[anyCpuRegister, anyCpuRegister, anyCpuRegister],
|
||||
);
|
||||
|
||||
@override
|
||||
InstructionConstraints? visitBoxDouble(BoxDouble instr) =>
|
||||
const InstructionConstraints(
|
||||
anyCpuRegister,
|
||||
[anyFpuRegister],
|
||||
[anyCpuRegister, anyCpuRegister, anyCpuRegister],
|
||||
);
|
||||
|
||||
@override
|
||||
InstructionConstraints? visitUnboxInt(UnboxInt instr) =>
|
||||
const InstructionConstraints(anyCpuRegister, [anyCpuRegister]);
|
||||
|
||||
@override
|
||||
InstructionConstraints? visitUnboxDouble(UnboxDouble instr) =>
|
||||
const InstructionConstraints(anyFpuRegister, [anyCpuRegister]);
|
||||
|
||||
@override
|
||||
InstructionConstraints? visitBinaryIntOp(BinaryIntOp instr) =>
|
||||
InstructionConstraints(anyCpuRegister, [
|
||||
|
||||
@@ -8,14 +8,15 @@ import 'package:native_compiler/back_end/arm64/assembler.dart';
|
||||
import 'package:native_compiler/back_end/assembler.dart';
|
||||
import 'package:native_compiler/back_end/locations.dart';
|
||||
import 'package:native_compiler/back_end/stub_code_generator.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';
|
||||
|
||||
abstract base class Arm64StubCodeGenerator implements StubCodeGenerator {
|
||||
final Arm64Assembler _asm;
|
||||
|
||||
Arm64StubCodeGenerator(VMOffsets vmOffsets)
|
||||
: _asm = Arm64Assembler(vmOffsets);
|
||||
Arm64StubCodeGenerator(VMOffsets vmOffsets, ObjectLayout objectLayout)
|
||||
: _asm = Arm64Assembler(vmOffsets, objectLayout);
|
||||
|
||||
void _generate();
|
||||
|
||||
@@ -44,7 +45,7 @@ final class AllocationStub extends Arm64StubCodeGenerator {
|
||||
|
||||
final ast.Class cls;
|
||||
|
||||
AllocationStub(super.vmOffsets, this.cls);
|
||||
AllocationStub(super.vmOffsets, super.objectLayout, this.cls);
|
||||
|
||||
@override
|
||||
void _generate() {
|
||||
@@ -89,7 +90,12 @@ final class WriteBarrierStub extends Arm64StubCodeGenerator {
|
||||
final Register objectReg;
|
||||
final Register valueReg;
|
||||
|
||||
WriteBarrierStub(super.vmOffsets, this.objectReg, this.valueReg);
|
||||
WriteBarrierStub(
|
||||
super.vmOffsets,
|
||||
super.objectLayout,
|
||||
this.objectReg,
|
||||
this.valueReg,
|
||||
);
|
||||
|
||||
@override
|
||||
void _generate() {
|
||||
@@ -104,15 +110,20 @@ final class WriteBarrierStub extends Arm64StubCodeGenerator {
|
||||
|
||||
final class Arm64StubFactory extends StubFactory {
|
||||
final VMOffsets vmOffsets;
|
||||
Arm64StubFactory(this.vmOffsets, super.consumeGeneratedCode);
|
||||
final ObjectLayout objectLayout;
|
||||
Arm64StubFactory(
|
||||
this.vmOffsets,
|
||||
this.objectLayout,
|
||||
super.consumeGeneratedCode,
|
||||
);
|
||||
|
||||
@override
|
||||
StubCodeGenerator allocationStubGenerator(ast.Class cls) =>
|
||||
AllocationStub(vmOffsets, cls);
|
||||
AllocationStub(vmOffsets, objectLayout, cls);
|
||||
|
||||
@override
|
||||
StubCodeGenerator writeBarrierStubGenerator(
|
||||
Register objectReg,
|
||||
Register valueReg,
|
||||
) => WriteBarrierStub(vmOffsets, objectReg, valueReg);
|
||||
) => WriteBarrierStub(vmOffsets, objectLayout, objectReg, valueReg);
|
||||
}
|
||||
|
||||
@@ -18,11 +18,13 @@ enum OperandSize {
|
||||
s8,
|
||||
s16,
|
||||
s32,
|
||||
s64;
|
||||
s64,
|
||||
simd128;
|
||||
|
||||
bool get is32 => (this == u32) || (this == s32);
|
||||
bool get is64 => (this == u64) || (this == s64);
|
||||
bool get is32or64 => is32 || is64;
|
||||
bool get is128 => (this == simd128);
|
||||
|
||||
bool get isSigned =>
|
||||
(this == s8) || (this == s16) || (this == s32) || (this == s64);
|
||||
@@ -32,6 +34,7 @@ enum OperandSize {
|
||||
u16 || s16 => 16,
|
||||
u32 || s32 => 32,
|
||||
u64 || s64 => 64,
|
||||
simd128 => 128,
|
||||
};
|
||||
|
||||
int get sizeInBytes => switch (this) {
|
||||
@@ -39,6 +42,7 @@ enum OperandSize {
|
||||
u16 || s16 => 2,
|
||||
u32 || s32 => 4,
|
||||
u64 || s64 => 8,
|
||||
simd128 => 16,
|
||||
};
|
||||
|
||||
int get log2sizeInBytes => switch (this) {
|
||||
@@ -46,6 +50,7 @@ enum OperandSize {
|
||||
u16 || s16 => 1,
|
||||
u32 || s32 => 2,
|
||||
u64 || s64 => 3,
|
||||
simd128 => 4,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:native_compiler/back_end/code.dart';
|
||||
import 'package:native_compiler/back_end/locations.dart';
|
||||
import 'package:native_compiler/back_end/stack_frame.dart';
|
||||
import 'package:native_compiler/back_end/stub_code_generator.dart';
|
||||
import 'package:native_compiler/passes/unboxing.dart';
|
||||
import 'package:native_compiler/runtime/object_layout.dart';
|
||||
import 'package:native_compiler/runtime/vm_defs.dart';
|
||||
|
||||
@@ -22,6 +23,9 @@ class BackEndState {
|
||||
/// Reusable stubs.
|
||||
late final StubFactory stubFactory;
|
||||
|
||||
/// Boxed/unboxed representation.
|
||||
late final Unboxing unboxing;
|
||||
|
||||
/// Block order for the code generation.
|
||||
late final List<Block> codeGenBlockOrder;
|
||||
|
||||
|
||||
@@ -78,7 +78,9 @@ final class LinearScanRegisterAllocator extends RegisterAllocator {
|
||||
LinearScanRegisterAllocator(super.backEndState, this.constraints);
|
||||
|
||||
RegisterClass registerClass(Definition instr) =>
|
||||
instr.type is DoubleType ? RegisterClass.fpu : RegisterClass.cpu;
|
||||
instr.type is DoubleType && backEndState.unboxing.hasUnboxedResult(instr)
|
||||
? RegisterClass.fpu
|
||||
: RegisterClass.cpu;
|
||||
|
||||
int instructionPos(Instruction instr) => _instructionPos[instr.id];
|
||||
int blockStartPos(Block block) => instructionPos(block);
|
||||
|
||||
@@ -23,6 +23,7 @@ import 'package:native_compiler/back_end/stack_frame.dart';
|
||||
import 'package:native_compiler/back_end/stub_code_generator.dart';
|
||||
import 'package:native_compiler/passes/lowering.dart';
|
||||
import 'package:native_compiler/passes/reorder_blocks.dart';
|
||||
import 'package:native_compiler/passes/unboxing.dart';
|
||||
import 'package:native_compiler/runtime/object_layout.dart';
|
||||
import 'package:native_compiler/runtime/vm_defs.dart';
|
||||
import 'package:native_compiler/snapshot/image_writer.dart';
|
||||
@@ -85,7 +86,11 @@ abstract base class Configuration {
|
||||
|
||||
StubFactory createStubFactory(CodeConsumer consumeGeneratedCode) =>
|
||||
switch (targetCPU) {
|
||||
TargetCPU.arm64 => Arm64StubFactory(vmOffsets, consumeGeneratedCode),
|
||||
TargetCPU.arm64 => Arm64StubFactory(
|
||||
vmOffsets,
|
||||
objectLayout,
|
||||
consumeGeneratedCode,
|
||||
),
|
||||
};
|
||||
|
||||
ImageWriter createImageWriter() => switch (imageFormat) {
|
||||
@@ -122,10 +127,12 @@ final class DevelopmentCompilerConfiguration extends Configuration {
|
||||
StubFactory stubFactory,
|
||||
CodeConsumer consumeGeneratedCode,
|
||||
) {
|
||||
final unboxing = Unboxing();
|
||||
final backEndState = BackEndState();
|
||||
backEndState.vmOffsets = vmOffsets;
|
||||
backEndState.objectLayout = objectLayout;
|
||||
backEndState.stubFactory = stubFactory;
|
||||
backEndState.unboxing = unboxing;
|
||||
backEndState.stackFrame = createStackFrame(function);
|
||||
backEndState.consumeGeneratedCode = consumeGeneratedCode;
|
||||
final constraints = createConstraints();
|
||||
@@ -135,6 +142,8 @@ final class DevelopmentCompilerConfiguration extends Configuration {
|
||||
ConstantPropagation(),
|
||||
ControlFlowOptimizations(),
|
||||
Lowering(functionRegistry, objectLayout),
|
||||
unboxing,
|
||||
ValueNumbering(simplification: Simplification()),
|
||||
ReorderBlocks(backEndState),
|
||||
LinearScanRegisterAllocator(backEndState, constraints),
|
||||
RegisterAllocationChecker(backEndState, constraints),
|
||||
|
||||
@@ -18,8 +18,6 @@ import 'package:native_compiler/runtime/object_layout.dart';
|
||||
/// Can replace instructions with multiple low-level
|
||||
/// instructions or combine instructions and their inputs
|
||||
/// into a single low-level instruction.
|
||||
///
|
||||
/// TODO: insert boxing/unboxing
|
||||
final class Lowering extends Pass with DefaultInstructionVisitor<void> {
|
||||
final FunctionRegistry functionRegistry;
|
||||
final ObjectLayout objectLayout;
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:cfg/ir/constant_value.dart';
|
||||
import 'package:cfg/ir/instructions.dart';
|
||||
import 'package:cfg/ir/types.dart';
|
||||
import 'package:cfg/passes/pass.dart';
|
||||
import 'package:cfg/utils/bit_vector.dart';
|
||||
|
||||
/// Insert boxing and unboxing instructions to make sure
|
||||
/// IR instructions take expected representation of their inputs.
|
||||
final class Unboxing extends Pass {
|
||||
late final BitVector _unboxedPhis = BitVector(graph.instructions.length);
|
||||
|
||||
Unboxing() : super('Unboxing');
|
||||
|
||||
@override
|
||||
void run() {
|
||||
_markUnboxedPhis();
|
||||
|
||||
var changed = false;
|
||||
for (final block in graph.reversePostorder) {
|
||||
currentBlock = block;
|
||||
for (final instr in block) {
|
||||
currentInstruction = instr;
|
||||
for (int i = 0, n = instr.inputCount; i < n; ++i) {
|
||||
final input = instr.inputDefAt(i);
|
||||
final isDefUnboxed = hasUnboxedResult(input);
|
||||
final isInputUnboxed = hasUnboxedInput(instr, i);
|
||||
if (isDefUnboxed != isInputUnboxed) {
|
||||
_convertInput(instr, i, input, isDefUnboxed);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
graph.invalidateInstructionNumbering();
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if [instr] takes unboxed value as [inputIndex]-th input.
|
||||
bool hasUnboxedInput(Instruction instr, int inputIndex) {
|
||||
return switch (instr) {
|
||||
Phi() => _unboxedPhis[instr.id],
|
||||
Comparison() => instr.op.isIntComparison || instr.op.isDoubleComparison,
|
||||
CompareAndBranch() =>
|
||||
instr.op.isIntComparison || instr.op.isDoubleComparison,
|
||||
BinaryIntOp() ||
|
||||
UnaryIntOp() ||
|
||||
BinaryDoubleOp() ||
|
||||
UnaryDoubleOp() ||
|
||||
Box() => true,
|
||||
StoreField() => false, // TODO: unboxed fields,
|
||||
CallInstruction() => false, // TODO: support unboxed parameters.
|
||||
Return() => false, // TODO: support unboxed return values.
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns true if result of [instr] is an unboxed value.
|
||||
bool hasUnboxedResult(Definition instr) {
|
||||
return switch (instr) {
|
||||
Phi() => _unboxedPhis[instr.id],
|
||||
BinaryIntOp() ||
|
||||
UnaryIntOp() ||
|
||||
BinaryDoubleOp() ||
|
||||
UnaryDoubleOp() ||
|
||||
Unbox() => true,
|
||||
LoadField() => false, // TODO: unboxed fields,
|
||||
Parameter() => false, // TODO: support unboxed parameters.
|
||||
CallInstruction() => false, // TODO: support unboxed return values.
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
/// Select representation for phis which can be unboxed.
|
||||
/// Phi is marked as unboxed if it takes at least one unboxed input.
|
||||
void _markUnboxedPhis() {
|
||||
for (final block in graph.reversePostorder) {
|
||||
if (block is! JoinBlock) {
|
||||
continue;
|
||||
}
|
||||
currentBlock = block;
|
||||
for (final phi in block.phis) {
|
||||
currentInstruction = phi;
|
||||
if (!_canBeUnboxed(phi)) {
|
||||
continue;
|
||||
}
|
||||
if (_findUnboxedInput(phi)) {
|
||||
_unboxedPhis[phi.id] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool _canBeUnboxed(Phi instr) {
|
||||
final type = instr.type;
|
||||
return type is IntType || type is DoubleType;
|
||||
}
|
||||
|
||||
/// Find at least one unboxed input while transitively traversing phis.
|
||||
bool _findUnboxedInput(Phi instr) {
|
||||
final visited = {instr};
|
||||
final workList = [instr];
|
||||
while (workList.isNotEmpty) {
|
||||
instr = workList.removeLast();
|
||||
for (int i = 0, n = instr.inputCount; i < n; ++i) {
|
||||
final input = instr.inputDefAt(i);
|
||||
if (input == instr) {
|
||||
continue;
|
||||
}
|
||||
if (hasUnboxedResult(input)) {
|
||||
return true;
|
||||
}
|
||||
if (input is Phi && _canBeUnboxed(input)) {
|
||||
if (visited.add(input)) {
|
||||
workList.add(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void _convertInput(
|
||||
Instruction instr,
|
||||
int inputIndex,
|
||||
Definition def,
|
||||
bool isDefUnboxed,
|
||||
) {
|
||||
final type = def.type;
|
||||
Definition replacement;
|
||||
if (def is Constant) {
|
||||
assert(!isDefUnboxed);
|
||||
replacement = graph.getConstant(
|
||||
ConstantValue(switch (type) {
|
||||
IntType() => UnboxedIntConstant(def.value.intValue),
|
||||
DoubleType() => UnboxedDoubleConstant(def.value.doubleValue),
|
||||
_ => throw 'Unexpected unboxed type $type',
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
final insertionPoint = (instr is Phi)
|
||||
? instr.block!.predecessors[inputIndex].lastInstruction
|
||||
: instr;
|
||||
replacement = isDefUnboxed
|
||||
? switch (type) {
|
||||
IntType() => BoxInt(graph, insertionPoint.sourcePosition, def),
|
||||
DoubleType() => BoxDouble(
|
||||
graph,
|
||||
insertionPoint.sourcePosition,
|
||||
def,
|
||||
),
|
||||
_ => throw 'Unexpected unboxed type $type',
|
||||
}
|
||||
: switch (type) {
|
||||
IntType() => UnboxInt(graph, insertionPoint.sourcePosition, def),
|
||||
DoubleType() => UnboxDouble(
|
||||
graph,
|
||||
insertionPoint.sourcePosition,
|
||||
def,
|
||||
),
|
||||
_ => throw 'Unexpected unboxed type $type',
|
||||
};
|
||||
replacement.insertBefore(insertionPoint);
|
||||
}
|
||||
instr.replaceInputAt(inputIndex, replacement);
|
||||
}
|
||||
}
|
||||
@@ -106,6 +106,8 @@ enum ObjectPoolEntryKind {
|
||||
staticFieldOffset,
|
||||
interfaceCall,
|
||||
dynamicCall,
|
||||
unboxedInt,
|
||||
unboxedDouble,
|
||||
}
|
||||
|
||||
abstract base class SerializationCluster {
|
||||
@@ -1292,7 +1294,7 @@ final class ObjectPoolSerializationCluster extends SerializationCluster {
|
||||
case ReservedEntry():
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
} else if (entry is! UnboxedConstant) {
|
||||
serializer.push(entry);
|
||||
}
|
||||
}
|
||||
@@ -1333,6 +1335,12 @@ final class ObjectPoolSerializationCluster extends SerializationCluster {
|
||||
serializer.writeRefId(icDatas[entry]);
|
||||
case ReservedEntry():
|
||||
}
|
||||
} else if (entry is UnboxedIntConstant) {
|
||||
serializer.writeUint(ObjectPoolEntryKind.unboxedInt.index);
|
||||
serializer.out.writeInt(entry.value);
|
||||
} else if (entry is UnboxedDoubleConstant) {
|
||||
serializer.writeUint(ObjectPoolEntryKind.unboxedDouble.index);
|
||||
serializer.out.writeDouble(entry.value);
|
||||
} else {
|
||||
serializer.writeUint(ObjectPoolEntryKind.objectRef.index);
|
||||
serializer.writeRefId(entry);
|
||||
|
||||
@@ -9,17 +9,23 @@ import 'package:native_compiler/back_end/arm64/assembler.dart';
|
||||
import 'package:native_compiler/back_end/assembler.dart';
|
||||
import 'package:native_compiler/back_end/code.dart';
|
||||
import 'package:native_compiler/back_end/object_pool.dart';
|
||||
import 'package:native_compiler/runtime/object_layout.dart';
|
||||
import 'package:native_compiler/runtime/vm_defs.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'disassembler.dart' show Disassembler;
|
||||
|
||||
void main() {
|
||||
final vmOffsets = Arm64VMOffsets();
|
||||
final objectLayout = ObjectLayout(
|
||||
vmOffsets,
|
||||
wordSize: wordSize,
|
||||
compressedWordSize: wordSize,
|
||||
);
|
||||
final objectPoolBase = vmOffsets.ObjectPool_elementOffset(0);
|
||||
late Arm64Assembler asm;
|
||||
|
||||
setUp(() {
|
||||
asm = Arm64Assembler(vmOffsets);
|
||||
asm = Arm64Assembler(vmOffsets, objectLayout);
|
||||
});
|
||||
|
||||
void expectDisassembly(String expected) {
|
||||
@@ -192,10 +198,14 @@ void main() {
|
||||
});
|
||||
test('loadConstant', () {
|
||||
asm.loadConstant(R0, ConstantValue.fromString('abc'));
|
||||
asm.loadConstant(R1, ConstantValue.fromInt(42));
|
||||
asm.loadConstant(R1, ConstantValue(UnboxedIntConstant(42)));
|
||||
asm.loadConstant(R2, ConstantValue.fromInt(42));
|
||||
asm.loadConstant(R3, ConstantValue.fromInt(0x7fffffff_ffffffff));
|
||||
expectDisassembly(
|
||||
'ldr r0, [pp, #${objectPoolBase}]\n'
|
||||
'movz r1, #0x2a\n',
|
||||
'movz r1, #0x2a\n'
|
||||
'movz r2, #0x54\n'
|
||||
'ldr r3, [pp, #${objectPoolBase + 8}]\n',
|
||||
);
|
||||
});
|
||||
test('loadImmediate', () {
|
||||
@@ -337,7 +347,15 @@ void main() {
|
||||
});
|
||||
test('inlineAllocation - object size 16', () {
|
||||
final slowPath = Label();
|
||||
asm.inlineAllocation(R0, R1, R2, R3, 16, slowPath);
|
||||
asm.inlineAllocation(
|
||||
R0,
|
||||
R1,
|
||||
R2,
|
||||
R3,
|
||||
16,
|
||||
slowPath,
|
||||
initializeFields: true,
|
||||
);
|
||||
asm.bind(slowPath);
|
||||
expectDisassembly(
|
||||
'ldp r0, r2, [thr, #${vmOffsets.Thread_top_offset}]\n'
|
||||
@@ -350,9 +368,39 @@ void main() {
|
||||
'add r0, r0, #0x1\n',
|
||||
);
|
||||
});
|
||||
test('inlineAllocation - object size 16, no field initialization', () {
|
||||
final slowPath = Label();
|
||||
asm.inlineAllocation(
|
||||
R0,
|
||||
R1,
|
||||
R2,
|
||||
R3,
|
||||
16,
|
||||
slowPath,
|
||||
initializeFields: false,
|
||||
);
|
||||
asm.bind(slowPath);
|
||||
expectDisassembly(
|
||||
'ldp r0, r2, [thr, #${vmOffsets.Thread_top_offset}]\n'
|
||||
'add r3, r0, #0x10\n'
|
||||
'cmp r2, r3\n'
|
||||
'bls +16\n'
|
||||
'str r3, [thr, #${vmOffsets.Thread_top_offset}]\n'
|
||||
'str r1, [r0]\n'
|
||||
'add r0, r0, #0x1\n',
|
||||
);
|
||||
});
|
||||
test('inlineAllocation - object size 32', () {
|
||||
final slowPath = Label();
|
||||
asm.inlineAllocation(R0, R1, R2, R3, 32, slowPath);
|
||||
asm.inlineAllocation(
|
||||
R0,
|
||||
R1,
|
||||
R2,
|
||||
R3,
|
||||
32,
|
||||
slowPath,
|
||||
initializeFields: true,
|
||||
);
|
||||
asm.bind(slowPath);
|
||||
expectDisassembly(
|
||||
'ldp r0, r2, [thr, #${vmOffsets.Thread_top_offset}]\n'
|
||||
@@ -368,7 +416,15 @@ void main() {
|
||||
});
|
||||
test('inlineAllocation - object size 160', () {
|
||||
final slowPath = Label();
|
||||
asm.inlineAllocation(R0, R1, R2, R3, 160, slowPath);
|
||||
asm.inlineAllocation(
|
||||
R0,
|
||||
R1,
|
||||
R2,
|
||||
R3,
|
||||
160,
|
||||
slowPath,
|
||||
initializeFields: true,
|
||||
);
|
||||
asm.bind(slowPath);
|
||||
expectDisassembly(
|
||||
'ldp r0, r2, [thr, #${vmOffsets.Thread_top_offset}]\n'
|
||||
@@ -626,6 +682,38 @@ void main() {
|
||||
expectDisassembly('uxth r1, r2\n');
|
||||
});
|
||||
|
||||
test('asr', () {
|
||||
asm.asr(R0, R1, 0);
|
||||
asm.asr(R0, R1, 0, .s32);
|
||||
asm.asr(R1, R2, 1);
|
||||
asm.asr(R1, R2, 1, .s32);
|
||||
asm.asr(R4, R2, 31, .s32);
|
||||
asm.asr(R4, R2, 63);
|
||||
expectDisassembly(
|
||||
'mov r0, r1\n'
|
||||
'movw r0, r1\n'
|
||||
'asr r1, r2, #1\n'
|
||||
'asrw r1, r2, #1\n'
|
||||
'asrw r4, r2, #31\n'
|
||||
'asr r4, r2, #63\n',
|
||||
);
|
||||
expectThrows(() {
|
||||
asm.asr(R4, R2, -1);
|
||||
});
|
||||
expectThrows(() {
|
||||
asm.asr(R4, R2, 64);
|
||||
});
|
||||
expectThrows(() {
|
||||
asm.asr(R4, R2, 32, .s32);
|
||||
});
|
||||
expectThrows(() {
|
||||
asm.asr(SP, R2, 1);
|
||||
});
|
||||
expectThrows(() {
|
||||
asm.asr(R4, SP, 1);
|
||||
});
|
||||
});
|
||||
|
||||
test('and', () {
|
||||
asm.and(R0, R1, R2);
|
||||
asm.and(R0, R0, Immediate(-512));
|
||||
@@ -835,6 +923,9 @@ void main() {
|
||||
expectThrows(() {
|
||||
asm.ldr(R0, WritebackRegOffsetAddress(R0, 8, isPostIndexed: true));
|
||||
});
|
||||
expectThrows(() {
|
||||
asm.ldr(R0, RegOffsetAddress(R1, 0), .simd128);
|
||||
});
|
||||
});
|
||||
|
||||
test('str', () {
|
||||
@@ -881,6 +972,89 @@ void main() {
|
||||
expectThrows(() {
|
||||
asm.str(R0, WritebackRegOffsetAddress(R0, 8, isPostIndexed: true));
|
||||
});
|
||||
expectThrows(() {
|
||||
asm.str(R0, RegOffsetAddress(R1, 0), .simd128);
|
||||
});
|
||||
});
|
||||
|
||||
test('fldr', () {
|
||||
asm.fldr(V0, RegOffsetAddress(R1, 7));
|
||||
asm.fldr(V1, RegOffsetAddress(R1, 7), .s16);
|
||||
asm.fldr(V2, RegOffsetAddress(R1, 32), .s32);
|
||||
asm.fldr(V3, RegOffsetAddress(R1, -5), .s64);
|
||||
asm.fldr(V4, RegOffsetAddress(SP, 32768), .simd128);
|
||||
asm.fldr(V5, WritebackRegOffsetAddress(R1, 16, isPostIndexed: true));
|
||||
asm.fldr(
|
||||
V6,
|
||||
WritebackRegOffsetAddress(R1, -8, isPostIndexed: false),
|
||||
.simd128,
|
||||
);
|
||||
asm.fldr(V0, WritebackRegOffsetAddress(R0, 8, isPostIndexed: true), .s32);
|
||||
expectDisassembly(
|
||||
'fldrd v0, [r1, #7]\n'
|
||||
'fldrh v1, [r1, #7]\n'
|
||||
'fldrs v2, [r1, #32]\n'
|
||||
'fldrd v3, [r1, #-5]\n'
|
||||
'fldrq v4, [csp, #32768]\n'
|
||||
'fldrd v5, [r1], #16 !\n'
|
||||
'fldrq v6, [r1, #-8]!\n'
|
||||
'fldrs v0, [r0], #8 !\n',
|
||||
);
|
||||
expectThrows(() {
|
||||
asm.fldr(V0, RegOffsetAddress(R1, 32768));
|
||||
});
|
||||
expectThrows(() {
|
||||
asm.fldr(V0, RegOffsetAddress(R1, 4097));
|
||||
});
|
||||
expectThrows(() {
|
||||
asm.fldr(V0, RegOffsetAddress(R1, -512));
|
||||
});
|
||||
expectThrows(() {
|
||||
asm.fldr(V0, WritebackRegOffsetAddress(R1, 512, isPostIndexed: true));
|
||||
});
|
||||
expectThrows(() {
|
||||
asm.fldr(V0, WritebackRegOffsetAddress(R1, -513, isPostIndexed: false));
|
||||
});
|
||||
});
|
||||
|
||||
test('fstr', () {
|
||||
asm.fstr(V0, RegOffsetAddress(R1, 7));
|
||||
asm.fstr(V1, RegOffsetAddress(R1, 7), .s16);
|
||||
asm.fstr(V2, RegOffsetAddress(R1, 32), .s32);
|
||||
asm.fstr(V3, RegOffsetAddress(R1, -5), .s64);
|
||||
asm.fstr(V4, RegOffsetAddress(SP, 32768), .simd128);
|
||||
asm.fstr(V5, WritebackRegOffsetAddress(R1, 16, isPostIndexed: true));
|
||||
asm.fstr(
|
||||
V6,
|
||||
WritebackRegOffsetAddress(R1, -8, isPostIndexed: false),
|
||||
.simd128,
|
||||
);
|
||||
asm.fstr(V0, WritebackRegOffsetAddress(R0, 8, isPostIndexed: true), .s32);
|
||||
expectDisassembly(
|
||||
'fstrd v0, [r1, #7]\n'
|
||||
'fstrh v1, [r1, #7]\n'
|
||||
'fstrs v2, [r1, #32]\n'
|
||||
'fstrd v3, [r1, #-5]\n'
|
||||
'fstrq v4, [csp, #32768]\n'
|
||||
'fstrd v5, [r1], #16 !\n'
|
||||
'fstrq v6, [r1, #-8]!\n'
|
||||
'fstrs v0, [r0], #8 !\n',
|
||||
);
|
||||
expectThrows(() {
|
||||
asm.fstr(V0, RegOffsetAddress(R1, 32768));
|
||||
});
|
||||
expectThrows(() {
|
||||
asm.fstr(V0, RegOffsetAddress(R1, 4097));
|
||||
});
|
||||
expectThrows(() {
|
||||
asm.fstr(V0, RegOffsetAddress(R1, -512));
|
||||
});
|
||||
expectThrows(() {
|
||||
asm.fstr(V0, WritebackRegOffsetAddress(R1, 512, isPostIndexed: true));
|
||||
});
|
||||
expectThrows(() {
|
||||
asm.fstr(V0, WritebackRegOffsetAddress(R1, -513, isPostIndexed: false));
|
||||
});
|
||||
});
|
||||
|
||||
test('ldp', () {
|
||||
|
||||
@@ -824,7 +824,11 @@ class ARM64Decoder {
|
||||
void printMemOperand(Instr instr) {
|
||||
final int rn = instr.rnField();
|
||||
if (instr.bit(24) == 1) {
|
||||
final int scale = instr.szField();
|
||||
int scale = instr.szField();
|
||||
if (instr.bit(26) == 1 && instr.bit(23) == 1 && scale == 0) {
|
||||
// 128-bit SIMD&FP memory op.
|
||||
scale = 4;
|
||||
}
|
||||
final int imm12 = instr.imm12Field();
|
||||
final int off = imm12 << scale;
|
||||
print("[");
|
||||
|
||||
@@ -34,6 +34,7 @@ import 'package:native_compiler/back_end/regalloc_checker.dart';
|
||||
import 'package:native_compiler/back_end/register_allocator.dart';
|
||||
import 'package:native_compiler/passes/lowering.dart';
|
||||
import 'package:native_compiler/passes/reorder_blocks.dart';
|
||||
import 'package:native_compiler/passes/unboxing.dart';
|
||||
import 'package:native_compiler/runtime/object_layout.dart';
|
||||
import 'package:native_compiler/runtime/vm_defs.dart';
|
||||
import 'package:test/test.dart';
|
||||
@@ -170,6 +171,8 @@ class CompileAndDumpIr extends RecursiveVisitor {
|
||||
ConstantPropagation(),
|
||||
ControlFlowOptimizations(),
|
||||
Lowering(functionRegistry, objectLayout),
|
||||
Unboxing(),
|
||||
ValueNumbering(simplification: Simplification()),
|
||||
ReorderBlocks(backEndState),
|
||||
LinearScanRegisterAllocator(backEndState, constraints),
|
||||
RegisterAllocationChecker(backEndState, constraints),
|
||||
|
||||
@@ -16,16 +16,15 @@ B0 = EntryBlock()
|
||||
ParallelMove output(param[1] -> vloc:R1)
|
||||
v3 = Parameter(x) # RA: param[2] <- ()
|
||||
ParallelMove output(param[2] -> vloc:R2)
|
||||
v20 = LoadInstanceField(C.#typeArguments, v2) # RA: R3 <- (R1)
|
||||
v10 = TypeTest(v3, v9, v20, List<C.T%>) # RA: R3 <- (R2, -, R3)
|
||||
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[1] -> R1)
|
||||
v19 = LoadInstanceField(C.#typeArguments, v2) # RA: R0 <- (R1)
|
||||
ParallelMove split(param[0] -> R1, param[2] -> R2)
|
||||
v16 = TypeCast(v3, v1, v19, Map<C.T%, C.typeParameters.U%>) # RA: R0 <- (R2, R1, R0)
|
||||
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)
|
||||
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)
|
||||
@@ -37,7 +36,9 @@ B0 = EntryBlock()
|
||||
ParallelMove output(param[0] -> vloc:R0)
|
||||
v2 = Parameter(b) # RA: param[1] <- ()
|
||||
ParallelMove output(param[1] -> vloc:R1)
|
||||
v7 = Comparison int & == 0(v1, v2) # RA: R0 <- (R0, R1)
|
||||
v9 = UnboxInt(v1) # RA: R0 <- (R0)
|
||||
v10 = UnboxInt(v2) # RA: R1 <- (R1)
|
||||
v7 = Comparison int & == 0(v9, v10) # RA: R0 <- (R0, R1)
|
||||
ParallelMove input(vloc:R0 -> R0)
|
||||
Return(v7) # RA: (R0)
|
||||
|
||||
@@ -51,7 +52,9 @@ B0 = EntryBlock() dominates:(B9, B11, B8)
|
||||
ParallelMove output(param[0] -> vloc:R0)
|
||||
v2 = Parameter(b) # RA: param[1] <- ()
|
||||
ParallelMove output(param[1] -> vloc:R1)
|
||||
v7 = Comparison int & == 0(v1, v2) # RA: R0 <- (R0, R1)
|
||||
v30 = UnboxInt(v1) # RA: R0 <- (R0)
|
||||
v31 = UnboxInt(v2) # RA: R1 <- (R1)
|
||||
v7 = Comparison int & == 0(v30, v31) # RA: R0 <- (R0, R1)
|
||||
ParallelMove spill(R0 -> stack[0])
|
||||
Branch(v7, true: B8, false: B9) # RA: (R0)
|
||||
B8 = TargetBlock() idom:B0
|
||||
@@ -79,7 +82,9 @@ B0 = EntryBlock() dominates:(B11, B13, B10)
|
||||
ParallelMove output(param[0] -> vloc:R0)
|
||||
v2 = Parameter(b) # RA: param[1] <- ()
|
||||
ParallelMove output(param[1] -> vloc:R1)
|
||||
v7 = Comparison int & == 0(v1, v2) # RA: R0 <- (R0, R1)
|
||||
v20 = UnboxInt(v1) # RA: R0 <- (R0)
|
||||
v21 = UnboxInt(v2) # RA: R1 <- (R1)
|
||||
v7 = Comparison int & == 0(v20, v21) # RA: R0 <- (R0, R1)
|
||||
ParallelMove spill(R0 -> stack[0])
|
||||
Branch(v7, true: B10, false: B11) # RA: (R0)
|
||||
B10 = TargetBlock() idom:B0
|
||||
@@ -103,7 +108,9 @@ B0 = EntryBlock() dominates:(B8, B14, B16, B7)
|
||||
ParallelMove output(param[1] -> vloc:R1)
|
||||
v3 = Parameter(c) # RA: param[2] <- ()
|
||||
ParallelMove output(param[2] -> vloc:R2)
|
||||
v6 = Comparison int >(v1, v2) # RA: R0 <- (R0, R1)
|
||||
v35 = UnboxInt(v1) # RA: R0 <- (R0)
|
||||
v36 = UnboxInt(v2) # RA: R1 <- (R1)
|
||||
v6 = Comparison int >(v35, v36) # RA: R0 <- (R0, R1)
|
||||
ParallelMove spill(R0 -> stack[0])
|
||||
Branch(v6, true: B7, false: B8) # RA: (R0)
|
||||
B7 = TargetBlock() idom:B0
|
||||
@@ -131,40 +138,50 @@ B28 = JoinBlock(B26, B25) idom:B14
|
||||
|
||||
--- test5
|
||||
B0 = EntryBlock() dominates:(B7, B6)
|
||||
v3 = Constant(32)
|
||||
Constant(32)
|
||||
v11 = Constant(1)
|
||||
v14 = Constant(16)
|
||||
v16 = Constant(8)
|
||||
Constant(16)
|
||||
Constant(8)
|
||||
v23 = Constant(2)
|
||||
v25 = Constant(257)
|
||||
Constant(257)
|
||||
v37 = Constant(3)
|
||||
v47 = Constant(4)
|
||||
v50 = Constant(7)
|
||||
Constant(7)
|
||||
v58 = Constant(5)
|
||||
v62 = Constant(-1)
|
||||
v69 = Constant(UnboxedIntConstant(32))
|
||||
v71 = Constant(UnboxedIntConstant(16))
|
||||
v72 = Constant(UnboxedIntConstant(8))
|
||||
v74 = Constant(UnboxedIntConstant(257))
|
||||
v77 = Constant(UnboxedIntConstant(7))
|
||||
v1 = Parameter(a) # RA: param[0] <- ()
|
||||
ParallelMove output(param[0] -> vloc:R0)
|
||||
CompareAndBranch int & != 0(v1, v3, true: B6, false: B7) # RA: (R0, -)
|
||||
v68 = UnboxInt(v1) # RA: R1 <- (R0)
|
||||
CompareAndBranch int & != 0(v68, v69, true: B6, false: B7) # RA: (R1, -)
|
||||
B6 = TargetBlock() idom:B0
|
||||
ParallelMove input(IntConstant(1) -> R0)
|
||||
Return(v11) # RA: (R0)
|
||||
B7 = TargetBlock() idom:B0 dominates:(B19, B18)
|
||||
v15 = BinaryIntOp &(v1, v14) # RA: R1 <- (R0, -)
|
||||
CompareAndBranch int ==(v15, v16, true: B18, false: B19) # RA: (R1, -)
|
||||
v70 = UnboxInt(v1) # RA: R1 <- (R0)
|
||||
v15 = BinaryIntOp &(v70, v71) # RA: R1 <- (R1, -)
|
||||
CompareAndBranch int ==(v15, v72, true: B18, false: B19) # RA: (R1, -)
|
||||
B18 = TargetBlock() idom:B7
|
||||
ParallelMove input(IntConstant(2) -> R0)
|
||||
Return(v23) # RA: (R0)
|
||||
B19 = TargetBlock() idom:B7 dominates:(B33, B32)
|
||||
v29 = BinaryIntOp &(v1, v25) # RA: R1 <- (R0, -)
|
||||
CompareAndBranch int ==(v29, v25, true: B32, false: B33) # RA: (R1, -)
|
||||
v73 = UnboxInt(v1) # RA: R1 <- (R0)
|
||||
v29 = BinaryIntOp &(v73, v74) # RA: R1 <- (R1, -)
|
||||
CompareAndBranch int ==(v29, v74, true: B32, false: B33) # RA: (R1, -)
|
||||
B32 = TargetBlock() idom:B19
|
||||
ParallelMove input(IntConstant(3) -> R0)
|
||||
Return(v37) # RA: (R0)
|
||||
B33 = TargetBlock() idom:B19 dominates:(B43, B42)
|
||||
CompareAndBranch int & != 0(v1, v16, true: B42, false: B43) # RA: (R0, -)
|
||||
v75 = UnboxInt(v1) # RA: R1 <- (R0)
|
||||
CompareAndBranch int & != 0(v75, v72, true: B42, false: B43) # RA: (R1, -)
|
||||
B42 = TargetBlock() idom:B33 dominates:(B54, B53)
|
||||
v51 = BinaryIntOp &(v1, v50) # RA: R0 <- (R0, -)
|
||||
CompareAndBranch int ==(v51, v50, true: B53, false: B54) # RA: (R0, -)
|
||||
v76 = UnboxInt(v1) # RA: R0 <- (R0)
|
||||
v51 = BinaryIntOp &(v76, v77) # RA: R0 <- (R0, -)
|
||||
CompareAndBranch int ==(v51, v77, true: B53, false: B54) # RA: (R0, -)
|
||||
B53 = TargetBlock() idom:B42
|
||||
ParallelMove input(IntConstant(-1) -> R0)
|
||||
Return(v62) # RA: (R0)
|
||||
|
||||
@@ -30,21 +30,25 @@ B16 = TargetBlock() idom:B7
|
||||
|
||||
--- test2
|
||||
B0 = EntryBlock() dominates:(B3)
|
||||
v1 = Constant(0)
|
||||
v6 = Constant(10)
|
||||
v12 = Constant(3)
|
||||
Constant(0)
|
||||
Constant(10)
|
||||
Constant(3)
|
||||
v19 = Constant("done")
|
||||
v21 = Constant(null)
|
||||
v24 = Constant(1)
|
||||
ParallelMove control(IntConstant(0) -> R0)
|
||||
Constant(1)
|
||||
v32 = Constant(UnboxedIntConstant(0))
|
||||
v33 = Constant(UnboxedIntConstant(10))
|
||||
v34 = Constant(UnboxedIntConstant(3))
|
||||
v35 = Constant(UnboxedIntConstant(1))
|
||||
ParallelMove control(UnboxedIntConstant(0) -> R0)
|
||||
Goto(B3)
|
||||
B3 = JoinBlock(B0, B15) idom:B0 dominates:(B9, B8) loop-header (depth:1 body:(B3, B8, B15) back-edges:(B15))
|
||||
v29 = Phi(v1, v25) # RA: R0 <- (-, R0)
|
||||
CompareAndBranch int <(v29, v6, true: B8, false: B9) # RA: (R0, -)
|
||||
v29 = Phi(v32, v25) # RA: R0 <- (-, R0)
|
||||
CompareAndBranch int <(v29, v33, true: B8, false: B9) # RA: (R0, -)
|
||||
B8 = TargetBlock() idom:B3 dominates:(B15, B14) in-loop:B3
|
||||
CompareAndBranch int >(v29, v12, true: B14, false: B15) # RA: (R0, -)
|
||||
CompareAndBranch int >(v29, v34, true: B14, false: B15) # RA: (R0, -)
|
||||
B15 = TargetBlock() idom:B8 in-loop:B3
|
||||
v25 = BinaryIntOp +(v29, v24) # RA: R0 <- (R0, -)
|
||||
v25 = BinaryIntOp +(v29, v35) # RA: R0 <- (R0, -)
|
||||
Goto(B3)
|
||||
B14 = TargetBlock() idom:B8
|
||||
DirectCall print(v19) # RA: 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]
|
||||
@@ -56,28 +60,31 @@ B9 = TargetBlock() idom:B3
|
||||
|
||||
--- test3
|
||||
B0 = EntryBlock() dominates:(B3)
|
||||
v1 = Constant(0)
|
||||
v6 = Constant(10)
|
||||
Constant(0)
|
||||
Constant(10)
|
||||
v28 = Constant("done")
|
||||
v33 = Constant(1)
|
||||
Constant(1)
|
||||
v42 = Constant(null)
|
||||
ParallelMove control(IntConstant(0) -> R0)
|
||||
v49 = Constant(UnboxedIntConstant(0))
|
||||
v50 = Constant(UnboxedIntConstant(10))
|
||||
v51 = Constant(UnboxedIntConstant(1))
|
||||
ParallelMove control(UnboxedIntConstant(0) -> R0)
|
||||
Goto(B3)
|
||||
B3 = JoinBlock(B0, B30) idom:B0 dominates:(B9, B8) loop-header (depth:1 body:(B3, B8, B12, B17, B30, B16, B24, B23) back-edges:(B30))
|
||||
v44 = Phi(v1, v39) # RA: R0 <- (-, R0)
|
||||
v44 = Phi(v49, v39) # RA: R0 <- (-, R0)
|
||||
ParallelMove spill(R0 -> stack[0])
|
||||
CompareAndBranch int <(v44, v6, true: B8, false: B9) # RA: (R0, -)
|
||||
CompareAndBranch int <(v44, v50, true: B8, false: B9) # RA: (R0, -)
|
||||
B8 = TargetBlock() idom:B3 dominates:(B12) in-loop:B3
|
||||
ParallelMove control(IntConstant(0) -> R1)
|
||||
ParallelMove control(UnboxedIntConstant(0) -> R1)
|
||||
Goto(B12)
|
||||
B12 = JoinBlock(B8, B24) idom:B8 dominates:(B17, B30, B16) loop-header (depth:2 body:(B12, B16, B24) back-edges:(B24))
|
||||
v45 = Phi(v1, v34) # RA: R1 <- (-, R1)
|
||||
CompareAndBranch int <(v45, v6, true: B16, false: B17) # RA: (R1, -)
|
||||
v45 = Phi(v49, v34) # RA: R1 <- (-, R1)
|
||||
CompareAndBranch int <(v45, v50, true: B16, false: B17) # RA: (R1, -)
|
||||
B16 = TargetBlock() idom:B12 dominates:(B24, B23) in-loop:B12
|
||||
v21 = BinaryIntOp +(v44, v45) # RA: R2 <- (R0, R1)
|
||||
CompareAndBranch int >(v21, v6, true: B23, false: B24) # RA: (R2, -)
|
||||
CompareAndBranch int >(v21, v50, true: B23, false: B24) # RA: (R2, -)
|
||||
B24 = TargetBlock() idom:B16 in-loop:B12
|
||||
v34 = BinaryIntOp +(v45, v33) # RA: R1 <- (R1, -)
|
||||
v34 = BinaryIntOp +(v45, v51) # RA: R1 <- (R1, -)
|
||||
Goto(B12)
|
||||
B23 = TargetBlock() idom:B16 in-loop:B3
|
||||
DirectCall print(v28) # RA: 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]
|
||||
@@ -86,7 +93,7 @@ B17 = TargetBlock() idom:B12 in-loop:B3
|
||||
Goto(B30)
|
||||
B30 = JoinBlock(B17, B23) idom:B12 in-loop:B3
|
||||
ParallelMove split(stack[0] -> R0)
|
||||
v39 = BinaryIntOp +(v44, v33) # RA: R0 <- (R0, -)
|
||||
v39 = BinaryIntOp +(v44, v51) # RA: R0 <- (R0, -)
|
||||
ParallelMove spill(R0 -> stack[0])
|
||||
Goto(B3)
|
||||
B9 = TargetBlock() idom:B3
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
int test1(int a, int b) => a + b;
|
||||
|
||||
int test2(int n) {
|
||||
var sum = 0;
|
||||
for (var i = 0; i < n; i++) {
|
||||
sum += i;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
int test3(int a1, int a2, bool c1, bool c2) {
|
||||
int b;
|
||||
if (c1) {
|
||||
b = a1 + 1;
|
||||
} else {
|
||||
if (c2) {
|
||||
b = 5;
|
||||
} else {
|
||||
b = a2;
|
||||
}
|
||||
}
|
||||
return b * 2;
|
||||
}
|
||||
|
||||
void main() {}
|
||||
@@ -0,0 +1,80 @@
|
||||
--- test1
|
||||
B0 = EntryBlock()
|
||||
v1 = Parameter(a) # RA: param[0] <- ()
|
||||
ParallelMove output(param[0] -> vloc:R0)
|
||||
v2 = Parameter(b) # RA: param[1] <- ()
|
||||
ParallelMove output(param[1] -> vloc:R1)
|
||||
v7 = UnboxInt(v1) # RA: R0 <- (R0)
|
||||
v8 = UnboxInt(v2) # RA: R1 <- (R1)
|
||||
v5 = BinaryIntOp +(v7, v8) # RA: R0 <- (R0, R1)
|
||||
v9 = BoxInt(v5) # RA: R0 <- (R0) temps: [R1, R2, R3]
|
||||
ParallelMove input(vloc:R0 -> R0)
|
||||
Return(v9) # RA: (R0)
|
||||
|
||||
--- test2
|
||||
B0 = EntryBlock() dominates:(B5)
|
||||
Constant(0)
|
||||
Constant(1)
|
||||
v27 = Constant(UnboxedIntConstant(0))
|
||||
v29 = Constant(UnboxedIntConstant(1))
|
||||
v1 = Parameter(n) # RA: param[0] <- ()
|
||||
ParallelMove output(param[0] -> vloc:R0)
|
||||
ParallelMove control(UnboxedIntConstant(0) -> R2, UnboxedIntConstant(0) -> R1)
|
||||
Goto(B5)
|
||||
B5 = JoinBlock(B0, B10) idom:B0 dominates:(B11, B10) loop-header (depth:1 body:(B5, B10) back-edges:(B10))
|
||||
v25 = Phi(v27, v19) # RA: R2 <- (-, R2)
|
||||
v24 = Phi(v27, v15) # RA: R1 <- (-, R1)
|
||||
v28 = UnboxInt(v1) # RA: R3 <- (R0)
|
||||
CompareAndBranch int <(v25, v28, true: B10, false: B11) # RA: (R2, R3)
|
||||
B10 = TargetBlock() idom:B5 in-loop:B5
|
||||
v15 = BinaryIntOp +(v24, v25) # RA: R1 <- (R1, R2)
|
||||
v19 = BinaryIntOp +(v25, v29) # RA: R2 <- (R2, -)
|
||||
Goto(B5)
|
||||
B11 = TargetBlock() idom:B5
|
||||
v30 = BoxInt(v24) # RA: R0 <- (R1) temps: [R1, R2, R3]
|
||||
ParallelMove input(vloc:R0 -> R0)
|
||||
Return(v30) # RA: (R0)
|
||||
|
||||
--- test3
|
||||
B0 = EntryBlock() dominates:(B7, B13, B6)
|
||||
Constant(1)
|
||||
v19 = Constant(5)
|
||||
Constant(2)
|
||||
v35 = Constant(UnboxedIntConstant(1))
|
||||
v1 = Parameter(a1) # RA: param[0] <- ()
|
||||
ParallelMove output(param[0] -> vloc:R0)
|
||||
v2 = Parameter(a2) # RA: param[1] <- ()
|
||||
ParallelMove output(param[1] -> vloc:R3)
|
||||
v3 = Parameter(c1) # RA: param[2] <- ()
|
||||
ParallelMove output(param[2] -> vloc:R1)
|
||||
v4 = Parameter(c2) # RA: param[3] <- ()
|
||||
ParallelMove output(param[3] -> vloc:R2)
|
||||
Branch(v3, true: B6, false: B7) # RA: (R1)
|
||||
B6 = TargetBlock() idom:B0
|
||||
v34 = UnboxInt(v1) # RA: R0 <- (R0)
|
||||
v11 = BinaryIntOp +(v34, v35) # RA: R0 <- (R0, -)
|
||||
Goto(B13)
|
||||
B7 = TargetBlock() idom:B0 dominates:(B17, B21, B16)
|
||||
Branch(v4, true: B16, false: B17) # RA: (R2)
|
||||
B16 = TargetBlock() idom:B7
|
||||
ParallelMove control(IntConstant(5) -> R3)
|
||||
Goto(B21)
|
||||
B17 = TargetBlock() idom:B7
|
||||
Goto(B21)
|
||||
B21 = JoinBlock(B17, B16) idom:B7
|
||||
v32 = Phi(v2, v19) # RA: R3 <- (R3, -)
|
||||
v36 = UnboxInt(v32) # RA: R0 <- (R3)
|
||||
Goto(B13)
|
||||
B13 = JoinBlock(B21, B6) idom:B0
|
||||
v31 = Phi(v36, v11) # RA: R0 <- (R0, R0)
|
||||
v33 = BinaryIntOp <<(v31, v35) # RA: R0 <- (R0, -)
|
||||
v37 = BoxInt(v33) # RA: R0 <- (R0) temps: [R1, R2, R3]
|
||||
ParallelMove input(vloc:R0 -> R0)
|
||||
Return(v37) # RA: (R0)
|
||||
|
||||
--- main
|
||||
B0 = EntryBlock()
|
||||
v1 = Constant(null)
|
||||
ParallelMove input(NullConstant(null) -> R0)
|
||||
Return(v1) # RA: (R0)
|
||||
|
||||
@@ -100,6 +100,8 @@ class ModuleSnapshot : public AllStatic {
|
||||
kStaticFieldOffset,
|
||||
kInterfaceCall,
|
||||
kDynamicCall,
|
||||
kUnboxedInt,
|
||||
kUnboxedDouble,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1196,6 +1198,18 @@ class ObjectPoolDeserializationCluster : public DeserializationCluster {
|
||||
entry2.raw_obj_ = StubCode::OneArgOptimizedCheckInlineCache().ptr();
|
||||
break;
|
||||
}
|
||||
case ModuleSnapshot::kUnboxedInt: {
|
||||
pool->untag()->entry_bits()[j] = immediate_entry_bits;
|
||||
UntaggedObjectPool::Entry& entry = pool->untag()->data()[j];
|
||||
entry.raw_value_ = d.Read<int64_t>();
|
||||
break;
|
||||
}
|
||||
case ModuleSnapshot::kUnboxedDouble: {
|
||||
pool->untag()->entry_bits()[j] = immediate_entry_bits;
|
||||
UntaggedObjectPool::Entry& entry = pool->untag()->data()[j];
|
||||
entry.raw_value_ = bit_cast<int64_t>(d.Read<double>());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user