github.com/llir/llvm@v0.3.6/ir/constant/expr_memory.go (about) 1 package constant 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/llir/llvm/internal/gep" 8 "github.com/llir/llvm/ir/types" 9 ) 10 11 // --- [ Memory expressions ] -------------------------------------------------- 12 13 // ~~~ [ getelementptr ] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 14 15 // ExprGetElementPtr is an LLVM IR getelementptr expression. 16 type ExprGetElementPtr struct { 17 // Element type. 18 ElemType types.Type 19 // Source address. 20 Src Constant 21 // Element indicies. 22 Indices []Constant // *Int, *Vector or *Index 23 24 // extra. 25 26 // Type of result produced by the constant expression. 27 Typ types.Type // *types.PointerType or *types.VectorType (with elements of pointer type) 28 // (optional) The result is a poison value if the calculated pointer is not 29 // an in bounds address of the allocated source object. 30 InBounds bool 31 } 32 33 // NewGetElementPtr returns a new getelementptr expression based on the given 34 // element type, source address and element indices. 35 func NewGetElementPtr(elemType types.Type, src Constant, indices ...Constant) *ExprGetElementPtr { 36 e := &ExprGetElementPtr{ElemType: elemType, Src: src, Indices: indices} 37 // Compute type. 38 e.Type() 39 return e 40 } 41 42 // String returns the LLVM syntax representation of the constant expression as a 43 // type-value pair. 44 func (e *ExprGetElementPtr) String() string { 45 return fmt.Sprintf("%s %s", e.Type(), e.Ident()) 46 } 47 48 // Type returns the type of the constant expression. 49 func (e *ExprGetElementPtr) Type() types.Type { 50 // Cache type if not present. 51 if e.Typ == nil { 52 e.Typ = gepExprType(e.ElemType, e.Src.Type(), e.Indices) 53 } 54 return e.Typ 55 } 56 57 // Ident returns the identifier associated with the constant expression. 58 func (e *ExprGetElementPtr) Ident() string { 59 // 'getelementptr' InBoundsopt '(' ElemType=Type ',' Src=TypeConst 60 // Indices=(',' GEPIndex)* ')' 61 buf := &strings.Builder{} 62 buf.WriteString("getelementptr") 63 if e.InBounds { 64 buf.WriteString(" inbounds") 65 } 66 fmt.Fprintf(buf, " (%s, %s", e.ElemType, e.Src) 67 for _, index := range e.Indices { 68 fmt.Fprintf(buf, ", %s", index) 69 } 70 buf.WriteString(")") 71 return buf.String() 72 } 73 74 // ___ [ gep indices ] _________________________________________________________ 75 76 // Index is an index of a getelementptr constant expression. 77 type Index struct { 78 // Element index. 79 Constant 80 81 // extra. 82 83 // (optional) States that the element index is not out the bounds of the 84 // allocated object. If inrange is stated but the element index is out of 85 // bounds, the behaviour is undefined. 86 InRange bool 87 } 88 89 // NewIndex returns a new gep element index. 90 func NewIndex(index Constant) *Index { 91 return &Index{Constant: index} 92 } 93 94 // String returns a string representation of the getelementptr index. 95 func (index *Index) String() string { 96 // OptInrange Type Constant 97 if index.InRange { 98 return fmt.Sprintf("inrange %s", index.Constant) 99 } 100 return index.Constant.String() 101 } 102 103 // ### [ Helper functions ] #################################################### 104 105 // gepExprType computes the result type of a getelementptr constant expression. 106 // 107 // getelementptr (ElemType, Src, Indices) 108 func gepExprType(elemType, src types.Type, indices []Constant) types.Type { 109 var idxs []gep.Index 110 for _, index := range indices { 111 idx := getIndex(index) 112 // Check if index is of vector type. 113 if indexType, ok := index.Type().(*types.VectorType); ok { 114 idx.VectorLen = indexType.Len 115 } 116 idxs = append(idxs, idx) 117 } 118 return gep.ResultType(elemType, src, idxs) 119 } 120 121 // NOTE: keep getIndex in sync with getIndex in: 122 // 123 // * ast/inst_memory.go 124 // * ir/inst_memory.go 125 // * ir/constant/expr_memory.go 126 // 127 // The reference point and source of truth is in ir/constant/expr_memory.go. 128 129 // getIndex returns the gep index corresponding to the given constant index. 130 func getIndex(index Constant) gep.Index { 131 // unpack inrange indices. 132 if idx, ok := index.(*Index); ok { 133 index = idx.Constant 134 } 135 // TODO: figure out how to simplify expressions for GEP instructions without 136 // creating import cycle on irutil. 137 138 // Use index.Simplify() to simplify the constant expression to a concrete 139 // integer constant or vector of integers constant. 140 //if idx, ok := index.(Expression); ok { 141 // index = idx.Simplify() 142 //} 143 switch index := index.(type) { 144 case *Int: 145 val := index.X.Int64() 146 return gep.NewIndex(val) 147 case *ZeroInitializer: 148 return gep.NewIndex(0) 149 case *Vector: 150 // ref: https://llvm.org/docs/LangRef.html#getelementptr-instruction 151 // 152 // > The getelementptr returns a vector of pointers, instead of a single 153 // > address, when one or more of its arguments is a vector. In such 154 // > cases, all vector arguments should have the same number of elements, 155 // > and every scalar argument will be effectively broadcast into a vector 156 // > during address calculation. 157 if len(index.Elems) == 0 { 158 return gep.Index{HasVal: false} 159 } 160 // Sanity check. All vector elements must be integers, and must have the 161 // same value. 162 var val int64 163 for i, elem := range index.Elems { 164 switch elem := elem.(type) { 165 case *Int: 166 x := elem.X.Int64() 167 if i == 0 { 168 val = x 169 } else if x != val { 170 // since all elements were not identical, we must conclude that 171 // the index vector does not have a concrete value. 172 return gep.Index{ 173 HasVal: false, 174 VectorLen: uint64(len(index.Elems)), 175 } 176 } 177 default: 178 // TODO: remove debug output. 179 panic(fmt.Errorf("support for gep index vector element type %T not yet implemented", elem)) 180 //return gep.Index{HasVal: false} 181 } 182 } 183 return gep.Index{ 184 HasVal: true, 185 Val: val, 186 VectorLen: uint64(len(index.Elems)), 187 } 188 case *Undef: 189 return gep.Index{HasVal: false} 190 case *Poison: 191 return gep.Index{HasVal: false} 192 case Expression: 193 // should already have been simplified to a form we can handle. 194 return gep.Index{HasVal: false} 195 default: 196 // TODO: add support for more constant expressions. 197 // TODO: remove debug output. 198 panic(fmt.Errorf("support for gep index type %T not yet implemented", index)) 199 //return gep.Index{HasVal: false} 200 } 201 } 202 203 // Example from dir.ll: 204 // %113 = getelementptr inbounds %struct.fileinfo, %struct.fileinfo* %96, <2 x i64> %110, !dbg !4736 205 // %116 = bitcast i8** %115 to <2 x %struct.fileinfo*>*, !dbg !4738 206 // store <2 x %struct.fileinfo*> %113, <2 x %struct.fileinfo*>* %116, align 8, !dbg !4738, !tbaa !1793