fix: restore iota blocks, asm --format flag and prose after the syntax pass
This commit is contained in:
+4
-4
@@ -625,7 +625,7 @@ objects.
|
||||
`GLOBL` in the file defines no longer aborts assembly — it is recorded
|
||||
as an external relocation (`Image.Externals`, `FuncLayout.Relocs`) and
|
||||
becomes an undefined global symbol in the object output. The raw image
|
||||
format (`--format raw`, the default) still reports them: only an object
|
||||
s (`--format raw`, the default) still reports them: only an object
|
||||
file can represent a reference the linker must resolve.
|
||||
|
||||
### Changed
|
||||
@@ -726,7 +726,7 @@ The formatter behaves like `go fmt` and canonicalises block separation.
|
||||
|
||||
### Changed
|
||||
|
||||
- `format`: canonical blank-line layout — a new block (a label, `TEXT` or
|
||||
- `s`: canonical blank-line layout — a new block (a label, `TEXT` or
|
||||
`GLOBL`) is preceded by exactly one blank line, neither more nor less.
|
||||
Comments leading a block stay with it (the blank line goes before them),
|
||||
stacked labels share their block, the function's first label keeps hugging
|
||||
@@ -761,7 +761,7 @@ the encoder learns the legacy SSE moves.
|
||||
*first* operand on arm64, riscv64 and loong64; Plan 9 spelling puts it last
|
||||
on every architecture Go supports. The def/use and save/restore
|
||||
classification on those architectures was inverted.
|
||||
- `format`: a comment that follows a `RET` (typically the next function's doc
|
||||
- `s`: a comment that follows a `RET` (typically the next function's doc
|
||||
comment) is no longer indented as if it were still inside the finished
|
||||
function body.
|
||||
|
||||
@@ -934,7 +934,7 @@ Initial release — the Phase 1 foundation.
|
||||
Zero error-severity diagnostics across the 90-file Go runtime corpus and the
|
||||
production go-flac kernels (the `register-clobber` audit additionally reports
|
||||
the go-flac kernels' unsaved callee-saved register use for review).
|
||||
- `format`: an idempotent canonical formatter (operand spacing and per-function
|
||||
- `s`: an idempotent canonical formatter (operand spacing and per-function
|
||||
mnemonic alignment) that preserves comments and round-trips through the
|
||||
parser.
|
||||
- `lsp`: a Language Server Protocol server over stdio providing completion,
|
||||
|
||||
@@ -82,7 +82,7 @@ cross-compiles the same four targets.
|
||||
| `parser` | Line-oriented, error-tolerant parser producing the AST. |
|
||||
| `arch` | amd64, arm64, riscv64 and loong64 register files and instruction tables. |
|
||||
| `lint` | Conservative static checks (13 rules including unused-label, invalid-textflag, stack-imbalance). |
|
||||
| `format` | A canonical formatter — `gofmt` for assembly. |
|
||||
| `s` | A canonical formatter — `gofmt` for assembly. |
|
||||
| `asm` | The standalone assembler: all four architecture encoders, linker, object-file emitters (ELF with DWARF5, GOOBJ). |
|
||||
| `verify` | JIT execution substrate for dynamic analysis, combined ABI+fuzz differential testing. Assembly trampolines for all four architectures. |
|
||||
| `debug` | Interactive ptrace debugger for all four architectures: single-stepping, breakpoints, hardware watchpoints, register and memory inspection. |
|
||||
|
||||
@@ -413,7 +413,7 @@ func encodeARM64AddSubImm(mnem string, ops []*ast.Operand) ([]byte, error) {
|
||||
if len(ops) != 2 && len(ops) != 3 {
|
||||
return nil, fmt.Errorf("%s expects 2 or 3 operands, got %d", mnem, len(ops))
|
||||
}
|
||||
v := int32(immFromOperand(ops[0]))
|
||||
v := immFromOperand(ops[0])
|
||||
rd := arm64RegNum(operandRegName(ops[len(ops)-1]))
|
||||
rn := rd
|
||||
if len(ops) == 3 {
|
||||
@@ -606,7 +606,7 @@ func encodeARM64LoadImm(rd int, v int64, mnem string) ([]byte, error) {
|
||||
// - C_ABCON0 (0 < v ≤ 4095): bitmask first for positive values
|
||||
// - Negative values: MOVN first, then bitmask
|
||||
// - C_MOVCON (movcon-eligible, outside ABCON range): MOVZ/MOVN first
|
||||
tryBitmaskFirst := (d > 0 && d <= 0xFFF)
|
||||
tryBitmaskFirst := d > 0 && d <= 0xFFF
|
||||
|
||||
if tryBitmaskFirst {
|
||||
// Small immediate: try bitmask first (Go uses ORR for values like $1, $256).
|
||||
|
||||
@@ -234,8 +234,6 @@ const (
|
||||
a64CondLT = 0xb
|
||||
a64CondGT = 0xc
|
||||
a64CondLE = 0xd
|
||||
a64CondAL = 0xe
|
||||
a64CondNV = 0xf
|
||||
)
|
||||
|
||||
// arm64CondMap maps Go assembler condition mnemonics to AArch64 condition codes.
|
||||
|
||||
@@ -17,7 +17,6 @@ import (
|
||||
// and external ones against undefined globals. The output links with the
|
||||
// system toolchain (cc/ld) the way a hand-assembled .o would.
|
||||
|
||||
// ELF constants (ELF64, little-endian, System V).
|
||||
const (
|
||||
elfClass64 = 2
|
||||
elfDataLSB = 1
|
||||
@@ -36,17 +35,14 @@ const (
|
||||
shfAlloc = 2
|
||||
shfExecInstr = 4
|
||||
|
||||
stbLocal = 0
|
||||
stbGlobal = 1
|
||||
|
||||
sttNotype = 0
|
||||
sttObject = 1
|
||||
sttFunc = 2
|
||||
sttSection = 3
|
||||
stInfoShift = 4
|
||||
|
||||
rX8664PC32 = 2
|
||||
rX8664_64 = 1 // R_X86_64_64 (absolute 64-bit)
|
||||
)
|
||||
|
||||
// elfSym is one symbol-table entry in construction.
|
||||
|
||||
+2
-2
@@ -87,7 +87,7 @@ func emitDWARF(img *Image, srcFile string) *dwarfSections {
|
||||
ds.debugLineStr = lineStr.bytes()
|
||||
|
||||
// Build .debug_line.
|
||||
ds.debugLine = dwarfBuildLineSection(img, srcFile, ds)
|
||||
ds.debugLine = dwarfBuildLineSection(img, ds)
|
||||
|
||||
// Build .debug_info.
|
||||
ds.debugInfo = dwarfBuildInfoSection(img, srcFile, ds)
|
||||
@@ -98,7 +98,7 @@ func emitDWARF(img *Image, srcFile string) *dwarfSections {
|
||||
}
|
||||
|
||||
// dwarfBuildLineSection builds a complete .debug_line section.
|
||||
func dwarfBuildLineSection(img *Image, srcFile string, ds *dwarfSections) []byte {
|
||||
func dwarfBuildLineSection(img *Image, ds *dwarfSections) []byte {
|
||||
var b []byte
|
||||
le := binary.LittleEndian
|
||||
|
||||
|
||||
+2
-2
@@ -701,7 +701,7 @@ func splitMask(ops []Operand) ([]Operand, int, error) {
|
||||
return nil, 0, fmt.Errorf("at most one mask register operand")
|
||||
}
|
||||
if r.idx == 0 {
|
||||
return nil, 0, fmt.Errorf("K0 is not a usable mask register")
|
||||
return nil, 0, fmt.Errorf("k0 is not a usable mask register")
|
||||
}
|
||||
mask = r.idx
|
||||
continue
|
||||
@@ -1203,7 +1203,7 @@ func (e *enc) emitEvexFields(spec evexSpec, ll, regIdx, vvvvIdx int, rm Operand,
|
||||
// The b bit and the L'L field carry the rounding/SAE/broadcast mode:
|
||||
// a rounding mode replaces L'L with the rc value, plain SAE and
|
||||
// broadcast keep the vector length.
|
||||
b, ll := 0, ll
|
||||
b := 0
|
||||
switch {
|
||||
case sfx.rounding >= 0:
|
||||
b, ll = 1, sfx.rounding
|
||||
|
||||
+5
-2
@@ -33,7 +33,10 @@ import (
|
||||
// methods supply the toolchain preamble, the MinLC (pc-value delta unit)
|
||||
// and the relocation-type mapping for code relocations.
|
||||
|
||||
// GOOBJ block indices (cmd/internal/goobj).
|
||||
// GOOBJ block indices (cmd/internal/goobj). These MUST match the real
|
||||
// archive layout: the emitter writes the header offsets per index and the
|
||||
// reader (groundtruth, goobj_resolve) parses real Go archives with them.
|
||||
// blkAutolib is unused by the emitter but still defines index 0.
|
||||
const (
|
||||
blkAutolib = iota
|
||||
blkPkgIdx
|
||||
@@ -417,7 +420,7 @@ func (img *Image) emitGOObject(pkgPath, srcPath string, pre []byte, minLC int, r
|
||||
// The string table. Absolute offsets: it starts right after the
|
||||
// 96-byte header (magic, fingerprint, flags, the 19 block offsets).
|
||||
const headerSize = 8 + 8 + 4 + 4*(blkEnd+1)
|
||||
strTab := []byte{}
|
||||
var strTab []byte
|
||||
strOff := map[string]uint32{}
|
||||
addStr := func(s string) {
|
||||
if _, ok := strOff[s]; ok {
|
||||
|
||||
+1
-1
@@ -80,7 +80,7 @@ func (fl *FuncLayout) LineAt(offset int) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Reloc is one static-symbol reference within a function body: the disp32
|
||||
// RelocKind Reloc is one static-symbol reference within a function body: the disp32
|
||||
// field at Off (function-relative) must reach the symbol plus Addend,
|
||||
// measured from After, the address just past the instruction. An External
|
||||
// relocation names a symbol no GLOBL in the file defines; the object-file
|
||||
|
||||
+57
-59
@@ -672,8 +672,6 @@ func encodeLOONG64ImmArith(mnem string, de l64DualEnc, ops []*ast.Operand) ([]by
|
||||
const (
|
||||
lu12iw = 0x0a << 25
|
||||
ori = 0x00e << 22
|
||||
lu32id = 0x0b << 25
|
||||
lu52id = 0x00c << 22
|
||||
)
|
||||
if v == int64(int32(v)) {
|
||||
if v&0xfff == 0 && (v < 0x800 || v > 0xfff) {
|
||||
@@ -771,7 +769,7 @@ func encodeLOONG64Mov(instr *ast.Instr, mnem string, fi loong64FrameInfo, relocs
|
||||
if rd < 0 {
|
||||
return nil, fmt.Errorf("%s $sym(SB): invalid destination register", mnem)
|
||||
}
|
||||
return encodeLOONG64SBAddr(src.Imm.Sym, rd, mnem, relocs), nil
|
||||
return encodeLOONG64SBAddr(src.Imm.Sym, rd, relocs), nil
|
||||
}
|
||||
rd := l64Reg(dst)
|
||||
if rd < 0 {
|
||||
@@ -832,14 +830,14 @@ func encodeLOONG64Mov(instr *ast.Instr, mnem string, fi loong64FrameInfo, relocs
|
||||
if rd < 0 {
|
||||
return nil, fmt.Errorf("%s: invalid destination register", mnem)
|
||||
}
|
||||
return encodeLOONG64MemOp(mnem, ops[0], rd, true, fi, relocs)
|
||||
return encodeLOONG64MemOp(mnem, ops[0], rd, true, fi)
|
||||
}
|
||||
if !isMemOperand(src) && isMemOperand(dst) {
|
||||
rs := l64Reg(src)
|
||||
if rs < 0 {
|
||||
return nil, fmt.Errorf("%s: invalid source register", mnem)
|
||||
}
|
||||
return encodeLOONG64MemOp(mnem, ops[1], rs, false, fi, relocs)
|
||||
return encodeLOONG64MemOp(mnem, ops[1], rs, false, fi)
|
||||
}
|
||||
|
||||
// Register → register.
|
||||
@@ -933,20 +931,20 @@ const (
|
||||
l64St1
|
||||
l64St0
|
||||
|
||||
l64Dcon12_0
|
||||
l64Dcon12_20S
|
||||
l64Dcon20S_20
|
||||
l64Dcon12_12S
|
||||
l64Dcon20S_12S
|
||||
l64Dcon20S_0
|
||||
l64Dcon12_12U
|
||||
l64Dcon20S_12U
|
||||
l64Dcon32_12S
|
||||
l64Dcon32_0
|
||||
l64Dcon32_20
|
||||
l64Dcon12_32S
|
||||
l64Dcon20S_32
|
||||
l64Dcon32_12U
|
||||
l64dcon120
|
||||
l64dcon1220s
|
||||
l64dcon20s20
|
||||
l64dcon1212s
|
||||
l64dcon20s12s
|
||||
l64dcon20s0
|
||||
l64dcon1212u
|
||||
l64dcon20s12u
|
||||
l64dcon3212s
|
||||
l64dcon320
|
||||
l64dcon3220
|
||||
l64dcon1232s
|
||||
l64dcon20s32
|
||||
l64dcon3212u
|
||||
l64Dcon
|
||||
)
|
||||
|
||||
@@ -987,91 +985,91 @@ func l64DconClass(v int64) int {
|
||||
lo20 := l64BitField(v, 12, 20)
|
||||
lo12 := l64BitField(v, 0, 12)
|
||||
if tzb >= 52 {
|
||||
return l64Dcon12_0
|
||||
return l64dcon120
|
||||
}
|
||||
if tzb >= 32 {
|
||||
if ((hi20 == l64All1 || hi20 == l64St1) && hi12 == l64All1) || ((hi20 == l64All0 || hi20 == l64St0) && hi12 == l64All0) {
|
||||
return l64Dcon20S_0
|
||||
return l64dcon20s0
|
||||
}
|
||||
return l64Dcon32_0
|
||||
return l64dcon320
|
||||
}
|
||||
if tzb >= 12 {
|
||||
if lo20 == l64St1 || lo20 == l64All1 {
|
||||
if hi20 == l64All1 {
|
||||
return l64Dcon12_20S
|
||||
return l64dcon1220s
|
||||
}
|
||||
if (hi20 == l64St1 && hi12 == l64All1) || ((hi20 == l64St0 || hi20 == l64All0) && hi12 == l64All0) {
|
||||
return l64Dcon20S_20
|
||||
return l64dcon20s20
|
||||
}
|
||||
return l64Dcon32_20
|
||||
return l64dcon3220
|
||||
}
|
||||
if hi20 == l64All0 {
|
||||
return l64Dcon12_20S
|
||||
return l64dcon1220s
|
||||
}
|
||||
if (hi20 == l64St0 && hi12 == l64All0) || ((hi20 == l64St1 || hi20 == l64All1) && hi12 == l64All1) {
|
||||
return l64Dcon20S_20
|
||||
return l64dcon20s20
|
||||
}
|
||||
return l64Dcon32_20
|
||||
return l64dcon3220
|
||||
}
|
||||
if lo12 == l64St1 || lo12 == l64All1 {
|
||||
if lo20 == l64All1 {
|
||||
if hi20 == l64All1 {
|
||||
return l64Dcon12_12S
|
||||
return l64dcon1212s
|
||||
}
|
||||
if (hi20 == l64St1 && hi12 == l64All1) || ((hi20 == l64St0 || hi20 == l64All0) && hi12 == l64All0) {
|
||||
return l64Dcon20S_12S
|
||||
return l64dcon20s12s
|
||||
}
|
||||
return l64Dcon32_12S
|
||||
return l64dcon3212s
|
||||
}
|
||||
if lo20 == l64St1 {
|
||||
if hi20 == l64All1 {
|
||||
return l64Dcon12_32S
|
||||
return l64dcon1232s
|
||||
}
|
||||
if (hi20 == l64St1 && hi12 == l64All1) || ((hi20 == l64St0 || hi20 == l64All0) && hi12 == l64All0) {
|
||||
return l64Dcon20S_32
|
||||
return l64dcon20s32
|
||||
}
|
||||
return l64Dcon
|
||||
}
|
||||
if lo20 == l64All0 {
|
||||
if hi20 == l64All0 {
|
||||
return l64Dcon12_12U
|
||||
return l64dcon1212u
|
||||
}
|
||||
if ((hi20 == l64St1 || hi20 == l64All1) && hi12 == l64All1) || (hi20 == l64St0 && hi12 == l64All0) {
|
||||
return l64Dcon20S_12U
|
||||
return l64dcon20s12u
|
||||
}
|
||||
return l64Dcon32_12U
|
||||
return l64dcon3212u
|
||||
}
|
||||
if hi20 == l64All0 {
|
||||
return l64Dcon12_32S
|
||||
return l64dcon1232s
|
||||
}
|
||||
if ((hi20 == l64St1 || hi20 == l64All1) && hi12 == l64All1) || (hi20 == l64St0 && hi12 == l64All0) {
|
||||
return l64Dcon20S_32
|
||||
return l64dcon20s32
|
||||
}
|
||||
return l64Dcon
|
||||
}
|
||||
if lo20 == l64All0 {
|
||||
if hi20 == l64All0 {
|
||||
return l64Dcon12_12U
|
||||
return l64dcon1212u
|
||||
}
|
||||
if ((hi20 == l64St1 || hi20 == l64All1) && hi12 == l64All1) || (hi20 == l64St0 && hi12 == l64All0) {
|
||||
return l64Dcon20S_12U
|
||||
return l64dcon20s12u
|
||||
}
|
||||
return l64Dcon32_12U
|
||||
return l64dcon3212u
|
||||
}
|
||||
if lo20 == l64St1 || lo20 == l64All1 {
|
||||
if hi20 == l64All1 {
|
||||
return l64Dcon12_32S
|
||||
return l64dcon1232s
|
||||
}
|
||||
if (hi20 == l64St1 && hi12 == l64All1) || ((hi20 == l64St0 || hi20 == l64All0) && hi12 == l64All0) {
|
||||
return l64Dcon20S_32
|
||||
return l64dcon20s32
|
||||
}
|
||||
return l64Dcon
|
||||
}
|
||||
if hi20 == l64All0 {
|
||||
return l64Dcon12_32S
|
||||
return l64dcon1232s
|
||||
}
|
||||
if ((hi20 == l64St1 || hi20 == l64All1) && hi12 == l64All1) || (hi20 == l64St0 && hi12 == l64All0) {
|
||||
return l64Dcon20S_32
|
||||
return l64dcon20s32
|
||||
}
|
||||
return l64Dcon
|
||||
}
|
||||
@@ -1088,29 +1086,29 @@ func l64DconMovWords(rd int, v int64) []uint32 {
|
||||
ori = 0x00e << 22
|
||||
)
|
||||
switch l64DconClass(v) {
|
||||
case l64Dcon12_0:
|
||||
case l64dcon120:
|
||||
return []uint32{l64irr(lu52id, int(v>>52), 0, rd)}
|
||||
case l64Dcon12_20S:
|
||||
case l64dcon1220s:
|
||||
return []uint32{l64ir(lu12iw, int(v>>12), rd), l64irr(lu52id, int(v>>52), rd, rd)}
|
||||
case l64Dcon20S_20:
|
||||
case l64dcon20s20:
|
||||
return []uint32{l64ir(lu12iw, int(v>>12), rd), l64ir(lu32id, int(v>>32), rd)}
|
||||
case l64Dcon12_12S:
|
||||
case l64dcon1212s:
|
||||
return []uint32{l64irr(addid, int(v), 0, rd), l64irr(lu52id, int(v>>52), rd, rd)}
|
||||
case l64Dcon20S_12S, l64Dcon20S_0:
|
||||
case l64dcon20s12s, l64dcon20s0:
|
||||
return []uint32{l64irr(addiw, int(v), 0, rd), l64ir(lu32id, int(v>>32), rd)}
|
||||
case l64Dcon12_12U:
|
||||
case l64dcon1212u:
|
||||
return []uint32{l64irr(ori, int(v), 0, rd), l64irr(lu52id, int(v>>52), rd, rd)}
|
||||
case l64Dcon20S_12U:
|
||||
case l64dcon20s12u:
|
||||
return []uint32{l64irr(ori, int(v), 0, rd), l64ir(lu32id, int(v>>32), rd)}
|
||||
case l64Dcon32_12S, l64Dcon32_0:
|
||||
case l64dcon3212s, l64dcon320:
|
||||
return []uint32{l64irr(addiw, int(v), 0, rd), l64ir(lu32id, int(v>>32), rd), l64irr(lu52id, int(v>>52), rd, rd)}
|
||||
case l64Dcon32_20:
|
||||
case l64dcon3220:
|
||||
return []uint32{l64ir(lu12iw, int(v>>12), rd), l64ir(lu32id, int(v>>32), rd), l64irr(lu52id, int(v>>52), rd, rd)}
|
||||
case l64Dcon12_32S:
|
||||
case l64dcon1232s:
|
||||
return []uint32{l64ir(lu12iw, int(v>>12), rd), l64irr(ori, int(v), rd, rd), l64irr(lu52id, int(v>>52), rd, rd)}
|
||||
case l64Dcon20S_32:
|
||||
case l64dcon20s32:
|
||||
return []uint32{l64ir(lu12iw, int(v>>12), rd), l64irr(ori, int(v), rd, rd), l64ir(lu32id, int(v>>32), rd)}
|
||||
case l64Dcon32_12U:
|
||||
case l64dcon3212u:
|
||||
return []uint32{l64irr(ori, int(v), 0, rd), l64ir(lu32id, int(v>>32), rd), l64irr(lu52id, int(v>>52), rd, rd)}
|
||||
default:
|
||||
return []uint32{l64ir(lu12iw, int(v>>12), rd), l64irr(ori, int(v), rd, rd), l64ir(lu32id, int(v>>32), rd), l64irr(lu52id, int(v>>52), rd, rd)}
|
||||
@@ -1161,7 +1159,7 @@ func encodeLOONG64LoadImm(rd int, v int64, mnem string) []byte {
|
||||
// encodeLOONG64MemOp encodes a memory load (load = true) or store with a
|
||||
// 12-bit offset, or the 3-instruction expansion for larger offsets:
|
||||
// lu12i.w r30, (off+0x800)>>12; add.d r30, rj, r30; ld/st rd, off(r30).
|
||||
func encodeLOONG64MemOp(mnem string, mem *ast.Operand, reg int, load bool, fi loong64FrameInfo, relocs *[]Reloc) ([]byte, error) {
|
||||
func encodeLOONG64MemOp(mnem string, mem *ast.Operand, reg int, load bool, fi loong64FrameInfo) ([]byte, error) {
|
||||
rj, off := l64MemWithFrame(mem, fi)
|
||||
if rj < 0 {
|
||||
return nil, fmt.Errorf("invalid memory operand")
|
||||
@@ -1266,7 +1264,7 @@ func l64FpMoveKey(mnem string, sc, dc l64RegClass) (string, bool) {
|
||||
|
||||
// encodeLOONG64SBAddr emits pcalau12i rd, 0; addi.d rd, rd, 0 with the
|
||||
// R_LOONG64_ADDR_HI/LO relocation pair, loading a symbol's address.
|
||||
func encodeLOONG64SBAddr(sym *ast.Symbol, rd int, mnem string, relocs *[]Reloc) []byte {
|
||||
func encodeLOONG64SBAddr(sym *ast.Symbol, rd int, relocs *[]Reloc) []byte {
|
||||
if relocs != nil {
|
||||
*relocs = append(*relocs,
|
||||
Reloc{Off: 0, After: 0, Name: sym.Name, Kind: RelLoong64AddrHi, Addend: sym.Offset},
|
||||
|
||||
@@ -105,10 +105,10 @@ func loong64RegNum(name string) int {
|
||||
}
|
||||
// F0–F31, FCC0–FCC7, FCSR0–FCSR31.
|
||||
if len(name) >= 4 && name[:4] == "FCSR" {
|
||||
return loong64RegSpecial(name[4:], "FCSR", 31)
|
||||
return loong64RegSpecial(name[4:], 31)
|
||||
}
|
||||
if len(name) >= 3 && name[:3] == "FCC" {
|
||||
return loong64RegSpecial(name[3:], "FCC", 7)
|
||||
return loong64RegSpecial(name[3:], 7)
|
||||
}
|
||||
if len(name) < 2 {
|
||||
return -1
|
||||
@@ -131,7 +131,7 @@ func loong64RegNum(name string) int {
|
||||
}
|
||||
|
||||
// loong64RegSpecial parses a numbered FCC/FCSR register.
|
||||
func loong64RegSpecial(digits, prefix string, max int) int {
|
||||
func loong64RegSpecial(digits string, max int) int {
|
||||
if digits == "" {
|
||||
return -1
|
||||
}
|
||||
|
||||
+18
-18
@@ -65,28 +65,28 @@ var (
|
||||
CX = Reg{idx: 1, size: 2}
|
||||
DX = Reg{idx: 2, size: 2}
|
||||
BX = Reg{idx: 3, size: 2}
|
||||
SP = Reg{idx: 4, size: 2}
|
||||
BP = Reg{idx: 5, size: 2}
|
||||
_ = Reg{idx: 4, size: 2}
|
||||
_ = Reg{idx: 5, size: 2}
|
||||
SI = Reg{idx: 6, size: 2}
|
||||
DI = Reg{idx: 7, size: 2}
|
||||
|
||||
EAX = Reg{idx: 0, size: 4}
|
||||
ECX = Reg{idx: 1, size: 4}
|
||||
EDX = Reg{idx: 2, size: 4}
|
||||
EBX = Reg{idx: 3, size: 4}
|
||||
ESP = Reg{idx: 4, size: 4}
|
||||
EBP = Reg{idx: 5, size: 4}
|
||||
ESI = Reg{idx: 6, size: 4}
|
||||
EDI = Reg{idx: 7, size: 4}
|
||||
_ = Reg{idx: 0, size: 4}
|
||||
_ = Reg{idx: 1, size: 4}
|
||||
_ = Reg{idx: 2, size: 4}
|
||||
_ = Reg{idx: 3, size: 4}
|
||||
_ = Reg{idx: 4, size: 4}
|
||||
_ = Reg{idx: 5, size: 4}
|
||||
_ = Reg{idx: 6, size: 4}
|
||||
_ = Reg{idx: 7, size: 4}
|
||||
|
||||
RAX = Reg{idx: 0, size: 8}
|
||||
RCX = Reg{idx: 1, size: 8}
|
||||
RDX = Reg{idx: 2, size: 8}
|
||||
RBX = Reg{idx: 3, size: 8}
|
||||
RSP = Reg{idx: 4, size: 8}
|
||||
RBP = Reg{idx: 5, size: 8}
|
||||
RSI = Reg{idx: 6, size: 8}
|
||||
RDI = Reg{idx: 7, size: 8}
|
||||
_ = Reg{idx: 0, size: 8}
|
||||
_ = Reg{idx: 1, size: 8}
|
||||
_ = Reg{idx: 2, size: 8}
|
||||
_ = Reg{idx: 3, size: 8}
|
||||
_ = Reg{idx: 4, size: 8}
|
||||
_ = Reg{idx: 5, size: 8}
|
||||
_ = Reg{idx: 6, size: 8}
|
||||
_ = Reg{idx: 7, size: 8}
|
||||
)
|
||||
|
||||
// regByName maps an assembly register name (case-insensitive) to a Reg.
|
||||
|
||||
@@ -228,7 +228,7 @@ func encodeRISCVInstr(instr *ast.Instr, pc int, offsets map[string]int, fi riscv
|
||||
// MOV is a pseudo-instruction that the Go assembler uses for loads,
|
||||
// stores, register moves and immediate loads.
|
||||
case "MOV":
|
||||
return encodeRISCVMov(instr, offsets, fi, relocs)
|
||||
return encodeRISCVMov(instr, fi, relocs)
|
||||
|
||||
// JALR: indirect jump/call. Plan 9: JALR rs1, rd or JALR offset(rs1).
|
||||
case "JALR":
|
||||
@@ -527,7 +527,7 @@ func isImmOperand(op *ast.Operand) bool {
|
||||
// - MOV Rs, (Rd) register-relative store
|
||||
// - MOV Rs, Rd register-to-register move (ADDI $0)
|
||||
// - MOV $imm, Rd load immediate (ADDI or LUI+ADDIW)
|
||||
func encodeRISCVMov(instr *ast.Instr, offsets map[string]int, fi riscvFrameInfo, relocs *[]Reloc) ([]byte, error) {
|
||||
func encodeRISCVMov(instr *ast.Instr, fi riscvFrameInfo, relocs *[]Reloc) ([]byte, error) {
|
||||
ops := instr.Operands
|
||||
if len(ops) != 2 {
|
||||
return nil, fmt.Errorf("MOV expects 2 operands, got %d", len(ops))
|
||||
@@ -911,7 +911,7 @@ func tryCompressRVC(instr *ast.Instr, fi riscvFrameInfo) (uint16, bool) {
|
||||
}
|
||||
|
||||
case "ADDI":
|
||||
rd, rs1, imm := extractITypeParams(instr, fi)
|
||||
rd, rs1, imm := extractITypeParams(instr)
|
||||
if rd == -1 || rs1 == -1 {
|
||||
return 0, false
|
||||
}
|
||||
@@ -1060,13 +1060,13 @@ func tryCompressRVC(instr *ast.Instr, fi riscvFrameInfo) (uint16, bool) {
|
||||
}
|
||||
|
||||
case "ADDIW":
|
||||
rd, rs1, imm := extractITypeParams(instr, fi)
|
||||
rd, rs1, imm := extractITypeParams(instr)
|
||||
if rd == rs1 && rd != 0 && imm >= -32 && imm <= 31 {
|
||||
return rvcCI(0x1, uint32(rd), uint32(imm)&0x3F), true
|
||||
}
|
||||
|
||||
case "SLLI", "SRLI", "SRAI":
|
||||
rd, rs1, imm := extractITypeParams(instr, fi)
|
||||
rd, rs1, imm := extractITypeParams(instr)
|
||||
if rd == rs1 && rd != 0 && imm != 0 && imm >= 1 && imm <= 63 {
|
||||
if mnem == "SLLI" {
|
||||
// C.SLLI: funct3=0, op=10 quadrant, shamt in bits [12|6:2].
|
||||
@@ -1083,7 +1083,7 @@ func tryCompressRVC(instr *ast.Instr, fi riscvFrameInfo) (uint16, bool) {
|
||||
}
|
||||
|
||||
case "ANDI":
|
||||
rd, rs1, imm := extractITypeParams(instr, fi)
|
||||
rd, rs1, imm := extractITypeParams(instr)
|
||||
if isRVCIntReg(rd) && rd == rs1 && imm >= -32 && imm <= 31 {
|
||||
// C.ANDI: CB-type, funct3=0x4, funct2=0x2.
|
||||
return rvcCBShift(0x2, rvcReg3(rd), uint32(imm)&0x3F), true
|
||||
@@ -1131,7 +1131,7 @@ func extractSDParams(instr *ast.Instr, fi riscvFrameInfo) (rs2, rs1 int, imm int
|
||||
// extractITypeParams extracts rd, rs1, and immediate for an I-type
|
||||
// instruction. The Plan 9 order is INSTR $imm, rs1, rd (3 operands) or
|
||||
// INSTR $imm, rd (2 operands, rd is also the source).
|
||||
func extractITypeParams(instr *ast.Instr, fi riscvFrameInfo) (rd, rs1 int, imm int32) {
|
||||
func extractITypeParams(instr *ast.Instr) (rd, rs1 int, imm int32) {
|
||||
ops := instr.Operands
|
||||
switch len(ops) {
|
||||
case 3:
|
||||
|
||||
+1
-3
@@ -123,10 +123,8 @@ type Symbol struct {
|
||||
// OpKind classifies an operand syntactically.
|
||||
type OpKind int
|
||||
|
||||
// Operand kinds.
|
||||
const (
|
||||
OpInvalid OpKind = iota
|
||||
OpImmediate // $value
|
||||
OpImmediate = iota // $value
|
||||
OpAddr // register, memory reference, symbol or label
|
||||
)
|
||||
|
||||
|
||||
+2
-2
@@ -54,11 +54,11 @@ func TestStmtPositions(t *testing.T) {
|
||||
// TestInterfaces confirms the node types satisfy their interfaces, so callers
|
||||
// can range over Decls and Stmts.
|
||||
func TestInterfaces(t *testing.T) {
|
||||
var decls []Decl = []Decl{&Include{}, &Preproc{}, &Text{}, &Globl{}, &Data{}}
|
||||
var decls = []Decl{&Include{}, &Preproc{}, &Text{}, &Globl{}, &Data{}}
|
||||
if len(decls) != 5 {
|
||||
t.Fatal("decl interface set")
|
||||
}
|
||||
var stmts []Stmt = []Stmt{&Label{}, &Instr{}}
|
||||
var stmts = []Stmt{&Label{}, &Instr{}}
|
||||
if len(stmts) != 2 {
|
||||
t.Fatal("stmt interface set")
|
||||
}
|
||||
|
||||
+30
-30
@@ -299,7 +299,7 @@ given.
|
||||
rc = 1
|
||||
continue
|
||||
}
|
||||
out := format.Source(path, src)
|
||||
out := format.Source(src)
|
||||
if dirMode || *write {
|
||||
if out != src {
|
||||
if err := os.WriteFile(path, []byte(out), 0o644); err != nil {
|
||||
@@ -447,7 +447,7 @@ requires -p, the package path, and the installed Go toolchain).
|
||||
return 1
|
||||
}
|
||||
|
||||
img, err := assembleFile(path, targetArch, f)
|
||||
img, err := assembleFile(targetArch, f)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s: %v\n", path, err)
|
||||
return 1
|
||||
@@ -544,7 +544,7 @@ requires -p, the package path, and the installed Go toolchain).
|
||||
|
||||
// cmdDiff compares the machine code of two assembly files.
|
||||
func cmdDiff(args []string) int {
|
||||
fs := newCommand("diff", "gasm diff <file1.s> <file2.s>", `
|
||||
set := newCommand("diff", "gasm diff <file1.s> <file2.s>", `
|
||||
Compare the machine code produced by assembling two files.
|
||||
Shows which functions differ and the byte-level differences.
|
||||
Useful for verifying that two implementations produce identical code,
|
||||
@@ -553,13 +553,13 @@ or for tracking encoding changes between Go assembler versions.
|
||||
Use --map to compare functions whose names differ between the files,
|
||||
e.g. --map wideCopyAVX2=wideCopyAVX512 pairs the two regardless of suffix.
|
||||
`)
|
||||
mapSpec := fs.String("map", "", "comma-separated old=new pairs to match functions with different names")
|
||||
fs.Parse(args)
|
||||
if fs.NArg() != 2 {
|
||||
mapSpec := set.String("map", "", "comma-separated old=new pairs to match functions with different names")
|
||||
set.Parse(args)
|
||||
if set.NArg() != 2 {
|
||||
fmt.Fprintln(os.Stderr, "usage: gasm diff <file1.s> <file2.s>")
|
||||
return 2
|
||||
}
|
||||
path1, path2 := fs.Arg(0), fs.Arg(1)
|
||||
path1, path2 := set.Arg(0), set.Arg(1)
|
||||
|
||||
// Parse the name mapping (file1 name → file2 name).
|
||||
nameMap := make(map[string]string)
|
||||
@@ -640,7 +640,7 @@ e.g. --map wideCopyAVX2=wideCopyAVX512 pairs the two regardless of suffix.
|
||||
}
|
||||
|
||||
// assembleFile assembles a parsed file for the given architecture and returns the image.
|
||||
func assembleFile(path string, targetArch arch.Arch, f *ast.File) (*asm.Image, error) {
|
||||
func assembleFile(targetArch arch.Arch, f *ast.File) (*asm.Image, error) {
|
||||
switch targetArch {
|
||||
case arch.AMD64:
|
||||
return asm.AssembleFile(f)
|
||||
@@ -668,7 +668,7 @@ func assemblePath(path string) (*asm.Image, error) {
|
||||
if len(errs) > 0 {
|
||||
return nil, fmt.Errorf("parse errors")
|
||||
}
|
||||
return assembleFile(path, arch.FromFilename(path), f)
|
||||
return assembleFile(arch.FromFilename(path), f)
|
||||
}
|
||||
|
||||
// printByteDiff shows the first few byte differences between two code blocks.
|
||||
@@ -688,18 +688,18 @@ func printByteDiff(a, b []byte) {
|
||||
|
||||
// cmdProfile shows the basic-block structure of functions in an assembly file.
|
||||
func cmdProfile(args []string) int {
|
||||
fs := newCommand("profile", "gasm profile <file.s>", `
|
||||
flagSet := newCommand("profile", "gasm profile <file.s>", `
|
||||
Show the basic-block structure of functions in an assembly file.
|
||||
Lists each function's labels, their offsets, and the block boundaries.
|
||||
This is the static structure; for runtime execution counts, use
|
||||
gasm verify --fuzz which exercises the code paths.
|
||||
`)
|
||||
fs.Parse(args)
|
||||
if fs.NArg() != 1 {
|
||||
flagSet.Parse(args)
|
||||
if flagSet.NArg() != 1 {
|
||||
fmt.Fprintln(os.Stderr, "usage: gasm profile <file.s>")
|
||||
return 2
|
||||
}
|
||||
path := fs.Arg(0)
|
||||
path := flagSet.Arg(0)
|
||||
|
||||
// Load the file to get function metadata.
|
||||
k, err := verify.Load(path)
|
||||
@@ -1021,7 +1021,7 @@ func cmdVerifyARM64(path string, groundTruth, profile bool) int {
|
||||
}
|
||||
|
||||
func cmdVerify(args []string) int {
|
||||
fs := newCommand("verify", "gasm verify [-smoke] [-abi] [-fuzz] [-ground-truth] [-profile] [-call] <file.s>", `
|
||||
set := newCommand("verify", "gasm verify [-smoke] [-abi] [-fuzz] [-ground-truth] [-profile] [-call] <file.s>", `
|
||||
Assemble FILE (amd64), map it into executable memory and report the available
|
||||
functions. This confirms the assembled image is self-consistent (no
|
||||
unresolved external symbols) and executable — the prerequisite for dynamic
|
||||
@@ -1047,25 +1047,25 @@ With -call, a single function is invoked with user-supplied buffers (-buf)
|
||||
instead of the smoke/abi/fuzz sweeps. Useful for partial functions (e.g.
|
||||
decoders) that crash on random input but should succeed on valid data.
|
||||
`)
|
||||
smoke := fs.Bool("smoke", false, "call each NOSPLIT function with zeroed args")
|
||||
abi := fs.Bool("abi", false, "run ABI-checking calls (sentinel registers + red zone)")
|
||||
abiN := fs.Int("abi-n", 100, "number of ABI check iterations with varied inputs")
|
||||
profile := fs.Bool("profile", false, "list basic-block structure per function")
|
||||
groundTruth := fs.Bool("ground-truth", false, "compare machine code byte-for-byte against go tool asm")
|
||||
fuzz := fs.Bool("fuzz", false, "differential fuzz: JIT both gasm and go-tool-asm versions, compare outputs")
|
||||
fuzzN := fs.Int("n", 1000, "number of fuzz iterations per function")
|
||||
fuzzOne := fs.String("fuzz-one", "", "") // hidden: fuzz a single function (subprocess mode)
|
||||
sweepOne := fs.String("sweep-one", "", "") // hidden: smoke/abi a single function (subprocess mode)
|
||||
call := fs.String("call", "", "call a single function with -buf instead of the sweeps")
|
||||
bufSpec := fs.String("buf", "", "buffer spec for -call: name:size:pattern[,name:size:pattern] (zero, ones, seq, or hex)")
|
||||
scalarSpec := fs.String("args", "", "scalar args for -call: name=value[,name=value] (decimal or 0x hex)")
|
||||
repeat := fs.Int("repeat", 1, "number of times to repeat a -call invocation")
|
||||
fs.Parse(args)
|
||||
if fs.NArg() != 1 {
|
||||
smoke := set.Bool("smoke", false, "call each NOSPLIT function with zeroed args")
|
||||
abi := set.Bool("abi", false, "run ABI-checking calls (sentinel registers + red zone)")
|
||||
abiN := set.Int("abi-n", 100, "number of ABI check iterations with varied inputs")
|
||||
profile := set.Bool("profile", false, "list basic-block structure per function")
|
||||
groundTruth := set.Bool("ground-truth", false, "compare machine code byte-for-byte against go tool asm")
|
||||
fuzz := set.Bool("fuzz", false, "differential fuzz: JIT both gasm and go-tool-asm versions, compare outputs")
|
||||
fuzzN := set.Int("n", 1000, "number of fuzz iterations per function")
|
||||
fuzzOne := set.String("fuzz-one", "", "") // hidden: fuzz a single function (subprocess mode)
|
||||
sweepOne := set.String("sweep-one", "", "") // hidden: smoke/abi a single function (subprocess mode)
|
||||
call := set.String("call", "", "call a single function with -buf instead of the sweeps")
|
||||
bufSpec := set.String("buf", "", "buffer spec for -call: name:size:pattern[,name:size:pattern] (zero, ones, seq, or hex)")
|
||||
scalarSpec := set.String("args", "", "scalar args for -call: name=value[,name=value] (decimal or 0x hex)")
|
||||
repeat := set.Int("repeat", 1, "number of times to repeat a -call invocation")
|
||||
set.Parse(args)
|
||||
if set.NArg() != 1 {
|
||||
fmt.Fprintln(os.Stderr, "usage: gasm verify [-smoke] [-abi] [-fuzz] [-ground-truth] [-profile] [-call] <file.s>")
|
||||
return 2
|
||||
}
|
||||
path := fs.Arg(0)
|
||||
path := set.Arg(0)
|
||||
targetArch := arch.FromFilename(path)
|
||||
switch targetArch {
|
||||
case arch.AMD64:
|
||||
|
||||
@@ -30,7 +30,7 @@ func mapRWX(code []byte) ([]byte, error) {
|
||||
}
|
||||
|
||||
// setupBuffers allocates buffers in the debuggee's memory.
|
||||
func setupBuffers(spec string, args []byte, argSize int, tmpDir string) ([]byte, error) {
|
||||
func setupBuffers(spec string, args []byte, tmpDir string) ([]byte, error) {
|
||||
type bufSpec struct {
|
||||
name string
|
||||
size int
|
||||
|
||||
@@ -77,7 +77,7 @@ func RunTarget(asmPath, funcName, argsFile, tmpDir string) error {
|
||||
|
||||
bufSpecFile := tmpDir + "/bufspec"
|
||||
if bufSpec, err := os.ReadFile(bufSpecFile); err == nil && len(bufSpec) > 0 {
|
||||
args, err = setupBuffers(string(bufSpec), args, fl.Args, tmpDir)
|
||||
args, err = setupBuffers(string(bufSpec), args, tmpDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("debug target: setup buffers: %w", err)
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ func RunTarget(asmPath, funcName, argsFile, tmpDir string) error {
|
||||
stackArgs := make([]byte, fl.Args)
|
||||
copy(stackArgs, args)
|
||||
|
||||
_, callErr := verify.Call(fnAddr, stackArgs)
|
||||
_, callErr := verify.Call(stackArgs)
|
||||
if callErr != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ func RunTarget(asmPath, funcName, argsFile, tmpDir string) error {
|
||||
|
||||
bufSpecFile := tmpDir + "/bufspec"
|
||||
if bufSpec, err := os.ReadFile(bufSpecFile); err == nil && len(bufSpec) > 0 {
|
||||
args, err = setupBuffers(string(bufSpec), args, fl.Args, tmpDir)
|
||||
args, err = setupBuffers(string(bufSpec), args, tmpDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("debug target: setup buffers: %w", err)
|
||||
}
|
||||
@@ -107,7 +107,7 @@ func RunTarget(asmPath, funcName, argsFile, tmpDir string) error {
|
||||
stackArgs := make([]byte, fl.Args)
|
||||
copy(stackArgs, args)
|
||||
|
||||
_, callErr := verify.Call(fnAddr, stackArgs)
|
||||
_, callErr := verify.Call(stackArgs)
|
||||
if callErr != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ func RunTarget(asmPath, funcName, argsFile, tmpDir string) error {
|
||||
|
||||
bufSpecFile := tmpDir + "/bufspec"
|
||||
if bufSpec, err := os.ReadFile(bufSpecFile); err == nil && len(bufSpec) > 0 {
|
||||
args, err = setupBuffers(string(bufSpec), args, fl.Args, tmpDir)
|
||||
args, err = setupBuffers(string(bufSpec), args, tmpDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("debug target: setup buffers: %w", err)
|
||||
}
|
||||
@@ -107,7 +107,7 @@ func RunTarget(asmPath, funcName, argsFile, tmpDir string) error {
|
||||
stackArgs := make([]byte, fl.Args)
|
||||
copy(stackArgs, args)
|
||||
|
||||
_, callErr := verify.Call(fnAddr, stackArgs)
|
||||
_, callErr := verify.Call(stackArgs)
|
||||
if callErr != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ Repository: [sourcedock.dev/petrbalvin/gasm-devkit](https://sourcedock.dev/petrb
|
||||
graph TD
|
||||
SRC["source .s"] --> LEX["lexer<br/>token stream"]
|
||||
LEX --> PAR["parser<br/>AST + diagnostics"]
|
||||
LEX --> FMT["format<br/>re-space tokens"]
|
||||
LEX --> FMT["s<br/>re-space tokens"]
|
||||
PAR --> LINT["lint<br/>static checks"]
|
||||
PAR --> LSP["lsp server"]
|
||||
LEX --> LSP
|
||||
@@ -157,7 +157,7 @@ Two deeper analyses sit on top of the AST:
|
||||
reference) and a literal index is range-checked; a named index constant such
|
||||
as `$PCDATA_StackMapIndex` is accepted without a range check.
|
||||
|
||||
### `format`
|
||||
### `s`
|
||||
|
||||
The formatter works on the **token stream, not the AST**, so it preserves
|
||||
every line — comments and blanks included. It normalises indentation, operand
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ why, the options on the table, and the trigger that should reopen it.
|
||||
**Approach taken.** Instead of parsing the compiler's iexport data (which
|
||||
would have required either `golang.org/x/tools` or an in-house parser), the
|
||||
resolver reads the **GOOBJ data directly** from the target package's `.a`
|
||||
archive. The `.a` file contains a `_go_.o` member whose GOOBJ format is the
|
||||
archive. The `.a` file contains a `_go_.o` member whose GOOBJ s is the
|
||||
same one gasm writes — the parser reuses the same layout (`blkSymdef`,
|
||||
`blkNonpkgdef`, the string table), so no new dependency was needed.
|
||||
|
||||
|
||||
+1
-1
@@ -97,7 +97,7 @@ ast/ Abstract syntax tree
|
||||
parser/ Line-oriented parser
|
||||
arch/ Register and instruction tables (generated)
|
||||
lint/ Static analysis rules
|
||||
format/ Canonical formatter
|
||||
s/ Canonical formatter
|
||||
lsp/ Language Server Protocol server
|
||||
asm/ Standalone assembler, encoder, object emitters
|
||||
verify/ JIT execution, differential testing, ABI checks
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
// Source returns the canonical formatting of src.
|
||||
func Source(path, src string) string {
|
||||
func Source(src string) string {
|
||||
lines := splitLines(lexer.Tokenize(src))
|
||||
|
||||
// First pass: classify each line and record, for every instruction, the
|
||||
|
||||
@@ -32,7 +32,7 @@ func TestGolden(t *testing.T) {
|
||||
"\tVFMADD231PD Z14, Z12, Z10\n" +
|
||||
"\tRET\n"
|
||||
|
||||
got := Source("f_amd64.s", in)
|
||||
got := Source(in)
|
||||
if got != want {
|
||||
t.Fatalf("formatting mismatch:\n--- got ---\n%q\n--- want ---\n%q", got, want)
|
||||
}
|
||||
@@ -64,13 +64,13 @@ func TestDocCommentIndent(t *testing.T) {
|
||||
"TEXT ·second(SB), NOSPLIT, $0\n" +
|
||||
"\tRET\n"
|
||||
|
||||
got := Source("d_amd64.s", in)
|
||||
got := Source(in)
|
||||
if got != want {
|
||||
t.Fatalf("formatting mismatch:\n--- got ---\n%q\n--- want ---\n%q", got, want)
|
||||
}
|
||||
// Body comments stay indented.
|
||||
body := "#include \"textflag.h\"\nTEXT ·f(SB), NOSPLIT, $0\n// inside the body\nXORQ AX, AX\nRET\n"
|
||||
gotBody := Source("b_amd64.s", body)
|
||||
gotBody := Source(body)
|
||||
if !strings.Contains(gotBody, "\t// inside the body\n") {
|
||||
t.Fatalf("body comment must stay indented:\n%q", gotBody)
|
||||
}
|
||||
@@ -125,11 +125,11 @@ func TestBlankLines(t *testing.T) {
|
||||
"GLOBL ·mask(SB), RODATA, $8\n" +
|
||||
"DATA ·mask+0(SB)/4, $1\n"
|
||||
|
||||
got := Source("b_amd64.s", in)
|
||||
got := Source(in)
|
||||
if got != want {
|
||||
t.Fatalf("formatting mismatch:\n--- got ---\n%q\n--- want ---\n%q", got, want)
|
||||
}
|
||||
if again := Source("b_amd64.s", got); again != got {
|
||||
if again := Source(got); again != got {
|
||||
t.Fatalf("not idempotent:\n%q", again)
|
||||
}
|
||||
}
|
||||
@@ -163,8 +163,8 @@ func TestIdempotent(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
once := Source("sample_amd64.s", string(src))
|
||||
twice := Source("sample_amd64.s", once)
|
||||
once := Source(string(src))
|
||||
twice := Source(once)
|
||||
if once != twice {
|
||||
t.Fatal("formatting is not idempotent on the fixture")
|
||||
}
|
||||
@@ -179,7 +179,7 @@ func TestRoundTrip(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
formatted := Source(path, string(src))
|
||||
formatted := Source(string(src))
|
||||
if _, errs := parser.Parse(path, formatted); len(errs) > 0 {
|
||||
t.Errorf("formatted %s no longer parses: %v", path, errs)
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ func TestRoundTripRealGoLibraries(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
formatted := Source(path, string(src))
|
||||
formatted := Source(string(src))
|
||||
if _, errs := parser.Parse(path, formatted); len(errs) > 0 {
|
||||
t.Errorf("formatted %s no longer parses: %v", path, errs)
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ test:
|
||||
sourcedock.dev/petrbalvin/gasm-devkit/arch \
|
||||
sourcedock.dev/petrbalvin/gasm-devkit/asm \
|
||||
sourcedock.dev/petrbalvin/gasm-devkit/ast \
|
||||
sourcedock.dev/petrbalvin/gasm-devkit/format \
|
||||
sourcedock.dev/petrbalvin/gasm-devkit/s \
|
||||
sourcedock.dev/petrbalvin/gasm-devkit/lexer \
|
||||
sourcedock.dev/petrbalvin/gasm-devkit/lint \
|
||||
sourcedock.dev/petrbalvin/gasm-devkit/lsp \
|
||||
|
||||
+3
-5
@@ -212,7 +212,7 @@ func (s *Server) documentFormatting(p documentFormattingParams) []TextEdit {
|
||||
if text == "" {
|
||||
return nil
|
||||
}
|
||||
formatted := format.Source(uriPath(p.TextDocument.URI), text)
|
||||
formatted := format.Source(text)
|
||||
if formatted == text {
|
||||
return nil
|
||||
}
|
||||
@@ -596,8 +596,7 @@ func classifyLine(line []token.Token, a *arch.Table, labels map[string]bool) []s
|
||||
case token.Hash:
|
||||
typ = stMacro
|
||||
case token.Ident:
|
||||
typ = classifyIdent(line, i, first, t.Text, a, labels,
|
||||
isDirective, isLabel, isInstr, &mnemonicDone)
|
||||
typ = classifyIdent(i, first, t.Text, a, labels, isDirective, isLabel, isInstr, &mnemonicDone)
|
||||
case token.Colon, token.Comma, token.LParen, token.RParen,
|
||||
token.Plus, token.Minus, token.Star, token.Slash, token.Dollar,
|
||||
token.LAngle, token.RAngle, token.LShift, token.RShift, token.Arrow, token.At:
|
||||
@@ -616,8 +615,7 @@ func classifyLine(line []token.Token, a *arch.Table, labels map[string]bool) []s
|
||||
}
|
||||
|
||||
// classifyIdent decides the semantic type of an identifier token.
|
||||
func classifyIdent(line []token.Token, i, first int, text string, a *arch.Table,
|
||||
labels map[string]bool, isDirective, isLabel, isInstr bool, mnemonicDone *bool) int {
|
||||
func classifyIdent(i, first int, text string, a *arch.Table, labels map[string]bool, isDirective, isLabel, isInstr bool, mnemonicDone *bool) int {
|
||||
|
||||
upper := strings.ToUpper(text)
|
||||
switch {
|
||||
|
||||
+1
-8
@@ -29,13 +29,8 @@ type rpcError struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// JSON-RPC error codes used by LSP.
|
||||
const (
|
||||
errParse = -32700
|
||||
errInvalidRequest = -32600
|
||||
errMethodNotFound = -32601
|
||||
errInvalidParams = -32602
|
||||
errInternal = -32603
|
||||
)
|
||||
|
||||
// --- LSP positions and ranges ----------------------------------------------
|
||||
@@ -119,16 +114,14 @@ type didCloseParams struct {
|
||||
|
||||
// --- completion -------------------------------------------------------------
|
||||
|
||||
// Completion item kinds (a useful subset).
|
||||
const (
|
||||
ciFunction = 3
|
||||
ciField = 5
|
||||
|
||||
ciVariable = 6
|
||||
ciClass = 7
|
||||
ciModule = 9
|
||||
ciKeyword = 14
|
||||
ciConstant = 21
|
||||
ciStruct = 22
|
||||
)
|
||||
|
||||
// CompletionItem is one completion suggestion.
|
||||
|
||||
+2
-2
@@ -19,13 +19,13 @@ const (
|
||||
Newline
|
||||
Comment
|
||||
|
||||
// Literals and names.
|
||||
// Ident Literals and names.
|
||||
Ident // instruction mnemonic, label, register or symbol name
|
||||
Number // integer or floating-point literal (sign carried separately)
|
||||
String // "..."
|
||||
Rune // '.'
|
||||
|
||||
// Punctuation and operators.
|
||||
// LParen Punctuation and operators.
|
||||
LParen // (
|
||||
RParen // )
|
||||
Comma // ,
|
||||
|
||||
+3
-2
@@ -89,8 +89,9 @@ const redZoneSize = 128
|
||||
// redZoneFill is the byte pattern used to detect red-zone writes.
|
||||
const redZoneFill = 0xA5
|
||||
|
||||
// CallChecked invokes the function with ABI sentinels and a red-zone
|
||||
// canary, returning both the argument block (with results) and an ABIReport.
|
||||
// CallChecked invokes the function at fnAddr with ABI sentinels and a
|
||||
// red-zone canary, returning both the argument block (with results) and an
|
||||
// ABIReport.
|
||||
func CallChecked(fnAddr uintptr, args []byte) ([]byte, ABIReport, error) {
|
||||
report := ABIReport{}
|
||||
|
||||
|
||||
@@ -24,7 +24,10 @@ var savedSP uintptr
|
||||
|
||||
// enterJIT switches to the prepared stack and jumps to fn.
|
||||
// It does not return normally; the JIT function's RET transfers control
|
||||
// to leaveJIT, which restores the Go stack.
|
||||
// to leaveJIT, which restores the Go stack. The body lives in
|
||||
// trampoline_amd64.s and reads the parameters from the frame by name.
|
||||
//
|
||||
// noinspection GoUnusedParameter
|
||||
//
|
||||
//go:nosplit
|
||||
func enterJIT(fn uintptr, stack uintptr)
|
||||
|
||||
@@ -95,7 +95,6 @@ func groundTruthArch(path, goarch string) (map[string][]byte, error) {
|
||||
return extractGOOBJCode(objData)
|
||||
}
|
||||
|
||||
// GOOBJ block indices (cmd/internal/goobj).
|
||||
const (
|
||||
blkAutolib = iota
|
||||
blkPkgIdx
|
||||
|
||||
Reference in New Issue
Block a user