fspl/llvm/gep.go

192 lines
6.1 KiB
Go

package llvm
import "fmt"
type gepIndex struct {
HasVal bool
Val int64
VectorLen uint64
}
func gepInstType (elemType, src Type, indices []Value) Type {
var idxs []gepIndex
for _, index := range indices {
var idx gepIndex
switch index := index.(type) {
case Const:
idx = getIndex(index)
default:
idx = gepIndex{HasVal: false}
// Check if index is of vector type.
if indexType, ok := index.Type().(*TypeVector); ok {
idx.VectorLen = indexType.Length
}
}
idxs = append(idxs, idx)
}
return resultType(elemType, src, idxs)
}
func getIndex (index Const) gepIndex {
// unpack inrange indices.
if idx, ok := index.(*ConstIndex); ok {
index = idx.Const
}
// TODO: figure out how to simplify expressions for GEP instructions without
// creating import cycle on irutil.
// Use index.Simplify() to simplify the constant expression to a concrete
// integer constant or vector of integers constant.
//if idx, ok := index.(constant.Expression); ok {
// index = idx.Simplify()
//}
switch index := index.(type) {
case *ConstInt:
return gepIndex {
HasVal: true,
Val: index.Value.Int64(),
}
case *ConstZeroInitializer:
return gepIndex {
HasVal: true,
Val: 0,
}
case *ConstVector:
// ref: https://llvm.org/docs/LangRef.html#getelementptr-instruction
//
// > The getelementptr returns a vector of pointers, instead of a single
// > address, when one or more of its arguments is a vector. In such
// > cases, all vector arguments should have the same number of elements,
// > and every scalar argument will be effectively broadcast into a vector
// > during address calculation.
if len(index.Elements) == 0 {
return gepIndex { HasVal: false }
}
// Sanity check. All vector elements must be integers, and must have the
// same value.
var val int64
for i, elem := range index.Elements {
switch elem := elem.(type) {
case *ConstInt:
x := elem.Value.Int64()
if i == 0 {
val = x
} else if x != val {
// since all elements were not identical, we must conclude that
// the index vector does not have a concrete value.
return gepIndex {
HasVal: false,
VectorLen: uint64(len(index.Elements)),
}
}
default:
// TODO: remove debug output.
panic(fmt.Errorf("support for gep index vector element type %T not yet implemented", elem))
//return gep.Index{HasVal: false}
}
}
return gepIndex {
HasVal: true,
Val: val,
VectorLen: uint64(len(index.Elements)),
}
case *ConstUndef:
return gepIndex { HasVal: false }
case *ConstPoison:
return gepIndex { HasVal: false }
case ConstExpr:
// should already have been simplified to a form we can handle.
return gepIndex { HasVal: false }
default:
// TODO: add support for more constant expressions.
// TODO: remove debug output.
panic(fmt.Errorf("support for gep index type %T not yet implemented", index))
//return gep.Index{HasVal: false}
}
}
func resultType (elemType, src Type, indices []gepIndex) Type {
// ref: http://llvm.org/docs/GetElementPtr.html#what-effect-do-address-spaces-have-on-geps
//
// > the address space qualifier on the second operand pointer type always
// > matches the address space qualifier on the result type.
var (
// Address space of src pointer type or src vector element pointer type.
addrSpace AddressSpace
// Length of vector of pointers result type; or 0 if pointer result type.
resultVectorLength uint64
)
// ref: https://llvm.org/docs/LangRef.html#getelementptr-instruction
//
// > The second argument is always a pointer or a vector of pointers.
switch src := src.(type) {
case *TypePointer:
addrSpace = src.AddressSpace
case *TypeVector:
vectorElemType, ok := src.Element.(*TypePointer)
if !ok {
panic(fmt.Errorf("invalid gep source vector element type %T", src.Element))
}
addrSpace = vectorElemType.AddressSpace
resultVectorLength = src.Length
default:
panic(fmt.Errorf("invalid gep source type %T", src))
}
// ref: https://llvm.org/docs/LangRef.html#getelementptr-instruction
//
// > The first argument is always a type used as the basis for the
// > calculations.
e := elemType
for i, index := range indices {
// ref: https://llvm.org/docs/LangRef.html#getelementptr-instruction
//
// > The getelementptr returns a vector of pointers, instead of a single
// > address, when one or more of its arguments is a vector. In such
// > cases, all vector arguments should have the same number of elements,
// > and every scalar argument will be effectively broadcast into a vector
// > during address calculation.
if index.VectorLen != 0 && resultVectorLength != 0 && index.VectorLen != resultVectorLength {
panic(fmt.Errorf("vector length mismatch of index vector (%d) and result type vector (%d)", index.VectorLen, resultVectorLength))
}
if resultVectorLength == 0 && index.VectorLen != 0 {
resultVectorLength = index.VectorLen
}
// ref: https://llvm.org/docs/GetElementPtr.html#why-is-the-extra-0-index-required
//
// > Since the second argument to the GEP instruction must always be a
// > value of pointer type, the first index steps through that pointer.
if i == 0 {
continue
}
switch elm := e.(type) {
case *TypePointer:
panic(fmt.Errorf("cannot index into pointer type at %d:th gep index, only valid at 0:th gep index; see https://llvm.org/docs/GetElementPtr.html#what-is-dereferenced-by-gep", i))
case *TypeArray:
e = elm.Element
case *TypeStruct:
// ref: https://llvm.org/docs/LangRef.html#getelementptr-instruction
//
// > When indexing into a (optionally packed) structure, only i32
// > integer constants are allowed (when using a vector of indices they
// > must all be the same i32 integer constant).
if !index.HasVal {
panic(fmt.Errorf("unable to index into struct type `%v` using gep with non-constant index", e))
}
e = elm.Fields[index.Val]
case *TypeDefined:
continue
default:
panic(fmt.Errorf("cannot index into type %T using gep", e))
}
}
ptr := &TypePointer { AddressSpace: addrSpace }
if resultVectorLength != 0 {
vec := &TypeVector {
Element: ptr,
Length: resultVectorLength,
}
return vec
}
return ptr
}