diff --git a/interp/frame.go b/interp/frame.go index cf429969..4cf87408 100644 --- a/interp/frame.go +++ b/interp/frame.go @@ -125,7 +125,10 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re } 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() { 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 { fr.locals[resultInst] = result } 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": // 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, // which breaks our assumptions. // Re-add this GEP, in the hope that it it is then of the correct type... - dstArray = dstArray.GetElementPtr([]uint32{0, 0}).(*LocalValue) - srcArray = srcArray.GetElementPtr([]uint32{0, 0}).(*LocalValue) + dstArrayValue, err := dstArray.GetElementPtr([]uint32{0, 0}) + 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 { 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?") } for i := int64(0); i < length; i++ { + var err error // *dst = *src dstArray.Store(srcArray.Load()) // 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++ - 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": // convert a string to a []byte diff --git a/interp/utils.go b/interp/utils.go index 8f0df4c7..018da8f1 100644 --- a/interp/utils.go +++ b/interp/utils.go @@ -24,7 +24,11 @@ func getStringBytes(strPtr Value, strLen llvm.Value) []byte { } buf := make([]byte, strLen.ZExtValue()) 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()) } return buf diff --git a/interp/values.go b/interp/values.go index bbd1b6af..a6e48c35 100644 --- a/interp/values.go +++ b/interp/values.go @@ -3,6 +3,7 @@ package interp // This file provides a litte bit of abstraction around LLVM values. import ( + "errors" "strconv" "tinygo.org/x/go-llvm" @@ -11,13 +12,13 @@ import ( // A Value is a LLVM value with some extra methods attached for easier // interpretation. type Value interface { - Value() llvm.Value // returns a LLVM value - Type() llvm.Type // equal to Value().Type() - IsConstant() bool // returns true if this value is a constant value - Load() llvm.Value // dereference a pointer - Store(llvm.Value) // store to a pointer - GetElementPtr([]uint32) Value // returns an interior pointer - String() string // string representation, for debugging + Value() llvm.Value // returns a LLVM value + Type() llvm.Type // equal to Value().Type() + IsConstant() bool // returns true if this value is a constant value + Load() llvm.Value // dereference a pointer + Store(llvm.Value) // store to a pointer + GetElementPtr([]uint32) (Value, error) // returns an interior pointer + String() string // string representation, for debugging } // 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. -func (v *LocalValue) GetElementPtr(indices []uint32) Value { +func (v *LocalValue) GetElementPtr(indices []uint32) (Value, error) { if !v.Underlying.IsAGlobalVariable().IsNil() { int32Type := v.Underlying.Type().Context().Int32Type() gep := llvm.ConstGEP(v.Underlying, getLLVMIndices(int32Type, indices)) - return &LocalValue{v.Eval, gep} + return &LocalValue{v.Eval, gep}, nil } if !v.Underlying.IsAConstantExpr().IsNil() { switch v.Underlying.Opcode() { case llvm.GetElementPtr, llvm.IntToPtr, llvm.BitCast: int32Type := v.Underlying.Type().Context().Int32Type() 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 @@ -309,8 +310,8 @@ func (v *MapValue) Store(value llvm.Value) { // GetElementPtr panics: maps are of reference type so their (interior) // addresses cannot be calculated. -func (v *MapValue) GetElementPtr(indices []uint32) Value { - panic("interp: GEP on a map") +func (v *MapValue) GetElementPtr(indices []uint32) (Value, error) { + return nil, errors.New("interp: GEP on a map") } // PutString does a map assign operation, assuming that the map is of type