mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-11 22:43:40 +00:00
interp: add 'max depth' parameter to limit cost
This commit is contained in:
+2
-1
@@ -19,6 +19,7 @@ var (
|
||||
errUnsupportedRuntimeInst = errors.New("interp: unsupported instruction (to be emitted at runtime)")
|
||||
errMapAlreadyCreated = errors.New("interp: map already created")
|
||||
errLoopUnrolled = errors.New("interp: loop unrolled")
|
||||
errDepthExceeded = errors.New("interp: depth exceeded")
|
||||
)
|
||||
|
||||
// This is one of the errors that can be returned from toLLVMValue when the
|
||||
@@ -29,7 +30,7 @@ var errInvalidPtrToIntSize = errors.New("interp: ptrtoint integer size does not
|
||||
func isRecoverableError(err error) bool {
|
||||
return err == errIntegerAsPointer || err == errUnsupportedInst ||
|
||||
err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated ||
|
||||
err == errLoopUnrolled
|
||||
err == errLoopUnrolled || err == errDepthExceeded
|
||||
}
|
||||
|
||||
// ErrorLine is one line in a traceback. The position may be missing.
|
||||
|
||||
+9
-7
@@ -31,10 +31,11 @@ type runner struct {
|
||||
globals map[llvm.Value]int // map from global to index in objects slice
|
||||
start time.Time
|
||||
timeout time.Duration
|
||||
maxDepth int
|
||||
callsExecuted uint64
|
||||
}
|
||||
|
||||
func newRunner(mod llvm.Module, timeout time.Duration, debug bool) *runner {
|
||||
func newRunner(mod llvm.Module, timeout time.Duration, maxDepth int, debug bool) *runner {
|
||||
r := runner{
|
||||
mod: mod,
|
||||
targetData: llvm.NewTargetData(mod.DataLayout()),
|
||||
@@ -44,6 +45,7 @@ func newRunner(mod llvm.Module, timeout time.Duration, debug bool) *runner {
|
||||
globals: make(map[llvm.Value]int),
|
||||
start: time.Now(),
|
||||
timeout: timeout,
|
||||
maxDepth: maxDepth,
|
||||
}
|
||||
r.pointerSize = uint32(r.targetData.PointerSize())
|
||||
r.i8ptrType = llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||
@@ -60,8 +62,8 @@ func (r *runner) dispose() {
|
||||
|
||||
// Run evaluates runtime.initAll function as much as possible at compile time.
|
||||
// Set debug to true if it should print output while running.
|
||||
func Run(mod llvm.Module, timeout time.Duration, debug bool) error {
|
||||
r := newRunner(mod, timeout, debug)
|
||||
func Run(mod llvm.Module, timeout time.Duration, maxDepth int, debug bool) error {
|
||||
r := newRunner(mod, timeout, maxDepth, debug)
|
||||
defer r.dispose()
|
||||
|
||||
initAll := mod.NamedFunction("runtime.initAll")
|
||||
@@ -114,7 +116,7 @@ func Run(mod llvm.Module, timeout time.Duration, debug bool) error {
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, "call:", fn.Name())
|
||||
}
|
||||
_, mem, callErr := r.run(r.getFunction(fn), nil, nil, " ")
|
||||
_, mem, callErr := r.run(r.getFunction(fn), nil, nil, 0, " ")
|
||||
call.EraseFromParentAsInstruction()
|
||||
if callErr != nil {
|
||||
if isRecoverableError(callErr.Err) {
|
||||
@@ -201,10 +203,10 @@ func Run(mod llvm.Module, timeout time.Duration, debug bool) error {
|
||||
|
||||
// RunFunc evaluates a single package initializer at compile time.
|
||||
// Set debug to true if it should print output while running.
|
||||
func RunFunc(fn llvm.Value, timeout time.Duration, debug bool) error {
|
||||
func RunFunc(fn llvm.Value, timeout time.Duration, maxDepth int, debug bool) error {
|
||||
// Create and initialize *runner object.
|
||||
mod := fn.GlobalParent()
|
||||
r := newRunner(mod, timeout, debug)
|
||||
r := newRunner(mod, timeout, maxDepth, debug)
|
||||
defer r.dispose()
|
||||
initName := fn.Name()
|
||||
if !strings.HasSuffix(initName, ".init") {
|
||||
@@ -235,7 +237,7 @@ func RunFunc(fn llvm.Value, timeout time.Duration, debug bool) error {
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, "interp:", fn.Name())
|
||||
}
|
||||
_, pkgMem, callErr := r.run(r.getFunction(fn), nil, nil, " ")
|
||||
_, pkgMem, callErr := r.run(r.getFunction(fn), nil, nil, 0, " ")
|
||||
if callErr != nil {
|
||||
if isRecoverableError(callErr.Err) {
|
||||
// Could not finish, but could recover from it.
|
||||
|
||||
@@ -53,7 +53,7 @@ func runTest(t *testing.T, pathPrefix string) {
|
||||
defer mod.Dispose()
|
||||
|
||||
// Perform the transform.
|
||||
err = Run(mod, 10*time.Minute, false)
|
||||
err = Run(mod, 10*time.Minute, 10, false)
|
||||
if err != nil {
|
||||
if err, match := err.(*Error); match {
|
||||
println(err.Error())
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent string) (value, memoryView, *Error) {
|
||||
func (r *runner) run(fn *function, params []value, parentMem *memoryView, depth int, indent string) (value, memoryView, *Error) {
|
||||
mem := memoryView{r: r, parent: parentMem}
|
||||
locals := make([]value, len(fn.locals))
|
||||
r.callsExecuted++
|
||||
@@ -35,6 +35,11 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
startRTInsts := len(mem.instructions)
|
||||
for instIndex := 0; instIndex < len(bb.instructions); instIndex++ {
|
||||
if instIndex == 0 {
|
||||
if r.maxDepth > 0 && depth >= r.maxDepth {
|
||||
fmt.Printf("interp: depth exceeded in %v\n", fn.name)
|
||||
return nil, mem, r.errorAt(fn.blocks[0].instructions[0], errDepthExceeded)
|
||||
}
|
||||
|
||||
// This is the start of a new basic block.
|
||||
if len(mem.instructions) != startRTInsts {
|
||||
if _, ok := runtimeBlocks[lastBB]; ok {
|
||||
@@ -523,7 +528,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, indent+"call:", callFn.name+"("+strings.Join(argStrings, ", ")+")")
|
||||
}
|
||||
retval, callMem, callErr := r.run(callFn, operands[1:], &mem, indent+" ")
|
||||
retval, callMem, callErr := r.run(callFn, operands[1:], &mem, depth+1, indent+" ")
|
||||
if callErr != nil {
|
||||
if isRecoverableError(callErr.Err) {
|
||||
// This error can be recovered by doing the call at
|
||||
|
||||
Reference in New Issue
Block a user