[vm, compiler] Handle 32-bit constant indices for Load/StoreIndexedInstr on ARM64 and RISC-V.
This extends the set of indices that are not processed by the register allocator to match X64. This avoids slowness in the register allocator for large list literals. For some cases, it also reduces code size. TEST=ci Bug: https://github.com/dart-lang/sdk/issues/62411 Change-Id: Iab71a6dea2f2f75b06c51e4b23626bc991e1a20f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/475720 Reviewed-by: Alexander Markov <alexmarkov@google.com> Commit-Queue: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
committed by
Commit Queue
parent
fefd2dc63f
commit
1d91605c25
@@ -0,0 +1,128 @@
|
||||
// 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.
|
||||
|
||||
// VMOptions=--optimization_counter_threshold=10 --no-background-compilation
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
import 'dart:typed_data';
|
||||
|
||||
const large = 1 << 25;
|
||||
|
||||
@pragma("vm:never-inline")
|
||||
@pragma("vm:entry-point")
|
||||
testArray(List array) {
|
||||
array[large]++;
|
||||
}
|
||||
|
||||
@pragma("vm:never-inline")
|
||||
@pragma("vm:entry-point")
|
||||
testUint8Clamped(Uint8ClampedList array) {
|
||||
array[large]++;
|
||||
}
|
||||
|
||||
@pragma("vm:never-inline")
|
||||
@pragma("vm:entry-point")
|
||||
testUint8(Uint8List array) {
|
||||
array[large]++;
|
||||
}
|
||||
|
||||
@pragma("vm:never-inline")
|
||||
@pragma("vm:entry-point")
|
||||
testUint16(Uint16List array) {
|
||||
array[large]++;
|
||||
}
|
||||
|
||||
@pragma("vm:never-inline")
|
||||
@pragma("vm:entry-point")
|
||||
testUint32(Uint32List array) {
|
||||
array[large]++;
|
||||
}
|
||||
|
||||
@pragma("vm:never-inline")
|
||||
@pragma("vm:entry-point")
|
||||
testUint64(Uint64List array) {
|
||||
array[large]++;
|
||||
}
|
||||
|
||||
@pragma("vm:never-inline")
|
||||
@pragma("vm:entry-point")
|
||||
testInt8(Int8List array) {
|
||||
array[large]++;
|
||||
}
|
||||
|
||||
@pragma("vm:never-inline")
|
||||
@pragma("vm:entry-point")
|
||||
testInt16(Int16List array) {
|
||||
array[large]++;
|
||||
}
|
||||
|
||||
@pragma("vm:never-inline")
|
||||
@pragma("vm:entry-point")
|
||||
testInt32(Int32List array) {
|
||||
array[large]++;
|
||||
}
|
||||
|
||||
@pragma("vm:never-inline")
|
||||
@pragma("vm:entry-point")
|
||||
testInt64(Int64List array) {
|
||||
array[large]++;
|
||||
}
|
||||
|
||||
@pragma("vm:never-inline")
|
||||
@pragma("vm:entry-point")
|
||||
testFloat32(Float32List array) {
|
||||
array[large]++;
|
||||
}
|
||||
|
||||
@pragma("vm:never-inline")
|
||||
@pragma("vm:entry-point")
|
||||
testFloat64(Float64List array) {
|
||||
array[large]++;
|
||||
}
|
||||
|
||||
main() {
|
||||
var x;
|
||||
for (var i = 0; i < 20; i++) {
|
||||
x = new List.filled(large + 1, 0);
|
||||
testArray(x);
|
||||
Expect.equals(x[large], 1);
|
||||
|
||||
x = new Uint8ClampedList(large + 1);
|
||||
testUint8Clamped(x);
|
||||
Expect.equals(x[large], 1);
|
||||
|
||||
x = new Uint8List(large + 1);
|
||||
testUint8(x);
|
||||
Expect.equals(x[large], 1);
|
||||
x = new Uint16List(large + 1);
|
||||
testUint16(x);
|
||||
Expect.equals(x[large], 1);
|
||||
x = new Uint32List(large + 1);
|
||||
testUint32(x);
|
||||
Expect.equals(x[large], 1);
|
||||
x = new Uint64List(large + 1);
|
||||
testUint64(x);
|
||||
Expect.equals(x[large], 1);
|
||||
|
||||
x = new Int8List(large + 1);
|
||||
testInt8(x);
|
||||
Expect.equals(x[large], 1);
|
||||
x = new Int16List(large + 1);
|
||||
testInt16(x);
|
||||
Expect.equals(x[large], 1);
|
||||
x = new Int32List(large + 1);
|
||||
testInt32(x);
|
||||
Expect.equals(x[large], 1);
|
||||
x = new Int64List(large + 1);
|
||||
testInt64(x);
|
||||
Expect.equals(x[large], 1);
|
||||
|
||||
x = new Float32List(large + 1);
|
||||
testFloat32(x);
|
||||
Expect.equals(x[large], 1);
|
||||
x = new Float64List(large + 1);
|
||||
testFloat64(x);
|
||||
Expect.equals(x[large], 1);
|
||||
}
|
||||
}
|
||||
@@ -2171,11 +2171,7 @@ bool Assembler::AddressCanHoldConstantIndex(const Object& constant,
|
||||
if (!IsSafeSmi(constant)) return false;
|
||||
const int64_t index = target::SmiValue(constant);
|
||||
const int64_t offset = index * index_scale + HeapDataOffset(is_external, cid);
|
||||
if (!Utils::IsInt(32, offset)) {
|
||||
return false;
|
||||
}
|
||||
return Address::CanHoldOffset(static_cast<int32_t>(offset), Address::Offset,
|
||||
Address::OperandSizeFor(cid));
|
||||
return Utils::IsInt(32, offset);
|
||||
}
|
||||
|
||||
Address Assembler::ElementAddressForIntIndex(bool is_external,
|
||||
@@ -2185,8 +2181,6 @@ Address Assembler::ElementAddressForIntIndex(bool is_external,
|
||||
intptr_t index) const {
|
||||
const int64_t offset = index * index_scale + HeapDataOffset(is_external, cid);
|
||||
ASSERT(Utils::IsInt(32, offset));
|
||||
const OperandSize size = Address::OperandSizeFor(cid);
|
||||
ASSERT(Address::CanHoldOffset(offset, Address::Offset, size));
|
||||
return Address(array, static_cast<int32_t>(offset));
|
||||
}
|
||||
|
||||
|
||||
@@ -1898,6 +1898,27 @@ class Assembler : public AssemblerBase {
|
||||
LoadCompressedFieldFromOffset(dest, dest, offset);
|
||||
}
|
||||
#endif
|
||||
void LoadS(VRegister dest, const Address& address) {
|
||||
if (address.type() == Address::AddressType::Offset) {
|
||||
LoadSFromOffset(dest, address.base(), address.offset());
|
||||
} else {
|
||||
fldrs(dest, address);
|
||||
}
|
||||
}
|
||||
void LoadD(VRegister dest, const Address& address) {
|
||||
if (address.type() == Address::AddressType::Offset) {
|
||||
LoadDFromOffset(dest, address.base(), address.offset());
|
||||
} else {
|
||||
fldrd(dest, address);
|
||||
}
|
||||
}
|
||||
void LoadQ(VRegister dest, const Address& address) {
|
||||
if (address.type() == Address::AddressType::Offset) {
|
||||
LoadQFromOffset(dest, address.base(), address.offset());
|
||||
} else {
|
||||
fldrq(dest, address);
|
||||
}
|
||||
}
|
||||
void LoadSFromOffset(VRegister dest, Register base, int32_t offset);
|
||||
void LoadDFromOffset(VRegister dest, Register base, int32_t offset);
|
||||
void LoadDFieldFromOffset(VRegister dest, Register base, int32_t offset) {
|
||||
@@ -1925,6 +1946,27 @@ class Assembler : public AssemblerBase {
|
||||
int32_t offset,
|
||||
OperandSize sz = kEightBytes);
|
||||
|
||||
void StoreS(VRegister src, const Address& address) {
|
||||
if (address.type() == Address::AddressType::Offset) {
|
||||
StoreSToOffset(src, address.base(), address.offset());
|
||||
} else {
|
||||
fstrs(src, address);
|
||||
}
|
||||
}
|
||||
void StoreD(VRegister src, const Address& address) {
|
||||
if (address.type() == Address::AddressType::Offset) {
|
||||
StoreDToOffset(src, address.base(), address.offset());
|
||||
} else {
|
||||
fstrd(src, address);
|
||||
}
|
||||
}
|
||||
void StoreQ(VRegister src, const Address& address) {
|
||||
if (address.type() == Address::AddressType::Offset) {
|
||||
StoreQToOffset(src, address.base(), address.offset());
|
||||
} else {
|
||||
fstrq(src, address);
|
||||
}
|
||||
}
|
||||
void StoreSToOffset(VRegister src, Register base, int32_t offset);
|
||||
void StoreDToOffset(VRegister src, Register base, int32_t offset);
|
||||
void StoreDFieldToOffset(VRegister src, Register base, int32_t offset) {
|
||||
|
||||
@@ -5644,11 +5644,11 @@ bool Assembler::AddressCanHoldConstantIndex(const Object& constant,
|
||||
if (!IsSafeSmi(constant)) return false;
|
||||
const int64_t index = target::SmiValue(constant);
|
||||
const int64_t offset = index * index_scale + HeapDataOffset(is_external, cid);
|
||||
if (IsITypeImm(offset)) {
|
||||
ASSERT(IsSTypeImm(offset));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
#if XLEN >= 64
|
||||
return Utils::IsInt(32, offset);
|
||||
#else
|
||||
return Utils::IsInt(32, offset) && Utils::IsInt(32, offset + 4);
|
||||
#endif
|
||||
}
|
||||
|
||||
Address Assembler::ElementAddressForIntIndex(bool is_external,
|
||||
|
||||
@@ -1332,6 +1332,12 @@ class Assembler : public MicroAssembler {
|
||||
Register index,
|
||||
ScaleFactor scale,
|
||||
OperandSize sz = kWordBytes) override;
|
||||
void LoadS(FRegister dest, const Address& address) {
|
||||
LoadSFromOffset(dest, address.base(), address.offset());
|
||||
}
|
||||
void LoadD(FRegister dest, const Address& address) {
|
||||
LoadDFromOffset(dest, address.base(), address.offset());
|
||||
}
|
||||
void LoadSFromOffset(FRegister dest, Register base, int32_t offset);
|
||||
void LoadDFromOffset(FRegister dest, Register base, int32_t offset);
|
||||
void LoadSFieldFromOffset(FRegister dest, Register base, int32_t offset) {
|
||||
@@ -1351,6 +1357,12 @@ class Assembler : public MicroAssembler {
|
||||
void StoreZero(const Address& address, Register temp = kNoRegister) {
|
||||
Store(ZR, address);
|
||||
}
|
||||
void StoreS(FRegister src, const Address& address) {
|
||||
StoreSToOffset(src, address.base(), address.offset());
|
||||
}
|
||||
void StoreD(FRegister src, const Address& address) {
|
||||
StoreDToOffset(src, address.base(), address.offset());
|
||||
}
|
||||
void StoreSToOffset(FRegister src, Register base, int32_t offset);
|
||||
void StoreSFieldToOffset(FRegister src, Register base, int32_t offset) {
|
||||
StoreSToOffset(src, base, offset - kHeapObjectTag);
|
||||
|
||||
@@ -1960,19 +1960,19 @@ void LoadIndexedInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
ASSERT(representation() == Boxing::NativeRepresentation(rep));
|
||||
if (RepresentationUtils::IsUnboxedInteger(rep)) {
|
||||
const Register result = locs()->out(0).reg();
|
||||
__ ldr(result, element_address, RepresentationUtils::OperandSize(rep));
|
||||
__ Load(result, element_address, RepresentationUtils::OperandSize(rep));
|
||||
} else if (RepresentationUtils::IsUnboxed(rep)) {
|
||||
const VRegister result = locs()->out(0).fpu_reg();
|
||||
if (rep == kUnboxedFloat) {
|
||||
// Load single precision float.
|
||||
__ fldrs(result, element_address);
|
||||
__ LoadS(result, element_address);
|
||||
} else if (rep == kUnboxedDouble) {
|
||||
// Load double precision float.
|
||||
__ fldrd(result, element_address);
|
||||
__ LoadD(result, element_address);
|
||||
} else {
|
||||
ASSERT(rep == kUnboxedInt32x4 || rep == kUnboxedFloat32x4 ||
|
||||
rep == kUnboxedFloat64x2);
|
||||
__ fldrq(result, element_address);
|
||||
__ LoadQ(result, element_address);
|
||||
}
|
||||
} else {
|
||||
const Register result = locs()->out(0).reg();
|
||||
@@ -2175,10 +2175,10 @@ void StoreIndexedInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
value = 0;
|
||||
}
|
||||
if (value == 0) {
|
||||
__ str(ZR, element_address, compiler::kUnsignedByte);
|
||||
__ Store(ZR, element_address, compiler::kUnsignedByte);
|
||||
} else {
|
||||
__ LoadImmediate(TMP, static_cast<int8_t>(value));
|
||||
__ str(TMP, element_address, compiler::kUnsignedByte);
|
||||
__ Store(TMP, element_address, compiler::kUnsignedByte);
|
||||
}
|
||||
} else {
|
||||
const Register value = locs()->in(2).reg();
|
||||
@@ -2186,36 +2186,35 @@ void StoreIndexedInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
__ CompareImmediate(value, 0xFF);
|
||||
__ csetm(TMP, GT); // TMP = value > 0xFF ? -1 : 0.
|
||||
__ csel(TMP, value, TMP, LS); // TMP = value in range ? value : TMP.
|
||||
__ str(TMP, element_address, compiler::kUnsignedByte);
|
||||
__ Store(TMP, element_address, compiler::kUnsignedByte);
|
||||
}
|
||||
} else if (RepresentationUtils::IsUnboxedInteger(rep)) {
|
||||
if (locs()->in(2).IsConstant()) {
|
||||
ASSERT(locs()->in(2).constant_instruction()->HasZeroRepresentation());
|
||||
__ str(ZR, element_address, RepresentationUtils::OperandSize(rep));
|
||||
__ Store(ZR, element_address, RepresentationUtils::OperandSize(rep));
|
||||
} else {
|
||||
__ str(locs()->in(2).reg(), element_address,
|
||||
RepresentationUtils::OperandSize(rep));
|
||||
__ Store(locs()->in(2).reg(), element_address,
|
||||
RepresentationUtils::OperandSize(rep));
|
||||
}
|
||||
} else if (RepresentationUtils::IsUnboxed(rep)) {
|
||||
if (rep == kUnboxedFloat) {
|
||||
if (locs()->in(2).IsConstant()) {
|
||||
ASSERT(locs()->in(2).constant_instruction()->HasZeroRepresentation());
|
||||
__ str(ZR, element_address, compiler::kFourBytes);
|
||||
__ Store(ZR, element_address, compiler::kFourBytes);
|
||||
} else {
|
||||
__ fstrs(locs()->in(2).fpu_reg(), element_address);
|
||||
__ StoreS(locs()->in(2).fpu_reg(), element_address);
|
||||
}
|
||||
} else if (rep == kUnboxedDouble) {
|
||||
if (locs()->in(2).IsConstant()) {
|
||||
ASSERT(locs()->in(2).constant_instruction()->HasZeroRepresentation());
|
||||
__ str(ZR, element_address, compiler::kEightBytes);
|
||||
__ Store(ZR, element_address, compiler::kEightBytes);
|
||||
} else {
|
||||
__ fstrd(locs()->in(2).fpu_reg(), element_address);
|
||||
__ StoreD(locs()->in(2).fpu_reg(), element_address);
|
||||
}
|
||||
} else {
|
||||
ASSERT(rep == kUnboxedInt32x4 || rep == kUnboxedFloat32x4 ||
|
||||
rep == kUnboxedFloat64x2);
|
||||
const VRegister value_reg = locs()->in(2).fpu_reg();
|
||||
__ fstrq(value_reg, element_address);
|
||||
__ StoreQ(locs()->in(2).fpu_reg(), element_address);
|
||||
}
|
||||
} else if (class_id() == kArrayCid) {
|
||||
ASSERT(!ShouldEmitStoreBarrier()); // Specially treated above.
|
||||
|
||||
@@ -2038,9 +2038,11 @@ void LoadIndexedInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
PairLocation* result_pair = locs()->out(0).AsPairLocation();
|
||||
const Register result_lo = result_pair->At(0).reg();
|
||||
const Register result_hi = result_pair->At(1).reg();
|
||||
__ lw(result_lo, element_address);
|
||||
__ lw(result_hi, compiler::Address(element_address.base(),
|
||||
element_address.offset() + 4));
|
||||
__ Load(result_lo, element_address, compiler::kFourBytes);
|
||||
__ Load(result_hi,
|
||||
compiler::Address(element_address.base(),
|
||||
element_address.offset() + 4),
|
||||
compiler::kFourBytes);
|
||||
} else {
|
||||
const Register result = locs()->out(0).reg();
|
||||
__ Load(result, element_address, RepresentationUtils::OperandSize(rep));
|
||||
@@ -2053,10 +2055,10 @@ void LoadIndexedInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
const FRegister result = locs()->out(0).fpu_reg();
|
||||
if (rep == kUnboxedFloat) {
|
||||
// Load single precision float.
|
||||
__ flw(result, element_address);
|
||||
__ LoadS(result, element_address);
|
||||
} else if (rep == kUnboxedDouble) {
|
||||
// Load double precision float.
|
||||
__ fld(result, element_address);
|
||||
__ LoadD(result, element_address);
|
||||
} else {
|
||||
ASSERT(rep == kUnboxedInt32x4 || rep == kUnboxedFloat32x4 ||
|
||||
rep == kUnboxedFloat64x2);
|
||||
@@ -2067,7 +2069,7 @@ void LoadIndexedInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
ASSERT((class_id() == kArrayCid) || (class_id() == kImmutableArrayCid) ||
|
||||
(class_id() == kTypeArgumentsCid) || (class_id() == kRecordCid));
|
||||
const Register result = locs()->out(0).reg();
|
||||
__ lx(result, element_address);
|
||||
__ Load(result, element_address);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2329,10 +2331,10 @@ void StoreIndexedInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
value = 0;
|
||||
}
|
||||
if (value == 0) {
|
||||
__ sb(ZR, element_address);
|
||||
__ Store(ZR, element_address, compiler::kByte);
|
||||
} else {
|
||||
__ LoadImmediate(TMP, static_cast<int8_t>(value));
|
||||
__ sb(TMP, element_address);
|
||||
__ Store(TMP, element_address, compiler::kByte);
|
||||
}
|
||||
} else {
|
||||
const Register value = locs()->in(2).reg();
|
||||
@@ -2340,7 +2342,7 @@ void StoreIndexedInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
__ li(TMP, 255);
|
||||
__ min(TMP, TMP, value);
|
||||
__ max(TMP, TMP, ZR);
|
||||
__ sb(TMP, element_address);
|
||||
__ Store(TMP, element_address, compiler::kByte);
|
||||
} else {
|
||||
compiler::Label store_zero, store_ff, done;
|
||||
__ blt(value, ZR, &store_zero, compiler::Assembler::kNearJump);
|
||||
@@ -2348,14 +2350,14 @@ void StoreIndexedInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
__ li(TMP, 0xFF);
|
||||
__ bgt(value, TMP, &store_ff, compiler::Assembler::kNearJump);
|
||||
|
||||
__ sb(value, element_address);
|
||||
__ Store(value, element_address, compiler::kByte);
|
||||
__ j(&done, compiler::Assembler::kNearJump);
|
||||
|
||||
__ Bind(&store_zero);
|
||||
__ mv(TMP, ZR);
|
||||
|
||||
__ Bind(&store_ff);
|
||||
__ sb(TMP, element_address);
|
||||
__ Store(TMP, element_address, compiler::kByte);
|
||||
|
||||
__ Bind(&done);
|
||||
}
|
||||
@@ -2364,26 +2366,28 @@ void StoreIndexedInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
if (rep == kUnboxedUint8 || rep == kUnboxedInt8) {
|
||||
if (locs()->in(2).IsConstant()) {
|
||||
ASSERT(locs()->in(2).constant_instruction()->HasZeroRepresentation());
|
||||
__ sb(ZR, element_address);
|
||||
__ Store(ZR, element_address, compiler::kByte);
|
||||
} else {
|
||||
const Register value = locs()->in(2).reg();
|
||||
__ sb(value, element_address);
|
||||
__ Store(value, element_address, compiler::kByte);
|
||||
}
|
||||
} else if (rep == kUnboxedInt64) {
|
||||
#if XLEN >= 64
|
||||
if (locs()->in(2).IsConstant()) {
|
||||
ASSERT(locs()->in(2).constant_instruction()->HasZeroRepresentation());
|
||||
__ sd(ZR, element_address);
|
||||
__ Store(ZR, element_address, compiler::kEightBytes);
|
||||
} else {
|
||||
__ sd(locs()->in(2).reg(), element_address);
|
||||
__ Store(locs()->in(2).reg(), element_address, compiler::kEightBytes);
|
||||
}
|
||||
#else
|
||||
PairLocation* value_pair = locs()->in(2).AsPairLocation();
|
||||
Register value_lo = value_pair->At(0).reg();
|
||||
Register value_hi = value_pair->At(1).reg();
|
||||
__ sw(value_lo, element_address);
|
||||
__ sw(value_hi, compiler::Address(element_address.base(),
|
||||
element_address.offset() + 4));
|
||||
__ Store(value_lo, element_address, compiler::kFourBytes);
|
||||
__ Store(value_hi,
|
||||
compiler::Address(element_address.base(),
|
||||
element_address.offset() + 4),
|
||||
compiler::kFourBytes);
|
||||
#endif
|
||||
} else {
|
||||
if (locs()->in(2).IsConstant()) {
|
||||
@@ -2398,20 +2402,20 @@ void StoreIndexedInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
if (rep == kUnboxedFloat) {
|
||||
if (locs()->in(2).IsConstant()) {
|
||||
ASSERT(locs()->in(2).constant_instruction()->HasZeroRepresentation());
|
||||
__ sw(ZR, element_address);
|
||||
__ Store(ZR, element_address, compiler::kFourBytes);
|
||||
} else {
|
||||
__ fsw(locs()->in(2).fpu_reg(), element_address);
|
||||
__ StoreS(locs()->in(2).fpu_reg(), element_address);
|
||||
}
|
||||
} else if (rep == kUnboxedDouble) {
|
||||
#if XLEN >= 64
|
||||
if (locs()->in(2).IsConstant()) {
|
||||
ASSERT(locs()->in(2).constant_instruction()->HasZeroRepresentation());
|
||||
__ sd(ZR, element_address);
|
||||
__ Store(ZR, element_address, compiler::kEightBytes);
|
||||
} else {
|
||||
__ fsd(locs()->in(2).fpu_reg(), element_address);
|
||||
__ StoreD(locs()->in(2).fpu_reg(), element_address);
|
||||
}
|
||||
#else
|
||||
__ fsd(locs()->in(2).fpu_reg(), element_address);
|
||||
__ StoreD(locs()->in(2).fpu_reg(), element_address);
|
||||
#endif
|
||||
} else {
|
||||
ASSERT(rep == kUnboxedInt32x4 || rep == kUnboxedFloat32x4 ||
|
||||
|
||||
Reference in New Issue
Block a user