fix(asm): read the arm64 move-wide immediate as an unsigned pattern
Assisted-by: GLM 5.3 Flash
This commit is contained in:
@@ -33,6 +33,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
resolved to nothing: only the forward forms counted. A negative count
|
||||
now walks the same instruction statements backwards, labels excluded,
|
||||
byte-identical with the toolchain.
|
||||
- **The arm64 move-wide family reads its immediate as an unsigned
|
||||
pattern.** `MOVK $(40000<<48)` folds to a negative int64 and was
|
||||
rejected; the toolchain picks the 16-bit lane from the 64-bit bit
|
||||
pattern, so the encoder now does the same, and a zero immediate is
|
||||
rejected where the toolchain rejects it.
|
||||
|
||||
## [0.35.0] - 2026-09-22
|
||||
|
||||
|
||||
+14
-14
@@ -3069,29 +3069,29 @@ func encodeARM64MoveWide(mnem string, baseOp uint32, ops []*ast.Operand) ([]byte
|
||||
// base, so MOVZ and MOVN come along for free.
|
||||
opc := baseOp >> 29 & 3
|
||||
sf := baseOp >> 31 & 1
|
||||
v := arm64Imm64(ops[0])
|
||||
if v < 0 {
|
||||
return nil, fmt.Errorf("%s: negative immediate %d", mnem, v)
|
||||
// The toolchain's optab case 33, shared by the whole family in both
|
||||
// widths: the immediate is one unsigned 64-bit pattern (a high-lane
|
||||
// constant such as $(40000<<48) arrives negative through int64
|
||||
// folding), it must occupy exactly one 16-bit lane, zero is rejected,
|
||||
// and the W forms cannot reach the top half.
|
||||
u := uint64(arm64Imm64(ops[0]))
|
||||
if u == 0 {
|
||||
return nil, fmt.Errorf("%s: zero immediate cannot be handled", mnem)
|
||||
}
|
||||
hw := -1
|
||||
for i := range 4 {
|
||||
if v>>(uint(i)*16)&0xFFFF != 0 {
|
||||
hw = i
|
||||
for lane := range 4 {
|
||||
if u&^(uint64(0xFFFF)<<(lane*16)) == 0 {
|
||||
hw = lane
|
||||
break
|
||||
}
|
||||
}
|
||||
if hw < 0 {
|
||||
hw = 0 // zero: every chunk is zero, hw = 0 carries it
|
||||
}
|
||||
for i := hw + 1; i < 4; i++ {
|
||||
if v>>(uint(i)*16)&0xFFFF != 0 {
|
||||
return nil, fmt.Errorf("%s: immediate %d does not fit one 16-bit chunk", mnem, v)
|
||||
}
|
||||
return nil, fmt.Errorf("%s: immediate %#x does not fit one 16-bit chunk", mnem, u)
|
||||
}
|
||||
if sf == 0 && hw > 1 {
|
||||
return nil, fmt.Errorf("%s: immediate %d out of range for the 32-bit form", mnem, v)
|
||||
return nil, fmt.Errorf("%s: immediate %#x out of range for the 32-bit form", mnem, u)
|
||||
}
|
||||
return a64wordLE(a64MoveWide(sf, opc, uint32(hw), uint32(v>>uint(hw*16)&0xFFFF), uint32(rd))), nil
|
||||
return a64wordLE(a64MoveWide(sf, opc, uint32(hw), uint32(u>>uint(hw*16)&0xFFFF), uint32(rd))), nil
|
||||
}
|
||||
|
||||
// ---- Bitfield/EXTR encoding ----
|
||||
|
||||
@@ -1051,6 +1051,41 @@ func TestArm64MOVK(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestArm64MOVKHighLane pins the shifted high-lane immediate the arm64 test
|
||||
// kernels write: $(40000<<48) folds to a negative int64, and the toolchain
|
||||
// reads the value as an unsigned 64-bit pattern when it picks the lane.
|
||||
func TestArm64MOVKHighLane(t *testing.T) {
|
||||
got := arm64Words(t, "\tMOVK $(40000<<48), R0\n\tMOVK $0x9c40000000000000, R1\n")
|
||||
want := []uint32{
|
||||
0xf2f38800, // MOVK $(40000<<48), R0 (go tool asm: f2f38800)
|
||||
0xf2f38801, // MOVK hw=3
|
||||
0xd65f03c0,
|
||||
}
|
||||
if len(got) != len(want) {
|
||||
t.Fatalf("word count = %d, want %d", len(got), len(want))
|
||||
}
|
||||
for i := range want {
|
||||
if got[i] != want[i] {
|
||||
t.Errorf("word %d = %08x, want %08x", i, got[i], want[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestArm64MoveWideZeroImmediate pins the toolchain's rejection of a zero
|
||||
// immediate in the move-wide family (optab case 33: "zero shifts cannot be
|
||||
// handled"): every lane is zero, so no hw field can carry it.
|
||||
func TestArm64MoveWideZeroImmediate(t *testing.T) {
|
||||
for _, mnem := range []string{"MOVK", "MOVZ", "MOVN"} {
|
||||
f, errs := parser.Parse("test_arm64.s", "#include \"textflag.h\"\n\nTEXT ·f(SB), NOSPLIT, $0-0\n\t"+mnem+" $0, R0\n\tRET\n")
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("%s: parse: %v", mnem, errs)
|
||||
}
|
||||
if _, err := AssembleFileARM64(f); err == nil {
|
||||
t.Errorf("%s $0: expected error, got nil", mnem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestArm64LoadImm64 tests 64-bit immediate loading.
|
||||
func TestArm64LoadImm64(t *testing.T) {
|
||||
src := `#include "textflag.h"
|
||||
|
||||
Reference in New Issue
Block a user