diff --git a/compiler/compiler.go b/compiler/compiler.go index 0a61d9b1..28b9f29b 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -29,18 +29,19 @@ func init() { // Configure the compiler. type Config struct { - Triple string // LLVM target triple, e.g. x86_64-unknown-linux-gnu (empty string means default) - CPU string // LLVM CPU name, e.g. atmega328p (empty string means default) - GOOS string // - GOARCH string // - GC string // garbage collection strategy - CFlags []string // cflags to pass to cgo - LDFlags []string // ldflags to pass to cgo - DumpSSA bool // dump Go SSA, for compiler debugging - Debug bool // add debug symbols for gdb - RootDir string // GOROOT for TinyGo - GOPATH string // GOPATH, like `go env GOPATH` - BuildTags []string // build tags for TinyGo (empty means {Config.GOOS/Config.GOARCH}) + Triple string // LLVM target triple, e.g. x86_64-unknown-linux-gnu (empty string means default) + CPU string // LLVM CPU name, e.g. atmega328p (empty string means default) + GOOS string // + GOARCH string // + GC string // garbage collection strategy + PanicStrategy string // panic strategy ("abort" or "trap") + CFlags []string // cflags to pass to cgo + LDFlags []string // ldflags to pass to cgo + DumpSSA bool // dump Go SSA, for compiler debugging + Debug bool // add debug symbols for gdb + RootDir string // GOROOT for TinyGo + GOPATH string // GOPATH, like `go env GOPATH` + BuildTags []string // build tags for TinyGo (empty means {Config.GOOS/Config.GOARCH}) } type Compiler struct { diff --git a/compiler/optimizer.go b/compiler/optimizer.go index dfa005da..d922b0a4 100644 --- a/compiler/optimizer.go +++ b/compiler/optimizer.go @@ -18,6 +18,10 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro } builder.AddCoroutinePassesToExtensionPoints() + if c.PanicStrategy == "trap" { + c.replacePanicsWithTrap() // -panic=trap + } + // Run function passes for each function. funcPasses := llvm.NewFunctionPassManagerForModule(c.mod) defer funcPasses.Dispose() @@ -113,6 +117,25 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro return nil } +// Replace panic calls with calls to llvm.trap, to reduce code size. This is the +// -panic=trap intrinsic. +func (c *Compiler) replacePanicsWithTrap() { + trap := c.mod.NamedFunction("llvm.trap") + for _, name := range []string{"runtime._panic", "runtime.runtimePanic"} { + fn := c.mod.NamedFunction(name) + if fn.IsNil() { + continue + } + for _, use := range getUses(fn) { + if use.IsACallInst().IsNil() || use.CalledValue() != fn { + panic("expected use of a panic function to be a call") + } + c.builder.SetInsertPointBefore(use) + c.builder.CreateCall(trap, nil, "") + } + } +} + // Eliminate created but not used maps. // // In the future, this should statically allocate created but never modified diff --git a/main.go b/main.go index 28cf34f3..c117de32 100644 --- a/main.go +++ b/main.go @@ -34,15 +34,16 @@ func (e *commandError) Error() string { } type BuildConfig struct { - opt string - gc string - printIR bool - dumpSSA bool - debug bool - printSizes string - cFlags []string - ldFlags []string - wasmAbi string + opt string + gc string + panicStrategy string + printIR bool + dumpSSA bool + debug bool + printSizes string + cFlags []string + ldFlags []string + wasmAbi string } // Helper function for Compiler object. @@ -56,18 +57,19 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act spec.LDFlags = append(spec.LDFlags, config.ldFlags...) compilerConfig := compiler.Config{ - Triple: spec.Triple, - CPU: spec.CPU, - GOOS: spec.GOOS, - GOARCH: spec.GOARCH, - GC: config.gc, - CFlags: spec.CFlags, - LDFlags: spec.LDFlags, - Debug: config.debug, - DumpSSA: config.dumpSSA, - RootDir: sourceDir(), - GOPATH: getGopath(), - BuildTags: spec.BuildTags, + Triple: spec.Triple, + CPU: spec.CPU, + GOOS: spec.GOOS, + GOARCH: spec.GOARCH, + GC: config.gc, + PanicStrategy: config.panicStrategy, + CFlags: spec.CFlags, + LDFlags: spec.LDFlags, + Debug: config.debug, + DumpSSA: config.dumpSSA, + RootDir: sourceDir(), + GOPATH: getGopath(), + BuildTags: spec.BuildTags, } c, err := compiler.NewCompiler(pkgName, compilerConfig) if err != nil { @@ -507,6 +509,7 @@ func main() { outpath := flag.String("o", "", "output filename") opt := flag.String("opt", "z", "optimization level: 0, 1, 2, s, z") gc := flag.String("gc", "", "garbage collector to use (none, dumb, marksweep)") + panicStrategy := flag.String("panic", "print", "panic strategy (abort, trap)") printIR := flag.Bool("printir", false, "print LLVM IR") dumpSSA := flag.Bool("dumpssa", false, "dump internal Go SSA") target := flag.String("target", "", "LLVM target") @@ -527,13 +530,14 @@ func main() { flag.CommandLine.Parse(os.Args[2:]) config := &BuildConfig{ - opt: *opt, - gc: *gc, - printIR: *printIR, - dumpSSA: *dumpSSA, - debug: !*nodebug, - printSizes: *printSize, - wasmAbi: *wasmAbi, + opt: *opt, + gc: *gc, + panicStrategy: *panicStrategy, + printIR: *printIR, + dumpSSA: *dumpSSA, + debug: !*nodebug, + printSizes: *printSize, + wasmAbi: *wasmAbi, } if *cFlags != "" { @@ -544,6 +548,12 @@ func main() { config.ldFlags = strings.Split(*ldFlags, " ") } + if *panicStrategy != "print" && *panicStrategy != "trap" { + fmt.Fprintln(os.Stderr, "Panic strategy must be either print or trap.") + usage() + os.Exit(1) + } + os.Setenv("CC", "clang -target="+*target) switch command {