
The frame pointer was already omitted in the object files that TinyGo emits, but wasn't yet omitted in the C files it compiles. Omitting the frame pointer is good for code size (and perhaps performance). The frame pointer was originally used for printing stack traces in a debugger. However, advances in DWARF debug info have made it largely unnecessary (debug info contains enough information now to recover the frame pointer even without an explicit frame pointer register). In fact, GDB has been able to produce backtraces in TinyGo compiled code for a while now while it didn't include a frame pointer.
102 строки
3,2 КиБ
Go
102 строки
3,2 КиБ
Go
package builder
|
|
|
|
import (
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/tinygo-org/tinygo/goenv"
|
|
)
|
|
|
|
// Library is a container for information about a single C library, such as a
|
|
// compiler runtime or libc.
|
|
type Library struct {
|
|
// The library name, such as compiler-rt or picolibc.
|
|
name string
|
|
|
|
cflags func() []string
|
|
|
|
// The source directory, relative to TINYGOROOT.
|
|
sourceDir string
|
|
|
|
// The source files, relative to sourceDir.
|
|
sources func(target string) []string
|
|
}
|
|
|
|
// fullPath returns the full path to the source directory.
|
|
func (l *Library) fullPath() string {
|
|
return filepath.Join(goenv.Get("TINYGOROOT"), l.sourceDir)
|
|
}
|
|
|
|
// sourcePaths returns a slice with the full paths to the source files.
|
|
func (l *Library) sourcePaths(target string) []string {
|
|
sources := l.sources(target)
|
|
paths := make([]string, len(sources))
|
|
for i, name := range sources {
|
|
paths[i] = filepath.Join(l.fullPath(), name)
|
|
}
|
|
return paths
|
|
}
|
|
|
|
// Load the library archive, possibly generating and caching it if needed.
|
|
func (l *Library) Load(target string) (path string, err error) {
|
|
// Try to load a precompiled library.
|
|
precompiledPath := filepath.Join(goenv.Get("TINYGOROOT"), "pkg", target, l.name+".a")
|
|
if _, err := os.Stat(precompiledPath); err == nil {
|
|
// Found a precompiled library for this OS/architecture. Return the path
|
|
// directly.
|
|
return precompiledPath, nil
|
|
}
|
|
|
|
outfile := l.name + "-" + target + ".a"
|
|
|
|
// Try to fetch this library from the cache.
|
|
if path, err := cacheLoad(outfile, commands["clang"][0], l.sourcePaths(target)); path != "" || err != nil {
|
|
// Cache hit.
|
|
return path, err
|
|
}
|
|
// Cache miss, build it now.
|
|
|
|
dirPrefix := "tinygo-" + l.name
|
|
remapDir := filepath.Join(os.TempDir(), dirPrefix)
|
|
dir, err := ioutil.TempDir(os.TempDir(), dirPrefix)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer os.RemoveAll(dir)
|
|
|
|
// Precalculate the flags to the compiler invocation.
|
|
args := append(l.cflags(), "-c", "-Oz", "-g", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir)
|
|
if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") {
|
|
args = append(args, "-fshort-enums", "-fomit-frame-pointer")
|
|
}
|
|
if strings.HasPrefix(target, "riscv32-") {
|
|
args = append(args, "-march=rv32imac", "-mabi=ilp32", "-fforce-enable-int128")
|
|
}
|
|
|
|
// Compile all sources.
|
|
var objs []string
|
|
for _, srcpath := range l.sourcePaths(target) {
|
|
objpath := filepath.Join(dir, filepath.Base(srcpath)+".o")
|
|
objs = append(objs, objpath)
|
|
// Note: -fdebug-prefix-map is necessary to make the output archive
|
|
// reproducible. Otherwise the temporary directory is stored in the
|
|
// archive itself, which varies each run.
|
|
err := runCCompiler("clang", append(args, "-o", objpath, srcpath)...)
|
|
if err != nil {
|
|
return "", &commandError{"failed to build", srcpath, err}
|
|
}
|
|
}
|
|
|
|
// Put all the object files in a single archive. This archive file will be
|
|
// used to statically link this library.
|
|
arpath := filepath.Join(dir, l.name+".a")
|
|
err = makeArchive(arpath, objs)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// Store this archive in the cache.
|
|
return cacheStore(arpath, outfile, commands["clang"][0], l.sourcePaths(target))
|
|
}
|