stacksize.go raw
1 package transform
2
3 import (
4 "path/filepath"
5
6 "moxie/compileopts"
7 "moxie/compiler/llvmutil"
8 "moxie/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(".moxie_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: "Moxie",
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 }
118