github.com/llir/llvm@v0.3.6/ir/inst_memory.go (about) 1 package ir 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/llir/llvm/internal/gep" 8 "github.com/llir/llvm/ir/constant" 9 "github.com/llir/llvm/ir/enum" 10 "github.com/llir/llvm/ir/types" 11 "github.com/llir/llvm/ir/value" 12 ) 13 14 // --- [ Memory instructions ] ------------------------------------------------- 15 16 // ~~~ [ alloca ] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 17 18 // InstAlloca is an LLVM IR alloca instruction. 19 type InstAlloca struct { 20 // Name of local variable associated with the result. 21 LocalIdent 22 // Element type. 23 ElemType types.Type 24 // (optional) Number of elements; nil if not present. 25 NElems value.Value 26 27 // extra. 28 29 // Type of result produced by the instruction, including an optional address 30 // space. 31 Typ *types.PointerType 32 // (optional) In-alloca. 33 InAlloca bool 34 // (optional) Swift error. 35 SwiftError bool 36 // (optional) Alignment; zero if not present. 37 Align Align 38 // (optional) Address space; zero if not present. 39 AddrSpace types.AddrSpace 40 // (optional) Metadata. 41 Metadata 42 } 43 44 // NewAlloca returns a new alloca instruction based on the given element type. 45 func NewAlloca(elemType types.Type) *InstAlloca { 46 inst := &InstAlloca{ElemType: elemType} 47 // Compute type. 48 inst.Type() 49 return inst 50 } 51 52 // String returns the LLVM syntax representation of the instruction as a 53 // type-value pair. 54 func (inst *InstAlloca) String() string { 55 return fmt.Sprintf("%s %s", inst.Type(), inst.Ident()) 56 } 57 58 // Type returns the type of the instruction. 59 func (inst *InstAlloca) Type() types.Type { 60 // Cache type if not present. 61 if inst.Typ == nil { 62 inst.Typ = types.NewPointer(inst.ElemType) 63 inst.Typ.AddrSpace = inst.AddrSpace 64 } 65 return inst.Typ 66 } 67 68 // LLString returns the LLVM syntax representation of the instruction. 69 // 70 // 'alloca' InAllocaopt SwiftErroropt ElemType=Type NElems=(',' TypeValue)? (',' Align)? (',' AddrSpace)? Metadata=(',' MetadataAttachment)+? 71 func (inst *InstAlloca) LLString() string { 72 buf := &strings.Builder{} 73 fmt.Fprintf(buf, "%s = ", inst.Ident()) 74 buf.WriteString("alloca") 75 if inst.InAlloca { 76 buf.WriteString(" inalloca") 77 } 78 if inst.SwiftError { 79 buf.WriteString(" swifterror") 80 } 81 fmt.Fprintf(buf, " %s", inst.ElemType) 82 if inst.NElems != nil { 83 fmt.Fprintf(buf, ", %s", inst.NElems) 84 } 85 if inst.Align != 0 { 86 fmt.Fprintf(buf, ", %s", inst.Align) 87 } 88 if inst.AddrSpace != 0 { 89 fmt.Fprintf(buf, ", %s", inst.AddrSpace) 90 } 91 for _, md := range inst.Metadata { 92 fmt.Fprintf(buf, ", %s", md) 93 } 94 return buf.String() 95 } 96 97 // Operands returns a mutable list of operands of the given instruction. 98 func (inst *InstAlloca) Operands() []*value.Value { 99 if inst.NElems != nil { 100 return []*value.Value{&inst.NElems} 101 } 102 return nil 103 } 104 105 // ~~~ [ load ] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 106 107 // InstLoad is an LLVM IR load instruction. 108 type InstLoad struct { 109 // Name of local variable associated with the result. 110 LocalIdent 111 // Element type of src. 112 ElemType types.Type 113 // Source address. 114 Src value.Value 115 116 // extra. 117 118 // (optional) Atomic. 119 Atomic bool 120 // (optional) Volatile. 121 Volatile bool 122 // (optional) Sync scope; empty if not present. 123 SyncScope string 124 // (optional) Atomic memory ordering constraints; zero if not present. 125 Ordering enum.AtomicOrdering 126 // (optional) Alignment; zero if not present. 127 Align Align 128 // (optional) Metadata. 129 Metadata 130 } 131 132 // NewLoad returns a new load instruction based on the given element type and 133 // source address. 134 func NewLoad(elemType types.Type, src value.Value) *InstLoad { 135 inst := &InstLoad{ElemType: elemType, Src: src} 136 return inst 137 } 138 139 // String returns the LLVM syntax representation of the instruction as a 140 // type-value pair. 141 func (inst *InstLoad) String() string { 142 return fmt.Sprintf("%s %s", inst.Type(), inst.Ident()) 143 } 144 145 // Type returns the type of the instruction. 146 func (inst *InstLoad) Type() types.Type { 147 return inst.ElemType 148 } 149 150 // LLString returns the LLVM syntax representation of the instruction. 151 // 152 // Load instruction. 153 // 154 // 'load' Volatileopt ElemType=Type ',' Src=TypeValue (',' Align)? Metadata=(',' MetadataAttachment)+? 155 // 156 // Atomic load instruction. 157 // 158 // 'load' Atomic Volatileopt ElemType=Type ',' Src=TypeValue SyncScopeopt Ordering=AtomicOrdering (',' Align)? Metadata=(',' MetadataAttachment)+? 159 func (inst *InstLoad) LLString() string { 160 buf := &strings.Builder{} 161 fmt.Fprintf(buf, "%s = ", inst.Ident()) 162 buf.WriteString("load") 163 if inst.Atomic { 164 buf.WriteString(" atomic") 165 } 166 if inst.Volatile { 167 buf.WriteString(" volatile") 168 } 169 fmt.Fprintf(buf, " %s, %s", inst.ElemType, inst.Src) 170 if len(inst.SyncScope) > 0 { 171 fmt.Fprintf(buf, " syncscope(%s)", quote(inst.SyncScope)) 172 } 173 if inst.Ordering != enum.AtomicOrderingNone { 174 fmt.Fprintf(buf, " %s", inst.Ordering) 175 } 176 if inst.Align != 0 { 177 fmt.Fprintf(buf, ", %s", inst.Align) 178 } 179 for _, md := range inst.Metadata { 180 fmt.Fprintf(buf, ", %s", md) 181 } 182 return buf.String() 183 } 184 185 // Operands returns a mutable list of operands of the given instruction. 186 func (inst *InstLoad) Operands() []*value.Value { 187 return []*value.Value{&inst.Src} 188 } 189 190 // ~~~ [ store ] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 191 192 // InstStore is an LLVM IR store instruction. 193 type InstStore struct { 194 // Source value. 195 Src value.Value 196 // Destination address. 197 Dst value.Value 198 199 // extra. 200 201 // (optional) Atomic. 202 Atomic bool 203 // (optional) Volatile. 204 Volatile bool 205 // (optional) Sync scope; empty if not present. 206 SyncScope string 207 // (optional) Atomic memory ordering constraints; zero if not present. 208 Ordering enum.AtomicOrdering 209 // (optional) Alignment; zero if not present. 210 Align Align 211 // (optional) Metadata. 212 Metadata 213 } 214 215 // NewStore returns a new store instruction based on the given source value and 216 // destination address. 217 func NewStore(src, dst value.Value) *InstStore { 218 // Type-check operands. 219 dstPtrType, ok := dst.Type().(*types.PointerType) 220 if !ok { 221 panic(fmt.Errorf("invalid store dst operand type; expected *types.Pointer, got %T", dst.Type())) 222 } 223 if !src.Type().Equal(dstPtrType.ElemType) { 224 panic(fmt.Errorf("store operands are not compatible: src=%v; dst=%v", src.Type(), dst.Type())) 225 } 226 return &InstStore{Src: src, Dst: dst} 227 } 228 229 // LLString returns the LLVM syntax representation of the instruction. 230 // 231 // Store instruction. 232 // 233 // 'store' Volatileopt Src=TypeValue ',' Dst=TypeValue (',' Align)? Metadata=(',' MetadataAttachment)+? 234 // 235 // Atomic store instruction. 236 // 237 // 'store' Atomic Volatileopt Src=TypeValue ',' Dst=TypeValue SyncScopeopt Ordering=AtomicOrdering (',' Align)? Metadata=(',' MetadataAttachment)+? 238 func (inst *InstStore) LLString() string { 239 buf := &strings.Builder{} 240 buf.WriteString("store") 241 if inst.Atomic { 242 buf.WriteString(" atomic") 243 } 244 if inst.Volatile { 245 buf.WriteString(" volatile") 246 } 247 fmt.Fprintf(buf, " %s, %s", inst.Src, inst.Dst) 248 if len(inst.SyncScope) > 0 { 249 fmt.Fprintf(buf, " syncscope(%s)", quote(inst.SyncScope)) 250 } 251 if inst.Ordering != enum.AtomicOrderingNone { 252 fmt.Fprintf(buf, " %s", inst.Ordering) 253 } 254 if inst.Align != 0 { 255 fmt.Fprintf(buf, ", %s", inst.Align) 256 } 257 for _, md := range inst.Metadata { 258 fmt.Fprintf(buf, ", %s", md) 259 } 260 return buf.String() 261 } 262 263 // Operands returns a mutable list of operands of the given instruction. 264 func (inst *InstStore) Operands() []*value.Value { 265 return []*value.Value{&inst.Src, &inst.Dst} 266 } 267 268 // ~~~ [ fence ] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 269 270 // InstFence is an LLVM IR fence instruction. 271 type InstFence struct { 272 // Atomic memory ordering constraints. 273 Ordering enum.AtomicOrdering 274 275 // extra. 276 277 // (optional) Sync scope; empty if not present. 278 SyncScope string 279 // (optional) Metadata. 280 Metadata 281 } 282 283 // NewFence returns a new fence instruction based on the given atomic ordering. 284 func NewFence(ordering enum.AtomicOrdering) *InstFence { 285 return &InstFence{Ordering: ordering} 286 } 287 288 // LLString returns the LLVM syntax representation of the instruction. 289 // 290 // 'fence' SyncScopeopt Ordering=AtomicOrdering Metadata=(',' MetadataAttachment)+? 291 func (inst *InstFence) LLString() string { 292 buf := &strings.Builder{} 293 buf.WriteString("fence") 294 if len(inst.SyncScope) > 0 { 295 fmt.Fprintf(buf, " syncscope(%s)", quote(inst.SyncScope)) 296 } 297 fmt.Fprintf(buf, " %s", inst.Ordering) 298 for _, md := range inst.Metadata { 299 fmt.Fprintf(buf, ", %s", md) 300 } 301 return buf.String() 302 } 303 304 // Operands returns a mutable list of operands of the given instruction. 305 func (inst *InstFence) Operands() []*value.Value { 306 return nil 307 } 308 309 // ~~~ [ cmpxchg ] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 310 311 // InstCmpXchg is an LLVM IR cmpxchg instruction. 312 type InstCmpXchg struct { 313 // Name of local variable associated with the result. 314 LocalIdent 315 // Address to read from, compare against and store to. 316 Ptr value.Value 317 // Value to compare against. 318 Cmp value.Value 319 // New value to store. 320 New value.Value 321 // Atomic memory ordering constraints on success. 322 SuccessOrdering enum.AtomicOrdering 323 // Atomic memory ordering constraints on failure. 324 FailureOrdering enum.AtomicOrdering 325 326 // extra. 327 328 // Type of result produced by the instruction; the first field of the struct 329 // holds the old value, and the second field indicates success. 330 Typ *types.StructType 331 // (optional) Weak. 332 Weak bool 333 // (optional) Volatile. 334 Volatile bool 335 // (optional) Sync scope; empty if not present. 336 SyncScope string 337 // (optional) Metadata. 338 Metadata 339 } 340 341 // NewCmpXchg returns a new cmpxchg instruction based on the given address, 342 // value to compare against, new value to store, and atomic orderings for 343 // success and failure. 344 func NewCmpXchg(ptr, cmp, new value.Value, successOrdering, failureOrdering enum.AtomicOrdering) *InstCmpXchg { 345 inst := &InstCmpXchg{Ptr: ptr, Cmp: cmp, New: new, SuccessOrdering: successOrdering, FailureOrdering: failureOrdering} 346 // Compute type. 347 inst.Type() 348 return inst 349 } 350 351 // String returns the LLVM syntax representation of the instruction as a 352 // type-value pair. 353 func (inst *InstCmpXchg) String() string { 354 return fmt.Sprintf("%s %s", inst.Type(), inst.Ident()) 355 } 356 357 // Type returns the type of the instruction. The result type is a struct type 358 // with two fields, the first field has the type of the old value and the second 359 // field has boolean type. 360 func (inst *InstCmpXchg) Type() types.Type { 361 // Cache type if not present. 362 if inst.Typ == nil { 363 oldType := inst.New.Type() 364 inst.Typ = types.NewStruct(oldType, types.I1) 365 } 366 return inst.Typ 367 } 368 369 // LLString returns the LLVM syntax representation of the instruction. 370 // 371 // 'cmpxchg' Weakopt Volatileopt Ptr=TypeValue ',' Cmp=TypeValue ',' New=TypeValue SyncScopeopt SuccessOrdering=AtomicOrdering FailureOrdering=AtomicOrdering Metadata=(',' MetadataAttachment)+? 372 func (inst *InstCmpXchg) LLString() string { 373 buf := &strings.Builder{} 374 fmt.Fprintf(buf, "%s = ", inst.Ident()) 375 buf.WriteString("cmpxchg") 376 if inst.Weak { 377 buf.WriteString(" weak") 378 } 379 if inst.Volatile { 380 buf.WriteString(" volatile") 381 } 382 fmt.Fprintf(buf, " %s, %s, %s", inst.Ptr, inst.Cmp, inst.New) 383 if len(inst.SyncScope) > 0 { 384 fmt.Fprintf(buf, " syncscope(%s)", quote(inst.SyncScope)) 385 } 386 fmt.Fprintf(buf, " %s", inst.SuccessOrdering) 387 fmt.Fprintf(buf, " %s", inst.FailureOrdering) 388 for _, md := range inst.Metadata { 389 fmt.Fprintf(buf, ", %s", md) 390 } 391 return buf.String() 392 } 393 394 // Operands returns a mutable list of operands of the given instruction. 395 func (inst *InstCmpXchg) Operands() []*value.Value { 396 return []*value.Value{&inst.Ptr, &inst.Cmp, &inst.New} 397 } 398 399 // ~~~ [ atomicrmw ] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 400 401 // InstAtomicRMW is an LLVM IR atomicrmw instruction. 402 type InstAtomicRMW struct { 403 // Name of local variable associated with the result. 404 LocalIdent 405 // Atomic operation. 406 Op enum.AtomicOp 407 // Destination address. 408 Dst value.Value 409 // Operand. 410 X value.Value 411 // Atomic memory ordering constraints. 412 Ordering enum.AtomicOrdering 413 414 // extra. 415 416 // Type of result produced by the instruction. 417 Typ types.Type 418 // (optional) Volatile. 419 Volatile bool 420 // (optional) Sync scope; empty if not present. 421 SyncScope string 422 // (optional) Metadata. 423 Metadata 424 } 425 426 // NewAtomicRMW returns a new atomicrmw instruction based on the given atomic 427 // operation, destination address, operand and atomic ordering. 428 func NewAtomicRMW(op enum.AtomicOp, dst, x value.Value, ordering enum.AtomicOrdering) *InstAtomicRMW { 429 inst := &InstAtomicRMW{Op: op, Dst: dst, X: x, Ordering: ordering} 430 // Compute type. 431 inst.Type() 432 return inst 433 } 434 435 // String returns the LLVM syntax representation of the instruction as a 436 // type-value pair. 437 func (inst *InstAtomicRMW) String() string { 438 return fmt.Sprintf("%s %s", inst.Type(), inst.Ident()) 439 } 440 441 // Type returns the type of the instruction. 442 func (inst *InstAtomicRMW) Type() types.Type { 443 // Cache type if not present. 444 if inst.Typ == nil { 445 t, ok := inst.Dst.Type().(*types.PointerType) 446 if !ok { 447 panic(fmt.Errorf("invalid destination type; expected *types.PointerType, got %T", inst.Dst.Type())) 448 } 449 inst.Typ = t.ElemType 450 } 451 return inst.Typ 452 } 453 454 // LLString returns the LLVM syntax representation of the instruction. 455 // 456 // 'atomicrmw' Volatileopt Op=AtomicOp Dst=TypeValue ',' X=TypeValue SyncScopeopt Ordering=AtomicOrdering Metadata=(',' MetadataAttachment)+? 457 func (inst *InstAtomicRMW) LLString() string { 458 buf := &strings.Builder{} 459 fmt.Fprintf(buf, "%s = ", inst.Ident()) 460 buf.WriteString("atomicrmw") 461 if inst.Volatile { 462 buf.WriteString(" volatile") 463 } 464 fmt.Fprintf(buf, " %s %s, %s", inst.Op, inst.Dst, inst.X) 465 if len(inst.SyncScope) > 0 { 466 fmt.Fprintf(buf, " syncscope(%s)", quote(inst.SyncScope)) 467 } 468 fmt.Fprintf(buf, " %s", inst.Ordering) 469 for _, md := range inst.Metadata { 470 fmt.Fprintf(buf, ", %s", md) 471 } 472 return buf.String() 473 } 474 475 // Operands returns a mutable list of operands of the given instruction. 476 func (inst *InstAtomicRMW) Operands() []*value.Value { 477 return []*value.Value{&inst.Dst, &inst.X} 478 } 479 480 // ~~~ [ getelementptr ] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 481 482 // InstGetElementPtr is an LLVM IR getelementptr instruction. 483 type InstGetElementPtr struct { 484 // Name of local variable associated with the result. 485 LocalIdent 486 // Element type. 487 ElemType types.Type 488 // Source address. 489 Src value.Value 490 // Element indicies. 491 Indices []value.Value 492 493 // extra. 494 495 // Type of result produced by the instruction. 496 Typ types.Type // *types.PointerType or *types.VectorType (with elements of pointer type) 497 // (optional) In-bounds. 498 InBounds bool 499 // (optional) Metadata. 500 Metadata 501 } 502 503 // NewGetElementPtr returns a new getelementptr instruction based on the given 504 // element type, source address and element indices. 505 func NewGetElementPtr(elemType types.Type, src value.Value, indices ...value.Value) *InstGetElementPtr { 506 inst := &InstGetElementPtr{ElemType: elemType, Src: src, Indices: indices} 507 // Compute type. 508 inst.Type() 509 return inst 510 } 511 512 // String returns the LLVM syntax representation of the instruction as a 513 // type-value pair. 514 func (inst *InstGetElementPtr) String() string { 515 return fmt.Sprintf("%s %s", inst.Type(), inst.Ident()) 516 } 517 518 // Type returns the type of the instruction. 519 func (inst *InstGetElementPtr) Type() types.Type { 520 // Cache type if not present. 521 if inst.Typ == nil { 522 inst.Typ = gepInstType(inst.ElemType, inst.Src.Type(), inst.Indices) 523 } 524 return inst.Typ 525 } 526 527 // LLString returns the LLVM syntax representation of the instruction. 528 // 529 // 'getelementptr' InBoundsopt ElemType=Type ',' Src=TypeValue Indices=(',' TypeValue)* Metadata=(',' MetadataAttachment)+? 530 func (inst *InstGetElementPtr) LLString() string { 531 buf := &strings.Builder{} 532 fmt.Fprintf(buf, "%s = ", inst.Ident()) 533 buf.WriteString("getelementptr") 534 if inst.InBounds { 535 buf.WriteString(" inbounds") 536 } 537 fmt.Fprintf(buf, " %s, %s", inst.ElemType, inst.Src) 538 for _, index := range inst.Indices { 539 fmt.Fprintf(buf, ", %s", index) 540 } 541 for _, md := range inst.Metadata { 542 fmt.Fprintf(buf, ", %s", md) 543 } 544 return buf.String() 545 } 546 547 // Operands returns a mutable list of operands of the given instruction. 548 func (inst *InstGetElementPtr) Operands() []*value.Value { 549 ops := make([]*value.Value, 0, 1+len(inst.Indices)) 550 ops = append(ops, &inst.Src) 551 for i := range inst.Indices { 552 ops = append(ops, &inst.Indices[i]) 553 } 554 return ops 555 } 556 557 // ### [ Helper functions ] #################################################### 558 559 // gepInstType computes the result type of a getelementptr instruction. 560 // 561 // getelementptr ElemType, Src, Indices 562 func gepInstType(elemType, src types.Type, indices []value.Value) types.Type { 563 var idxs []gep.Index 564 for _, index := range indices { 565 var idx gep.Index 566 switch index := index.(type) { 567 case constant.Constant: 568 idx = getIndex(index) 569 default: 570 idx = gep.Index{HasVal: false} 571 // Check if index is of vector type. 572 if indexType, ok := index.Type().(*types.VectorType); ok { 573 idx.VectorLen = indexType.Len 574 } 575 } 576 idxs = append(idxs, idx) 577 } 578 return gep.ResultType(elemType, src, idxs) 579 } 580 581 // NOTE: keep getIndex in sync with getIndex in: 582 // 583 // * ast/inst_memory.go 584 // * ir/inst_memory.go 585 // * ir/constant/expr_memory.go 586 // 587 // The reference point and source of truth is in ir/constant/expr_memory.go. 588 589 // getIndex returns the gep index corresponding to the given constant index. 590 func getIndex(index constant.Constant) gep.Index { 591 // unpack inrange indices. 592 if idx, ok := index.(*constant.Index); ok { 593 index = idx.Constant 594 } 595 // TODO: figure out how to simplify expressions for GEP instructions without 596 // creating import cycle on irutil. 597 598 // Use index.Simplify() to simplify the constant expression to a concrete 599 // integer constant or vector of integers constant. 600 //if idx, ok := index.(constant.Expression); ok { 601 // index = idx.Simplify() 602 //} 603 switch index := index.(type) { 604 case *constant.Int: 605 val := index.X.Int64() 606 return gep.NewIndex(val) 607 case *constant.ZeroInitializer: 608 return gep.NewIndex(0) 609 case *constant.Vector: 610 // ref: https://llvm.org/docs/LangRef.html#getelementptr-instruction 611 // 612 // > The getelementptr returns a vector of pointers, instead of a single 613 // > address, when one or more of its arguments is a vector. In such 614 // > cases, all vector arguments should have the same number of elements, 615 // > and every scalar argument will be effectively broadcast into a vector 616 // > during address calculation. 617 if len(index.Elems) == 0 { 618 return gep.Index{HasVal: false} 619 } 620 // Sanity check. All vector elements must be integers, and must have the 621 // same value. 622 var val int64 623 for i, elem := range index.Elems { 624 switch elem := elem.(type) { 625 case *constant.Int: 626 x := elem.X.Int64() 627 if i == 0 { 628 val = x 629 } else if x != val { 630 // since all elements were not identical, we must conclude that 631 // the index vector does not have a concrete value. 632 return gep.Index{ 633 HasVal: false, 634 VectorLen: uint64(len(index.Elems)), 635 } 636 } 637 default: 638 // TODO: remove debug output. 639 panic(fmt.Errorf("support for gep index vector element type %T not yet implemented", elem)) 640 //return gep.Index{HasVal: false} 641 } 642 } 643 return gep.Index{ 644 HasVal: true, 645 Val: val, 646 VectorLen: uint64(len(index.Elems)), 647 } 648 case *constant.Undef: 649 return gep.Index{HasVal: false} 650 case *constant.Poison: 651 return gep.Index{HasVal: false} 652 case constant.Expression: 653 // should already have been simplified to a form we can handle. 654 return gep.Index{HasVal: false} 655 default: 656 // TODO: add support for more constant expressions. 657 // TODO: remove debug output. 658 panic(fmt.Errorf("support for gep index type %T not yet implemented", index)) 659 //return gep.Index{HasVal: false} 660 } 661 }