llvm.go raw

   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