make interp timeout configurable from command line

Этот коммит содержится в:
Damian Gryski 2022-08-04 15:26:44 -07:00 коммит произвёл Ron Evans
родитель a4ee98e0e1
коммит 0b77e92c50
7 изменённых файлов: 34 добавлений и 28 удалений

Просмотреть файл

@ -431,7 +431,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
if pkgInit.IsNil() { if pkgInit.IsNil() {
panic("init not found for " + pkg.Pkg.Path()) panic("init not found for " + pkg.Pkg.Path())
} }
err := interp.RunFunc(pkgInit, config.DumpSSA()) err := interp.RunFunc(pkgInit, config.Options.InterpTimeout, config.DumpSSA())
if err != nil { if err != nil {
return err return err
} }
@ -1055,7 +1055,7 @@ func createEmbedObjectFile(data, hexSum, sourceFile, sourceDir, tmpdir string, c
// needed to convert a program to its final form. Some transformations are not // needed to convert a program to its final form. Some transformations are not
// optional and must be run as the compiler expects them to run. // optional and must be run as the compiler expects them to run.
func optimizeProgram(mod llvm.Module, config *compileopts.Config) error { func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
err := interp.Run(mod, config.DumpSSA()) err := interp.Run(mod, config.Options.InterpTimeout, config.DumpSSA())
if err != nil { if err != nil {
return err return err
} }

Просмотреть файл

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"regexp" "regexp"
"strings" "strings"
"time"
) )
var ( var (
@ -29,6 +30,7 @@ type Options struct {
Scheduler string Scheduler string
Serial string Serial string
Work bool // -work flag to print temporary build directory Work bool // -work flag to print temporary build directory
InterpTimeout time.Duration
PrintIR bool PrintIR bool
DumpSSA bool DumpSSA bool
VerifyIR bool VerifyIR bool

Просмотреть файл

@ -30,10 +30,11 @@ type runner struct {
objects []object // slice of objects in memory objects []object // slice of objects in memory
globals map[llvm.Value]int // map from global to index in objects slice globals map[llvm.Value]int // map from global to index in objects slice
start time.Time start time.Time
timeout time.Duration
callsExecuted uint64 callsExecuted uint64
} }
func newRunner(mod llvm.Module, debug bool) *runner { func newRunner(mod llvm.Module, timeout time.Duration, debug bool) *runner {
r := runner{ r := runner{
mod: mod, mod: mod,
targetData: llvm.NewTargetData(mod.DataLayout()), targetData: llvm.NewTargetData(mod.DataLayout()),
@ -42,6 +43,7 @@ func newRunner(mod llvm.Module, debug bool) *runner {
objects: []object{{}}, objects: []object{{}},
globals: make(map[llvm.Value]int), globals: make(map[llvm.Value]int),
start: time.Now(), start: time.Now(),
timeout: timeout,
} }
r.pointerSize = uint32(r.targetData.PointerSize()) r.pointerSize = uint32(r.targetData.PointerSize())
r.i8ptrType = llvm.PointerType(mod.Context().Int8Type(), 0) r.i8ptrType = llvm.PointerType(mod.Context().Int8Type(), 0)
@ -58,8 +60,8 @@ func (r *runner) dispose() {
// Run evaluates runtime.initAll function as much as possible at compile time. // Run evaluates runtime.initAll function as much as possible at compile time.
// Set debug to true if it should print output while running. // Set debug to true if it should print output while running.
func Run(mod llvm.Module, debug bool) error { func Run(mod llvm.Module, timeout time.Duration, debug bool) error {
r := newRunner(mod, debug) r := newRunner(mod, timeout, debug)
defer r.dispose() defer r.dispose()
initAll := mod.NamedFunction("runtime.initAll") initAll := mod.NamedFunction("runtime.initAll")
@ -199,10 +201,10 @@ func Run(mod llvm.Module, debug bool) error {
// RunFunc evaluates a single package initializer at compile time. // RunFunc evaluates a single package initializer at compile time.
// Set debug to true if it should print output while running. // Set debug to true if it should print output while running.
func RunFunc(fn llvm.Value, debug bool) error { func RunFunc(fn llvm.Value, timeout time.Duration, debug bool) error {
// Create and initialize *runner object. // Create and initialize *runner object.
mod := fn.GlobalParent() mod := fn.GlobalParent()
r := newRunner(mod, debug) r := newRunner(mod, timeout, debug)
defer r.dispose() defer r.dispose()
initName := fn.Name() initName := fn.Name()
if !strings.HasSuffix(initName, ".init") { if !strings.HasSuffix(initName, ".init") {

Просмотреть файл

@ -5,6 +5,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"testing" "testing"
"time"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
@ -52,7 +53,7 @@ func runTest(t *testing.T, pathPrefix string) {
defer mod.Dispose() defer mod.Dispose()
// Perform the transform. // Perform the transform.
err = Run(mod, false) err = Run(mod, 10*time.Minute, false)
if err != nil { if err != nil {
if err, match := err.(*Error); match { if err, match := err.(*Error); match {
println(err.Error()) println(err.Error())

Просмотреть файл

@ -17,8 +17,6 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
locals := make([]value, len(fn.locals)) locals := make([]value, len(fn.locals))
r.callsExecuted++ r.callsExecuted++
t0 := time.Since(r.start)
// Parameters are considered a kind of local values. // Parameters are considered a kind of local values.
for i, param := range params { for i, param := range params {
locals[i] = param locals[i] = param
@ -143,11 +141,10 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
} }
switch inst.opcode { switch inst.opcode {
case llvm.Ret: case llvm.Ret:
const maxInterpSeconds = 180 if time.Since(r.start) > r.timeout {
if t0 > maxInterpSeconds*time.Second { // Running for more than the allowed timeout; This shouldn't happen, but it does.
// Running for more than maxInterpSeconds seconds. This should never happen, but does.
// See github.com/tinygo-org/tinygo/issues/2124 // See github.com/tinygo-org/tinygo/issues/2124
return nil, mem, r.errorAt(fn.blocks[0].instructions[0], fmt.Errorf("interp: running for more than %d seconds, timing out (executed calls: %d)", maxInterpSeconds, r.callsExecuted)) return nil, mem, r.errorAt(fn.blocks[0].instructions[0], fmt.Errorf("interp: running for more than %s, timing out (executed calls: %d)", r.timeout, r.callsExecuted))
} }
if len(operands) != 0 { if len(operands) != 0 {

Просмотреть файл

@ -1295,6 +1295,7 @@ func main() {
scheduler := flag.String("scheduler", "", "which scheduler to use (none, tasks, asyncify)") scheduler := flag.String("scheduler", "", "which scheduler to use (none, tasks, asyncify)")
serial := flag.String("serial", "", "which serial output to use (none, uart, usb)") serial := flag.String("serial", "", "which serial output to use (none, uart, usb)")
work := flag.Bool("work", false, "print the name of the temporary build directory and do not delete this directory on exit") work := flag.Bool("work", false, "print the name of the temporary build directory and do not delete this directory on exit")
interpTimeout := flag.Duration("interp-timeout", 180*time.Second, "interp optimization pass timeout")
printIR := flag.Bool("printir", false, "print LLVM IR") printIR := flag.Bool("printir", false, "print LLVM IR")
dumpSSA := flag.Bool("dumpssa", false, "dump internal Go SSA") dumpSSA := flag.Bool("dumpssa", false, "dump internal Go SSA")
verifyIR := flag.Bool("verifyir", false, "run extra verification steps on LLVM IR") verifyIR := flag.Bool("verifyir", false, "run extra verification steps on LLVM IR")
@ -1385,6 +1386,7 @@ func main() {
Scheduler: *scheduler, Scheduler: *scheduler,
Serial: *serial, Serial: *serial,
Work: *work, Work: *work,
InterpTimeout: *interpTimeout,
PrintIR: *printIR, PrintIR: *printIR,
DumpSSA: *dumpSSA, DumpSSA: *dumpSSA,
VerifyIR: *verifyIR, VerifyIR: *verifyIR,

Просмотреть файл

@ -275,14 +275,15 @@ func emuCheck(t *testing.T, options compileopts.Options) {
func optionsFromTarget(target string, sema chan struct{}) compileopts.Options { func optionsFromTarget(target string, sema chan struct{}) compileopts.Options {
return compileopts.Options{ return compileopts.Options{
// GOOS/GOARCH are only used if target == "" // GOOS/GOARCH are only used if target == ""
GOOS: goenv.Get("GOOS"), GOOS: goenv.Get("GOOS"),
GOARCH: goenv.Get("GOARCH"), GOARCH: goenv.Get("GOARCH"),
GOARM: goenv.Get("GOARM"), GOARM: goenv.Get("GOARM"),
Target: target, Target: target,
Semaphore: sema, Semaphore: sema,
Debug: true, InterpTimeout: 180 * time.Second,
VerifyIR: true, Debug: true,
Opt: "z", VerifyIR: true,
Opt: "z",
} }
} }
@ -292,12 +293,13 @@ func optionsFromTarget(target string, sema chan struct{}) compileopts.Options {
func optionsFromOSARCH(osarch string, sema chan struct{}) compileopts.Options { func optionsFromOSARCH(osarch string, sema chan struct{}) compileopts.Options {
parts := strings.Split(osarch, "/") parts := strings.Split(osarch, "/")
options := compileopts.Options{ options := compileopts.Options{
GOOS: parts[0], GOOS: parts[0],
GOARCH: parts[1], GOARCH: parts[1],
Semaphore: sema, Semaphore: sema,
Debug: true, InterpTimeout: 180 * time.Second,
VerifyIR: true, Debug: true,
Opt: "z", VerifyIR: true,
Opt: "z",
} }
if options.GOARCH == "arm" { if options.GOARCH == "arm" {
options.GOARM = parts[2] options.GOARM = parts[2]