feat(cmd): generate go_asm.h for package-context assembly
Assisted-by: GLM 5.3 Flash
This commit is contained in:
+98
-8
@@ -418,7 +418,9 @@ func cmdAuditCorpus(args []string, dirs includeDirs) error {
|
||||
// The toolchain's shipped headers (funcdata.h and friends) define the
|
||||
// macros GOROOT files include; a corpus audit measures those files, so
|
||||
// the header directory joins the search path automatically. go_asm.h
|
||||
// is compiler-generated per package and stays unresolvable on purpose.
|
||||
// is compiler-generated per package, so it is not resolved from here:
|
||||
// files that include it get one generated per target architecture,
|
||||
// which runCorpusAudit arranges.
|
||||
if out, err := exec.Command("go", "env", "GOROOT").Output(); err == nil {
|
||||
pkgInclude := filepath.Join(strings.TrimSpace(string(out)), "pkg", "include")
|
||||
if fi, err := os.Stat(pkgInclude); err == nil && fi.IsDir() {
|
||||
@@ -462,9 +464,15 @@ var goPortSuffixes = []string{
|
||||
"s390x", "wasm",
|
||||
}
|
||||
|
||||
// otherPortFile reports whether the file's name carries a Go-architecture
|
||||
// suffix gasm does not support.
|
||||
// otherPortFile reports whether the file belongs to a build no supported
|
||||
// target ever compiles: either its name carries a Go-architecture suffix
|
||||
// gasm does not support, or, for a file with no architecture suffix at all,
|
||||
// it names another GOOS, which go/build drops from the file set
|
||||
// (rt0_js_wasm.s is a javascript build, not a generic one).
|
||||
func otherPortFile(path string) bool {
|
||||
if otherGOOSFile(path) {
|
||||
return true
|
||||
}
|
||||
base := path
|
||||
if i := strings.LastIndexByte(base, '/'); i >= 0 {
|
||||
base = base[i+1:]
|
||||
@@ -477,6 +485,29 @@ func otherPortFile(path string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// goOSNames are the GOOS values go/build recognises in file names.
|
||||
var goOSNames = map[string]bool{
|
||||
"aix": true, "darwin": true, "dragonfly": true, "freebsd": true,
|
||||
"ios": true, "js": true, "linux": true, "netbsd": true,
|
||||
"openbsd": true, "plan9": true, "solaris": true, "wasip1": true,
|
||||
"windows": true,
|
||||
}
|
||||
|
||||
// otherGOOSFile reports whether the file's name names a GOOS other than the
|
||||
// host's, by go/build's file-name rules.
|
||||
func otherGOOSFile(path string) bool {
|
||||
base := path
|
||||
if i := strings.LastIndexByte(base, '/'); i >= 0 {
|
||||
base = base[i+1:]
|
||||
}
|
||||
for seg := range strings.SplitSeq(strings.TrimSuffix(base, ".s"), "_") {
|
||||
if goOSNames[seg] && seg != runtime.GOOS {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func runCorpusAudit(root string, dirs includeDirs) (*corpusStats, error) {
|
||||
files, err := asmFiles(root)
|
||||
if err != nil {
|
||||
@@ -497,12 +528,20 @@ func runCorpusAudit(root string, dirs includeDirs) (*corpusStats, error) {
|
||||
// its name allows assembles it.
|
||||
full, generic, otherPort := 0, 0, 0
|
||||
|
||||
// Header generation is created on first use, so a corpus with no
|
||||
// go_asm.h includes never pays for a temp directory.
|
||||
var hdr *asmhdrCache
|
||||
defer func() {
|
||||
if hdr != nil {
|
||||
hdr.close()
|
||||
}
|
||||
}()
|
||||
|
||||
for _, path := range files {
|
||||
src, err := readSource(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, errs := parser.ParseWithOptions(path, src, parser.Options{Expand: true, IncludeDirs: dirs})
|
||||
|
||||
var wanted []int // indexes into targets
|
||||
if a := arch.FromFilename(path); a != arch.Unknown {
|
||||
@@ -513,10 +552,11 @@ func runCorpusAudit(root string, dirs includeDirs) (*corpusStats, error) {
|
||||
}
|
||||
} else if otherPortFile(path) {
|
||||
// A file named for a Go port gasm does not support (arm,
|
||||
// 386, s390x, ...) is compiled by no supported-arch build,
|
||||
// so it is neither generic nor a per-arch attempt: counting
|
||||
// it as generic would make the headline unreachably low
|
||||
// for reasons no supported target can fix.
|
||||
// 386, s390x, ...) or for another GOOS is compiled by no
|
||||
// supported-arch build, so it is neither generic nor a
|
||||
// per-arch attempt: counting it as generic would make the
|
||||
// headline unreachably low for reasons no supported target
|
||||
// can fix.
|
||||
otherPort++
|
||||
} else {
|
||||
generic++
|
||||
@@ -525,6 +565,54 @@ func runCorpusAudit(root string, dirs includeDirs) (*corpusStats, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// A file that includes go_asm.h parses against a per-target header:
|
||||
// the defines differ per architecture (internal/cpu's layout, for
|
||||
// one), so the parse cannot be shared the way a header-free file's
|
||||
// can. A generation failure is a failure for every target, named
|
||||
// for the package rather than a bare "include not found". A header
|
||||
// already resolvable in the package directory or the -I list is
|
||||
// left alone.
|
||||
if len(wanted) > 0 && needsGoAsmHeader(src) && !goAsmHeaderResolved(filepath.Dir(path), dirs) {
|
||||
if hdr == nil {
|
||||
if hdr, err = newAsmhdrCache(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
pkgDir := filepath.Dir(path)
|
||||
ok := true
|
||||
for _, i := range wanted {
|
||||
tg, t := targets[i], tallies[i]
|
||||
t.attempted++
|
||||
hdrDir, err := hdr.dirFor(pkgDir, goarchName(tg.a))
|
||||
if err != nil {
|
||||
ok = false
|
||||
t.fail(path, corpusReason(err))
|
||||
continue
|
||||
}
|
||||
f, errs := parser.ParseWithOptions(path, src, parser.Options{
|
||||
Expand: true,
|
||||
IncludeDirs: append(slices.Clone(dirs), hdrDir),
|
||||
})
|
||||
if len(errs) > 0 {
|
||||
ok = false
|
||||
t.fail(path, corpusReason(errs[0]))
|
||||
continue
|
||||
}
|
||||
if _, err := assembleFile(tg.a, f); err != nil {
|
||||
ok = false
|
||||
t.fail(path, corpusReason(err))
|
||||
continue
|
||||
}
|
||||
t.assembled++
|
||||
}
|
||||
if ok && len(wanted) > 0 {
|
||||
full++
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
f, errs := parser.ParseWithOptions(path, src, parser.Options{Expand: true, IncludeDirs: dirs})
|
||||
|
||||
ok := true
|
||||
for _, i := range wanted {
|
||||
tg, t := targets[i], tallies[i]
|
||||
@@ -581,6 +669,8 @@ func printCorpusStats(s *corpusStats) {
|
||||
func corpusReason(err error) string {
|
||||
msg := err.Error()
|
||||
switch {
|
||||
case strings.Contains(msg, "go_asm.h for GOARCH"):
|
||||
return "go_asm.h generation failed"
|
||||
case strings.Contains(msg, "unsupported"), strings.Contains(msg, "cannot encode"):
|
||||
return "instruction not encodable"
|
||||
case strings.Contains(msg, "undefined label"):
|
||||
|
||||
Reference in New Issue
Block a user