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