interp: show backtrace with error
This should make it much easier to figure out why and where an error happens at package initialization time.
Этот коммит содержится в:
родитель
2501602b4f
коммит
cc4a4c755f
6 изменённых файлов: 70 добавлений и 73 удалений
|
@ -887,7 +887,8 @@ func (b *builder) createFunctionDefinition() {
|
||||||
if b.fn.Synthetic == "package initializer" {
|
if b.fn.Synthetic == "package initializer" {
|
||||||
// Package initializers have no debug info. Create some fake debug
|
// Package initializers have no debug info. Create some fake debug
|
||||||
// info to at least have *something*.
|
// info to at least have *something*.
|
||||||
b.difunc = b.attachDebugInfoRaw(b.fn, b.fn.LLVMFn, "", "", 0)
|
filename := b.fn.Package().Pkg.Path() + "/<init>"
|
||||||
|
b.difunc = b.attachDebugInfoRaw(b.fn, b.fn.LLVMFn, "", filename, 0)
|
||||||
} else if b.fn.Syntax() != nil {
|
} else if b.fn.Syntax() != nil {
|
||||||
// Create debug info file if needed.
|
// Create debug info file if needed.
|
||||||
b.difunc = b.attachDebugInfo(b.fn)
|
b.difunc = b.attachDebugInfo(b.fn)
|
||||||
|
|
|
@ -13,55 +13,44 @@ import (
|
||||||
|
|
||||||
// errUnreachable is returned when an unreachable instruction is executed. This
|
// errUnreachable is returned when an unreachable instruction is executed. This
|
||||||
// error should not be visible outside of the interp package.
|
// error should not be visible outside of the interp package.
|
||||||
var errUnreachable = errors.New("interp: unreachable executed")
|
var errUnreachable = &Error{Err: errors.New("interp: unreachable executed")}
|
||||||
|
|
||||||
// Unsupported is the specific error that is returned when an unsupported
|
|
||||||
// instruction is hit while trying to interpret all initializers.
|
|
||||||
type Unsupported struct {
|
|
||||||
ImportPath string
|
|
||||||
Inst llvm.Value
|
|
||||||
Pos token.Position
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e Unsupported) Error() string {
|
|
||||||
// TODO: how to return the actual instruction string?
|
|
||||||
// It looks like LLVM provides no function for that...
|
|
||||||
return scanner.Error{
|
|
||||||
Pos: e.Pos,
|
|
||||||
Msg: "interp: unsupported instruction",
|
|
||||||
}.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
// unsupportedInstructionError returns a new "unsupported instruction" error for
|
// unsupportedInstructionError returns a new "unsupported instruction" error for
|
||||||
// the given instruction. It includes source location information, when
|
// the given instruction. It includes source location information, when
|
||||||
// available.
|
// available.
|
||||||
func (e *evalPackage) unsupportedInstructionError(inst llvm.Value) *Unsupported {
|
func (e *evalPackage) unsupportedInstructionError(inst llvm.Value) *Error {
|
||||||
return &Unsupported{
|
return e.errorAt(inst, errors.New("interp: unsupported instruction"))
|
||||||
ImportPath: e.packagePath,
|
|
||||||
Inst: inst,
|
|
||||||
Pos: getPosition(inst),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
// Error encapsulates compile-time interpretation errors with an associated
|
||||||
// import path. The errors may not have a precise location attached.
|
// import path. The errors may not have a precise location attached.
|
||||||
type Error struct {
|
type Error struct {
|
||||||
ImportPath string
|
ImportPath string
|
||||||
Errs []scanner.Error
|
Inst llvm.Value
|
||||||
|
Pos token.Position
|
||||||
|
Err error
|
||||||
|
Traceback []ErrorLine
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error returns the string of the first error in the list of errors.
|
// Error returns the string of the first error in the list of errors.
|
||||||
func (e Error) Error() string {
|
func (e *Error) Error() string {
|
||||||
return e.Errs[0].Error()
|
return e.Pos.String() + ": " + e.Err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
// errorAt returns an error value for the currently interpreted package at the
|
// errorAt returns an error value for the currently interpreted package at the
|
||||||
// location of the instruction. The location information may not be complete as
|
// location of the instruction. The location information may not be complete as
|
||||||
// it depends on debug information in the IR.
|
// it depends on debug information in the IR.
|
||||||
func (e *evalPackage) errorAt(inst llvm.Value, msg string) Error {
|
func (e *evalPackage) errorAt(inst llvm.Value, err error) *Error {
|
||||||
return Error{
|
return &Error{
|
||||||
ImportPath: e.packagePath,
|
ImportPath: e.packagePath,
|
||||||
Errs: []scanner.Error{errorAt(inst, msg)},
|
Pos: getPosition(inst),
|
||||||
|
Err: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ package interp
|
||||||
// functions.
|
// functions.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
|
@ -21,7 +22,7 @@ type frame struct {
|
||||||
// Most of it works at compile time. Some calls get translated into calls to be
|
// Most of it works at compile time. Some calls get translated into calls to be
|
||||||
// executed at runtime: calls to functions with side effects, external calls,
|
// executed at runtime: calls to functions with side effects, external calls,
|
||||||
// and operations on the result of such instructions.
|
// and operations on the result of such instructions.
|
||||||
func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (retval Value, outgoing []llvm.Value, err error) {
|
func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (retval Value, outgoing []llvm.Value, err *Error) {
|
||||||
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
||||||
if fr.Debug {
|
if fr.Debug {
|
||||||
print(indent)
|
print(indent)
|
||||||
|
@ -97,7 +98,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||||
value = operand.Load()
|
value = operand.Load()
|
||||||
}
|
}
|
||||||
if value.Type() != inst.Type() {
|
if value.Type() != inst.Type() {
|
||||||
return nil, nil, fr.errorAt(inst, "interp: load: type does not match")
|
return nil, nil, fr.errorAt(inst, errors.New("interp: load: type does not match"))
|
||||||
}
|
}
|
||||||
fr.locals[inst] = fr.getValue(value)
|
fr.locals[inst] = fr.getValue(value)
|
||||||
case !inst.IsAStoreInst().IsNil():
|
case !inst.IsAStoreInst().IsNil():
|
||||||
|
@ -121,16 +122,16 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||||
// Not a constant operation.
|
// Not a constant operation.
|
||||||
// This should be detected by the scanner, but isn't at the
|
// This should be detected by the scanner, but isn't at the
|
||||||
// moment.
|
// moment.
|
||||||
return nil, nil, fr.errorAt(inst, "todo: non-const gep")
|
return nil, nil, fr.errorAt(inst, errors.New("todo: non-const gep"))
|
||||||
}
|
}
|
||||||
indices[i] = uint32(operand.Value().ZExtValue())
|
indices[i] = uint32(operand.Value().ZExtValue())
|
||||||
}
|
}
|
||||||
result, err := value.GetElementPtr(indices)
|
result, err := value.GetElementPtr(indices)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fr.errorAt(inst, err.Error())
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
}
|
}
|
||||||
if result.Type() != inst.Type() {
|
if result.Type() != inst.Type() {
|
||||||
return nil, nil, fr.errorAt(inst, "interp: gep: type does not match")
|
return nil, nil, fr.errorAt(inst, errors.New("interp: gep: type does not match"))
|
||||||
}
|
}
|
||||||
fr.locals[inst] = result
|
fr.locals[inst] = result
|
||||||
|
|
||||||
|
@ -185,7 +186,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// It is not possible in Go to bitcast a map value to a pointer.
|
// It is not possible in Go to bitcast a map value to a pointer.
|
||||||
return nil, nil, fr.errorAt(inst, "unimplemented: bitcast of map")
|
return nil, nil, fr.errorAt(inst, errors.New("unimplemented: bitcast of map"))
|
||||||
}
|
}
|
||||||
value := fr.getLocal(operand).(*LocalValue)
|
value := fr.getLocal(operand).(*LocalValue)
|
||||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateBitCast(value.Value(), inst.Type(), "")}
|
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateBitCast(value.Value(), inst.Type(), "")}
|
||||||
|
@ -269,7 +270,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||||
} else {
|
} else {
|
||||||
result, err := result.GetElementPtr([]uint32{0, 0})
|
result, err := result.GetElementPtr([]uint32{0, 0})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, errorAt(inst, err.Error())
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
}
|
}
|
||||||
fr.locals[resultInst] = result
|
fr.locals[resultInst] = result
|
||||||
}
|
}
|
||||||
|
@ -374,30 +375,30 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||||
// Re-add this GEP, in the hope that it it is then of the correct type...
|
// Re-add this GEP, in the hope that it it is then of the correct type...
|
||||||
dstArrayValue, err := dstArray.GetElementPtr([]uint32{0, 0})
|
dstArrayValue, err := dstArray.GetElementPtr([]uint32{0, 0})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, errorAt(inst, err.Error())
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
}
|
}
|
||||||
dstArray = dstArrayValue.(*LocalValue)
|
dstArray = dstArrayValue.(*LocalValue)
|
||||||
srcArrayValue, err := srcArray.GetElementPtr([]uint32{0, 0})
|
srcArrayValue, err := srcArray.GetElementPtr([]uint32{0, 0})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, errorAt(inst, err.Error())
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
}
|
}
|
||||||
srcArray = srcArrayValue.(*LocalValue)
|
srcArray = srcArrayValue.(*LocalValue)
|
||||||
}
|
}
|
||||||
if fr.Eval.TargetData.TypeAllocSize(dstArray.Type().ElementType()) != elementSize {
|
if fr.Eval.TargetData.TypeAllocSize(dstArray.Type().ElementType()) != elementSize {
|
||||||
return nil, nil, fr.errorAt(inst, "interp: slice dst element size does not match pointer type")
|
return nil, nil, fr.errorAt(inst, errors.New("interp: slice dst element size does not match pointer type"))
|
||||||
}
|
}
|
||||||
if fr.Eval.TargetData.TypeAllocSize(srcArray.Type().ElementType()) != elementSize {
|
if fr.Eval.TargetData.TypeAllocSize(srcArray.Type().ElementType()) != elementSize {
|
||||||
return nil, nil, fr.errorAt(inst, "interp: slice src element size does not match pointer type")
|
return nil, nil, fr.errorAt(inst, errors.New("interp: slice src element size does not match pointer type"))
|
||||||
}
|
}
|
||||||
if dstArray.Type() != srcArray.Type() {
|
if dstArray.Type() != srcArray.Type() {
|
||||||
return nil, nil, fr.errorAt(inst, "interp: slice element types don't match")
|
return nil, nil, fr.errorAt(inst, errors.New("interp: slice element types don't match"))
|
||||||
}
|
}
|
||||||
length := dstLen.Value().SExtValue()
|
length := dstLen.Value().SExtValue()
|
||||||
if srcLength := srcLen.Value().SExtValue(); srcLength < length {
|
if srcLength := srcLen.Value().SExtValue(); srcLength < length {
|
||||||
length = srcLength
|
length = srcLength
|
||||||
}
|
}
|
||||||
if length < 0 {
|
if length < 0 {
|
||||||
return nil, nil, fr.errorAt(inst, "interp: trying to copy a slice with negative length?")
|
return nil, nil, fr.errorAt(inst, errors.New("interp: trying to copy a slice with negative length?"))
|
||||||
}
|
}
|
||||||
for i := int64(0); i < length; i++ {
|
for i := int64(0); i < length; i++ {
|
||||||
var err error
|
var err error
|
||||||
|
@ -406,13 +407,13 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||||
// dst++
|
// dst++
|
||||||
dstArrayValue, err := dstArray.GetElementPtr([]uint32{1})
|
dstArrayValue, err := dstArray.GetElementPtr([]uint32{1})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, errorAt(inst, err.Error())
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
}
|
}
|
||||||
dstArray = dstArrayValue.(*LocalValue)
|
dstArray = dstArrayValue.(*LocalValue)
|
||||||
// src++
|
// src++
|
||||||
srcArrayValue, err := srcArray.GetElementPtr([]uint32{1})
|
srcArrayValue, err := srcArray.GetElementPtr([]uint32{1})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, errorAt(inst, err.Error())
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
}
|
}
|
||||||
srcArray = srcArrayValue.(*LocalValue)
|
srcArray = srcArrayValue.(*LocalValue)
|
||||||
}
|
}
|
||||||
|
@ -444,11 +445,11 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||||
actualTypeInt := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying
|
actualTypeInt := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying
|
||||||
assertedType := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying
|
assertedType := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying
|
||||||
if actualTypeInt.IsAConstantExpr().IsNil() || actualTypeInt.Opcode() != llvm.PtrToInt {
|
if actualTypeInt.IsAConstantExpr().IsNil() || actualTypeInt.Opcode() != llvm.PtrToInt {
|
||||||
return nil, nil, fr.errorAt(inst, "interp: expected typecode in runtime.typeAssert to be a ptrtoint")
|
return nil, nil, fr.errorAt(inst, errors.New("interp: expected typecode in runtime.typeAssert to be a ptrtoint"))
|
||||||
}
|
}
|
||||||
actualType := actualTypeInt.Operand(0)
|
actualType := actualTypeInt.Operand(0)
|
||||||
if actualType.IsAConstant().IsNil() || assertedType.IsAConstant().IsNil() {
|
if actualType.IsAConstant().IsNil() || assertedType.IsAConstant().IsNil() {
|
||||||
return nil, nil, fr.errorAt(inst, "interp: unimplemented: type assert with non-constant interface value")
|
return nil, nil, fr.errorAt(inst, errors.New("interp: unimplemented: type assert with non-constant interface value"))
|
||||||
}
|
}
|
||||||
assertOk := uint64(0)
|
assertOk := uint64(0)
|
||||||
if llvm.ConstExtractValue(actualType.Initializer(), []uint32{0}) == assertedType {
|
if llvm.ConstExtractValue(actualType.Initializer(), []uint32{0}) == assertedType {
|
||||||
|
@ -459,16 +460,16 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||||
typecode := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying
|
typecode := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying
|
||||||
interfaceMethodSet := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying
|
interfaceMethodSet := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying
|
||||||
if typecode.IsAConstantExpr().IsNil() || typecode.Opcode() != llvm.PtrToInt {
|
if typecode.IsAConstantExpr().IsNil() || typecode.Opcode() != llvm.PtrToInt {
|
||||||
return nil, nil, fr.errorAt(inst, "interp: expected typecode to be a ptrtoint")
|
return nil, nil, fr.errorAt(inst, errors.New("interp: expected typecode to be a ptrtoint"))
|
||||||
}
|
}
|
||||||
typecode = typecode.Operand(0)
|
typecode = typecode.Operand(0)
|
||||||
if interfaceMethodSet.IsAConstantExpr().IsNil() || interfaceMethodSet.Opcode() != llvm.GetElementPtr {
|
if interfaceMethodSet.IsAConstantExpr().IsNil() || interfaceMethodSet.Opcode() != llvm.GetElementPtr {
|
||||||
return nil, nil, fr.errorAt(inst, "interp: expected method set in runtime.interfaceImplements to be a constant gep")
|
return nil, nil, fr.errorAt(inst, errors.New("interp: expected method set in runtime.interfaceImplements to be a constant gep"))
|
||||||
}
|
}
|
||||||
interfaceMethodSet = interfaceMethodSet.Operand(0).Initializer()
|
interfaceMethodSet = interfaceMethodSet.Operand(0).Initializer()
|
||||||
methodSet := llvm.ConstExtractValue(typecode.Initializer(), []uint32{1})
|
methodSet := llvm.ConstExtractValue(typecode.Initializer(), []uint32{1})
|
||||||
if methodSet.IsAConstantExpr().IsNil() || methodSet.Opcode() != llvm.GetElementPtr {
|
if methodSet.IsAConstantExpr().IsNil() || methodSet.Opcode() != llvm.GetElementPtr {
|
||||||
return nil, nil, fr.errorAt(inst, "interp: expected method set to be a constant gep")
|
return nil, nil, fr.errorAt(inst, errors.New("interp: expected method set to be a constant gep"))
|
||||||
}
|
}
|
||||||
methodSet = methodSet.Operand(0).Initializer()
|
methodSet = methodSet.Operand(0).Initializer()
|
||||||
|
|
||||||
|
@ -568,6 +569,11 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||||
// interpret anyway and hope for the best.
|
// interpret anyway and hope for the best.
|
||||||
ret, err = fr.function(callee, params, indent+" ")
|
ret, err = fr.function(callee, params, indent+" ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// Record this function call in the backtrace.
|
||||||
|
err.Traceback = append(err.Traceback, ErrorLine{
|
||||||
|
Pos: getPosition(inst),
|
||||||
|
Inst: inst,
|
||||||
|
})
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -586,7 +592,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||||
fr.locals[inst] = fr.getValue(newValue)
|
fr.locals[inst] = fr.getValue(newValue)
|
||||||
} else {
|
} else {
|
||||||
if len(indices) != 1 {
|
if len(indices) != 1 {
|
||||||
return nil, nil, fr.errorAt(inst, "interp: cannot handle extractvalue with not exactly 1 index")
|
return nil, nil, fr.errorAt(inst, errors.New("interp: cannot handle extractvalue with not exactly 1 index"))
|
||||||
}
|
}
|
||||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateExtractValue(agg.Underlying, int(indices[0]), inst.Name())}
|
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateExtractValue(agg.Underlying, int(indices[0]), inst.Name())}
|
||||||
}
|
}
|
||||||
|
@ -599,7 +605,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||||
fr.locals[inst] = &LocalValue{fr.Eval, newValue}
|
fr.locals[inst] = &LocalValue{fr.Eval, newValue}
|
||||||
} else {
|
} else {
|
||||||
if len(indices) != 1 {
|
if len(indices) != 1 {
|
||||||
return nil, nil, fr.errorAt(inst, "interp: cannot handle insertvalue with not exactly 1 index")
|
return nil, nil, fr.errorAt(inst, errors.New("interp: cannot handle insertvalue with not exactly 1 index"))
|
||||||
}
|
}
|
||||||
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateInsertValue(agg.Underlying, val.Value(), int(indices[0]), inst.Name())}
|
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateInsertValue(agg.Underlying, val.Value(), int(indices[0]), inst.Name())}
|
||||||
}
|
}
|
||||||
|
@ -624,17 +630,17 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||||
// conditional branch (if/then/else)
|
// conditional branch (if/then/else)
|
||||||
cond := fr.getLocal(inst.Operand(0)).Value()
|
cond := fr.getLocal(inst.Operand(0)).Value()
|
||||||
if cond.Type() != fr.Mod.Context().Int1Type() {
|
if cond.Type() != fr.Mod.Context().Int1Type() {
|
||||||
return nil, nil, fr.errorAt(inst, "expected an i1 in a branch instruction")
|
return nil, nil, fr.errorAt(inst, errors.New("expected an i1 in a branch instruction"))
|
||||||
}
|
}
|
||||||
thenBB := inst.Operand(1)
|
thenBB := inst.Operand(1)
|
||||||
elseBB := inst.Operand(2)
|
elseBB := inst.Operand(2)
|
||||||
if !cond.IsAInstruction().IsNil() {
|
if !cond.IsAInstruction().IsNil() {
|
||||||
return nil, nil, fr.errorAt(inst, "interp: branch on a non-constant")
|
return nil, nil, fr.errorAt(inst, errors.New("interp: branch on a non-constant"))
|
||||||
}
|
}
|
||||||
if !cond.IsAConstantExpr().IsNil() {
|
if !cond.IsAConstantExpr().IsNil() {
|
||||||
// This may happen when the instruction builder could not
|
// This may happen when the instruction builder could not
|
||||||
// const-fold some instructions.
|
// const-fold some instructions.
|
||||||
return nil, nil, fr.errorAt(inst, "interp: branch on a non-const-propagated constant expression")
|
return nil, nil, fr.errorAt(inst, errors.New("interp: branch on a non-const-propagated constant expression"))
|
||||||
}
|
}
|
||||||
switch cond {
|
switch cond {
|
||||||
case llvm.ConstInt(fr.Mod.Context().Int1Type(), 0, false): // false
|
case llvm.ConstInt(fr.Mod.Context().Int1Type(), 0, false): // false
|
||||||
|
@ -642,7 +648,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||||
case llvm.ConstInt(fr.Mod.Context().Int1Type(), 1, false): // true
|
case llvm.ConstInt(fr.Mod.Context().Int1Type(), 1, false): // true
|
||||||
return nil, []llvm.Value{elseBB}, nil // else
|
return nil, []llvm.Value{elseBB}, nil // else
|
||||||
default:
|
default:
|
||||||
return nil, nil, fr.errorAt(inst, "branch was not true or false")
|
return nil, nil, fr.errorAt(inst, errors.New("branch was not true or false"))
|
||||||
}
|
}
|
||||||
case !inst.IsABranchInst().IsNil() && inst.OperandsCount() == 1:
|
case !inst.IsABranchInst().IsNil() && inst.OperandsCount() == 1:
|
||||||
// unconditional branch (goto)
|
// unconditional branch (goto)
|
||||||
|
|
|
@ -97,7 +97,7 @@ func Run(mod llvm.Module, debug bool) error {
|
||||||
// function interprets the given function. The params are the function params
|
// function interprets the given function. The params are the function params
|
||||||
// and the indent is the string indentation to use when dumping all interpreted
|
// and the indent is the string indentation to use when dumping all interpreted
|
||||||
// instructions.
|
// instructions.
|
||||||
func (e *evalPackage) function(fn llvm.Value, params []Value, indent string) (Value, error) {
|
func (e *evalPackage) function(fn llvm.Value, params []Value, indent string) (Value, *Error) {
|
||||||
fr := frame{
|
fr := frame{
|
||||||
evalPackage: e,
|
evalPackage: e,
|
||||||
fn: fn,
|
fn: fn,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package interp
|
package interp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
|
@ -40,7 +41,7 @@ type sideEffectResult struct {
|
||||||
// hasSideEffects scans this function and all descendants, recursively. It
|
// hasSideEffects scans this function and all descendants, recursively. It
|
||||||
// returns whether this function has side effects and if it does, which globals
|
// returns whether this function has side effects and if it does, which globals
|
||||||
// it mentions anywhere in this function or any called functions.
|
// it mentions anywhere in this function or any called functions.
|
||||||
func (e *evalPackage) hasSideEffects(fn llvm.Value) (*sideEffectResult, error) {
|
func (e *evalPackage) hasSideEffects(fn llvm.Value) (*sideEffectResult, *Error) {
|
||||||
name := fn.Name()
|
name := fn.Name()
|
||||||
switch {
|
switch {
|
||||||
case name == "runtime.alloc":
|
case name == "runtime.alloc":
|
||||||
|
@ -99,7 +100,7 @@ func (e *evalPackage) hasSideEffects(fn llvm.Value) (*sideEffectResult, error) {
|
||||||
switch inst.InstructionOpcode() {
|
switch inst.InstructionOpcode() {
|
||||||
case llvm.IndirectBr, llvm.Invoke:
|
case llvm.IndirectBr, llvm.Invoke:
|
||||||
// Not emitted by the compiler.
|
// Not emitted by the compiler.
|
||||||
return nil, e.errorAt(inst, "unknown instructions")
|
return nil, e.errorAt(inst, errors.New("unknown instructions"))
|
||||||
case llvm.Call:
|
case llvm.Call:
|
||||||
child := inst.CalledValue()
|
child := inst.CalledValue()
|
||||||
if !child.IsAInlineAsm().IsNil() {
|
if !child.IsAInlineAsm().IsNil() {
|
||||||
|
|
26
main.go
26
main.go
|
@ -658,22 +658,22 @@ func usage() {
|
||||||
// to limitations in the LLVM bindings.
|
// to limitations in the LLVM bindings.
|
||||||
func printCompilerError(logln func(...interface{}), err error) {
|
func printCompilerError(logln func(...interface{}), err error) {
|
||||||
switch err := err.(type) {
|
switch err := err.(type) {
|
||||||
case *interp.Unsupported:
|
|
||||||
// hit an unknown/unsupported instruction
|
|
||||||
logln("#", err.ImportPath)
|
|
||||||
msg := "unsupported instruction during init evaluation:"
|
|
||||||
if err.Pos.String() != "" {
|
|
||||||
msg = err.Pos.String() + " " + msg
|
|
||||||
}
|
|
||||||
logln(msg)
|
|
||||||
err.Inst.Dump()
|
|
||||||
logln()
|
|
||||||
case types.Error, scanner.Error:
|
case types.Error, scanner.Error:
|
||||||
logln(err)
|
logln(err)
|
||||||
case interp.Error:
|
case *interp.Error:
|
||||||
logln("#", err.ImportPath)
|
logln("#", err.ImportPath)
|
||||||
for _, err := range err.Errs {
|
logln(err.Error())
|
||||||
logln(err)
|
if !err.Inst.IsNil() {
|
||||||
|
err.Inst.Dump()
|
||||||
|
logln()
|
||||||
|
}
|
||||||
|
if len(err.Traceback) > 0 {
|
||||||
|
logln("\ntraceback:")
|
||||||
|
for _, line := range err.Traceback {
|
||||||
|
logln(line.Pos.String() + ":")
|
||||||
|
line.Inst.Dump()
|
||||||
|
logln()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case loader.Errors:
|
case loader.Errors:
|
||||||
logln("#", err.Pkg.ImportPath)
|
logln("#", err.Pkg.ImportPath)
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче