2023-11-23 01:37:16 +00:00
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 ]
2024-01-25 22:57:52 +00:00
case * TypeDefined :
continue
2023-11-23 01:37:16 +00:00
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
}