feat(cli): add gasm profile command for basic-block structure
Assisted-by: DeepSeek V4 Pro
This commit is contained in:
@@ -16,6 +16,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
@@ -57,6 +58,8 @@ func main() {
|
||||
os.Exit(cmdDebug(os.Args[2:]))
|
||||
case "diff":
|
||||
os.Exit(cmdDiff(os.Args[2:]))
|
||||
case "profile":
|
||||
os.Exit(cmdProfile(os.Args[2:]))
|
||||
case "lsp":
|
||||
os.Exit(cmdLSP(os.Args[2:]))
|
||||
case "version", "--version", "-V":
|
||||
@@ -118,6 +121,7 @@ func usage(w io.Writer) {
|
||||
{"verify", "JIT-assemble and run dynamic checks (amd64, riscv64)"},
|
||||
{"debug", "interactive source-level debugger (amd64)"},
|
||||
{"diff", "compare machine code of two .s files"},
|
||||
{"profile", "show basic-block structure of functions"},
|
||||
{"lsp", "run the language server over stdio"},
|
||||
{"version", "print the version (same as --version)"},
|
||||
}
|
||||
@@ -637,6 +641,67 @@ 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>", `
|
||||
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 {
|
||||
fmt.Fprintln(os.Stderr, "usage: gasm profile <file.s>")
|
||||
return 2
|
||||
}
|
||||
path := fs.Arg(0)
|
||||
|
||||
// Load the file to get function metadata.
|
||||
k, err := verify.Load(path)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "gasm profile: %v\n", err)
|
||||
return 1
|
||||
}
|
||||
defer k.Close()
|
||||
|
||||
for _, name := range k.FuncNames() {
|
||||
fl, err := k.Func(name)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
fmt.Printf("%s: %d bytes, args=%d, frame=%d", name, fl.Size, fl.Args, fl.Frame)
|
||||
if fl.NoSplit {
|
||||
fmt.Printf(" NOSPLIT")
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
// Show labels and their offsets.
|
||||
if len(fl.Labels) > 0 {
|
||||
fmt.Println(" labels:")
|
||||
// Sort labels by offset.
|
||||
type labelOff struct {
|
||||
name string
|
||||
off int
|
||||
}
|
||||
var labels []labelOff
|
||||
for name, off := range fl.Labels {
|
||||
labels = append(labels, labelOff{name, off})
|
||||
}
|
||||
sort.Slice(labels, func(i, j int) bool { return labels[i].off < labels[j].off })
|
||||
for _, l := range labels {
|
||||
fmt.Printf(" %-20s +%#04x\n", l.name, l.off)
|
||||
}
|
||||
}
|
||||
|
||||
// Show basic blocks.
|
||||
blocks, err := k.Blocks(name)
|
||||
if err == nil && len(blocks) > 0 {
|
||||
fmt.Printf(" basic blocks: %d\n", len(blocks))
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// cmdVerifyRISCV handles the verify subcommand for RISC-V files.
|
||||
// JIT requires RISC-V hardware; only ground-truth and profile are available.
|
||||
func cmdVerifyRISCV(path string, groundTruth, profile bool) int {
|
||||
|
||||
Reference in New Issue
Block a user