github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/transform/llvm.go (about)

     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  }