
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.
126 строки
3,1 КиБ
Go
126 строки
3,1 КиБ
Go
package interp
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"tinygo.org/x/go-llvm"
|
|
)
|
|
|
|
// Return a list of values (actually, instructions) where this value is used as
|
|
// an operand.
|
|
func getUses(value llvm.Value) []llvm.Value {
|
|
var uses []llvm.Value
|
|
use := value.FirstUse()
|
|
for !use.IsNil() {
|
|
uses = append(uses, use.User())
|
|
use = use.NextUse()
|
|
}
|
|
return uses
|
|
}
|
|
|
|
// getStringBytes loads the byte slice of a Go string represented as a
|
|
// {ptr, len} pair.
|
|
func getStringBytes(strPtr Value, strLen llvm.Value) ([]byte, error) {
|
|
if !strLen.IsConstant() {
|
|
return nil, errors.New("getStringBytes with a non-constant length")
|
|
}
|
|
buf := make([]byte, strLen.ZExtValue())
|
|
for i := range buf {
|
|
gep, err := strPtr.GetElementPtr([]uint32{uint32(i)})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
c, err := gep.Load()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
buf[i] = byte(c.ZExtValue())
|
|
}
|
|
return buf, nil
|
|
}
|
|
|
|
// getLLVMIndices converts an []uint32 into an []llvm.Value, for use in
|
|
// llvm.ConstGEP.
|
|
func getLLVMIndices(int32Type llvm.Type, indices []uint32) []llvm.Value {
|
|
llvmIndices := make([]llvm.Value, len(indices))
|
|
for i, index := range indices {
|
|
llvmIndices[i] = llvm.ConstInt(int32Type, uint64(index), false)
|
|
}
|
|
return llvmIndices
|
|
}
|
|
|
|
// Return true if this type is a scalar value (integer or floating point), false
|
|
// otherwise.
|
|
func isScalar(t llvm.Type) bool {
|
|
switch t.TypeKind() {
|
|
case llvm.IntegerTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// isPointerNil returns whether this is a nil pointer or not. The ok value
|
|
// indicates whether the result is certain: if it is false the result boolean is
|
|
// not valid.
|
|
func isPointerNil(v llvm.Value) (result bool, ok bool) {
|
|
if !v.IsAConstantExpr().IsNil() {
|
|
switch v.Opcode() {
|
|
case llvm.IntToPtr:
|
|
// Whether a constant inttoptr is nil is easy to
|
|
// determine.
|
|
result, ok = isZero(v.Operand(0))
|
|
if ok {
|
|
return
|
|
}
|
|
case llvm.BitCast, llvm.GetElementPtr:
|
|
// These const instructions are just a kind of wrappers for the
|
|
// underlying pointer.
|
|
return isPointerNil(v.Operand(0))
|
|
}
|
|
}
|
|
if !v.IsAConstantPointerNull().IsNil() {
|
|
// A constant pointer null is always null, of course.
|
|
return true, true
|
|
}
|
|
if !v.IsAGlobalValue().IsNil() {
|
|
// A global value is never null.
|
|
return false, true
|
|
}
|
|
return false, false // not valid
|
|
}
|
|
|
|
// isZero returns whether the value in v is the integer zero, and whether that
|
|
// can be known right now.
|
|
func isZero(v llvm.Value) (result bool, ok bool) {
|
|
if !v.IsAConstantExpr().IsNil() {
|
|
switch v.Opcode() {
|
|
case llvm.PtrToInt:
|
|
return isPointerNil(v.Operand(0))
|
|
}
|
|
}
|
|
if !v.IsAConstantInt().IsNil() {
|
|
val := v.ZExtValue()
|
|
return val == 0, true
|
|
}
|
|
return false, false // not valid
|
|
}
|
|
|
|
// unwrap returns the underlying value, with GEPs removed. This can be useful to
|
|
// get the underlying global of a GEP pointer.
|
|
func unwrap(value llvm.Value) llvm.Value {
|
|
for {
|
|
if !value.IsAConstantExpr().IsNil() {
|
|
switch value.Opcode() {
|
|
case llvm.GetElementPtr:
|
|
value = value.Operand(0)
|
|
continue
|
|
}
|
|
} else if !value.IsAGetElementPtrInst().IsNil() {
|
|
value = value.Operand(0)
|
|
continue
|
|
}
|
|
break
|
|
}
|
|
return value
|
|
}
|