github.com/llir/llvm@v0.3.6/internal/gep/gep.go (about)

     1  // Package gep computes the result type of getelementptr instructions and
     2  // constant expressions.
     3  //
     4  // ref: https://llvm.org/docs/GetElementPtr.html
     5  // ref: https://llvm.org/docs/LangRef.html#getelementptr-instruction
     6  package gep
     7  
     8  import (
     9  	"fmt"
    10  	"log"
    11  
    12  	"github.com/llir/llvm/ir/types"
    13  )
    14  
    15  // Index is a gep index.
    16  type Index struct {
    17  	// HasVal specifies whether Val has a valid value. If index is a constant
    18  	// integer or a constant integer vector of which all elements have the same
    19  	// value, then HasVal is set. Note, this is a requirement to index into
    20  	// structure types.
    21  	HasVal bool
    22  	// Index integer value. Val is only valid if HasVal is set.
    23  	Val int64
    24  	// Length of index vector; or 0 if index is scalar. VectorLen may be non-zero
    25  	// even if HasVal is false.
    26  	VectorLen uint64
    27  }
    28  
    29  // NewIndex returns a new constant index with the given value.
    30  func NewIndex(val int64) Index {
    31  	return Index{
    32  		HasVal: true,
    33  		Val:    val,
    34  	}
    35  }
    36  
    37  // ResultType computes the result type of a getelementptr instruction or
    38  // constant expression.
    39  //
    40  //    getelementptr (ElemType, Src, Indices)
    41  func ResultType(elemType, src types.Type, indices []Index) types.Type {
    42  	// ref: http://llvm.org/docs/GetElementPtr.html#what-effect-do-address-spaces-have-on-geps
    43  	//
    44  	// > the address space qualifier on the second operand pointer type always
    45  	// > matches the address space qualifier on the result type.
    46  	var (
    47  		// Address space of src pointer type or src vector element pointer type.
    48  		addrSpace types.AddrSpace
    49  		// Length of vector of pointers result type; or 0 if pointer result type.
    50  		resultVectorLength uint64
    51  	)
    52  	// ref: https://llvm.org/docs/LangRef.html#getelementptr-instruction
    53  	//
    54  	// > The second argument is always a pointer or a vector of pointers.
    55  	switch src := src.(type) {
    56  	case *types.PointerType:
    57  		addrSpace = src.AddrSpace
    58  	case *types.VectorType:
    59  		vectorElemType, ok := src.ElemType.(*types.PointerType)
    60  		if !ok {
    61  			panic(fmt.Errorf("invalid gep source vector element type; expected *types.PointerType, got %T", src.ElemType))
    62  		}
    63  		addrSpace = vectorElemType.AddrSpace
    64  		resultVectorLength = src.Len
    65  	default:
    66  		panic(fmt.Errorf("invalid gep source type; expected pointer or vector of pointers type, got %T", src))
    67  	}
    68  	// ref: https://llvm.org/docs/LangRef.html#getelementptr-instruction
    69  	//
    70  	// > The first argument is always a type used as the basis for the
    71  	// > calculations.
    72  	e := elemType
    73  	for i, index := range indices {
    74  		// ref: https://llvm.org/docs/LangRef.html#getelementptr-instruction
    75  		//
    76  		// > The getelementptr returns a vector of pointers, instead of a single
    77  		// > address, when one or more of its arguments is a vector. In such
    78  		// > cases, all vector arguments should have the same number of elements,
    79  		// > and every scalar argument will be effectively broadcast into a vector
    80  		// > during address calculation.
    81  		if index.VectorLen != 0 && resultVectorLength != 0 && index.VectorLen != resultVectorLength {
    82  			panic(fmt.Errorf("vector length mismatch of index vector (%d) and result type vector (%d)", index.VectorLen, resultVectorLength))
    83  		}
    84  		if resultVectorLength == 0 && index.VectorLen != 0 {
    85  			resultVectorLength = index.VectorLen
    86  		}
    87  		// ref: https://llvm.org/docs/GetElementPtr.html#why-is-the-extra-0-index-required
    88  		//
    89  		// > Since the second argument to the GEP instruction must always be a
    90  		// > value of pointer type, the first index steps through that pointer.
    91  		if i == 0 {
    92  			continue
    93  		}
    94  		switch elm := e.(type) {
    95  		case *types.PointerType:
    96  			panic(fmt.Errorf("cannot index into pointer type at %d:th gep index, only valid at 0:th gep index; see https://llvm.org/docs/GetElementPtr.html#what-is-dereferenced-by-gep", i))
    97  		case *types.VectorType:
    98  			// ref: https://llvm.org/docs/GetElementPtr.html#can-gep-index-into-vector-elements
    99  			//
   100  			// > This hasn’t always been forcefully disallowed, though it’s not
   101  			// > recommended. It leads to awkward special cases in the optimizers,
   102  			// > and fundamental inconsistency in the IR. In the future, it will
   103  			// > probably be outright disallowed.
   104  			log.Printf("using gep to index into vector types will be disallowed in a future release or llir/llvm; see https://llvm.org/docs/GetElementPtr.html#can-gep-index-into-vector-elements")
   105  			e = elm.ElemType
   106  		case *types.ArrayType:
   107  			e = elm.ElemType
   108  		case *types.StructType:
   109  			// ref: https://llvm.org/docs/LangRef.html#getelementptr-instruction
   110  			//
   111  			// > When indexing into a (optionally packed) structure, only i32
   112  			// > integer constants are allowed (when using a vector of indices they
   113  			// > must all be the same i32 integer constant).
   114  			if !index.HasVal {
   115  				panic(fmt.Errorf("unable to index into struct type `%v` using gep with non-constant index", e))
   116  			}
   117  			e = elm.Fields[index.Val]
   118  		default:
   119  			panic(fmt.Errorf("cannot index into type %T using gep", e))
   120  		}
   121  	}
   122  	ptr := types.NewPointer(e)
   123  	ptr.AddrSpace = addrSpace
   124  	if resultVectorLength != 0 {
   125  		vec := types.NewVector(resultVectorLength, ptr)
   126  		return vec
   127  	}
   128  	return ptr
   129  }