interp: error location for "unknown GEP" error

This commit removes a panic and replaces it with a proper source
location. The message still isn't very helpful, but at least it points
to a location in the source code.

I'm not very happy with all the `err.Error()` calls, but that's the way
to fit this in a `scanner.Error`. Eventually we should make a
replacement for `scanner.Error` that does proper wrapping of the
original error message.
Этот коммит содержится в:
Ayke van Laethem 2019-12-31 19:59:34 +01:00 коммит произвёл Ron Evans
родитель ec467da83c
коммит 25cff20117
3 изменённых файлов: 49 добавлений и 20 удалений

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

@ -125,7 +125,10 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
} }
indices[i] = uint32(operand.Value().ZExtValue()) indices[i] = uint32(operand.Value().ZExtValue())
} }
result := value.GetElementPtr(indices) result, err := value.GetElementPtr(indices)
if err != nil {
return nil, nil, fr.errorAt(inst, err.Error())
}
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, "interp: gep: type does not match")
} }
@ -264,7 +267,11 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
if elementCount == 1 { if elementCount == 1 {
fr.locals[resultInst] = result fr.locals[resultInst] = result
} else { } else {
fr.locals[resultInst] = result.GetElementPtr([]uint32{0, 0}) result, err := result.GetElementPtr([]uint32{0, 0})
if err != nil {
return nil, nil, errorAt(inst, err.Error())
}
fr.locals[resultInst] = result
} }
case callee.Name() == "runtime.hashmapMake": case callee.Name() == "runtime.hashmapMake":
// create a map // create a map
@ -365,8 +372,16 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
// a bitcast of the original array instead of the GEP, // a bitcast of the original array instead of the GEP,
// which breaks our assumptions. // which breaks our assumptions.
// 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...
dstArray = dstArray.GetElementPtr([]uint32{0, 0}).(*LocalValue) dstArrayValue, err := dstArray.GetElementPtr([]uint32{0, 0})
srcArray = srcArray.GetElementPtr([]uint32{0, 0}).(*LocalValue) if err != nil {
return nil, nil, errorAt(inst, err.Error())
}
dstArray = dstArrayValue.(*LocalValue)
srcArrayValue, err := srcArray.GetElementPtr([]uint32{0, 0})
if err != nil {
return nil, nil, errorAt(inst, err.Error())
}
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, "interp: slice dst element size does not match pointer type")
@ -385,12 +400,21 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
return nil, nil, fr.errorAt(inst, "interp: trying to copy a slice with negative length?") return nil, nil, fr.errorAt(inst, "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()) dstArray.Store(srcArray.Load())
// dst++ // dst++
dstArray = dstArray.GetElementPtr([]uint32{1}).(*LocalValue) dstArrayValue, err := dstArray.GetElementPtr([]uint32{1})
if err != nil {
return nil, nil, errorAt(inst, err.Error())
}
dstArray = dstArrayValue.(*LocalValue)
// src++ // src++
srcArray = srcArray.GetElementPtr([]uint32{1}).(*LocalValue) srcArrayValue, err := srcArray.GetElementPtr([]uint32{1})
if err != nil {
return nil, nil, errorAt(inst, err.Error())
}
srcArray = srcArrayValue.(*LocalValue)
} }
case callee.Name() == "runtime.stringToBytes": case callee.Name() == "runtime.stringToBytes":
// convert a string to a []byte // convert a string to a []byte

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

@ -24,7 +24,11 @@ func getStringBytes(strPtr Value, strLen llvm.Value) []byte {
} }
buf := make([]byte, strLen.ZExtValue()) buf := make([]byte, strLen.ZExtValue())
for i := range buf { for i := range buf {
c := strPtr.GetElementPtr([]uint32{uint32(i)}).Load() gep, err := strPtr.GetElementPtr([]uint32{uint32(i)})
if err != nil {
panic(err) // TODO
}
c := gep.Load()
buf[i] = byte(c.ZExtValue()) buf[i] = byte(c.ZExtValue())
} }
return buf return buf

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

@ -3,6 +3,7 @@ package interp
// This file provides a litte bit of abstraction around LLVM values. // This file provides a litte bit of abstraction around LLVM values.
import ( import (
"errors"
"strconv" "strconv"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
@ -11,13 +12,13 @@ import (
// A Value is a LLVM value with some extra methods attached for easier // A Value is a LLVM value with some extra methods attached for easier
// interpretation. // interpretation.
type Value interface { 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 // dereference a pointer
Store(llvm.Value) // store to a pointer Store(llvm.Value) // store to a pointer
GetElementPtr([]uint32) Value // returns an interior pointer GetElementPtr([]uint32) (Value, error) // returns an interior pointer
String() string // string representation, for debugging String() string // string representation, for debugging
} }
// A type that simply wraps a LLVM constant value. // A type that simply wraps a LLVM constant value.
@ -97,21 +98,21 @@ func (v *LocalValue) Store(value llvm.Value) {
} }
// GetElementPtr returns a GEP when the underlying value is of pointer type. // GetElementPtr returns a GEP when the underlying value is of pointer type.
func (v *LocalValue) GetElementPtr(indices []uint32) Value { func (v *LocalValue) GetElementPtr(indices []uint32) (Value, error) {
if !v.Underlying.IsAGlobalVariable().IsNil() { if !v.Underlying.IsAGlobalVariable().IsNil() {
int32Type := v.Underlying.Type().Context().Int32Type() int32Type := v.Underlying.Type().Context().Int32Type()
gep := llvm.ConstGEP(v.Underlying, getLLVMIndices(int32Type, indices)) gep := llvm.ConstGEP(v.Underlying, getLLVMIndices(int32Type, indices))
return &LocalValue{v.Eval, gep} return &LocalValue{v.Eval, gep}, nil
} }
if !v.Underlying.IsAConstantExpr().IsNil() { if !v.Underlying.IsAConstantExpr().IsNil() {
switch v.Underlying.Opcode() { switch v.Underlying.Opcode() {
case llvm.GetElementPtr, llvm.IntToPtr, llvm.BitCast: case llvm.GetElementPtr, llvm.IntToPtr, llvm.BitCast:
int32Type := v.Underlying.Type().Context().Int32Type() int32Type := v.Underlying.Type().Context().Int32Type()
llvmIndices := getLLVMIndices(int32Type, indices) llvmIndices := getLLVMIndices(int32Type, indices)
return &LocalValue{v.Eval, llvm.ConstGEP(v.Underlying, llvmIndices)} return &LocalValue{v.Eval, llvm.ConstGEP(v.Underlying, llvmIndices)}, nil
} }
} }
panic("interp: unknown GEP") return nil, errors.New("interp: unknown GEP")
} }
// stripPointerCasts removes all const bitcasts from pointer values, if there // stripPointerCasts removes all const bitcasts from pointer values, if there
@ -309,8 +310,8 @@ func (v *MapValue) Store(value llvm.Value) {
// GetElementPtr panics: maps are of reference type so their (interior) // GetElementPtr panics: maps are of reference type so their (interior)
// addresses cannot be calculated. // addresses cannot be calculated.
func (v *MapValue) GetElementPtr(indices []uint32) Value { func (v *MapValue) GetElementPtr(indices []uint32) (Value, error) {
panic("interp: GEP on a map") return nil, errors.New("interp: GEP on a map")
} }
// 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