github.com/llir/llvm@v0.3.6/asm/inst_memory.go (about) 1 package asm 2 3 import ( 4 "fmt" 5 6 "github.com/llir/ll/ast" 7 asmenum "github.com/llir/llvm/asm/enum" 8 "github.com/llir/llvm/internal/gep" 9 "github.com/llir/llvm/ir" 10 "github.com/llir/llvm/ir/types" 11 "github.com/llir/llvm/ir/value" 12 "github.com/pkg/errors" 13 ) 14 15 // === [ Create IR ] =========================================================== 16 17 // newAllocaInst returns a new IR alloca instruction (without body but with 18 // type) based on the given AST alloca instruction. 19 func (fgen *funcGen) newAllocaInst(ident ir.LocalIdent, old *ast.AllocaInst) (*ir.InstAlloca, error) { 20 elemType, err := fgen.gen.irType(old.ElemType()) 21 if err != nil { 22 return nil, errors.WithStack(err) 23 } 24 inst := &ir.InstAlloca{LocalIdent: ident, ElemType: elemType} 25 // Cache inst.Typ. 26 inst.Type() 27 return inst, nil 28 } 29 30 // newLoadInst returns a new IR load instruction (without body but with type) 31 // based on the given AST load instruction. 32 func (fgen *funcGen) newLoadInst(ident ir.LocalIdent, old *ast.LoadInst) (*ir.InstLoad, error) { 33 elemType, err := fgen.gen.irType(old.ElemType()) 34 if err != nil { 35 return nil, errors.WithStack(err) 36 } 37 return &ir.InstLoad{LocalIdent: ident, ElemType: elemType}, nil 38 } 39 40 // newCmpXchgInst returns a new IR cmpxchg instruction (without body but with 41 // type) based on the given AST cmpxchg instruction. 42 func (fgen *funcGen) newCmpXchgInst(ident ir.LocalIdent, old *ast.CmpXchgInst) (*ir.InstCmpXchg, error) { 43 oldType, err := fgen.gen.irType(old.New().Typ()) 44 if err != nil { 45 return nil, errors.WithStack(err) 46 } 47 typ := types.NewStruct(oldType, types.I8) 48 return &ir.InstCmpXchg{LocalIdent: ident, Typ: typ}, nil 49 } 50 51 // newAtomicRMWInst returns a new IR atomicrmw instruction (without body but 52 // with type) based on the given AST atomicrmw instruction. 53 func (fgen *funcGen) newAtomicRMWInst(ident ir.LocalIdent, old *ast.AtomicRMWInst) (*ir.InstAtomicRMW, error) { 54 dstType, err := fgen.gen.irType(old.Dst().Typ()) 55 if err != nil { 56 return nil, errors.WithStack(err) 57 } 58 dt, ok := dstType.(*types.PointerType) 59 if !ok { 60 panic(fmt.Errorf("invalid pointer type; expected *types.PointerType, got %T", dstType)) 61 } 62 return &ir.InstAtomicRMW{LocalIdent: ident, Typ: dt.ElemType}, nil 63 } 64 65 // newGetElementPtrInst returns a new IR getelementptr instruction (without body 66 // but with type) based on the given AST getelementptr instruction. 67 func (fgen *funcGen) newGetElementPtrInst(ident ir.LocalIdent, old *ast.GetElementPtrInst) (*ir.InstGetElementPtr, error) { 68 // TODO: handle address space of Src? 69 elemType, err := fgen.gen.irType(old.ElemType()) 70 if err != nil { 71 return nil, errors.WithStack(err) 72 } 73 srcType, err := fgen.gen.irType(old.Src().Typ()) 74 if err != nil { 75 return nil, errors.WithStack(err) 76 } 77 typ, err := fgen.gen.gepInstType(elemType, srcType, old.Indices()) 78 if err != nil { 79 return nil, errors.WithStack(err) 80 } 81 return &ir.InstGetElementPtr{LocalIdent: ident, ElemType: elemType, Typ: typ}, nil 82 } 83 84 // === [ Translate AST to IR ] ================================================= 85 86 // --- [ alloca ] -------------------------------------------------------------- 87 88 // irAllocaInst translates the given AST alloca instruction into an equivalent 89 // IR instruction. 90 func (fgen *funcGen) irAllocaInst(new ir.Instruction, old *ast.AllocaInst) error { 91 inst, ok := new.(*ir.InstAlloca) 92 if !ok { 93 panic(fmt.Errorf("invalid IR instruction for AST instruction; expected *ir.InstAlloca, got %T", new)) 94 } 95 // Element type. 96 elemType, err := fgen.gen.irType(old.ElemType()) 97 if err != nil { 98 return errors.WithStack(err) 99 } 100 inst.ElemType = elemType 101 // (optional) Number of elements. 102 if n, ok := old.NElems(); ok { 103 nelems, err := fgen.irTypeValue(n) 104 if err != nil { 105 return errors.WithStack(err) 106 } 107 inst.NElems = nelems 108 } 109 // (optional) In-alloca. 110 _, inst.InAlloca = old.InAllocatok() 111 // (optional) Swift error. 112 _, inst.SwiftError = old.SwiftError() 113 // (optional) Alignment. 114 if n, ok := old.Align(); ok { 115 inst.Align = irAlign(n) 116 } 117 // (optional) Address space; stored in i.Typ. 118 if n, ok := old.AddrSpace(); ok { 119 inst.AddrSpace = irAddrSpace(n) 120 } 121 // (optional) Metadata. 122 md, err := fgen.gen.irMetadataAttachments(old.Metadata()) 123 if err != nil { 124 return errors.WithStack(err) 125 } 126 inst.Metadata = md 127 return nil 128 } 129 130 // --- [ load ] ---------------------------------------------------------------- 131 132 // irLoadInst translates the given AST load instruction into an equivalent IR 133 // instruction. 134 func (fgen *funcGen) irLoadInst(new ir.Instruction, old *ast.LoadInst) error { 135 inst, ok := new.(*ir.InstLoad) 136 if !ok { 137 panic(fmt.Errorf("invalid IR instruction for AST instruction; expected *ir.InstLoad, got %T", new)) 138 } 139 // Source address. 140 src, err := fgen.irTypeValue(old.Src()) 141 if err != nil { 142 return errors.WithStack(err) 143 } 144 inst.Src = src 145 // (optional) Atomic. 146 _, inst.Atomic = old.Atomic() 147 // (optional) Volatile. 148 _, inst.Volatile = old.Volatile() 149 // (optional) Sync scope. 150 if n, ok := old.SyncScope(); ok { 151 inst.SyncScope = stringLit(n.Scope()) 152 } 153 // (optional) Atomic memory ordering constraints. 154 if n, ok := old.Ordering(); ok { 155 inst.Ordering = asmenum.AtomicOrderingFromString(n.Text()) 156 } 157 // (optional) Alignment. 158 if n, ok := old.Align(); ok { 159 inst.Align = irAlign(n) 160 } 161 // (optional) Metadata. 162 md, err := fgen.gen.irMetadataAttachments(old.Metadata()) 163 if err != nil { 164 return errors.WithStack(err) 165 } 166 inst.Metadata = md 167 return nil 168 } 169 170 // --- [ store ] --------------------------------------------------------------- 171 172 // irStoreInst translates the given AST store instruction into an equivalent IR 173 // instruction. 174 func (fgen *funcGen) irStoreInst(new ir.Instruction, old *ast.StoreInst) error { 175 inst, ok := new.(*ir.InstStore) 176 if !ok { 177 panic(fmt.Errorf("invalid IR instruction for AST instruction; expected *ir.InstStore, got %T", new)) 178 } 179 // Source value. 180 src, err := fgen.irTypeValue(old.Src()) 181 if err != nil { 182 return errors.WithStack(err) 183 } 184 inst.Src = src 185 // Destination address. 186 dst, err := fgen.irTypeValue(old.Dst()) 187 if err != nil { 188 return errors.WithStack(err) 189 } 190 inst.Dst = dst 191 // (optional) Atomic. 192 _, inst.Atomic = old.Atomic() 193 // (optional) Volatile. 194 _, inst.Volatile = old.Volatile() 195 // (optional) Sync scope. 196 if n, ok := old.SyncScope(); ok { 197 inst.SyncScope = stringLit(n.Scope()) 198 } 199 // (optional) Atomic memory ordering constraints. 200 if n, ok := old.Ordering(); ok { 201 inst.Ordering = asmenum.AtomicOrderingFromString(n.Text()) 202 } 203 // (optional) Alignment. 204 if n, ok := old.Align(); ok { 205 inst.Align = irAlign(n) 206 } 207 // (optional) Metadata. 208 md, err := fgen.gen.irMetadataAttachments(old.Metadata()) 209 if err != nil { 210 return errors.WithStack(err) 211 } 212 inst.Metadata = md 213 return nil 214 } 215 216 // --- [ fence ] --------------------------------------------------------------- 217 218 // irFenceInst translates the given AST fence instruction into an equivalent IR 219 // instruction. 220 func (fgen *funcGen) irFenceInst(new ir.Instruction, old *ast.FenceInst) error { 221 inst, ok := new.(*ir.InstFence) 222 if !ok { 223 panic(fmt.Errorf("invalid IR instruction for AST instruction; expected *ir.InstFence, got %T", new)) 224 } 225 // Atomic memory ordering constraints. 226 inst.Ordering = asmenum.AtomicOrderingFromString(old.Ordering().Text()) 227 // (optional) Sync scope. 228 if n, ok := old.SyncScope(); ok { 229 inst.SyncScope = stringLit(n.Scope()) 230 } 231 // (optional) Metadata. 232 md, err := fgen.gen.irMetadataAttachments(old.Metadata()) 233 if err != nil { 234 return errors.WithStack(err) 235 } 236 inst.Metadata = md 237 return nil 238 } 239 240 // --- [ cmpxchg ] ------------------------------------------------------------- 241 242 // irCmpXchgInst translates the given AST cmpxchg instruction into an equivalent 243 // IR instruction. 244 func (fgen *funcGen) irCmpXchgInst(new ir.Instruction, old *ast.CmpXchgInst) error { 245 inst, ok := new.(*ir.InstCmpXchg) 246 if !ok { 247 panic(fmt.Errorf("invalid IR instruction for AST instruction; expected *ir.InstCmpXchg, got %T", new)) 248 } 249 // Address to read from, compare against and store to. 250 ptr, err := fgen.irTypeValue(old.Ptr()) 251 if err != nil { 252 return errors.WithStack(err) 253 } 254 inst.Ptr = ptr 255 // Value to compare against. 256 cmp, err := fgen.irTypeValue(old.Cmp()) 257 if err != nil { 258 return errors.WithStack(err) 259 } 260 inst.Cmp = cmp 261 // New value to store. 262 newValue, err := fgen.irTypeValue(old.New()) 263 if err != nil { 264 return errors.WithStack(err) 265 } 266 inst.New = newValue 267 // Atomic memory ordering constraints on success. 268 inst.SuccessOrdering = asmenum.AtomicOrderingFromString(old.SuccessOrdering().Text()) 269 // Atomic memory ordering constraints on failure. 270 inst.FailureOrdering = asmenum.AtomicOrderingFromString(old.FailureOrdering().Text()) 271 // (optional) Weak. 272 _, inst.Weak = old.Weak() 273 // (optional) Volatile. 274 _, inst.Volatile = old.Volatile() 275 // (optional) Sync scope. 276 if n, ok := old.SyncScope(); ok { 277 inst.SyncScope = stringLit(n.Scope()) 278 } 279 // (optional) Metadata. 280 md, err := fgen.gen.irMetadataAttachments(old.Metadata()) 281 if err != nil { 282 return errors.WithStack(err) 283 } 284 inst.Metadata = md 285 return nil 286 } 287 288 // --- [ atomicrmw ] ----------------------------------------------------------- 289 290 // irAtomicRMWInst translates the given AST atomicrmw instruction into an 291 // equivalent IR instruction. 292 func (fgen *funcGen) irAtomicRMWInst(new ir.Instruction, old *ast.AtomicRMWInst) error { 293 inst, ok := new.(*ir.InstAtomicRMW) 294 if !ok { 295 panic(fmt.Errorf("invalid IR instruction for AST instruction; expected *ir.InstAtomicRMW, got %T", new)) 296 } 297 // Atomic operation. 298 inst.Op = asmenum.AtomicOpFromString(old.Op().Text()) 299 // Destination address. 300 dst, err := fgen.irTypeValue(old.Dst()) 301 if err != nil { 302 return errors.WithStack(err) 303 } 304 inst.Dst = dst 305 // Operand. 306 x, err := fgen.irTypeValue(old.X()) 307 if err != nil { 308 return errors.WithStack(err) 309 } 310 inst.X = x 311 // Atomic memory ordering constraints. 312 inst.Ordering = asmenum.AtomicOrderingFromString(old.Ordering().Text()) 313 // (optional) Volatile. 314 _, inst.Volatile = old.Volatile() 315 // (optional) Sync scope. 316 if n, ok := old.SyncScope(); ok { 317 inst.SyncScope = stringLit(n.Scope()) 318 } 319 // (optional) Metadata. 320 md, err := fgen.gen.irMetadataAttachments(old.Metadata()) 321 if err != nil { 322 return errors.WithStack(err) 323 } 324 inst.Metadata = md 325 return nil 326 } 327 328 // --- [ getelementptr ] ------------------------------------------------------- 329 330 // irGetElementPtrInst translates the given AST getelementptr instruction into 331 // an equivalent IR instruction. 332 func (fgen *funcGen) irGetElementPtrInst(new ir.Instruction, old *ast.GetElementPtrInst) error { 333 inst, ok := new.(*ir.InstGetElementPtr) 334 if !ok { 335 panic(fmt.Errorf("invalid IR instruction for AST instruction; expected *ir.InstGetElementPtr, got %T", new)) 336 } 337 // Element type; already handled in fgen.newValueInst. 338 // Source address. 339 src, err := fgen.irTypeValue(old.Src()) 340 if err != nil { 341 return errors.WithStack(err) 342 } 343 inst.Src = src 344 // Element indicies. 345 if oldIndices := old.Indices(); len(oldIndices) > 0 { 346 inst.Indices = make([]value.Value, len(oldIndices)) 347 for i, oldIndex := range oldIndices { 348 index, err := fgen.irTypeValue(oldIndex) 349 if err != nil { 350 return errors.WithStack(err) 351 } 352 inst.Indices[i] = index 353 } 354 } 355 // (optional) In-bounds. 356 _, inst.InBounds = old.InBounds() 357 // (optional) Metadata. 358 md, err := fgen.gen.irMetadataAttachments(old.Metadata()) 359 if err != nil { 360 return errors.WithStack(err) 361 } 362 inst.Metadata = md 363 return nil 364 } 365 366 // ### [ Helper functions ] #################################################### 367 368 // gepInstType computes the result type of a getelementptr instruction. 369 // 370 // getelementptr ElemType, Src, Indices 371 func (gen *generator) gepInstType(elemType, src types.Type, indices []ast.TypeValue) (types.Type, error) { 372 var idxs []gep.Index 373 for _, index := range indices { 374 var idx gep.Index 375 if indexVal, ok := index.Val().(ast.Constant); ok { 376 idx = gen.getIndex(indexVal) 377 } else { 378 idx = gep.Index{HasVal: false} 379 // Check if index is of vector type. 380 indexType, err := gen.irType(index.Typ()) 381 if err != nil { 382 return nil, errors.WithStack(err) 383 } 384 if indexType, ok := indexType.(*types.VectorType); ok { 385 idx.VectorLen = indexType.Len 386 } 387 } 388 idxs = append(idxs, idx) 389 } 390 return gep.ResultType(elemType, src, idxs), nil 391 } 392 393 // NOTE: keep getIndex in sync with getIndex in: 394 // 395 // * ast/inst_memory.go 396 // * ir/inst_memory.go 397 // * ir/constant/expr_memory.go 398 // 399 // The reference point and source of truth is in ir/constant/expr_memory.go. 400 401 // getIndex returns the gep index corresponding to the given constant index. 402 func (gen *generator) getIndex(index ast.Constant) gep.Index { 403 switch index := index.(type) { 404 case *ast.IntConst: 405 // use types.I64 as dummy type for gep indices, the type doesn't matter. 406 idx, err := gen.irIntConst(types.I64, index) 407 if err != nil { 408 panic(fmt.Errorf("unable to parse integer %q; %v", index.Text(), err)) 409 } 410 val := idx.X.Int64() 411 return gep.NewIndex(val) 412 case *ast.BoolConst: 413 if boolLit(index.BoolLit()) { 414 return gep.NewIndex(1) 415 } 416 return gep.NewIndex(0) 417 case *ast.ZeroInitializerConst: 418 return gep.NewIndex(0) 419 case *ast.VectorConst: 420 // ref: https://llvm.org/docs/LangRef.html#getelementptr-instruction 421 // 422 // > The getelementptr returns a vector of pointers, instead of a single 423 // > address, when one or more of its arguments is a vector. In such 424 // > cases, all vector arguments should have the same number of elements, 425 // > and every scalar argument will be effectively broadcast into a vector 426 // > during address calculation. 427 elems := index.Elems() 428 if len(elems) == 0 { 429 return gep.Index{HasVal: false} 430 } 431 // Sanity check. All vector elements must be integers, and must have the 432 // same value. 433 var val int64 434 for i, elem := range elems { 435 switch elem := elem.Val().(type) { 436 case *ast.IntConst: 437 // use types.I64 as dummy type for gep indices, the type doesn't matter. 438 idx, err := gen.irIntConst(types.I64, elem) 439 if err != nil { 440 panic(fmt.Errorf("unable to parse integer %q; %v", elem.Text(), err)) 441 } 442 x := idx.X.Int64() 443 if i == 0 { 444 val = x 445 } else if x != val { 446 // since all elements were not identical, we must conclude that 447 // the index vector does not have a concrete value. 448 return gep.Index{ 449 HasVal: false, 450 VectorLen: uint64(len(elems)), 451 } 452 } 453 default: 454 // TODO: remove debug output. 455 panic(fmt.Errorf("support for gep index vector element type %T not yet implemented", elem)) 456 //return gep.Index{HasVal: false} 457 } 458 } 459 return gep.Index{ 460 HasVal: true, 461 Val: val, 462 VectorLen: uint64(len(elems)), 463 } 464 case *ast.PtrToIntExpr: 465 return gep.Index{HasVal: false} 466 case *ast.UndefConst: 467 return gep.Index{HasVal: false} 468 case *ast.PoisonConst: 469 return gep.Index{HasVal: false} 470 default: 471 // TODO: add support for more constant expressions. 472 // TODO: remove debug output. 473 panic(fmt.Errorf("support for gep index type %T not yet implemented", index)) 474 //return gep.Index{HasVal: false} 475 } 476 }