github.com/decomp/exp@v0.0.0-20210624183419-6d058f5e1da6/lift/x86/gep.go (about)

     1  package x86
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/llir/llvm/ir"
     7  	"github.com/llir/llvm/ir/constant"
     8  	"github.com/llir/llvm/ir/types"
     9  	"github.com/llir/llvm/ir/value"
    10  )
    11  
    12  // getElementPtr returns a pointer to the LLVM IR value located at the specified
    13  // offset from the source value.
    14  func (f *Func) getElementPtr(src value.Value, offset uint64) *ir.InstGetElementPtr {
    15  	dbg.Println("offset:", offset)
    16  	srcType, ok := src.Type().(*types.PointerType)
    17  	if !ok {
    18  		panic(fmt.Errorf("invalid source address type; expected *types.PointerType, got %T", src.Type()))
    19  	}
    20  	elem := srcType.ElemType
    21  	e := elem
    22  	total := uint64(0)
    23  	var indices []value.Value
    24  	// n specifies a byte offset into an integer element.
    25  	var n uint64
    26  loop:
    27  	for i := int64(0); ; i++ {
    28  		if total > offset {
    29  			panic("unreachable; or at least should be :)")
    30  		}
    31  		dbg.Println("   total:", total)
    32  		dbg.Println("   e:", e)
    33  		if i == 0 {
    34  			// Ignore checking the 0th index as it simply follows the pointer of
    35  			// src.
    36  			//
    37  			// ref: http://llvm.org/docs/GetElementPtr.html#why-is-the-extra-0-index-required
    38  			index := constant.NewInt(types.I64, 0)
    39  			indices = append(indices, index)
    40  			continue
    41  		}
    42  		switch t := e.(type) {
    43  		case *types.PointerType:
    44  			if total == offset {
    45  				break loop
    46  			}
    47  			// ref: http://llvm.org/docs/GetElementPtr.html#what-is-dereferenced-by-gep
    48  			panic("unable to index into element of pointer type; for more information, see http://llvm.org/docs/GetElementPtr.html#what-is-dereferenced-by-gep")
    49  		case *types.ArrayType:
    50  			elemSize := f.l.sizeOfType(t.ElemType)
    51  			j := int64(0)
    52  			for ; j < int64(t.Len); j++ {
    53  				if total+elemSize > offset {
    54  					break
    55  				}
    56  				total += elemSize
    57  			}
    58  			index := constant.NewInt(types.I64, j)
    59  			indices = append(indices, index)
    60  			e = t.ElemType
    61  		case *types.StructType:
    62  			j := int64(0)
    63  			for ; j < int64(len(t.Fields)); j++ {
    64  				fieldSize := f.l.sizeOfType(t.Fields[j])
    65  				if total+fieldSize > offset {
    66  					break
    67  				}
    68  				total += fieldSize
    69  			}
    70  			index := constant.NewInt(types.I64, j)
    71  			indices = append(indices, index)
    72  			e = t.Fields[j]
    73  		case *types.IntType:
    74  			if total == offset {
    75  				break loop
    76  			}
    77  			warn.Printf("indexing into the middle of an integer element at offset %d in type %v", total, src.Type())
    78  			n = t.BitSize / 8
    79  			if total+n < offset {
    80  				panic(fmt.Errorf("unable to locate offset %d in type %v; indexing into integer type of byte size %d when at total offset %d", offset, src.Type(), n, total))
    81  			}
    82  			break loop
    83  		default:
    84  			panic(fmt.Errorf("support for indexing element type %T not yet implemented", e))
    85  		}
    86  	}
    87  	v := f.cur.NewGetElementPtr(src, indices...)
    88  	if n > 0 {
    89  		src := f.cur.NewLoad(v)
    90  		typ := types.NewPointer(types.NewArray(uint64(n), types.I8))
    91  		tmp1 := f.cur.NewBitCast(src, typ)
    92  		indices := []value.Value{
    93  			constant.NewInt(types.I64, 0),
    94  			constant.NewInt(types.I64, int64(offset)-int64(total)),
    95  		}
    96  		tmp2 := f.cur.NewGetElementPtr(tmp1, indices...)
    97  		return tmp2
    98  	}
    99  	return v
   100  }