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