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 }