diff options
| author | Alexander Musman <alexander.musman@gmail.com> | 2026-01-18 20:07:50 +0300 |
|---|---|---|
| committer | Gopher Robot <gobot@golang.org> | 2026-02-27 12:41:17 -0800 |
| commit | f2633386e0d2f692b4363134626f472fc95b9858 (patch) | |
| tree | 49ce347d022e16c584c3830484747baa1b864f14 /src/cmd/internal | |
| parent | 62a1da372a2bfeb29644246ff0f61dc048bb912c (diff) | |
| download | go-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.go | 35 | ||||
| -rw-r--r-- | src/cmd/internal/obj/arm64/asm_test.go | 157 |
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 |
