github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/transform/stacksize.go (about) 1 package transform 2 3 import ( 4 "path/filepath" 5 6 "github.com/tinygo-org/tinygo/compileopts" 7 "github.com/tinygo-org/tinygo/compiler/llvmutil" 8 "github.com/tinygo-org/tinygo/goenv" 9 "tinygo.org/x/go-llvm" 10 ) 11 12 // CreateStackSizeLoads replaces internal/task.getGoroutineStackSize calls with 13 // loads from internal/task.stackSizes that will be updated after linking. This 14 // way the stack sizes are loaded from a separate section and can easily be 15 // modified after linking. 16 func CreateStackSizeLoads(mod llvm.Module, config *compileopts.Config) []string { 17 functionMap := map[llvm.Value][]llvm.Value{} 18 var functions []llvm.Value // ptrtoint values of functions 19 var functionNames []string 20 var functionValues []llvm.Value // direct references to functions 21 for _, use := range getUses(mod.NamedFunction("internal/task.getGoroutineStackSize")) { 22 if use.FirstUse().IsNil() { 23 // Apparently this stack size isn't used. 24 use.EraseFromParentAsInstruction() 25 continue 26 } 27 ptrtoint := use.Operand(0) 28 if _, ok := functionMap[ptrtoint]; !ok { 29 functions = append(functions, ptrtoint) 30 functionNames = append(functionNames, ptrtoint.Operand(0).Name()) 31 functionValues = append(functionValues, ptrtoint.Operand(0)) 32 } 33 functionMap[ptrtoint] = append(functionMap[ptrtoint], use) 34 } 35 36 if len(functions) == 0 { 37 // Nothing to do. 38 return nil 39 } 40 41 ctx := mod.Context() 42 targetData := llvm.NewTargetData(mod.DataLayout()) 43 defer targetData.Dispose() 44 uintptrType := ctx.IntType(targetData.PointerSize() * 8) 45 46 // Create the new global with stack sizes, that will be put in a new section 47 // just for itself. 48 stackSizesGlobalType := llvm.ArrayType(functions[0].Type(), len(functions)) 49 stackSizesGlobal := llvm.AddGlobal(mod, stackSizesGlobalType, "internal/task.stackSizes") 50 stackSizesGlobal.SetSection(".tinygo_stacksizes") 51 defaultStackSizes := make([]llvm.Value, len(functions)) 52 defaultStackSize := llvm.ConstInt(functions[0].Type(), config.StackSize(), false) 53 alignment := targetData.ABITypeAlignment(functions[0].Type()) 54 for i := range defaultStackSizes { 55 defaultStackSizes[i] = defaultStackSize 56 } 57 stackSizesGlobal.SetInitializer(llvm.ConstArray(functions[0].Type(), defaultStackSizes)) 58 stackSizesGlobal.SetAlignment(alignment) 59 // TODO: make this a constant. For some reason, that incrases code size though. 60 if config.Debug() { 61 dibuilder := llvm.NewDIBuilder(mod) 62 dibuilder.CreateCompileUnit(llvm.DICompileUnit{ 63 Language: 0xb, // DW_LANG_C99 (0xc, off-by-one?) 64 File: "<unknown>", 65 Dir: "", 66 Producer: "TinyGo", 67 Optimized: true, 68 }) 69 ditype := dibuilder.CreateArrayType(llvm.DIArrayType{ 70 SizeInBits: targetData.TypeAllocSize(stackSizesGlobalType) * 8, 71 AlignInBits: uint32(alignment * 8), 72 ElementType: dibuilder.CreateBasicType(llvm.DIBasicType{ 73 Name: "uintptr", 74 SizeInBits: targetData.TypeAllocSize(functions[0].Type()) * 8, 75 Encoding: llvm.DW_ATE_unsigned, 76 }), 77 Subscripts: []llvm.DISubrange{ 78 { 79 Lo: 0, 80 Count: int64(len(functions)), 81 }, 82 }, 83 }) 84 diglobal := dibuilder.CreateGlobalVariableExpression(llvm.Metadata{}, llvm.DIGlobalVariableExpression{ 85 Name: "internal/task.stackSizes", 86 File: dibuilder.CreateFile("internal/task/task_stack.go", filepath.Join(goenv.Get("TINYGOROOT"), "src")), 87 Line: 1, 88 Type: ditype, 89 Expr: dibuilder.CreateExpression(nil), 90 }) 91 stackSizesGlobal.AddMetadata(0, diglobal) 92 93 dibuilder.Finalize() 94 dibuilder.Destroy() 95 } 96 97 // Add all relevant values to llvm.used (for LTO). 98 llvmutil.AppendToGlobal(mod, "llvm.used", append([]llvm.Value{stackSizesGlobal}, functionValues...)...) 99 100 // Replace the calls with loads from the new global with stack sizes. 101 irbuilder := ctx.NewBuilder() 102 defer irbuilder.Dispose() 103 for i, function := range functions { 104 for _, use := range functionMap[function] { 105 ptr := llvm.ConstGEP(stackSizesGlobalType, stackSizesGlobal, []llvm.Value{ 106 llvm.ConstInt(ctx.Int32Type(), 0, false), 107 llvm.ConstInt(ctx.Int32Type(), uint64(i), false), 108 }) 109 irbuilder.SetInsertPointBefore(use) 110 stacksize := irbuilder.CreateLoad(uintptrType, ptr, "stacksize") 111 use.ReplaceAllUsesWith(stacksize) 112 use.EraseFromParentAsInstruction() 113 } 114 } 115 116 return functionNames 117 }