interp: replace some panics with error messages
A number of functions now return errors instead of panicking, which should help greatly when investigating interp errors. It at least shows the package responsible for it.
Этот коммит содержится в:
родитель
d1ac0138e6
коммит
ccb803e35d
3 изменённых файлов: 87 добавлений и 37 удалений
|
@ -95,7 +95,11 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||||
if !operand.IsConstant() || inst.IsVolatile() || (!operand.Underlying.IsAConstantExpr().IsNil() && operand.Underlying.Opcode() == llvm.BitCast) {
|
if !operand.IsConstant() || inst.IsVolatile() || (!operand.Underlying.IsAConstantExpr().IsNil() && operand.Underlying.Opcode() == llvm.BitCast) {
|
||||||
value = fr.builder.CreateLoad(operand.Value(), inst.Name())
|
value = fr.builder.CreateLoad(operand.Value(), inst.Name())
|
||||||
} else {
|
} else {
|
||||||
value = operand.Load()
|
var err error
|
||||||
|
value, err = operand.Load()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if value.Type() != inst.Type() {
|
if value.Type() != inst.Type() {
|
||||||
return nil, nil, fr.errorAt(inst, errors.New("interp: load: type does not match"))
|
return nil, nil, fr.errorAt(inst, errors.New("interp: load: type does not match"))
|
||||||
|
@ -308,7 +312,10 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||||
}
|
}
|
||||||
// "key" is a Go string value, which in the TinyGo calling convention is split up
|
// "key" is a Go string value, which in the TinyGo calling convention is split up
|
||||||
// into separate pointer and length parameters.
|
// into separate pointer and length parameters.
|
||||||
m.PutString(keyBuf, keyLen, valPtr)
|
err := m.PutString(keyBuf, keyLen, valPtr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
|
}
|
||||||
case callee.Name() == "runtime.hashmapBinarySet":
|
case callee.Name() == "runtime.hashmapBinarySet":
|
||||||
// set a binary (int etc.) key in the map
|
// set a binary (int etc.) key in the map
|
||||||
keyBuf := fr.getLocal(inst.Operand(1)).(*LocalValue)
|
keyBuf := fr.getLocal(inst.Operand(1)).(*LocalValue)
|
||||||
|
@ -329,15 +336,24 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||||
fr.builder.CreateCall(callee, llvmParams, "")
|
fr.builder.CreateCall(callee, llvmParams, "")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
m.PutBinary(keyBuf, valPtr)
|
err := m.PutBinary(keyBuf, valPtr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
|
}
|
||||||
case callee.Name() == "runtime.stringConcat":
|
case callee.Name() == "runtime.stringConcat":
|
||||||
// adding two strings together
|
// adding two strings together
|
||||||
buf1Ptr := fr.getLocal(inst.Operand(0))
|
buf1Ptr := fr.getLocal(inst.Operand(0))
|
||||||
buf1Len := fr.getLocal(inst.Operand(1))
|
buf1Len := fr.getLocal(inst.Operand(1))
|
||||||
buf2Ptr := fr.getLocal(inst.Operand(2))
|
buf2Ptr := fr.getLocal(inst.Operand(2))
|
||||||
buf2Len := fr.getLocal(inst.Operand(3))
|
buf2Len := fr.getLocal(inst.Operand(3))
|
||||||
buf1 := getStringBytes(buf1Ptr, buf1Len.Value())
|
buf1, err := getStringBytes(buf1Ptr, buf1Len.Value())
|
||||||
buf2 := getStringBytes(buf2Ptr, buf2Len.Value())
|
if err != nil {
|
||||||
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
|
}
|
||||||
|
buf2, err := getStringBytes(buf2Ptr, buf2Len.Value())
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
|
}
|
||||||
result := []byte(string(buf1) + string(buf2))
|
result := []byte(string(buf1) + string(buf2))
|
||||||
vals := make([]llvm.Value, len(result))
|
vals := make([]llvm.Value, len(result))
|
||||||
for i := range vals {
|
for i := range vals {
|
||||||
|
@ -401,9 +417,12 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||||
return nil, nil, fr.errorAt(inst, errors.New("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
|
|
||||||
// *dst = *src
|
// *dst = *src
|
||||||
dstArray.Store(srcArray.Load())
|
val, err := srcArray.Load()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
|
}
|
||||||
|
dstArray.Store(val)
|
||||||
// dst++
|
// dst++
|
||||||
dstArrayValue, err := dstArray.GetElementPtr([]uint32{1})
|
dstArrayValue, err := dstArray.GetElementPtr([]uint32{1})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -421,7 +440,10 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||||
// convert a string to a []byte
|
// convert a string to a []byte
|
||||||
bufPtr := fr.getLocal(inst.Operand(0))
|
bufPtr := fr.getLocal(inst.Operand(0))
|
||||||
bufLen := fr.getLocal(inst.Operand(1))
|
bufLen := fr.getLocal(inst.Operand(1))
|
||||||
result := getStringBytes(bufPtr, bufLen.Value())
|
result, err := getStringBytes(bufPtr, bufLen.Value())
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fr.errorAt(inst, err)
|
||||||
|
}
|
||||||
vals := make([]llvm.Value, len(result))
|
vals := make([]llvm.Value, len(result))
|
||||||
for i := range vals {
|
for i := range vals {
|
||||||
vals[i] = llvm.ConstInt(fr.Mod.Context().Int8Type(), uint64(result[i]), false)
|
vals[i] = llvm.ConstInt(fr.Mod.Context().Int8Type(), uint64(result[i]), false)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package interp
|
package interp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -18,20 +20,23 @@ func getUses(value llvm.Value) []llvm.Value {
|
||||||
|
|
||||||
// getStringBytes loads the byte slice of a Go string represented as a
|
// getStringBytes loads the byte slice of a Go string represented as a
|
||||||
// {ptr, len} pair.
|
// {ptr, len} pair.
|
||||||
func getStringBytes(strPtr Value, strLen llvm.Value) []byte {
|
func getStringBytes(strPtr Value, strLen llvm.Value) ([]byte, error) {
|
||||||
if !strLen.IsConstant() {
|
if !strLen.IsConstant() {
|
||||||
panic("getStringBytes with a non-constant length")
|
return nil, errors.New("getStringBytes with a non-constant length")
|
||||||
}
|
}
|
||||||
buf := make([]byte, strLen.ZExtValue())
|
buf := make([]byte, strLen.ZExtValue())
|
||||||
for i := range buf {
|
for i := range buf {
|
||||||
gep, err := strPtr.GetElementPtr([]uint32{uint32(i)})
|
gep, err := strPtr.GetElementPtr([]uint32{uint32(i)})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err) // TODO
|
return nil, err
|
||||||
|
}
|
||||||
|
c, err := gep.Load()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
c := gep.Load()
|
|
||||||
buf[i] = byte(c.ZExtValue())
|
buf[i] = byte(c.ZExtValue())
|
||||||
}
|
}
|
||||||
return buf
|
return buf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getLLVMIndices converts an []uint32 into an []llvm.Value, for use in
|
// getLLVMIndices converts an []uint32 into an []llvm.Value, for use in
|
||||||
|
|
|
@ -15,7 +15,7 @@ type Value interface {
|
||||||
Value() llvm.Value // returns a LLVM value
|
Value() llvm.Value // returns a LLVM value
|
||||||
Type() llvm.Type // equal to Value().Type()
|
Type() llvm.Type // equal to Value().Type()
|
||||||
IsConstant() bool // returns true if this value is a constant value
|
IsConstant() bool // returns true if this value is a constant value
|
||||||
Load() llvm.Value // dereference a pointer
|
Load() (llvm.Value, error) // dereference a pointer
|
||||||
Store(llvm.Value) // store to a pointer
|
Store(llvm.Value) // store to a pointer
|
||||||
GetElementPtr([]uint32) (Value, error) // returns an interior pointer
|
GetElementPtr([]uint32) (Value, error) // returns an interior pointer
|
||||||
String() string // string representation, for debugging
|
String() string // string representation, for debugging
|
||||||
|
@ -44,23 +44,26 @@ func (v *LocalValue) IsConstant() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load loads a constant value if this is a constant pointer.
|
// Load loads a constant value if this is a constant pointer.
|
||||||
func (v *LocalValue) Load() llvm.Value {
|
func (v *LocalValue) Load() (llvm.Value, error) {
|
||||||
if !v.Underlying.IsAGlobalVariable().IsNil() {
|
if !v.Underlying.IsAGlobalVariable().IsNil() {
|
||||||
return v.Underlying.Initializer()
|
return v.Underlying.Initializer(), nil
|
||||||
}
|
}
|
||||||
switch v.Underlying.Opcode() {
|
switch v.Underlying.Opcode() {
|
||||||
case llvm.GetElementPtr:
|
case llvm.GetElementPtr:
|
||||||
indices := v.getConstGEPIndices()
|
indices := v.getConstGEPIndices()
|
||||||
if indices[0] != 0 {
|
if indices[0] != 0 {
|
||||||
panic("invalid GEP")
|
return llvm.Value{}, errors.New("invalid GEP")
|
||||||
}
|
}
|
||||||
global := v.Eval.getValue(v.Underlying.Operand(0))
|
global := v.Eval.getValue(v.Underlying.Operand(0))
|
||||||
agg := global.Load()
|
agg, err := global.Load()
|
||||||
return llvm.ConstExtractValue(agg, indices[1:])
|
if err != nil {
|
||||||
|
return llvm.Value{}, err
|
||||||
|
}
|
||||||
|
return llvm.ConstExtractValue(agg, indices[1:]), nil
|
||||||
case llvm.BitCast:
|
case llvm.BitCast:
|
||||||
panic("interp: load from a bitcast")
|
return llvm.Value{}, errors.New("interp: load from a bitcast")
|
||||||
default:
|
default:
|
||||||
panic("interp: load from a constant")
|
return llvm.Value{}, errors.New("interp: load from a constant")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +91,10 @@ func (v *LocalValue) Store(value llvm.Value) {
|
||||||
panic("invalid GEP")
|
panic("invalid GEP")
|
||||||
}
|
}
|
||||||
global := &LocalValue{v.Eval, v.Underlying.Operand(0)}
|
global := &LocalValue{v.Eval, v.Underlying.Operand(0)}
|
||||||
agg := global.Load()
|
agg, err := global.Load()
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // TODO
|
||||||
|
}
|
||||||
agg = llvm.ConstInsertValue(agg, value, indices[1:])
|
agg = llvm.ConstInsertValue(agg, value, indices[1:])
|
||||||
global.Store(agg)
|
global.Store(agg)
|
||||||
return
|
return
|
||||||
|
@ -225,7 +231,11 @@ func (v *MapValue) Value() llvm.Value {
|
||||||
keyPtr := llvm.ConstExtractValue(llvmKey, []uint32{0})
|
keyPtr := llvm.ConstExtractValue(llvmKey, []uint32{0})
|
||||||
keyLen := llvm.ConstExtractValue(llvmKey, []uint32{1})
|
keyLen := llvm.ConstExtractValue(llvmKey, []uint32{1})
|
||||||
keyPtrVal := v.Eval.getValue(keyPtr)
|
keyPtrVal := v.Eval.getValue(keyPtr)
|
||||||
keyBuf = getStringBytes(keyPtrVal, keyLen)
|
var err error
|
||||||
|
keyBuf, err = getStringBytes(keyPtrVal, keyLen)
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // TODO
|
||||||
|
}
|
||||||
} else if key.Type().TypeKind() == llvm.IntegerTypeKind {
|
} else if key.Type().TypeKind() == llvm.IntegerTypeKind {
|
||||||
keyBuf = make([]byte, v.Eval.TargetData.TypeAllocSize(key.Type()))
|
keyBuf = make([]byte, v.Eval.TargetData.TypeAllocSize(key.Type()))
|
||||||
n := key.Value().ZExtValue()
|
n := key.Value().ZExtValue()
|
||||||
|
@ -299,7 +309,7 @@ func (v *MapValue) IsConstant() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load panics: maps are of reference type so cannot be dereferenced.
|
// Load panics: maps are of reference type so cannot be dereferenced.
|
||||||
func (v *MapValue) Load() llvm.Value {
|
func (v *MapValue) Load() (llvm.Value, error) {
|
||||||
panic("interp: load from a map")
|
panic("interp: load from a map")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,23 +326,26 @@ func (v *MapValue) GetElementPtr(indices []uint32) (Value, error) {
|
||||||
|
|
||||||
// PutString does a map assign operation, assuming that the map is of type
|
// PutString does a map assign operation, assuming that the map is of type
|
||||||
// map[string]T.
|
// map[string]T.
|
||||||
func (v *MapValue) PutString(keyBuf, keyLen, valPtr *LocalValue) {
|
func (v *MapValue) PutString(keyBuf, keyLen, valPtr *LocalValue) error {
|
||||||
if !v.Underlying.IsNil() {
|
if !v.Underlying.IsNil() {
|
||||||
panic("map already created")
|
return errors.New("map already created")
|
||||||
}
|
}
|
||||||
|
|
||||||
if valPtr.Underlying.Opcode() == llvm.BitCast {
|
if valPtr.Underlying.Opcode() == llvm.BitCast {
|
||||||
valPtr = &LocalValue{v.Eval, valPtr.Underlying.Operand(0)}
|
valPtr = &LocalValue{v.Eval, valPtr.Underlying.Operand(0)}
|
||||||
}
|
}
|
||||||
value := valPtr.Load()
|
value, err := valPtr.Load()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if v.ValueType.IsNil() {
|
if v.ValueType.IsNil() {
|
||||||
v.ValueType = value.Type()
|
v.ValueType = value.Type()
|
||||||
if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize {
|
if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize {
|
||||||
panic("interp: map store value type has the wrong size")
|
return errors.New("interp: map store value type has the wrong size")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if value.Type() != v.ValueType {
|
if value.Type() != v.ValueType {
|
||||||
panic("interp: map store value type is inconsistent")
|
return errors.New("interp: map store value type is inconsistent")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,26 +358,31 @@ func (v *MapValue) PutString(keyBuf, keyLen, valPtr *LocalValue) {
|
||||||
// TODO: avoid duplicate keys
|
// TODO: avoid duplicate keys
|
||||||
v.Keys = append(v.Keys, &LocalValue{v.Eval, key})
|
v.Keys = append(v.Keys, &LocalValue{v.Eval, key})
|
||||||
v.Values = append(v.Values, &LocalValue{v.Eval, value})
|
v.Values = append(v.Values, &LocalValue{v.Eval, value})
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PutBinary does a map assign operation.
|
// PutBinary does a map assign operation.
|
||||||
func (v *MapValue) PutBinary(keyPtr, valPtr *LocalValue) {
|
func (v *MapValue) PutBinary(keyPtr, valPtr *LocalValue) error {
|
||||||
if !v.Underlying.IsNil() {
|
if !v.Underlying.IsNil() {
|
||||||
panic("map already created")
|
return errors.New("map already created")
|
||||||
}
|
}
|
||||||
|
|
||||||
if valPtr.Underlying.Opcode() == llvm.BitCast {
|
if valPtr.Underlying.Opcode() == llvm.BitCast {
|
||||||
valPtr = &LocalValue{v.Eval, valPtr.Underlying.Operand(0)}
|
valPtr = &LocalValue{v.Eval, valPtr.Underlying.Operand(0)}
|
||||||
}
|
}
|
||||||
value := valPtr.Load()
|
value, err := valPtr.Load()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if v.ValueType.IsNil() {
|
if v.ValueType.IsNil() {
|
||||||
v.ValueType = value.Type()
|
v.ValueType = value.Type()
|
||||||
if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize {
|
if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize {
|
||||||
panic("interp: map store value type has the wrong size")
|
return errors.New("interp: map store value type has the wrong size")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if value.Type() != v.ValueType {
|
if value.Type() != v.ValueType {
|
||||||
panic("interp: map store value type is inconsistent")
|
return errors.New("interp: map store value type is inconsistent")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,21 +393,26 @@ func (v *MapValue) PutBinary(keyPtr, valPtr *LocalValue) {
|
||||||
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
|
keyPtr = &LocalValue{v.Eval, keyPtr.Underlying.Operand(0)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
key := keyPtr.Load()
|
key, err := keyPtr.Load()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if v.KeyType.IsNil() {
|
if v.KeyType.IsNil() {
|
||||||
v.KeyType = key.Type()
|
v.KeyType = key.Type()
|
||||||
if int(v.Eval.TargetData.TypeAllocSize(v.KeyType)) != v.KeySize {
|
if int(v.Eval.TargetData.TypeAllocSize(v.KeyType)) != v.KeySize {
|
||||||
panic("interp: map store key type has the wrong size")
|
return errors.New("interp: map store key type has the wrong size")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if key.Type() != v.KeyType {
|
if key.Type() != v.KeyType {
|
||||||
panic("interp: map store key type is inconsistent")
|
return errors.New("interp: map store key type is inconsistent")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: avoid duplicate keys
|
// TODO: avoid duplicate keys
|
||||||
v.Keys = append(v.Keys, &LocalValue{v.Eval, key})
|
v.Keys = append(v.Keys, &LocalValue{v.Eval, key})
|
||||||
v.Values = append(v.Values, &LocalValue{v.Eval, value})
|
v.Values = append(v.Values, &LocalValue{v.Eval, value})
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get FNV-1a hash of this string.
|
// Get FNV-1a hash of this string.
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче