110 lines
3.5 KiB
Go
110 lines
3.5 KiB
Go
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package asm
|
|
|
|
// Operand is an instruction operand: a Reg, a Mem reference or an Imm value.
|
|
type Operand interface {
|
|
isOperand()
|
|
}
|
|
|
|
// Imm is an immediate value. Its encoded width is chosen by the instruction
|
|
// (sign-extended imm8 where possible, otherwise imm32, imm64 for MOV).
|
|
type Imm int64
|
|
|
|
func (Imm) isOperand() {}
|
|
|
|
// RegList is a bracketed register range, [Z0-Z3]: the four-register source
|
|
// of the 4FMAPS and 4VNNIW families. The EVEX emit path carries the list's
|
|
// low register through the inverted 5-bit V'VVVV field; the three higher
|
|
// registers are implied by the instruction, so only the pair travels here.
|
|
type RegList struct {
|
|
Lo Reg
|
|
Hi Reg // implied by the encoding; Lo.idx+3 by construction
|
|
}
|
|
|
|
func (RegList) isOperand() {}
|
|
|
|
// FloatImm is a floating-point immediate ($-1.0). The SSE mnemonics whose
|
|
// encoding takes an XMM/memory source at that position rewrite it as a read
|
|
// from a read-only pool constant ($f64.<hex> or $f32.<hex>), the toolchain's
|
|
// own behaviour; every other instruction rejects it.
|
|
type FloatImm struct {
|
|
Text string // the numeric text as written, sign excluded
|
|
Neg bool // a leading minus
|
|
}
|
|
|
|
func (FloatImm) isOperand() {}
|
|
|
|
// TLSMem is a thread-local access, the source form off(base)(TLS*1) with the
|
|
// base dropped: the toolchain's one-instruction TLS rewrite assembles it as
|
|
// the segment-prefixed absolute whose disp32 carries an R_TLS_LE patch site
|
|
// (the linker fills the TLS slot offset).
|
|
type TLSMem struct {
|
|
Disp int64
|
|
Size int
|
|
Seg byte // the segment override: FS (0x64) or GS (0x65) on windows
|
|
}
|
|
|
|
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
|
|
Index Reg
|
|
Scale int // 1, 2, 4 or 8; 0 means no index
|
|
Disp int64
|
|
Size int // operand width in bytes
|
|
HasBase bool
|
|
HasIndex bool
|
|
Seg byte // segment override prefix (0x64 FS, 0x65 GS); 0 = none
|
|
}
|
|
|
|
func (Mem) isOperand() {}
|
|
|
|
// Ptr builds a plain displaced memory operand (base)+disp of the given size.
|
|
func Ptr(base Reg, disp int64, size int) Mem {
|
|
return Mem{Base: base, Disp: disp, Size: size, HasBase: true}
|
|
}
|
|
|
|
// Idx builds an indexed memory operand disp(base)(index*scale).
|
|
func Idx(base, index Reg, scale int, disp int64, size int) Mem {
|
|
return Mem{Base: base, Index: index, Scale: scale, Disp: disp, Size: size, HasBase: true, HasIndex: true}
|
|
}
|
|
|
|
// sbMem is a memory operand that references a static (SB) symbol. It encodes
|
|
// as a RIP-relative reference with a placeholder displacement; the encoder
|
|
// records a patch site so the file-level layout can fill in the true rel32
|
|
// once the symbol's address is known.
|
|
type sbMem struct {
|
|
size int
|
|
name string // static symbol name (the GLOBL identifier)
|
|
addend int64 // byte offset within the symbol
|
|
}
|
|
|
|
func (sbMem) isOperand() {}
|
|
|
|
// isX86Mem reports whether the operand is an amd64 memory reference: a base
|
|
// or indexed Mem, or an SB-relative sbMem. Encoders that gate on "memory in
|
|
// this position" must accept both; the r/m emitters distinguish the two
|
|
// themselves.
|
|
func isX86Mem(o Operand) bool {
|
|
switch o.(type) {
|
|
case Mem, sbMem:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|