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
|
(`BL R9` beside `BL (R9)`, both BLR) and the zero-immediate store
|
||||||
(`MOVD $0, mem` through the zero register, rejecting non-zero immediates
|
(`MOVD $0, mem` through the zero register, rejecting non-zero immediates
|
||||||
as the toolchain does); `PCALIGN` now aligns on amd64, padding with the
|
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
|
the `GOARCH_<arch>` and `GOOS_<goos>` macros the go command passes to
|
||||||
`go tool asm`, so GOROOT headers' `#ifdef GOARCH_amd64` platform blocks
|
`go tool asm`, so GOROOT headers' `#ifdef GOARCH_amd64` platform blocks
|
||||||
(`go_tls.h`'s `get_tls` and friends) select as intended. The GOROOT
|
(`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
|
corpus measure moves to 291 of 353 files assembling for every target
|
||||||
architecture (80.7 %), 91.4 % of the real-code corpus (280 of 303),
|
architecture (82.4 %), 97.4 % of the real-code corpus, from 70.8 %
|
||||||
from 70.8 % and 82.2 %.
|
and 82.2 %.
|
||||||
|
|
||||||
### Added
|
### 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).
|
// Memory with a real base register: (base), off(base), (base)(index*scale).
|
||||||
if a.Base != "" {
|
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)
|
base, ok := ParseReg(a.Base)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("unknown base register %q", a.Base)
|
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
|
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)
|
idx, ok := ParseReg(a.Index)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("unknown index register %q", a.Index)
|
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
|
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.
|
// Bare register.
|
||||||
if a.Sym != nil && a.Sym.Pseudo == "" && a.Sym.Name != "" {
|
if a.Sym != nil && a.Sym.Pseudo == "" && a.Sym.Name != "" {
|
||||||
if r, ok := ParseReg(a.Sym.Name); ok {
|
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.disp = le32(r.Disp)
|
||||||
i.tls = true
|
i.tls = true
|
||||||
return nil
|
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:
|
default:
|
||||||
return fmt.Errorf("invalid r/m operand %T", rm)
|
return fmt.Errorf("invalid r/m operand %T", rm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setMem(i *instr, regField int, m Mem) error {
|
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)
|
modrm, sib, disp, xBit, bBit, err := memComponents(regField, m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -707,6 +715,16 @@ func setMem(i *instr, regField int, m Mem) error {
|
|||||||
return nil
|
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
|
// 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
|
// 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.
|
// 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)
|
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:
|
case Imm:
|
||||||
if dstIsReg {
|
if dstIsReg {
|
||||||
v := int64(src)
|
v := int64(src)
|
||||||
@@ -245,11 +256,24 @@ func (e *enc) encodeMov(ops []Operand, size int) error {
|
|||||||
i.imm = imm
|
i.imm = imm
|
||||||
return e.emit(i)
|
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)
|
op := byte(0xC7)
|
||||||
if size == 1 {
|
if size == 1 {
|
||||||
op = 0xC6
|
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})
|
i := newInstr(size, []byte{op})
|
||||||
if err := setRMDigit(i, 0, dst, size); err != nil {
|
if err := setRMDigit(i, 0, dst, size); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -48,6 +48,17 @@ type TLSMem struct {
|
|||||||
|
|
||||||
func (TLSMem) isOperand() {}
|
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).
|
// Mem is a memory operand of the form disp(base)(index*scale).
|
||||||
type Mem struct {
|
type Mem struct {
|
||||||
Base Reg
|
Base Reg
|
||||||
@@ -57,6 +68,7 @@ type Mem struct {
|
|||||||
Size int // operand width in bytes
|
Size int // operand width in bytes
|
||||||
HasBase bool
|
HasBase bool
|
||||||
HasIndex bool
|
HasIndex bool
|
||||||
|
Seg byte // segment override prefix (0x64 FS, 0x65 GS); 0 = none
|
||||||
}
|
}
|
||||||
|
|
||||||
func (Mem) isOperand() {}
|
func (Mem) isOperand() {}
|
||||||
|
|||||||
@@ -684,6 +684,26 @@ func parseAddress(g []token.Token) ast.Address {
|
|||||||
if i > 0 && i < len(g) {
|
if i > 0 && i < len(g) {
|
||||||
addr.Shift = joinRaw(g[i:])
|
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
|
return addr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user