
This change triggers a revert whenever a basic block runs instructions at runtime twice. As a result, a loop body with runtime-only instructions will no longer be unrolled. This should help some extreme cases where loops can be expanded into hundreds or thousands of instructions.
96 строки
3,1 КиБ
Go
96 строки
3,1 КиБ
Go
package interp
|
|
|
|
// This file provides useful types for errors encountered during IR evaluation.
|
|
|
|
import (
|
|
"errors"
|
|
"go/scanner"
|
|
"go/token"
|
|
"path/filepath"
|
|
|
|
"tinygo.org/x/go-llvm"
|
|
)
|
|
|
|
// These errors are expected during normal execution and can be recovered from
|
|
// by running the affected function at runtime instead of compile time.
|
|
var (
|
|
errIntegerAsPointer = errors.New("interp: trying to use an integer as a pointer (memory-mapped I/O?)")
|
|
errUnsupportedInst = errors.New("interp: unsupported instruction")
|
|
errUnsupportedRuntimeInst = errors.New("interp: unsupported instruction (to be emitted at runtime)")
|
|
errMapAlreadyCreated = errors.New("interp: map already created")
|
|
errLoopUnrolled = errors.New("interp: loop unrolled")
|
|
)
|
|
|
|
// This is one of the errors that can be returned from toLLVMValue when the
|
|
// passed type does not fit the data to serialize. It is recoverable by
|
|
// serializing without a type (using rawValue.rawLLVMValue).
|
|
var errInvalidPtrToIntSize = errors.New("interp: ptrtoint integer size does not equal pointer size")
|
|
|
|
func isRecoverableError(err error) bool {
|
|
return err == errIntegerAsPointer || err == errUnsupportedInst ||
|
|
err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated ||
|
|
err == errLoopUnrolled
|
|
}
|
|
|
|
// ErrorLine is one line in a traceback. The position may be missing.
|
|
type ErrorLine struct {
|
|
Pos token.Position
|
|
Inst llvm.Value
|
|
}
|
|
|
|
// Error encapsulates compile-time interpretation errors with an associated
|
|
// import path. The errors may not have a precise location attached.
|
|
type Error struct {
|
|
ImportPath string
|
|
Inst llvm.Value
|
|
Pos token.Position
|
|
Err error
|
|
Traceback []ErrorLine
|
|
}
|
|
|
|
// Error returns the string of the first error in the list of errors.
|
|
func (e *Error) Error() string {
|
|
return e.Pos.String() + ": " + e.Err.Error()
|
|
}
|
|
|
|
// errorAt returns an error value for the currently interpreted package at the
|
|
// location of the instruction. The location information may not be complete as
|
|
// it depends on debug information in the IR.
|
|
func (r *runner) errorAt(inst instruction, err error) *Error {
|
|
pos := getPosition(inst.llvmInst)
|
|
return &Error{
|
|
ImportPath: r.pkgName,
|
|
Inst: inst.llvmInst,
|
|
Pos: pos,
|
|
Err: err,
|
|
Traceback: []ErrorLine{{pos, inst.llvmInst}},
|
|
}
|
|
}
|
|
|
|
// errorAt returns an error value at the location of the instruction.
|
|
// The location information may not be complete as it depends on debug
|
|
// information in the IR.
|
|
func errorAt(inst llvm.Value, msg string) scanner.Error {
|
|
return scanner.Error{
|
|
Pos: getPosition(inst),
|
|
Msg: msg,
|
|
}
|
|
}
|
|
|
|
// getPosition returns the position information for the given instruction, as
|
|
// far as it is available.
|
|
func getPosition(inst llvm.Value) token.Position {
|
|
if inst.IsAInstruction().IsNil() {
|
|
return token.Position{}
|
|
}
|
|
loc := inst.InstructionDebugLoc()
|
|
if loc.IsNil() {
|
|
return token.Position{}
|
|
}
|
|
file := loc.LocationScope().ScopeFile()
|
|
return token.Position{
|
|
Filename: filepath.Join(file.FileDirectory(), file.FileFilename()),
|
|
Line: int(loc.LocationLine()),
|
|
Column: int(loc.LocationColumn()),
|
|
}
|
|
}
|