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  }