[modular_aot] Support more cases of parallel moves and double immediates
Issue: https://github.com/dart-lang/sdk/issues/61635 Change-Id: I8b167ea819d4247ca748f5200b5509e99a68d56e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/503560 Commit-Queue: Alexander Markov <alexmarkov@google.com> Reviewed-by: Slava Egorov <vegorov@google.com>
This commit is contained in:
committed by
dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent
2edcad4892
commit
ca9c12f1b6
@@ -2,6 +2,8 @@
|
||||
// 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 'dart:typed_data';
|
||||
|
||||
/// Compares elements of the given lists.
|
||||
bool listEquals(List<Object?> a, List<Object?> b) {
|
||||
if (a.length != b.length) return false;
|
||||
@@ -67,3 +69,17 @@ int roundUp(int value, int alignment) {
|
||||
assert(value >= 0);
|
||||
return roundDown(value + alignment - 1, alignment);
|
||||
}
|
||||
|
||||
final ByteData _conversionBuffer = ByteData(8);
|
||||
|
||||
/// Reinterpret double [value] as int (without changing bits).
|
||||
int doubleToIntBits(double value) {
|
||||
_conversionBuffer.setFloat64(0, value, Endian.little);
|
||||
return _conversionBuffer.getInt64(0, Endian.little);
|
||||
}
|
||||
|
||||
/// Reinterpret int [value] as double (without changing bits).
|
||||
double intBitsToDouble(int value) {
|
||||
_conversionBuffer.setInt64(0, value, Endian.little);
|
||||
return _conversionBuffer.getFloat64(0, Endian.little);
|
||||
}
|
||||
|
||||
@@ -78,4 +78,35 @@ void main() {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('doubleToIntBits and intBitsToDouble', () {
|
||||
List<(double, int)> values = [
|
||||
(1.0, 0x3FF00000_00000000),
|
||||
(-1.0, 0xBFF00000_00000000),
|
||||
(2.0, 0x40000000_00000000),
|
||||
(-0.25, 0xBFD00000_00000000),
|
||||
(1e100, 0x54b249ad_2594c37d),
|
||||
(0.0, 0x00000000_00000000),
|
||||
(-0.0, 0x80000000_00000000),
|
||||
(double.nan, 0xFFF80000_00000000),
|
||||
(double.infinity, 0x7ff00000_00000000),
|
||||
(double.negativeInfinity, 0xFFF00000_00000000),
|
||||
// More NaNs.
|
||||
(intBitsToDouble(0xFFF81234_56789abc), 0xFFF81234_56789abc),
|
||||
(intBitsToDouble(0xFFF00000_00000001), 0xFFF00000_00000001),
|
||||
(intBitsToDouble(0xFFF01234_56789abc), 0xFFF01234_56789abc),
|
||||
(intBitsToDouble(0x7FF80000_00000000), 0x7FF80000_00000000),
|
||||
(intBitsToDouble(0x7FF81234_56789abc), 0x7FF81234_56789abc),
|
||||
(intBitsToDouble(0x7FF00000_00000001), 0x7FF00000_00000001),
|
||||
(intBitsToDouble(0x7FF01234_56789abc), 0x7FF01234_56789abc),
|
||||
];
|
||||
for (var (d, i) in values) {
|
||||
print('($d, ${doubleToIntBits(d).toRadixString(16)})');
|
||||
expect(doubleToIntBits(d), equals(i));
|
||||
expect(intBitsToDouble(i), same(d));
|
||||
}
|
||||
for (var d = -100.0; d < 100.0; d += 0.1) {
|
||||
expect(intBitsToDouble(doubleToIntBits(d)), same(d));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// 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/utils/misc.dart';
|
||||
import 'package:native_compiler/back_end/arm64/stack_frame.dart';
|
||||
import 'package:native_compiler/back_end/assembler.dart';
|
||||
import 'package:native_compiler/back_end/code.dart';
|
||||
@@ -642,6 +643,24 @@ final class Arm64Assembler extends Assembler with Uint32OutputBuffer {
|
||||
assert(initialized);
|
||||
}
|
||||
|
||||
@override
|
||||
void loadDoubleImmediate(FPRegister reg, double v) {
|
||||
final imm = Immediate(doubleToIntBits(v));
|
||||
if (imm.tryEncodingFpImm(.s64) != null) {
|
||||
fmov(reg, imm);
|
||||
return;
|
||||
}
|
||||
if (imm.value == 0) {
|
||||
fmov(reg, ZR);
|
||||
return;
|
||||
}
|
||||
int poolIndex = objectPool.getObject(UnboxedDoubleConstant(v));
|
||||
fldr(
|
||||
reg,
|
||||
address(poolPointerReg, vmOffsets.ObjectPool_elementOffset(poolIndex)),
|
||||
);
|
||||
}
|
||||
|
||||
bool canEncodeImm12(int value) =>
|
||||
_isUint(12, value) || (value & 0xfff == 0 && _isUint(12, value >> 12));
|
||||
|
||||
@@ -1900,6 +1919,56 @@ final class Arm64Assembler extends Assembler with Uint32OutputBuffer {
|
||||
(srcSize.is64 ? B31 : 0),
|
||||
);
|
||||
}
|
||||
|
||||
void fmov(FPRegister rd, Operand o, [OperandSize sz = OperandSize.s64]) {
|
||||
assert(sz.is16or32or64);
|
||||
switch (o) {
|
||||
case FPRegister():
|
||||
emit(
|
||||
B14 |
|
||||
B21 |
|
||||
B25 |
|
||||
B26 |
|
||||
B27 |
|
||||
B28 |
|
||||
rd.encodingRd |
|
||||
o.encodingRn |
|
||||
(sz.is64 ? B22 : (sz.is32 ? 0 : (B22 | B23))),
|
||||
);
|
||||
break;
|
||||
case Register():
|
||||
emit(
|
||||
B16 |
|
||||
B17 |
|
||||
B18 |
|
||||
B21 |
|
||||
B25 |
|
||||
B26 |
|
||||
B27 |
|
||||
B28 |
|
||||
rd.encodingRd |
|
||||
o.encodingRn() |
|
||||
(sz.is64 ? B22 : (sz.is32 ? 0 : (B22 | B23))) |
|
||||
(sz.is64 ? B31 : 0),
|
||||
);
|
||||
break;
|
||||
case Immediate():
|
||||
emit(
|
||||
B12 |
|
||||
B21 |
|
||||
B25 |
|
||||
B26 |
|
||||
B27 |
|
||||
B28 |
|
||||
rd.encodingRd |
|
||||
(o.encodingFpImm(sz) << 13) |
|
||||
(sz.is64 ? B22 : (sz.is32 ? 0 : (B22 | B23))),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw 'Unexpect operand ${o.runtimeType}';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool _isUint(int numBits, int value) => (value >>> numBits) == 0;
|
||||
@@ -1930,6 +1999,7 @@ extension on Register {
|
||||
extension on FPRegister {
|
||||
int get encodingRd => index;
|
||||
int get encodingRt => index;
|
||||
int get encodingRn => index << 5;
|
||||
}
|
||||
|
||||
extension on Immediate {
|
||||
@@ -2075,6 +2145,30 @@ extension on Immediate {
|
||||
((value >>> 32) & 0x00000000ffffffff) + (value & 0x00000000ffffffff);
|
||||
return value;
|
||||
}
|
||||
|
||||
int encodingFpImm(OperandSize sz) =>
|
||||
tryEncodingFpImm(sz) ??
|
||||
(throw 'Immediate $value cannot be encoded as FP immediate');
|
||||
|
||||
int? tryEncodingFpImm(OperandSize sz) {
|
||||
if (!sz.is64) {
|
||||
throw 'Unimplemented FP immediates of size $sz';
|
||||
}
|
||||
// value: aBbbbbbb bbcdefgh 00000000 00000000 00000000 00000000 00000000 00000000
|
||||
// encoded as: abcdefgh (where B = NOT(b)).
|
||||
final int value = this.value;
|
||||
if (value & ((1 << 48) - 1) != 0) {
|
||||
return null;
|
||||
}
|
||||
final int bitB = (value >> 62) & 0x1;
|
||||
const int maskb = 0xff << 54;
|
||||
if ((value & maskb) != ((bitB - 1) & maskb)) {
|
||||
return null;
|
||||
}
|
||||
return (((value >> 63) & 0x1) << 7) |
|
||||
((((~value) >> 62) & 0x1) << 6) |
|
||||
((value >> 48) & 0x3f);
|
||||
}
|
||||
}
|
||||
|
||||
extension on ExtRegOperand {
|
||||
|
||||
@@ -1892,6 +1892,17 @@ final class Arm64CodeGenerator extends CodeGenerator {
|
||||
case Register():
|
||||
_asm.ldr(to, _asm.address(FP, stackFrame.offsetFromFP(from)));
|
||||
return;
|
||||
case FPRegister():
|
||||
_asm.fldr(to, _asm.address(FP, stackFrame.offsetFromFP(from)));
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
case FPRegister():
|
||||
switch (to) {
|
||||
case StackLocation():
|
||||
_asm.fstr(from, _asm.address(FP, stackFrame.offsetFromFP(to)));
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1905,13 +1916,19 @@ final class Arm64CodeGenerator extends CodeGenerator {
|
||||
|
||||
@override
|
||||
void generateLoadConstant(ConstantValue value, Location to) {
|
||||
if (to is Register) {
|
||||
_asm.loadConstant(to, value);
|
||||
return;
|
||||
switch (to) {
|
||||
case Register():
|
||||
_asm.loadConstant(to, value);
|
||||
return;
|
||||
case FPRegister():
|
||||
assert(value.isDouble && value.isUnboxed);
|
||||
_asm.loadDoubleImmediate(to, value.doubleValue);
|
||||
return;
|
||||
case StackLocation():
|
||||
_asm.loadConstant(tempReg, value);
|
||||
_asm.str(tempReg, _asm.address(FP, stackFrame.offsetFromFP(to)));
|
||||
return;
|
||||
}
|
||||
_asm.unimplemented(
|
||||
'Unimplemented: code generation for generateLoadConstant',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -163,6 +163,9 @@ abstract base class Assembler {
|
||||
/// Load arbitrary integer [value] into register.
|
||||
void loadImmediate(Register reg, int value);
|
||||
|
||||
/// Load arbitrary double [value] into a floating-point register.
|
||||
void loadDoubleImmediate(FPRegister reg, double v);
|
||||
|
||||
/// [dst] = [src] + arbitrary integer [value].
|
||||
void addImmediate(
|
||||
Register dst,
|
||||
|
||||
@@ -210,13 +210,33 @@ abstract base class CodeGenerator extends Pass
|
||||
void visitParallelMove(ParallelMove instr) {
|
||||
// TODO: merge subsequent ParallelMove instructions.
|
||||
final map = <Location, Location>{};
|
||||
Set<Location>? overwritten;
|
||||
for (final move in instr.moves) {
|
||||
if (move is Move) {
|
||||
final from = move.from.physicalLocation;
|
||||
final to = move.to.physicalLocation;
|
||||
assert(!(overwritten?.contains(from) ?? false));
|
||||
if (from != to) {
|
||||
assert(!map.containsKey(from));
|
||||
map[from] = to;
|
||||
if (to is StackLocation) {
|
||||
// Moves into spill slots cannot participate in cycles.
|
||||
// Generate them eagerly and do not put them into the map
|
||||
// as they may have the same source as register/register moves.
|
||||
assert(!map.containsKey(to));
|
||||
assert(() {
|
||||
(overwritten ??= {}).add(to);
|
||||
return true;
|
||||
}());
|
||||
if (from is StackLocation) {
|
||||
final temp = getMoveTempRegister(RegisterClass.cpu);
|
||||
generateMove(from, temp);
|
||||
generateMove(temp, to);
|
||||
} else {
|
||||
generateMove(from, to);
|
||||
}
|
||||
} else {
|
||||
assert(!map.containsKey(from));
|
||||
map[from] = to;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1775,10 +1775,7 @@ class SnapshotStreamWriter {
|
||||
}
|
||||
|
||||
void writeDouble(double value) {
|
||||
final buf = ByteData(8);
|
||||
buf.setFloat64(0, value, Endian.little);
|
||||
final intValue = buf.getInt64(0, Endian.little);
|
||||
writeInt(intValue);
|
||||
writeInt(doubleToIntBits(value));
|
||||
}
|
||||
|
||||
ByteData _bufferAt(int offset) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:cfg/ir/constant_value.dart';
|
||||
import 'package:cfg/utils/misc.dart';
|
||||
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';
|
||||
@@ -259,6 +260,20 @@ void main() {
|
||||
'mov r5, 0xff00ff00ff00ff00\n',
|
||||
);
|
||||
});
|
||||
test('loadDoubleImmediate', () {
|
||||
asm.loadDoubleImmediate(V0, 1.0);
|
||||
asm.loadDoubleImmediate(V31, -3.5);
|
||||
asm.loadDoubleImmediate(V1, 0.0);
|
||||
asm.loadDoubleImmediate(V2, 0.123456789);
|
||||
asm.loadDoubleImmediate(V3, double.nan);
|
||||
expectDisassembly(
|
||||
'fmovd v0, 1.0\n'
|
||||
'fmovd v31, -3.5\n'
|
||||
'fmovdr v1, zr\n'
|
||||
'fldrd v2, [pp, #${objectPoolBase}]\n'
|
||||
'fldrd v3, [pp, #${objectPoolBase + 8}]\n',
|
||||
);
|
||||
});
|
||||
test('addImmediate', () {
|
||||
asm.addImmediate(R0, R0, 0);
|
||||
asm.addImmediate(R1, R2, 0);
|
||||
@@ -1645,5 +1660,28 @@ void main() {
|
||||
asm.scvtf(V0, SP);
|
||||
});
|
||||
});
|
||||
|
||||
test('fmov', () {
|
||||
asm.fmov(V0, R0);
|
||||
asm.fmov(V2, ZR);
|
||||
asm.fmov(V3, R2, .s32);
|
||||
asm.fmov(V0, Immediate(doubleToIntBits(1.0)));
|
||||
asm.fmov(V1, Immediate(doubleToIntBits(2.0)));
|
||||
asm.fmov(V31, Immediate(doubleToIntBits(-0.25)));
|
||||
expectDisassembly(
|
||||
'fmovdr v0, r0\n'
|
||||
'fmovdr v2, zr\n'
|
||||
'fmovsrw v3, r2\n'
|
||||
'fmovd v0, 1.0\n'
|
||||
'fmovd v1, 2.0\n'
|
||||
'fmovd v31, -0.25\n',
|
||||
);
|
||||
expectThrows(() {
|
||||
asm.fmov(V0, Immediate(doubleToIntBits(0.0)));
|
||||
});
|
||||
expectThrows(() {
|
||||
asm.fmov(V1, Immediate(doubleToIntBits(1.23456789)));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:cfg/utils/misc.dart';
|
||||
|
||||
// --- Constants from runtime/vm/constants_arm64.h ---
|
||||
|
||||
// enum Register
|
||||
@@ -691,22 +693,12 @@ extension type Instr(int value) {
|
||||
int imm8Field() => bits(13, 8); // kImm8Shift is not defined
|
||||
|
||||
static int vfpExpandImm(int imm8) {
|
||||
int sign = (imm8 >> 7) & 0x1;
|
||||
int exp = (imm8 >> 4) & 0x7;
|
||||
int frac = imm8 & 0xf;
|
||||
|
||||
if ((exp & 0x4) == 0) {
|
||||
exp = (0x2 | (exp & 0x1)) << 1;
|
||||
if ((exp & 0x2) == 0) {
|
||||
exp = ((exp & 0x1) ^ 0x1);
|
||||
} else {
|
||||
exp = ((exp & 0x1) | 0x2);
|
||||
}
|
||||
exp = (0x3ff ^ 0x7) | (exp << 2);
|
||||
} else {
|
||||
exp = 0x3ff;
|
||||
}
|
||||
return (sign << 63) | (exp << 52) | (frac << 48);
|
||||
int sign = ((imm8 & 0x80) >> 7) << 63;
|
||||
int expHigh = (((~imm8) & 0x40) >> 6) << 62;
|
||||
int expMid = (((imm8 & 0x40) >> 6) == 0) ? 0 : (0xff << 54);
|
||||
int expLow = ((imm8 & 0x30) >> 4) << 52;
|
||||
int frac = (imm8 & 0x0f) << 48;
|
||||
return sign | expHigh | expMid | expLow | frac;
|
||||
}
|
||||
|
||||
int immLogical() {
|
||||
@@ -1047,9 +1039,7 @@ class ARM64Decoder {
|
||||
return 5;
|
||||
} else if (option.startsWith('immd')) {
|
||||
final imm = Instr.vfpExpandImm(instr.imm8Field());
|
||||
final d = ByteData(8)
|
||||
..setInt64(0, imm, Endian.host)
|
||||
..getFloat64(0, Endian.host);
|
||||
final d = intBitsToDouble(imm);
|
||||
print(d.toString());
|
||||
return 4;
|
||||
} else if (option.startsWith('immr')) {
|
||||
|
||||
Reference in New Issue
Block a user