aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/internal
diff options
context:
space:
mode:
authorAlexander Musman <alexander.musman@gmail.com>2026-01-18 20:07:50 +0300
committerGopher Robot <gobot@golang.org>2026-02-27 12:41:17 -0800
commitf2633386e0d2f692b4363134626f472fc95b9858 (patch)
tree49ce347d022e16c584c3830484747baa1b864f14 /src/cmd/internal
parent62a1da372a2bfeb29644246ff0f61dc048bb912c (diff)
downloadgo-f2633386e0d2f692b4363134626f472fc95b9858.tar.xz
cmd/internal/obj: arm64 assembler support unshifted hi for large offsets
Extend splitImm24uScaled to support an unshifted hi value (hi <= 0xfff) in addition to the shifted hi value (hi & ^0xfff000 == 0). This allows load/store instructions to handle more offsets using ADD + load/store sequences instead of falling back to the literal pool. This will be used by a subsequent change to add FMOVQ support in SSA form. Change-Id: I78490f5b1a60d49c1d42ad4daefb5d4e6021c965 Reviewed-on: https://go-review.googlesource.com/c/go/+/737320 Reviewed-by: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@google.com> Auto-Submit: Keith Randall <khr@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com>
Diffstat (limited to 'src/cmd/internal')
-rw-r--r--src/cmd/internal/obj/arm64/asm7.go35
-rw-r--r--src/cmd/internal/obj/arm64/asm_test.go157
2 files changed, 170 insertions, 22 deletions
diff --git a/src/cmd/internal/obj/arm64/asm7.go b/src/cmd/internal/obj/arm64/asm7.go
index eedba28e3b..898977d62a 100644
--- a/src/cmd/internal/obj/arm64/asm7.go
+++ b/src/cmd/internal/obj/arm64/asm7.go
@@ -1392,10 +1392,8 @@ func roundUp(x, to uint32) uint32 {
return (x + to - 1) &^ (to - 1)
}
-// splitImm24uScaled splits an immediate into a scaled 12 bit unsigned lo value
-// and an unscaled shifted 12 bit unsigned hi value. These are typically used
-// by adding or subtracting the hi value and using the lo value as the offset
-// for a load or store.
+// splitImm24uScaled returns hi, lo such that v == hi + lo<<shift.
+// Always 0 <= lo <= 0xfff, and hi is either 0 <= hi <= 0xfff, or (hi&0xfff == 0 && 0 <= hi <= 0xfff000).
func splitImm24uScaled(v int32, shift int) (int32, int32, error) {
if v < 0 {
return 0, 0, fmt.Errorf("%d is not a 24 bit unsigned immediate", v)
@@ -1403,19 +1401,28 @@ func splitImm24uScaled(v int32, shift int) (int32, int32, error) {
if v > 0xfff000+0xfff<<shift {
return 0, 0, fmt.Errorf("%d is too large for a scaled 24 bit unsigned immediate", v)
}
- if v&((1<<shift)-1) != 0 {
- return 0, 0, fmt.Errorf("%d is not a multiple of %d", v, 1<<shift)
+
+ // Try hi <= 0xfff and lo <= 0xfff such that v = hi + (lo << shift).
+ hi := max(v-(0xfff<<shift), v&((1<<shift)-1))
+ if hi <= 0xfff {
+ lo := (v - hi) >> shift
+ if lo <= 0xfff {
+ return hi, lo, nil
+ }
}
+
+ // Try hi shifted left by 12 bits.
lo := (v >> shift) & 0xfff
- hi := v - (lo << shift)
+ hi = v - (lo << shift)
if hi > 0xfff000 {
hi = 0xfff000
lo = (v - hi) >> shift
}
- if hi & ^0xfff000 != 0 {
- panic(fmt.Sprintf("bad split for %x with shift %v (%x, %x)", v, shift, hi, lo))
+ if hi&^0xfff000 == 0 && hi+lo<<shift == v {
+ return hi, lo, nil
}
- return hi, lo, nil
+
+ return 0, 0, fmt.Errorf("%d cannot be split into valid hi/lo", v)
}
func (c *ctxt7) regoff(a *obj.Addr) int32 {
@@ -1969,28 +1976,28 @@ func (c *ctxt7) loadStoreClass(p *obj.Prog, lsc int, v int64) int {
if cmp(C_UAUTO8K, lsc) || cmp(C_UOREG8K, lsc) {
return lsc
}
- if v >= 0 && v <= 0xfff000+0xfff<<1 && v&1 == 0 {
+ if v >= 0 && v <= 0xfff000+0xfff<<1 && (v&1 == 0 || v <= 0xfff+0xfff<<1) {
needsPool = false
}
case AMOVW, AMOVWU, AFMOVS:
if cmp(C_UAUTO16K, lsc) || cmp(C_UOREG16K, lsc) {
return lsc
}
- if v >= 0 && v <= 0xfff000+0xfff<<2 && v&3 == 0 {
+ if v >= 0 && v <= 0xfff000+0xfff<<2 && (v&3 == 0 || v <= 0xfff+0xfff<<2) {
needsPool = false
}
case AMOVD, AFMOVD:
if cmp(C_UAUTO32K, lsc) || cmp(C_UOREG32K, lsc) {
return lsc
}
- if v >= 0 && v <= 0xfff000+0xfff<<3 && v&7 == 0 {
+ if v >= 0 && v <= 0xfff000+0xfff<<3 && (v&7 == 0 || v <= 0xfff+0xfff<<3) {
needsPool = false
}
case AFMOVQ:
if cmp(C_UAUTO64K, lsc) || cmp(C_UOREG64K, lsc) {
return lsc
}
- if v >= 0 && v <= 0xfff000+0xfff<<4 && v&15 == 0 {
+ if v >= 0 && v <= 0xfff000+0xfff<<4 && (v&15 == 0 || v <= 0xfff+0xfff<<4) {
needsPool = false
}
}
diff --git a/src/cmd/internal/obj/arm64/asm_test.go b/src/cmd/internal/obj/arm64/asm_test.go
index 6347bafc7e..bf00e4648b 100644
--- a/src/cmd/internal/obj/arm64/asm_test.go
+++ b/src/cmd/internal/obj/arm64/asm_test.go
@@ -46,8 +46,8 @@ func TestSplitImm24uScaled(t *testing.T) {
{
v: 0x1001,
shift: 0,
- wantHi: 0x1000,
- wantLo: 0x1,
+ wantHi: 0x2,
+ wantLo: 0xfff,
},
{
v: 0xffffff,
@@ -75,8 +75,8 @@ func TestSplitImm24uScaled(t *testing.T) {
{
v: 0x2002,
shift: 1,
- wantHi: 0x2000,
- wantLo: 0x1,
+ wantHi: 0x4,
+ wantLo: 0xfff,
},
{
v: 0xfffffe,
@@ -96,6 +96,11 @@ func TestSplitImm24uScaled(t *testing.T) {
wantErr: true,
},
{
+ v: 0x1000001,
+ shift: 1,
+ wantErr: true,
+ },
+ {
v: 0xfffffe,
shift: 2,
wantErr: true,
@@ -103,8 +108,8 @@ func TestSplitImm24uScaled(t *testing.T) {
{
v: 0x4004,
shift: 2,
- wantHi: 0x4000,
- wantLo: 0x1,
+ wantHi: 0x8,
+ wantLo: 0xfff,
},
{
v: 0xfffffc,
@@ -131,8 +136,8 @@ func TestSplitImm24uScaled(t *testing.T) {
{
v: 0x8008,
shift: 3,
- wantHi: 0x8000,
- wantLo: 0x1,
+ wantHi: 0x10,
+ wantLo: 0xfff,
},
{
v: 0xfffff8,
@@ -151,6 +156,127 @@ func TestSplitImm24uScaled(t *testing.T) {
shift: 3,
wantErr: true,
},
+ // Unshifted hi cases - hi <= 0xfff fits directly
+ {
+ v: 7,
+ shift: 3,
+ wantHi: 7,
+ wantLo: 0,
+ },
+ {
+ v: 0x8ff7,
+ shift: 3,
+ wantHi: 0xfff,
+ wantLo: 0xfff,
+ },
+ {
+ v: 0x7ff8,
+ shift: 3,
+ wantHi: 0,
+ wantLo: 0xfff,
+ },
+ {
+ v: 0xfff,
+ shift: 1,
+ wantHi: 1,
+ wantLo: 0x7ff,
+ },
+ {
+ v: 0xfff,
+ shift: 2,
+ wantHi: 3,
+ wantLo: 0x3ff,
+ },
+ {
+ v: 0xfff,
+ shift: 3,
+ wantHi: 7,
+ wantLo: 0x1ff,
+ },
+ {
+ v: 0x1ffe,
+ shift: 2,
+ wantHi: 2,
+ wantLo: 0x7ff,
+ },
+ {
+ v: 0x1ffe,
+ shift: 3,
+ wantHi: 6,
+ wantLo: 0x3ff,
+ },
+ {
+ v: 0x1fff,
+ shift: 1,
+ wantHi: 1,
+ wantLo: 0xfff,
+ },
+ {
+ v: 0x1fff,
+ shift: 2,
+ wantHi: 3,
+ wantLo: 0x7ff,
+ },
+ {
+ v: 0x1fff,
+ shift: 3,
+ wantHi: 7,
+ wantLo: 0x3ff,
+ },
+ {
+ v: 0x1001,
+ shift: 1,
+ wantHi: 1,
+ wantLo: 0x800,
+ },
+ {
+ v: 0x1001,
+ shift: 2,
+ wantHi: 1,
+ wantLo: 0x400,
+ },
+ {
+ v: 0x1001,
+ shift: 3,
+ wantHi: 1,
+ wantLo: 0x200,
+ },
+ {
+ v: 0x1000,
+ shift: 0,
+ wantHi: 0x1,
+ wantLo: 0xfff,
+ },
+ {
+ v: 0x8000,
+ shift: 3,
+ wantHi: 0x8,
+ wantLo: 0xfff,
+ },
+ {
+ v: 0xfff,
+ shift: 0,
+ wantHi: 0,
+ wantLo: 0xfff,
+ },
+ {
+ v: 0x1ffe,
+ shift: 1,
+ wantHi: 0,
+ wantLo: 0xfff,
+ },
+ {
+ v: 0x3ffc,
+ shift: 2,
+ wantHi: 0,
+ wantLo: 0xfff,
+ },
+ {
+ v: 0x10fef,
+ shift: 4,
+ wantHi: 0xfff,
+ wantLo: 0xfff,
+ },
}
for _, test := range tests {
hi, lo, err := splitImm24uScaled(test.v, test.shift)
@@ -179,6 +305,21 @@ func TestSplitImm24uScaled(t *testing.T) {
}
}
}
+
+ // Test the unshifted hi range specifically, including unaligned values.
+ // This exercises values where the unshifted path may be used.
+ for shift := 0; shift <= 3; shift++ {
+ maxUnshifted := int32(0xfff + 0xfff<<shift)
+ for v := int32(0); v <= maxUnshifted; v++ {
+ hi, lo, err := splitImm24uScaled(v, shift)
+ if err != nil {
+ t.Fatalf("splitImm24uScaled(%x, %x) failed: %v", v, shift, err)
+ }
+ if hi+lo<<shift != v {
+ t.Fatalf("splitImm24uScaled(%x, %x) = (%x, %x) is incorrect", v, shift, hi, lo)
+ }
+ }
+ }
}
// TestLarge generates a very large file to verify that large