github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/compiler/llvmutil/llvm.go (about) 1 // Package llvmutil contains utility functions used across multiple compiler 2 // packages. For example, they may be used by both the compiler pacakge 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 "tinygo.org/x/go-llvm" 12 ) 13 14 // CreateEntryBlockAlloca creates a new alloca in the entry block, even though 15 // the IR builder is located elsewhere. It assumes that the insert point is 16 // at the end of the current block. 17 func CreateEntryBlockAlloca(builder llvm.Builder, t llvm.Type, name string) llvm.Value { 18 currentBlock := builder.GetInsertBlock() 19 entryBlock := currentBlock.Parent().EntryBasicBlock() 20 if entryBlock.FirstInstruction().IsNil() { 21 builder.SetInsertPointAtEnd(entryBlock) 22 } else { 23 builder.SetInsertPointBefore(entryBlock.FirstInstruction()) 24 } 25 alloca := builder.CreateAlloca(t, name) 26 builder.SetInsertPointAtEnd(currentBlock) 27 return alloca 28 } 29 30 // CreateTemporaryAlloca creates a new alloca in the entry block and adds 31 // lifetime start infromation in the IR signalling that the alloca won't be used 32 // before this point. 33 // 34 // This is useful for creating temporary allocas for intrinsics. Don't forget to 35 // end the lifetime using emitLifetimeEnd after you're done with it. 36 func CreateTemporaryAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, name string) (alloca, size llvm.Value) { 37 ctx := t.Context() 38 targetData := llvm.NewTargetData(mod.DataLayout()) 39 defer targetData.Dispose() 40 alloca = CreateEntryBlockAlloca(builder, t, name) 41 size = llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false) 42 fnType, fn := getLifetimeStartFunc(mod) 43 builder.CreateCall(fnType, fn, []llvm.Value{size, alloca}, "") 44 return 45 } 46 47 // CreateInstructionAlloca creates an alloca in the entry block, and places lifetime control intrinsics around the instruction 48 func CreateInstructionAlloca(builder llvm.Builder, mod llvm.Module, t llvm.Type, inst llvm.Value, name string) llvm.Value { 49 ctx := mod.Context() 50 targetData := llvm.NewTargetData(mod.DataLayout()) 51 defer targetData.Dispose() 52 53 alloca := CreateEntryBlockAlloca(builder, t, name) 54 builder.SetInsertPointBefore(inst) 55 size := llvm.ConstInt(ctx.Int64Type(), targetData.TypeAllocSize(t), false) 56 fnType, fn := getLifetimeStartFunc(mod) 57 builder.CreateCall(fnType, fn, []llvm.Value{size, alloca}, "") 58 if next := llvm.NextInstruction(inst); !next.IsNil() { 59 builder.SetInsertPointBefore(next) 60 } else { 61 builder.SetInsertPointAtEnd(inst.InstructionParent()) 62 } 63 fnType, fn = getLifetimeEndFunc(mod) 64 builder.CreateCall(fnType, fn, []llvm.Value{size, alloca}, "") 65 return alloca 66 } 67 68 // EmitLifetimeEnd signals the end of an (alloca) lifetime by calling the 69 // llvm.lifetime.end intrinsic. It is commonly used together with 70 // createTemporaryAlloca. 71 func EmitLifetimeEnd(builder llvm.Builder, mod llvm.Module, ptr, size llvm.Value) { 72 fnType, fn := getLifetimeEndFunc(mod) 73 builder.CreateCall(fnType, fn, []llvm.Value{size, ptr}, "") 74 } 75 76 // getLifetimeStartFunc returns the llvm.lifetime.start intrinsic and creates it 77 // first if it doesn't exist yet. 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{ctx.Int64Type(), 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 func getLifetimeEndFunc(mod llvm.Module) (llvm.Type, llvm.Value) { 93 fnName := "llvm.lifetime.end.p0" 94 fn := mod.NamedFunction(fnName) 95 ctx := mod.Context() 96 ptrType := llvm.PointerType(ctx.Int8Type(), 0) 97 fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{ctx.Int64Type(), ptrType}, false) 98 if fn.IsNil() { 99 fn = llvm.AddFunction(mod, fnName, fnType) 100 } 101 return fnType, fn 102 } 103 104 // SplitBasicBlock splits a LLVM basic block into two parts. All instructions 105 // after afterInst are moved into a new basic block (created right after the 106 // current one) with the given name. 107 func SplitBasicBlock(builder llvm.Builder, afterInst llvm.Value, insertAfter llvm.BasicBlock, name string) llvm.BasicBlock { 108 oldBlock := afterInst.InstructionParent() 109 newBlock := afterInst.Type().Context().InsertBasicBlock(insertAfter, name) 110 var nextInstructions []llvm.Value // values to move 111 112 // Collect to-be-moved instructions. 113 inst := afterInst 114 for { 115 inst = llvm.NextInstruction(inst) 116 if inst.IsNil() { 117 break 118 } 119 nextInstructions = append(nextInstructions, inst) 120 } 121 122 // Move instructions. 123 builder.SetInsertPointAtEnd(newBlock) 124 for _, inst := range nextInstructions { 125 inst.RemoveFromParentAsInstruction() 126 builder.Insert(inst) 127 } 128 129 // Find PHI nodes to update. 130 var phiNodes []llvm.Value // PHI nodes to update 131 for bb := insertAfter.Parent().FirstBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) { 132 for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) { 133 if inst.IsAPHINode().IsNil() { 134 continue 135 } 136 needsUpdate := false 137 incomingCount := inst.IncomingCount() 138 for i := 0; i < incomingCount; i++ { 139 if inst.IncomingBlock(i) == oldBlock { 140 needsUpdate = true 141 break 142 } 143 } 144 if !needsUpdate { 145 // PHI node has no incoming edge from the old block. 146 continue 147 } 148 phiNodes = append(phiNodes, inst) 149 } 150 } 151 152 // Update PHI nodes. 153 for _, phi := range phiNodes { 154 builder.SetInsertPointBefore(phi) 155 newPhi := builder.CreatePHI(phi.Type(), "") 156 incomingCount := phi.IncomingCount() 157 incomingVals := make([]llvm.Value, incomingCount) 158 incomingBlocks := make([]llvm.BasicBlock, incomingCount) 159 for i := 0; i < incomingCount; i++ { 160 value := phi.IncomingValue(i) 161 block := phi.IncomingBlock(i) 162 if block == oldBlock { 163 block = newBlock 164 } 165 incomingVals[i] = value 166 incomingBlocks[i] = block 167 } 168 newPhi.AddIncoming(incomingVals, incomingBlocks) 169 phi.ReplaceAllUsesWith(newPhi) 170 phi.EraseFromParentAsInstruction() 171 } 172 173 return newBlock 174 } 175 176 // Append the given values to a global array like llvm.used. The global might 177 // not exist yet. The values can be any pointer type, they will be cast to i8*. 178 func AppendToGlobal(mod llvm.Module, globalName string, values ...llvm.Value) { 179 // Read the existing values in the llvm.used array (if it exists). 180 var usedValues []llvm.Value 181 if used := mod.NamedGlobal(globalName); !used.IsNil() { 182 builder := mod.Context().NewBuilder() 183 defer builder.Dispose() 184 usedInitializer := used.Initializer() 185 num := usedInitializer.Type().ArrayLength() 186 for i := 0; i < num; i++ { 187 usedValues = append(usedValues, builder.CreateExtractValue(usedInitializer, i, "")) 188 } 189 used.EraseFromParentAsGlobal() 190 } 191 192 // Add the new values. 193 ptrType := llvm.PointerType(mod.Context().Int8Type(), 0) 194 for _, value := range values { 195 // Note: the bitcast is necessary to cast AVR function pointers to 196 // address space 0 pointer types. 197 usedValues = append(usedValues, llvm.ConstPointerCast(value, ptrType)) 198 } 199 200 // Create a new array (with the old and new values). 201 usedInitializer := llvm.ConstArray(ptrType, usedValues) 202 used := llvm.AddGlobal(mod, usedInitializer.Type(), globalName) 203 used.SetInitializer(usedInitializer) 204 used.SetLinkage(llvm.AppendingLinkage) 205 }