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