Skip to content

Commit 1d91605

Browse files
rmacnak-googleCommit Queue
authored andcommitted
[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: #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>
1 parent fefd2dc commit 1d91605

7 files changed

Lines changed: 230 additions & 51 deletions

File tree

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// VMOptions=--optimization_counter_threshold=10 --no-background-compilation
6+
7+
import "package:expect/expect.dart";
8+
import 'dart:typed_data';
9+
10+
const large = 1 << 25;
11+
12+
@pragma("vm:never-inline")
13+
@pragma("vm:entry-point")
14+
testArray(List array) {
15+
array[large]++;
16+
}
17+
18+
@pragma("vm:never-inline")
19+
@pragma("vm:entry-point")
20+
testUint8Clamped(Uint8ClampedList array) {
21+
array[large]++;
22+
}
23+
24+
@pragma("vm:never-inline")
25+
@pragma("vm:entry-point")
26+
testUint8(Uint8List array) {
27+
array[large]++;
28+
}
29+
30+
@pragma("vm:never-inline")
31+
@pragma("vm:entry-point")
32+
testUint16(Uint16List array) {
33+
array[large]++;
34+
}
35+
36+
@pragma("vm:never-inline")
37+
@pragma("vm:entry-point")
38+
testUint32(Uint32List array) {
39+
array[large]++;
40+
}
41+
42+
@pragma("vm:never-inline")
43+
@pragma("vm:entry-point")
44+
testUint64(Uint64List array) {
45+
array[large]++;
46+
}
47+
48+
@pragma("vm:never-inline")
49+
@pragma("vm:entry-point")
50+
testInt8(Int8List array) {
51+
array[large]++;
52+
}
53+
54+
@pragma("vm:never-inline")
55+
@pragma("vm:entry-point")
56+
testInt16(Int16List array) {
57+
array[large]++;
58+
}
59+
60+
@pragma("vm:never-inline")
61+
@pragma("vm:entry-point")
62+
testInt32(Int32List array) {
63+
array[large]++;
64+
}
65+
66+
@pragma("vm:never-inline")
67+
@pragma("vm:entry-point")
68+
testInt64(Int64List array) {
69+
array[large]++;
70+
}
71+
72+
@pragma("vm:never-inline")
73+
@pragma("vm:entry-point")
74+
testFloat32(Float32List array) {
75+
array[large]++;
76+
}
77+
78+
@pragma("vm:never-inline")
79+
@pragma("vm:entry-point")
80+
testFloat64(Float64List array) {
81+
array[large]++;
82+
}
83+
84+
main() {
85+
var x;
86+
for (var i = 0; i < 20; i++) {
87+
x = new List.filled(large + 1, 0);
88+
testArray(x);
89+
Expect.equals(x[large], 1);
90+
91+
x = new Uint8ClampedList(large + 1);
92+
testUint8Clamped(x);
93+
Expect.equals(x[large], 1);
94+
95+
x = new Uint8List(large + 1);
96+
testUint8(x);
97+
Expect.equals(x[large], 1);
98+
x = new Uint16List(large + 1);
99+
testUint16(x);
100+
Expect.equals(x[large], 1);
101+
x = new Uint32List(large + 1);
102+
testUint32(x);
103+
Expect.equals(x[large], 1);
104+
x = new Uint64List(large + 1);
105+
testUint64(x);
106+
Expect.equals(x[large], 1);
107+
108+
x = new Int8List(large + 1);
109+
testInt8(x);
110+
Expect.equals(x[large], 1);
111+
x = new Int16List(large + 1);
112+
testInt16(x);
113+
Expect.equals(x[large], 1);
114+
x = new Int32List(large + 1);
115+
testInt32(x);
116+
Expect.equals(x[large], 1);
117+
x = new Int64List(large + 1);
118+
testInt64(x);
119+
Expect.equals(x[large], 1);
120+
121+
x = new Float32List(large + 1);
122+
testFloat32(x);
123+
Expect.equals(x[large], 1);
124+
x = new Float64List(large + 1);
125+
testFloat64(x);
126+
Expect.equals(x[large], 1);
127+
}
128+
}

runtime/vm/compiler/assembler/assembler_arm64.cc

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2171,11 +2171,7 @@ bool Assembler::AddressCanHoldConstantIndex(const Object& constant,
21712171
if (!IsSafeSmi(constant)) return false;
21722172
const int64_t index = target::SmiValue(constant);
21732173
const int64_t offset = index * index_scale + HeapDataOffset(is_external, cid);
2174-
if (!Utils::IsInt(32, offset)) {
2175-
return false;
2176-
}
2177-
return Address::CanHoldOffset(static_cast<int32_t>(offset), Address::Offset,
2178-
Address::OperandSizeFor(cid));
2174+
return Utils::IsInt(32, offset);
21792175
}
21802176

21812177
Address Assembler::ElementAddressForIntIndex(bool is_external,
@@ -2185,8 +2181,6 @@ Address Assembler::ElementAddressForIntIndex(bool is_external,
21852181
intptr_t index) const {
21862182
const int64_t offset = index * index_scale + HeapDataOffset(is_external, cid);
21872183
ASSERT(Utils::IsInt(32, offset));
2188-
const OperandSize size = Address::OperandSizeFor(cid);
2189-
ASSERT(Address::CanHoldOffset(offset, Address::Offset, size));
21902184
return Address(array, static_cast<int32_t>(offset));
21912185
}
21922186

runtime/vm/compiler/assembler/assembler_arm64.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1898,6 +1898,27 @@ class Assembler : public AssemblerBase {
18981898
LoadCompressedFieldFromOffset(dest, dest, offset);
18991899
}
19001900
#endif
1901+
void LoadS(VRegister dest, const Address& address) {
1902+
if (address.type() == Address::AddressType::Offset) {
1903+
LoadSFromOffset(dest, address.base(), address.offset());
1904+
} else {
1905+
fldrs(dest, address);
1906+
}
1907+
}
1908+
void LoadD(VRegister dest, const Address& address) {
1909+
if (address.type() == Address::AddressType::Offset) {
1910+
LoadDFromOffset(dest, address.base(), address.offset());
1911+
} else {
1912+
fldrd(dest, address);
1913+
}
1914+
}
1915+
void LoadQ(VRegister dest, const Address& address) {
1916+
if (address.type() == Address::AddressType::Offset) {
1917+
LoadQFromOffset(dest, address.base(), address.offset());
1918+
} else {
1919+
fldrq(dest, address);
1920+
}
1921+
}
19011922
void LoadSFromOffset(VRegister dest, Register base, int32_t offset);
19021923
void LoadDFromOffset(VRegister dest, Register base, int32_t offset);
19031924
void LoadDFieldFromOffset(VRegister dest, Register base, int32_t offset) {
@@ -1925,6 +1946,27 @@ class Assembler : public AssemblerBase {
19251946
int32_t offset,
19261947
OperandSize sz = kEightBytes);
19271948

1949+
void StoreS(VRegister src, const Address& address) {
1950+
if (address.type() == Address::AddressType::Offset) {
1951+
StoreSToOffset(src, address.base(), address.offset());
1952+
} else {
1953+
fstrs(src, address);
1954+
}
1955+
}
1956+
void StoreD(VRegister src, const Address& address) {
1957+
if (address.type() == Address::AddressType::Offset) {
1958+
StoreDToOffset(src, address.base(), address.offset());
1959+
} else {
1960+
fstrd(src, address);
1961+
}
1962+
}
1963+
void StoreQ(VRegister src, const Address& address) {
1964+
if (address.type() == Address::AddressType::Offset) {
1965+
StoreQToOffset(src, address.base(), address.offset());
1966+
} else {
1967+
fstrq(src, address);
1968+
}
1969+
}
19281970
void StoreSToOffset(VRegister src, Register base, int32_t offset);
19291971
void StoreDToOffset(VRegister src, Register base, int32_t offset);
19301972
void StoreDFieldToOffset(VRegister src, Register base, int32_t offset) {

runtime/vm/compiler/assembler/assembler_riscv.cc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5644,11 +5644,11 @@ bool Assembler::AddressCanHoldConstantIndex(const Object& constant,
56445644
if (!IsSafeSmi(constant)) return false;
56455645
const int64_t index = target::SmiValue(constant);
56465646
const int64_t offset = index * index_scale + HeapDataOffset(is_external, cid);
5647-
if (IsITypeImm(offset)) {
5648-
ASSERT(IsSTypeImm(offset));
5649-
return true;
5650-
}
5651-
return false;
5647+
#if XLEN >= 64
5648+
return Utils::IsInt(32, offset);
5649+
#else
5650+
return Utils::IsInt(32, offset) && Utils::IsInt(32, offset + 4);
5651+
#endif
56525652
}
56535653

56545654
Address Assembler::ElementAddressForIntIndex(bool is_external,

runtime/vm/compiler/assembler/assembler_riscv.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,6 +1332,12 @@ class Assembler : public MicroAssembler {
13321332
Register index,
13331333
ScaleFactor scale,
13341334
OperandSize sz = kWordBytes) override;
1335+
void LoadS(FRegister dest, const Address& address) {
1336+
LoadSFromOffset(dest, address.base(), address.offset());
1337+
}
1338+
void LoadD(FRegister dest, const Address& address) {
1339+
LoadDFromOffset(dest, address.base(), address.offset());
1340+
}
13351341
void LoadSFromOffset(FRegister dest, Register base, int32_t offset);
13361342
void LoadDFromOffset(FRegister dest, Register base, int32_t offset);
13371343
void LoadSFieldFromOffset(FRegister dest, Register base, int32_t offset) {
@@ -1351,6 +1357,12 @@ class Assembler : public MicroAssembler {
13511357
void StoreZero(const Address& address, Register temp = kNoRegister) {
13521358
Store(ZR, address);
13531359
}
1360+
void StoreS(FRegister src, const Address& address) {
1361+
StoreSToOffset(src, address.base(), address.offset());
1362+
}
1363+
void StoreD(FRegister src, const Address& address) {
1364+
StoreDToOffset(src, address.base(), address.offset());
1365+
}
13541366
void StoreSToOffset(FRegister src, Register base, int32_t offset);
13551367
void StoreSFieldToOffset(FRegister src, Register base, int32_t offset) {
13561368
StoreSToOffset(src, base, offset - kHeapObjectTag);

runtime/vm/compiler/backend/il_arm64.cc

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1960,19 +1960,19 @@ void LoadIndexedInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
19601960
ASSERT(representation() == Boxing::NativeRepresentation(rep));
19611961
if (RepresentationUtils::IsUnboxedInteger(rep)) {
19621962
const Register result = locs()->out(0).reg();
1963-
__ ldr(result, element_address, RepresentationUtils::OperandSize(rep));
1963+
__ Load(result, element_address, RepresentationUtils::OperandSize(rep));
19641964
} else if (RepresentationUtils::IsUnboxed(rep)) {
19651965
const VRegister result = locs()->out(0).fpu_reg();
19661966
if (rep == kUnboxedFloat) {
19671967
// Load single precision float.
1968-
__ fldrs(result, element_address);
1968+
__ LoadS(result, element_address);
19691969
} else if (rep == kUnboxedDouble) {
19701970
// Load double precision float.
1971-
__ fldrd(result, element_address);
1971+
__ LoadD(result, element_address);
19721972
} else {
19731973
ASSERT(rep == kUnboxedInt32x4 || rep == kUnboxedFloat32x4 ||
19741974
rep == kUnboxedFloat64x2);
1975-
__ fldrq(result, element_address);
1975+
__ LoadQ(result, element_address);
19761976
}
19771977
} else {
19781978
const Register result = locs()->out(0).reg();
@@ -2175,47 +2175,46 @@ void StoreIndexedInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
21752175
value = 0;
21762176
}
21772177
if (value == 0) {
2178-
__ str(ZR, element_address, compiler::kUnsignedByte);
2178+
__ Store(ZR, element_address, compiler::kUnsignedByte);
21792179
} else {
21802180
__ LoadImmediate(TMP, static_cast<int8_t>(value));
2181-
__ str(TMP, element_address, compiler::kUnsignedByte);
2181+
__ Store(TMP, element_address, compiler::kUnsignedByte);
21822182
}
21832183
} else {
21842184
const Register value = locs()->in(2).reg();
21852185
// Clamp to 0x00 or 0xFF respectively.
21862186
__ CompareImmediate(value, 0xFF);
21872187
__ csetm(TMP, GT); // TMP = value > 0xFF ? -1 : 0.
21882188
__ csel(TMP, value, TMP, LS); // TMP = value in range ? value : TMP.
2189-
__ str(TMP, element_address, compiler::kUnsignedByte);
2189+
__ Store(TMP, element_address, compiler::kUnsignedByte);
21902190
}
21912191
} else if (RepresentationUtils::IsUnboxedInteger(rep)) {
21922192
if (locs()->in(2).IsConstant()) {
21932193
ASSERT(locs()->in(2).constant_instruction()->HasZeroRepresentation());
2194-
__ str(ZR, element_address, RepresentationUtils::OperandSize(rep));
2194+
__ Store(ZR, element_address, RepresentationUtils::OperandSize(rep));
21952195
} else {
2196-
__ str(locs()->in(2).reg(), element_address,
2197-
RepresentationUtils::OperandSize(rep));
2196+
__ Store(locs()->in(2).reg(), element_address,
2197+
RepresentationUtils::OperandSize(rep));
21982198
}
21992199
} else if (RepresentationUtils::IsUnboxed(rep)) {
22002200
if (rep == kUnboxedFloat) {
22012201
if (locs()->in(2).IsConstant()) {
22022202
ASSERT(locs()->in(2).constant_instruction()->HasZeroRepresentation());
2203-
__ str(ZR, element_address, compiler::kFourBytes);
2203+
__ Store(ZR, element_address, compiler::kFourBytes);
22042204
} else {
2205-
__ fstrs(locs()->in(2).fpu_reg(), element_address);
2205+
__ StoreS(locs()->in(2).fpu_reg(), element_address);
22062206
}
22072207
} else if (rep == kUnboxedDouble) {
22082208
if (locs()->in(2).IsConstant()) {
22092209
ASSERT(locs()->in(2).constant_instruction()->HasZeroRepresentation());
2210-
__ str(ZR, element_address, compiler::kEightBytes);
2210+
__ Store(ZR, element_address, compiler::kEightBytes);
22112211
} else {
2212-
__ fstrd(locs()->in(2).fpu_reg(), element_address);
2212+
__ StoreD(locs()->in(2).fpu_reg(), element_address);
22132213
}
22142214
} else {
22152215
ASSERT(rep == kUnboxedInt32x4 || rep == kUnboxedFloat32x4 ||
22162216
rep == kUnboxedFloat64x2);
2217-
const VRegister value_reg = locs()->in(2).fpu_reg();
2218-
__ fstrq(value_reg, element_address);
2217+
__ StoreQ(locs()->in(2).fpu_reg(), element_address);
22192218
}
22202219
} else if (class_id() == kArrayCid) {
22212220
ASSERT(!ShouldEmitStoreBarrier()); // Specially treated above.

0 commit comments

Comments
 (0)