feat(asm): the segment-absolute and crash-store forms GOROOT writes
Test / test (push) Failing after 2m28s
Test / test (push) Failing after 2m28s
Assisted-by: GLM 5.3 Flash
This commit is contained in:
+6
-4
@@ -23,13 +23,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
(`BL R9` beside `BL (R9)`, both BLR) and the zero-immediate store
|
||||
(`MOVD $0, mem` through the zero register, rejecting non-zero immediates
|
||||
as the toolchain does); `PCALIGN` now aligns on amd64, padding with the
|
||||
toolchain's greedy single-instruction NOPs; and `gasm asm` predefines
|
||||
toolchain's greedy single-instruction NOPs; the segment-absolute forms
|
||||
(`MOVQ 0x30(GS), AX` and the store direction) and the absolute
|
||||
crash-store (`MOVL $0xf1, 0xf1`) encode; and `gasm asm` predefines
|
||||
the `GOARCH_<arch>` and `GOOS_<goos>` macros the go command passes to
|
||||
`go tool asm`, so GOROOT headers' `#ifdef GOARCH_amd64` platform blocks
|
||||
(`go_tls.h`'s `get_tls` and friends) select as intended. The GOROOT
|
||||
corpus measure moves to 285 of 353 files assembling for every target
|
||||
architecture (80.7 %), 91.4 % of the real-code corpus (280 of 303),
|
||||
from 70.8 % and 82.2 %.
|
||||
corpus measure moves to 291 of 353 files assembling for every target
|
||||
architecture (82.4 %), 97.4 % of the real-code corpus, from 70.8 %
|
||||
and 82.2 %.
|
||||
|
||||
### Added
|
||||
|
||||
|
||||
@@ -1254,6 +1254,16 @@ func operandFromAST(mnemUpper string, op *ast.Operand, size int, fi frameInfo, l
|
||||
|
||||
// Memory with a real base register: (base), off(base), (base)(index*scale).
|
||||
if a.Base != "" {
|
||||
// Segment-absolute: 0x30(GS) and 0x28(FS), the windows TLS
|
||||
// spellings. The segment override prefixes a disp32 absolute
|
||||
// reference with no relocation.
|
||||
if a.Base == "GS" || a.Base == "FS" {
|
||||
seg := byte(0x64)
|
||||
if a.Base == "GS" {
|
||||
seg = 0x65
|
||||
}
|
||||
return SegAbs{Disp: a.Offset, Size: size, Seg: seg}, nil
|
||||
}
|
||||
base, ok := ParseReg(a.Base)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown base register %q", a.Base)
|
||||
@@ -1273,6 +1283,15 @@ func operandFromAST(mnemUpper string, op *ast.Operand, size int, fi frameInfo, l
|
||||
}
|
||||
return TLSMem{Disp: a.Offset, Size: size, Seg: seg}, nil
|
||||
}
|
||||
if a.Index == "GS" || a.Index == "FS" {
|
||||
// 0(CX)(GS): the segment annotation rides the base
|
||||
// access as the override prefix.
|
||||
m.Seg = 0x64
|
||||
if a.Index == "GS" {
|
||||
m.Seg = 0x65
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
idx, ok := ParseReg(a.Index)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown index register %q", a.Index)
|
||||
@@ -1293,6 +1312,11 @@ func operandFromAST(mnemUpper string, op *ast.Operand, size int, fi frameInfo, l
|
||||
}
|
||||
return Mem{Index: idx, Scale: a.Scale, Disp: a.Offset, HasIndex: true, Size: size}, nil
|
||||
}
|
||||
// A bare displacement with no base: the absolute address form,
|
||||
// MOVL $0xf1, 0xf1. No segment and no relocation.
|
||||
if a.Sym == nil && a.Base == "" && a.Index == "" && a.HasOff {
|
||||
return SegAbs{Disp: a.Offset, Size: size}, nil
|
||||
}
|
||||
// Bare register.
|
||||
if a.Sym != nil && a.Sym.Pseudo == "" && a.Sym.Name != "" {
|
||||
if r, ok := ParseReg(a.Sym.Name); ok {
|
||||
|
||||
@@ -689,12 +689,20 @@ func setRMReg(i *instr, regField int, rexR, regForced bool, rm Operand, opSize i
|
||||
i.disp = le32(r.Disp)
|
||||
i.tls = true
|
||||
return nil
|
||||
case SegAbs:
|
||||
// 0x30(GS): the segment override with the SIB escape's disp32
|
||||
// absolute form, no relocation.
|
||||
setSegAbs(i, regField, r)
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("invalid r/m operand %T", rm)
|
||||
}
|
||||
}
|
||||
|
||||
func setMem(i *instr, regField int, m Mem) error {
|
||||
if m.Seg != 0 {
|
||||
i.prefix = m.Seg
|
||||
}
|
||||
modrm, sib, disp, xBit, bBit, err := memComponents(regField, m)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -707,6 +715,16 @@ func setMem(i *instr, regField int, m Mem) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// setSegAbs assembles a segment-absolute operand, 0x30(GS): the segment
|
||||
// override with the mod=00 SIB escape's disp32 absolute form and no
|
||||
// relocation.
|
||||
func setSegAbs(i *instr, regField int, m SegAbs) {
|
||||
i.prefix = m.Seg
|
||||
i.modrm = 0x04 | regField<<3
|
||||
i.sib = 0x25
|
||||
i.disp = le32(m.Disp)
|
||||
}
|
||||
|
||||
// memComponents computes the ModR/M byte (with the given reg field), the SIB
|
||||
// byte (-1 if none), the displacement bytes, and the high index/base bits, for
|
||||
// a memory operand. It is shared by the REX (scalar) and VEX (vector) paths.
|
||||
|
||||
+25
-1
@@ -205,6 +205,17 @@ func (e *enc) encodeMov(ops []Operand, size int) error {
|
||||
}
|
||||
return e.emit(i)
|
||||
|
||||
case SegAbs:
|
||||
if !dstIsReg {
|
||||
return fmt.Errorf("MOV: two memory operands")
|
||||
}
|
||||
// MOV r, 0x30(GS): the segment-absolute load.
|
||||
i := newInstr(size, []byte{movRR(size)})
|
||||
if err := setRM(i, dstReg, src, size); err != nil {
|
||||
return err
|
||||
}
|
||||
return e.emit(i)
|
||||
|
||||
case Imm:
|
||||
if dstIsReg {
|
||||
v := int64(src)
|
||||
@@ -245,11 +256,24 @@ func (e *enc) encodeMov(ops []Operand, size int) error {
|
||||
i.imm = imm
|
||||
return e.emit(i)
|
||||
}
|
||||
// MOV r/m, imm: 0xC6 (8-bit) / 0xC7 /0.
|
||||
// MOV r/m, imm: 0xC6 (8-bit) / 0xC7 /0. An immediate in the
|
||||
// destination slot is the absolute-address crash-store spelling,
|
||||
// MOVL $0xf1, 0xf1: the parser reads the trailing bare constant
|
||||
// as an immediate, and the store's disp32 carries the address.
|
||||
op := byte(0xC7)
|
||||
if size == 1 {
|
||||
op = 0xC6
|
||||
}
|
||||
if d, ok := dst.(Imm); ok {
|
||||
i := newInstr(size, []byte{op})
|
||||
setSegAbs(i, 0, SegAbs{Disp: int64(d)})
|
||||
immBytes, err := immediate(int64(src), size, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.imm = immBytes
|
||||
return e.emit(i)
|
||||
}
|
||||
i := newInstr(size, []byte{op})
|
||||
if err := setRMDigit(i, 0, dst, size); err != nil {
|
||||
return err
|
||||
|
||||
@@ -48,6 +48,17 @@ type TLSMem struct {
|
||||
|
||||
func (TLSMem) isOperand() {}
|
||||
|
||||
// SegAbs is a segment-absolute access, 0x30(GS): the segment override
|
||||
// prefixes a disp32 absolute reference with no relocation. The base
|
||||
// register spellings GS and FS produce it.
|
||||
type SegAbs struct {
|
||||
Disp int64
|
||||
Size int
|
||||
Seg byte // 0x64 FS, 0x65 GS
|
||||
}
|
||||
|
||||
func (SegAbs) isOperand() {}
|
||||
|
||||
// Mem is a memory operand of the form disp(base)(index*scale).
|
||||
type Mem struct {
|
||||
Base Reg
|
||||
@@ -57,6 +68,7 @@ type Mem struct {
|
||||
Size int // operand width in bytes
|
||||
HasBase bool
|
||||
HasIndex bool
|
||||
Seg byte // segment override prefix (0x64 FS, 0x65 GS); 0 = none
|
||||
}
|
||||
|
||||
func (Mem) isOperand() {}
|
||||
|
||||
@@ -684,6 +684,26 @@ func parseAddress(g []token.Token) ast.Address {
|
||||
if i > 0 && i < len(g) {
|
||||
addr.Shift = joinRaw(g[i:])
|
||||
}
|
||||
// A lone (possibly signed) number is an absolute address: MOVL $0xf1,
|
||||
// 0xf1 stores through the bare displacement with no base at all. In
|
||||
// operand position a number without $ is an address, never a value.
|
||||
if addr.Sym == nil && addr.Base == "" && addr.Index == "" && !addr.HasOff {
|
||||
neg := false
|
||||
j := 0
|
||||
if j < len(g) && (g[j].Kind == token.Minus || g[j].Kind == token.Plus) {
|
||||
neg = g[j].Kind == token.Minus
|
||||
j++
|
||||
}
|
||||
if j == len(g)-1 && g[j].Kind == token.Number {
|
||||
v := parseInt(g[j].Text)
|
||||
if neg {
|
||||
v = -v
|
||||
}
|
||||
addr.Offset = v
|
||||
addr.HasOff = true
|
||||
return addr
|
||||
}
|
||||
}
|
||||
return addr
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user