llvm.go raw

   1  package transform
   2  
   3  import (
   4  	"reflect"
   5  
   6  	"tinygo.org/x/go-llvm"
   7  )
   8  
   9  // Return a list of values (actually, instructions) where this value is used as
  10  // an operand.
  11  func getUses(value llvm.Value) []llvm.Value {
  12  	if value.IsNil() {
  13  		return nil
  14  	}
  15  	var uses []llvm.Value
  16  	use := value.FirstUse()
  17  	for !use.IsNil() {
  18  		uses = append(uses, use.User())
  19  		use = use.NextUse()
  20  	}
  21  	return uses
  22  }
  23  
  24  // hasUses returns whether the given value has any uses. It is equivalent to
  25  // getUses(value) != nil but faster.
  26  func hasUses(value llvm.Value) bool {
  27  	if value.IsNil() {
  28  		return false
  29  	}
  30  	return !value.FirstUse().IsNil()
  31  }
  32  
  33  // makeGlobalArray creates a new LLVM global with the given name and integers as
  34  // contents, and returns the global and initializer type.
  35  // Note that it is left with the default linkage etc., you should set
  36  // linkage/constant/etc properties yourself.
  37  func makeGlobalArray(mod llvm.Module, bufItf interface{}, name string, elementType llvm.Type) (llvm.Type, llvm.Value) {
  38  	buf := reflect.ValueOf(bufItf)
  39  	var values []llvm.Value
  40  	for i := 0; i < buf.Len(); i++ {
  41  		ch := buf.Index(i).Uint()
  42  		values = append(values, llvm.ConstInt(elementType, ch, false))
  43  	}
  44  	value := llvm.ConstArray(elementType, values)
  45  	global := llvm.AddGlobal(mod, value.Type(), name)
  46  	global.SetInitializer(value)
  47  	return value.Type(), global
  48  }
  49  
  50  // getGlobalBytes returns the slice contained in the array of the provided
  51  // global. It can recover the bytes originally created using makeGlobalArray, if
  52  // makeGlobalArray was given a byte slice.
  53  //
  54  // The builder parameter is only used for constant operations.
  55  func getGlobalBytes(global llvm.Value, builder llvm.Builder) []byte {
  56  	value := global.Initializer()
  57  	buf := make([]byte, value.Type().ArrayLength())
  58  	for i := range buf {
  59  		buf[i] = byte(builder.CreateExtractValue(value, i, "").ZExtValue())
  60  	}
  61  	return buf
  62  }
  63  
  64  // replaceGlobalByteWithArray replaces a global integer type in the module with
  65  // an integer array, using a GEP to make the types match. It is a convenience
  66  // function used for creating reflection sidetables, for example.
  67  func replaceGlobalIntWithArray(mod llvm.Module, name string, buf interface{}) llvm.Value {
  68  	oldGlobal := mod.NamedGlobal(name)
  69  	globalType, global := makeGlobalArray(mod, buf, name+".tmp", oldGlobal.GlobalValueType())
  70  	gep := llvm.ConstGEP(globalType, global, []llvm.Value{
  71  		llvm.ConstInt(mod.Context().Int32Type(), 0, false),
  72  		llvm.ConstInt(mod.Context().Int32Type(), 0, false),
  73  	})
  74  	oldGlobal.ReplaceAllUsesWith(gep)
  75  	oldGlobal.EraseFromParentAsGlobal()
  76  	global.SetName(name)
  77  	return global
  78  }
  79  
  80  // stripPointerCasts strips instruction pointer casts (getelementptr and
  81  // bitcast) and returns the original value without the casts.
  82  func stripPointerCasts(value llvm.Value) llvm.Value {
  83  	if !value.IsAConstantExpr().IsNil() {
  84  		switch value.Opcode() {
  85  		case llvm.GetElementPtr, llvm.BitCast:
  86  			return stripPointerCasts(value.Operand(0))
  87  		}
  88  	}
  89  	if !value.IsAInstruction().IsNil() {
  90  		switch value.InstructionOpcode() {
  91  		case llvm.GetElementPtr, llvm.BitCast:
  92  			return stripPointerCasts(value.Operand(0))
  93  		}
  94  	}
  95  	return value
  96  }
  97