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  }