1 // Package llvmutil contains utility functions used across multiple compiler
2 // packages. For example, they may be used by both the compiler package and
3 // transformation packages.
4 //
5 // Normally, utility packages are avoided. However, in this case, the utility
6 // functions are non-trivial and hard to get right. Copying them to multiple
7 // places would be a big risk if only one of them is updated.
8 package llvmutil
9 10 import (
11 "encoding/binary"
12 "strconv"
13 "strings"
14 15 "tinygo.org/x/go-llvm"
16 )
17 18 // CreateEntryBlockAlloca creates a new alloca in the entry block, even though
19 // the IR builder is located elsewhere. It assumes that the insert point is
20 // at the end of the current block.
21 func CreateEntryBlockAlloca(builder llvm.Builder, t llvm.Type, name string) llvm.Value {
22 currentBlock := builder.GetInsertBlock()
23 entryBlock := currentBlock.Parent().EntryBasicBlock()
24 if entryBlock.FirstInstruction().IsNil() {
25 builder.SetInsertPointAtEnd(entryBlock)
26 } else {
27 builder.SetInsertPointBefore(entryBlock.FirstInstruction())
28 }
29 alloca := builder.CreateAlloca(t, name)
30 builder.SetInsertPointAtEnd(currentBlock)
31 return alloca
32 }
33 34 // CreateTemporaryAlloca creates a new alloca in the entry block and adds
35 // lifetime start information in the IR signalling that the alloca won't be used
36 // before this point.
37 //
38 // This is useful for creating temporary allocas for intrinsics. Don't forget to
39 // end the lifetime using emitLifetimeEnd after you're done with it.
40 func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, name string) (alloca, size llvm.Value) {
41 ctx := t.Context()
42 targetData := llvm.NewTargetData(mod.DataLayout())
43 defer targetData.Dispose()
44 alloca = CreateEntryBlockAlloca(builder, t, name)
45 size = llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false)
46 fnType, fn := getLifetimeStartFunc(mod)
47 builder.CreateCall(fnType, fn, []llvm.Value{alloca}, "")
48 return
49 }
50 51 // CreateInstructionAlloca creates an alloca in the entry block, and places lifetime control intrinsics around the instruction
52 func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, inst llvm.Value, name string) llvm.Value {
53 alloca := CreateEntryBlockAlloca(builder, t, name)
54 builder.SetInsertPointBefore(inst)
55 fnType, fn := getLifetimeStartFunc(mod)
56 builder.CreateCall(fnType, fn, []llvm.Value{alloca}, "")
57 if next := llvm.NextInstruction(inst); !next.IsNil() {
58 builder.SetInsertPointBefore(next)
59 } else {
60 builder.SetInsertPointAtEnd(inst.InstructionParent())
61 }
62 fnType, fn = getLifetimeEndFunc(mod)
63 builder.CreateCall(fnType, fn, []llvm.Value{alloca}, "")
64 return alloca
65 }
66 67 // EmitLifetimeEnd signals the end of an (alloca) lifetime by calling the
68 // llvm.lifetime.end intrinsic. It is commonly used together with
69 // createTemporaryAlloca.
70 func EmitLifetimeEnd(builder llvm.Builder, mod llvm.Module, ptr, size llvm.Value) {
71 fnType, fn := getLifetimeEndFunc(mod)
72 builder.CreateCall(fnType, fn, []llvm.Value{ptr}, "")
73 }
74 75 // getLifetimeStartFunc returns the llvm.lifetime.start intrinsic and creates it
76 // first if it doesn't exist yet.
77 // LLVM 22: signature is (ptr), no size argument.
78 func getLifetimeStartFunc(mod llvm.Module) (llvm.Type, llvm.Value) {
79 fnName := "llvm.lifetime.start.p0"
80 fn := mod.NamedFunction(fnName)
81 ctx := mod.Context()
82 ptrType := llvm.PointerType(ctx.Int8Type(), 0)
83 fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ptrType}, false)
84 if fn.IsNil() {
85 fn = llvm.AddFunction(mod, fnName, fnType)
86 }
87 return fnType, fn
88 }
89 90 // getLifetimeEndFunc returns the llvm.lifetime.end intrinsic and creates it
91 // first if it doesn't exist yet.
92 // LLVM 22: signature is (ptr), no size argument.
93 func getLifetimeEndFunc(mod llvm.Module) (llvm.Type, llvm.Value) {
94 fnName := "llvm.lifetime.end.p0"
95 fn := mod.NamedFunction(fnName)
96 ctx := mod.Context()
97 ptrType := llvm.PointerType(ctx.Int8Type(), 0)
98 fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ptrType}, false)
99 if fn.IsNil() {
100 fn = llvm.AddFunction(mod, fnName, fnType)
101 }
102 return fnType, fn
103 }
104 105 // SplitBasicBlock splits a LLVM basic block into two parts. All instructions
106 // after afterInst are moved into a new basic block (created right after the
107 // current one) with the given name.
108 func SplitBasicBlock(builder llvm.Builder, afterInst llvm.Value, insertAfter llvm.BasicBlock, name string) llvm.BasicBlock {
109 oldBlock := afterInst.InstructionParent()
110 newBlock := afterInst.Type().Context().InsertBasicBlock(insertAfter, name)
111 var nextInstructions []llvm.Value // values to move
112 113 // Collect to-be-moved instructions.
114 inst := afterInst
115 for {
116 inst = llvm.NextInstruction(inst)
117 if inst.IsNil() {
118 break
119 }
120 nextInstructions = append(nextInstructions, inst)
121 }
122 123 // Move instructions.
124 builder.SetInsertPointAtEnd(newBlock)
125 for _, inst := range nextInstructions {
126 inst.RemoveFromParentAsInstruction()
127 builder.Insert(inst)
128 }
129 130 // Find PHI nodes to update.
131 var phiNodes []llvm.Value // PHI nodes to update
132 for bb := insertAfter.Parent().FirstBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
133 for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
134 if inst.IsAPHINode().IsNil() {
135 continue
136 }
137 needsUpdate := false
138 incomingCount := inst.IncomingCount()
139 for i := 0; i < incomingCount; i++ {
140 if inst.IncomingBlock(i) == oldBlock {
141 needsUpdate = true
142 break
143 }
144 }
145 if !needsUpdate {
146 // PHI node has no incoming edge from the old block.
147 continue
148 }
149 phiNodes = append(phiNodes, inst)
150 }
151 }
152 153 // Update PHI nodes.
154 for _, phi := range phiNodes {
155 builder.SetInsertPointBefore(phi)
156 newPhi := builder.CreatePHI(phi.Type(), "")
157 incomingCount := phi.IncomingCount()
158 incomingVals := make([]llvm.Value, incomingCount)
159 incomingBlocks := make([]llvm.BasicBlock, incomingCount)
160 for i := 0; i < incomingCount; i++ {
161 value := phi.IncomingValue(i)
162 block := phi.IncomingBlock(i)
163 if block == oldBlock {
164 block = newBlock
165 }
166 incomingVals[i] = value
167 incomingBlocks[i] = block
168 }
169 newPhi.AddIncoming(incomingVals, incomingBlocks)
170 phi.ReplaceAllUsesWith(newPhi)
171 phi.EraseFromParentAsInstruction()
172 }
173 174 return newBlock
175 }
176 177 // AppendToGlobal appends the given values to a global array like llvm.used. The global might
178 // not exist yet. The values can be any pointer type, they will be cast to i8*.
179 func AppendToGlobal(mod llvm.Module, globalName string, values ...llvm.Value) {
180 // Read the existing values in the llvm.used array (if it exists).
181 var usedValues []llvm.Value
182 if used := mod.NamedGlobal(globalName); !used.IsNil() {
183 builder := mod.Context().NewBuilder()
184 defer builder.Dispose()
185 usedInitializer := used.Initializer()
186 num := usedInitializer.Type().ArrayLength()
187 for i := 0; i < num; i++ {
188 usedValues = append(usedValues, builder.CreateExtractValue(usedInitializer, i, ""))
189 }
190 used.EraseFromParentAsGlobal()
191 }
192 193 // Add the new values.
194 ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
195 for _, value := range values {
196 // Note: the bitcast is necessary to cast AVR function pointers to
197 // address space 0 pointer types.
198 usedValues = append(usedValues, llvm.ConstPointerCast(value, ptrType))
199 }
200 201 // Create a new array (with the old and new values).
202 usedInitializer := llvm.ConstArray(ptrType, usedValues)
203 used := llvm.AddGlobal(mod, usedInitializer.Type(), globalName)
204 used.SetInitializer(usedInitializer)
205 used.SetLinkage(llvm.AppendingLinkage)
206 }
207 208 // Version returns the LLVM major version.
209 func Version() int {
210 majorStr := strings.Split(llvm.Version, ".")[0]
211 major, err := strconv.Atoi(majorStr)
212 if err != nil {
213 panic("unexpected error while parsing LLVM version: " + err.Error()) // should not happen
214 }
215 return major
216 }
217 218 // ByteOrder returns the byte order for the given target triple. Most targets are little
219 // endian, but for example MIPS can be big-endian.
220 func ByteOrder(target string) binary.ByteOrder {
221 if strings.HasPrefix(target, "mips-") {
222 return binary.BigEndian
223 } else {
224 return binary.LittleEndian
225 }
226 }
227