feat(asm): add LoongArch encoder with ELF and GOOBJ emission
Assisted-by: DeepSeek V4 Pro
This commit is contained in:
+116
-13
@@ -398,8 +398,8 @@ func cmdAsm(args []string) int {
|
||||
fs := newCommand("asm", "gasm asm [--format raw|elf|goobj] [-p pkg] [-o out] <file>", `
|
||||
Assemble FILE without the Go toolchain: every TEXT function is encoded to
|
||||
machine code and printed as a hex dump. Supported architectures: amd64
|
||||
(including VEX/AVX2 and EVEX/AVX-512) and riscv64 (RV64IMAFDC + RVC);
|
||||
arm64 and loong64 encoding is not yet implemented.
|
||||
(including VEX/AVX2 and EVEX/AVX-512), riscv64 (RV64IMAFDC + RVC) and
|
||||
loong64 (LoongArch base ISA); arm64 encoding is not yet implemented.
|
||||
|
||||
With -o the output is written to a file instead. The --format flag selects
|
||||
what is written: raw (the default) concatenates the functions and the data
|
||||
@@ -493,16 +493,22 @@ requires -p, the package path, and the installed Go toolchain).
|
||||
}
|
||||
obj, kind = img.Bytes(), "raw image"
|
||||
case "elf":
|
||||
if targetArch == arch.RISCV {
|
||||
switch targetArch {
|
||||
case arch.RISCV:
|
||||
obj, err = img.ELFRISCVObject()
|
||||
} else {
|
||||
case arch.LOONG64:
|
||||
obj, err = img.ELFLOONG64Object()
|
||||
default:
|
||||
obj, err = img.ELFObject()
|
||||
}
|
||||
kind = "ELF object"
|
||||
case "goobj":
|
||||
if targetArch == arch.RISCV {
|
||||
switch targetArch {
|
||||
case arch.RISCV:
|
||||
obj, err = img.GOObjectRISCV(*pkg, path)
|
||||
} else {
|
||||
case arch.LOONG64:
|
||||
obj, err = img.GOObjectLOONG64(*pkg, path)
|
||||
default:
|
||||
obj, err = img.GOObject(*pkg, path)
|
||||
}
|
||||
kind = "Go object"
|
||||
@@ -822,6 +828,99 @@ func cmdVerifyRISCV(path string, groundTruth, profile bool) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// cmdVerifyLOONG64 verifies a loong64 source file against `go tool asm`
|
||||
// (GOARCH=loong64) — the ground-truth oracle — since gasm cannot JIT-load
|
||||
// LoongArch code on an amd64 host. Relocation sites are masked before the
|
||||
// byte comparison, as the toolchain leaves them zero for the linker.
|
||||
func cmdVerifyLOONG64(path string, groundTruth, profile bool) int {
|
||||
src, err := readSource(path)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "gasm verify: %v\n", err)
|
||||
return 1
|
||||
}
|
||||
f, errs := parser.Parse(path, src)
|
||||
for _, e := range errs {
|
||||
fmt.Fprintf(os.Stderr, "%s: %v\n", path, e)
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return 1
|
||||
}
|
||||
img, err := asm.AssembleFileLOONG64(f)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "gasm verify: %v\n", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
if groundTruth {
|
||||
gt, err := verify.GroundTruthLOONG64(path)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "gasm verify: ground truth: %v\n", err)
|
||||
return 1
|
||||
}
|
||||
matched, total := 0, 0
|
||||
for _, fn := range img.Funcs {
|
||||
gasmCode := img.Code[fn.Offset : fn.Offset+fn.Size]
|
||||
goCode, ok := gt[fn.Name]
|
||||
if !ok {
|
||||
fmt.Printf(" %s: SKIP (not in go tool asm output)\n", fn.Name)
|
||||
continue
|
||||
}
|
||||
total++
|
||||
gasmCmp := make([]byte, len(gasmCode))
|
||||
goCmp := make([]byte, len(goCode))
|
||||
copy(gasmCmp, gasmCode)
|
||||
copy(goCmp, goCode)
|
||||
for _, r := range fn.Relocs {
|
||||
for j := r.Off; j < r.Off+4 && j < len(gasmCmp); j++ {
|
||||
gasmCmp[j] = 0
|
||||
}
|
||||
for j := r.Off; j < r.Off+4 && j < len(goCmp); j++ {
|
||||
goCmp[j] = 0
|
||||
}
|
||||
}
|
||||
if bytes.Equal(gasmCmp, goCmp) {
|
||||
matched++
|
||||
if len(fn.Relocs) > 0 {
|
||||
fmt.Printf(" %s: MATCH (%d bytes, %d relocs masked)\n", fn.Name, fn.Size, len(fn.Relocs))
|
||||
} else {
|
||||
fmt.Printf(" %s: MATCH (%d bytes)\n", fn.Name, fn.Size)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf(" %s: MISMATCH (%d vs %d bytes)\n", fn.Name, fn.Size, len(goCode))
|
||||
for i := 0; i < len(gasmCode) || i < len(goCode); i += 16 {
|
||||
var gb, gs string
|
||||
for j := i; j < i+16 && j < len(gasmCode); j++ {
|
||||
gb += fmt.Sprintf(" %02x", gasmCode[j])
|
||||
}
|
||||
for j := i; j < i+16 && j < len(goCode); j++ {
|
||||
gs += fmt.Sprintf(" %02x", goCode[j])
|
||||
}
|
||||
fmt.Printf(" %04x: gasm:%s\n", i, gb)
|
||||
fmt.Printf(" %04x: gt: %s\n", i, gs)
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Printf("%s: %d/%d matched\n", path, matched, total)
|
||||
if matched < total {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
if profile {
|
||||
for _, fn := range img.Funcs {
|
||||
fmt.Printf("%s: %d bytes, labels: %v\n", fn.Name, fn.Size, fn.Labels)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
fmt.Printf("%s: %d functions assembled\n", path, len(img.Funcs))
|
||||
for _, fn := range img.Funcs {
|
||||
fmt.Printf(" %s: %d bytes\n", fn.Name, fn.Size)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func cmdVerify(args []string) int {
|
||||
fs := 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
|
||||
@@ -867,14 +966,18 @@ decoders) that crash on random input but should succeed on valid data.
|
||||
}
|
||||
path := fs.Arg(0)
|
||||
targetArch := arch.FromFilename(path)
|
||||
if targetArch != arch.AMD64 && targetArch != arch.RISCV {
|
||||
fmt.Fprintln(os.Stderr, "gasm verify: only amd64 and riscv64 are supported")
|
||||
return 1
|
||||
}
|
||||
|
||||
// RISC-V: ground-truth only (no JIT on non-RISC-V hosts).
|
||||
if targetArch == arch.RISCV {
|
||||
switch targetArch {
|
||||
case arch.AMD64:
|
||||
// JIT-based verification below.
|
||||
case arch.RISCV:
|
||||
// RISC-V: ground-truth only (no JIT on non-RISC-V hosts).
|
||||
return cmdVerifyRISCV(path, *groundTruth, *profile)
|
||||
case arch.LOONG64:
|
||||
// LoongArch: ground-truth only (no JIT on non-LoongArch hosts).
|
||||
return cmdVerifyLOONG64(path, *groundTruth, *profile)
|
||||
default:
|
||||
fmt.Fprintln(os.Stderr, "gasm verify: only amd64, riscv64 and loong64 are supported")
|
||||
return 1
|
||||
}
|
||||
|
||||
k, err := verify.Load(path)
|
||||
|
||||
Reference in New Issue
Block a user