fix(asm): resolve negative numeric PC-relative jumps
Assisted-by: GLM 5.3 Flash
This commit is contained in:
@@ -28,6 +28,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
to the document that started it, so renaming a symbol used in a second
|
to the document that started it, so renaming a symbol used in a second
|
||||||
file moved that file's text into the first. Each edit now applies to the
|
file moved that file's text into the first. Each edit now applies to the
|
||||||
document it was collected in.
|
document it was collected in.
|
||||||
|
- **Negative numeric PC-relative jumps.** `JMP -3(PC)`, the shape the
|
||||||
|
runtime's exit loops write (sys_linux_amd64.s, sys_netbsd_amd64.s),
|
||||||
|
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.
|
||||||
|
|
||||||
## [0.35.0] - 2026-09-22
|
## [0.35.0] - 2026-09-22
|
||||||
|
|
||||||
|
|||||||
+17
-1
@@ -543,11 +543,27 @@ func pcJumpOffset(op *ast.Operand) (int, bool) {
|
|||||||
// pcJumpTarget resolves a numeric jump at statement index j: N counts the
|
// pcJumpTarget resolves a numeric jump at statement index j: N counts the
|
||||||
// instruction statements after the jump itself (N = 0 is the jump's own
|
// instruction statements after the jump itself (N = 0 is the jump's own
|
||||||
// address, the classic park loop), and the target is the start of the Nth
|
// address, the classic park loop), and the target is the start of the Nth
|
||||||
// one. It reports false when the count runs past the end of the function.
|
// one. A negative N counts the same way backwards, before the jump: the
|
||||||
|
// exit loops write JMP -3(PC) to land three instructions earlier. Labels
|
||||||
|
// count not, in either direction. It reports false when the count runs
|
||||||
|
// past the end of the function, or before its first instruction.
|
||||||
func pcJumpTarget(t *ast.Text, j, n int, pcs []int) (int, bool) {
|
func pcJumpTarget(t *ast.Text, j, n int, pcs []int) (int, bool) {
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
return pcs[j], true
|
return pcs[j], true
|
||||||
}
|
}
|
||||||
|
if n < 0 {
|
||||||
|
seen := 0
|
||||||
|
for k := j - 1; k >= 0; k-- {
|
||||||
|
if _, ok := t.Body[k].(*ast.Instr); !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen--
|
||||||
|
if seen == n {
|
||||||
|
return pcs[k], true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
seen := 0
|
seen := 0
|
||||||
for k := j + 1; k < len(t.Body); k++ {
|
for k := j + 1; k < len(t.Body); k++ {
|
||||||
if _, ok := t.Body[k].(*ast.Instr); !ok {
|
if _, ok := t.Body[k].(*ast.Instr); !ok {
|
||||||
|
|||||||
@@ -370,6 +370,58 @@ end:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestAssembleNumericPCJumps pins the numeric ±N(PC) branch operands: N
|
||||||
|
// counts instruction statements, skipping labels, in both directions (the
|
||||||
|
// runtime's exit loops write JMP -3(PC)), N = 0 parks on the jump itself.
|
||||||
|
func TestAssembleNumericPCJumps(t *testing.T) {
|
||||||
|
fn := firstText(t, `
|
||||||
|
#include "textflag.h"
|
||||||
|
TEXT ·exit(SB), NOSPLIT, $0
|
||||||
|
MOVB $1, AL
|
||||||
|
lab:
|
||||||
|
MOVB $2, AL
|
||||||
|
MOVB $3, AL
|
||||||
|
JMP -3(PC)
|
||||||
|
MOVB $4, AL
|
||||||
|
park:
|
||||||
|
JMP 0(PC)
|
||||||
|
MOVB $5, AL
|
||||||
|
JMP 2(PC)
|
||||||
|
MOVB $6, AL
|
||||||
|
RET
|
||||||
|
`)
|
||||||
|
code, _, err := Assemble(fn)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Assemble: %v", err)
|
||||||
|
}
|
||||||
|
// From the Go-assembled function:
|
||||||
|
// MOVB $1, AL b001
|
||||||
|
// MOVB $2, AL b002
|
||||||
|
// MOVB $3, AL b003
|
||||||
|
// JMP -3(PC) ebf8 (three instructions back, past lab:)
|
||||||
|
// MOVB $4, AL b004
|
||||||
|
// JMP 0(PC) ebfe (the park loop)
|
||||||
|
// MOVB $5, AL b005
|
||||||
|
// JMP 2(PC) eb02 (over MOVB $6 to the RET)
|
||||||
|
// MOVB $6, AL b006
|
||||||
|
// RET c3
|
||||||
|
want := []byte{
|
||||||
|
0xb0, 0x01,
|
||||||
|
0xb0, 0x02,
|
||||||
|
0xb0, 0x03,
|
||||||
|
0xeb, 0xf8,
|
||||||
|
0xb0, 0x04,
|
||||||
|
0xeb, 0xfe,
|
||||||
|
0xb0, 0x05,
|
||||||
|
0xeb, 0x02,
|
||||||
|
0xb0, 0x06,
|
||||||
|
0xc3,
|
||||||
|
}
|
||||||
|
if hexBytes(code) != hexBytes(want) {
|
||||||
|
t.Errorf("numeric-PC mismatch:\n got: %s\n want: %s", hexBytes(code), hexBytes(want))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestAssemblePrefetch(t *testing.T) {
|
func TestAssemblePrefetch(t *testing.T) {
|
||||||
fn := firstText(t, `
|
fn := firstText(t, `
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|||||||
Reference in New Issue
Block a user