github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/builder/build.go (about)

     1  // Package builder is the compiler driver of TinyGo. It takes in a package name
     2  // and an output path, and outputs an executable. It manages the entire
     3  // compilation pipeline in between.
     4  package builder
     5  
     6  import (
     7  	"crypto/sha256"
     8  	"crypto/sha512"
     9  	"debug/elf"
    10  	"encoding/binary"
    11  	"encoding/hex"
    12  	"encoding/json"
    13  	"errors"
    14  	"fmt"
    15  	"go/types"
    16  	"hash/crc32"
    17  	"io/fs"
    18  	"math/bits"
    19  	"os"
    20  	"os/exec"
    21  	"path/filepath"
    22  	"runtime"
    23  	"sort"
    24  	"strconv"
    25  	"strings"
    26  
    27  	"github.com/gofrs/flock"
    28  	"github.com/tinygo-org/tinygo/compileopts"
    29  	"github.com/tinygo-org/tinygo/compiler"
    30  	"github.com/tinygo-org/tinygo/goenv"
    31  	"github.com/tinygo-org/tinygo/interp"
    32  	"github.com/tinygo-org/tinygo/loader"
    33  	"github.com/tinygo-org/tinygo/stacksize"
    34  	"github.com/tinygo-org/tinygo/transform"
    35  	"tinygo.org/x/go-llvm"
    36  )
    37  
    38  // BuildResult is the output of a build. This includes the binary itself and
    39  // some other metadata that is obtained while building the binary.
    40  type BuildResult struct {
    41  	// The executable directly from the linker, usually including debug
    42  	// information. Used for GDB for example.
    43  	Executable string
    44  
    45  	// A path to the output binary. It is stored in the tmpdir directory of the
    46  	// Build function, so if it should be kept it must be copied or moved away.
    47  	// It is often the same as Executable, but differs if the output format is
    48  	// .hex for example (instead of the usual ELF).
    49  	Binary string
    50  
    51  	// The directory of the main package. This is useful for testing as the test
    52  	// binary must be run in the directory of the tested package.
    53  	MainDir string
    54  
    55  	// The root of the Go module tree.  This is used for running tests in emulator
    56  	// that restrict file system access to allow them to grant access to the entire
    57  	// source tree they're likely to need to read testdata from.
    58  	ModuleRoot string
    59  
    60  	// ImportPath is the import path of the main package. This is useful for
    61  	// correctly printing test results: the import path isn't always the same as
    62  	// the path listed on the command line.
    63  	ImportPath string
    64  }
    65  
    66  // packageAction is the struct that is serialized to JSON and hashed, to work as
    67  // a cache key of compiled packages. It should contain all the information that
    68  // goes into a compiled package to avoid using stale data.
    69  //
    70  // Right now it's still important to include a hash of every import, because a
    71  // dependency might have a public constant that this package uses and thus this
    72  // package will need to be recompiled if that constant changes. In the future,
    73  // the type data should be serialized to disk which can then be used as cache
    74  // key, avoiding the need for recompiling all dependencies when only the
    75  // implementation of an imported package changes.
    76  type packageAction struct {
    77  	ImportPath       string
    78  	CompilerBuildID  string
    79  	TinyGoVersion    string
    80  	LLVMVersion      string
    81  	Config           *compiler.Config
    82  	CFlags           []string
    83  	FileHashes       map[string]string // hash of every file that's part of the package
    84  	EmbeddedFiles    map[string]string // hash of all the //go:embed files in the package
    85  	Imports          map[string]string // map from imported package to action ID hash
    86  	OptLevel         string            // LLVM optimization level (O0, O1, O2, Os, Oz)
    87  	UndefinedGlobals []string          // globals that are left as external globals (no initializer)
    88  }
    89  
    90  // Build performs a single package to executable Go build. It takes in a package
    91  // name, an output path, and set of compile options and from that it manages the
    92  // whole compilation process.
    93  //
    94  // The error value may be of type *MultiError. Callers will likely want to check
    95  // for this case and print such errors individually.
    96  func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildResult, error) {
    97  	// Read the build ID of the tinygo binary.
    98  	// Used as a cache key for package builds.
    99  	compilerBuildID, err := ReadBuildID()
   100  	if err != nil {
   101  		return BuildResult{}, err
   102  	}
   103  
   104  	if config.Options.Work {
   105  		fmt.Printf("WORK=%s\n", tmpdir)
   106  	}
   107  
   108  	// Look up the build cache directory, which is used to speed up incremental
   109  	// builds.
   110  	cacheDir := goenv.Get("GOCACHE")
   111  	if cacheDir == "off" {
   112  		// Use temporary build directory instead, effectively disabling the
   113  		// build cache.
   114  		cacheDir = tmpdir
   115  	}
   116  
   117  	// Create default global values.
   118  	globalValues := map[string]map[string]string{
   119  		"runtime": {
   120  			"buildVersion": goenv.Version(),
   121  		},
   122  		"testing": {},
   123  	}
   124  	if config.TestConfig.CompileTestBinary {
   125  		// The testing.testBinary is set to "1" when in a test.
   126  		// This is needed for testing.Testing() to work correctly.
   127  		globalValues["testing"]["testBinary"] = "1"
   128  	}
   129  
   130  	// Copy over explicitly set global values, like
   131  	// -ldflags="-X main.Version="1.0"
   132  	for pkgPath, vals := range config.Options.GlobalValues {
   133  		if _, ok := globalValues[pkgPath]; !ok {
   134  			globalValues[pkgPath] = map[string]string{}
   135  		}
   136  		for k, v := range vals {
   137  			globalValues[pkgPath][k] = v
   138  		}
   139  	}
   140  
   141  	// Check for a libc dependency.
   142  	// As a side effect, this also creates the headers for the given libc, if
   143  	// the libc needs them.
   144  	root := goenv.Get("TINYGOROOT")
   145  	var libcDependencies []*compileJob
   146  	switch config.Target.Libc {
   147  	case "darwin-libSystem":
   148  		job := makeDarwinLibSystemJob(config, tmpdir)
   149  		libcDependencies = append(libcDependencies, job)
   150  	case "musl":
   151  		job, unlock, err := Musl.load(config, tmpdir)
   152  		if err != nil {
   153  			return BuildResult{}, err
   154  		}
   155  		defer unlock()
   156  		libcDependencies = append(libcDependencies, dummyCompileJob(filepath.Join(filepath.Dir(job.result), "crt1.o")))
   157  		libcDependencies = append(libcDependencies, job)
   158  	case "picolibc":
   159  		libcJob, unlock, err := Picolibc.load(config, tmpdir)
   160  		if err != nil {
   161  			return BuildResult{}, err
   162  		}
   163  		defer unlock()
   164  		libcDependencies = append(libcDependencies, libcJob)
   165  	case "wasi-libc":
   166  		path := filepath.Join(root, "lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a")
   167  		if _, err := os.Stat(path); errors.Is(err, fs.ErrNotExist) {
   168  			return BuildResult{}, errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
   169  		}
   170  		libcDependencies = append(libcDependencies, dummyCompileJob(path))
   171  	case "wasmbuiltins":
   172  		libcJob, unlock, err := WasmBuiltins.load(config, tmpdir)
   173  		if err != nil {
   174  			return BuildResult{}, err
   175  		}
   176  		defer unlock()
   177  		libcDependencies = append(libcDependencies, libcJob)
   178  	case "mingw-w64":
   179  		_, unlock, err := MinGW.load(config, tmpdir)
   180  		if err != nil {
   181  			return BuildResult{}, err
   182  		}
   183  		unlock()
   184  		libcDependencies = append(libcDependencies, makeMinGWExtraLibs(tmpdir, config.GOARCH())...)
   185  	case "":
   186  		// no library specified, so nothing to do
   187  	default:
   188  		return BuildResult{}, fmt.Errorf("unknown libc: %s", config.Target.Libc)
   189  	}
   190  
   191  	optLevel, speedLevel, sizeLevel := config.OptLevel()
   192  	compilerConfig := &compiler.Config{
   193  		Triple:          config.Triple(),
   194  		CPU:             config.CPU(),
   195  		Features:        config.Features(),
   196  		ABI:             config.ABI(),
   197  		GOOS:            config.GOOS(),
   198  		GOARCH:          config.GOARCH(),
   199  		CodeModel:       config.CodeModel(),
   200  		RelocationModel: config.RelocationModel(),
   201  		SizeLevel:       sizeLevel,
   202  		TinyGoVersion:   goenv.Version(),
   203  
   204  		Scheduler:          config.Scheduler(),
   205  		AutomaticStackSize: config.AutomaticStackSize(),
   206  		DefaultStackSize:   config.StackSize(),
   207  		MaxStackAlloc:      config.MaxStackAlloc(),
   208  		NeedsStackObjects:  config.NeedsStackObjects(),
   209  		Debug:              !config.Options.SkipDWARF, // emit DWARF except when -internal-nodwarf is passed
   210  		PanicStrategy:      config.PanicStrategy(),
   211  	}
   212  
   213  	// Load the target machine, which is the LLVM object that contains all
   214  	// details of a target (alignment restrictions, pointer size, default
   215  	// address spaces, etc).
   216  	machine, err := compiler.NewTargetMachine(compilerConfig)
   217  	if err != nil {
   218  		return BuildResult{}, err
   219  	}
   220  	defer machine.Dispose()
   221  
   222  	// Load entire program AST into memory.
   223  	lprogram, err := loader.Load(config, pkgName, types.Config{
   224  		Sizes: compiler.Sizes(machine),
   225  	})
   226  	if err != nil {
   227  		return BuildResult{}, err
   228  	}
   229  	result := BuildResult{
   230  		ModuleRoot: lprogram.MainPkg().Module.Dir,
   231  		MainDir:    lprogram.MainPkg().Dir,
   232  		ImportPath: lprogram.MainPkg().ImportPath,
   233  	}
   234  	if result.ModuleRoot == "" {
   235  		// If there is no module root, just the regular root.
   236  		result.ModuleRoot = lprogram.MainPkg().Root
   237  	}
   238  	err = lprogram.Parse()
   239  	if err != nil {
   240  		return result, err
   241  	}
   242  
   243  	// Create the *ssa.Program. This does not yet build the entire SSA of the
   244  	// program so it's pretty fast and doesn't need to be parallelized.
   245  	program := lprogram.LoadSSA()
   246  
   247  	// Add jobs to compile each package.
   248  	// Packages that have a cache hit will not be compiled again.
   249  	var packageJobs []*compileJob
   250  	packageActionIDJobs := make(map[string]*compileJob)
   251  
   252  	var embedFileObjects []*compileJob
   253  	for _, pkg := range lprogram.Sorted() {
   254  		pkg := pkg // necessary to avoid a race condition
   255  
   256  		var undefinedGlobals []string
   257  		for name := range globalValues[pkg.Pkg.Path()] {
   258  			undefinedGlobals = append(undefinedGlobals, name)
   259  		}
   260  		sort.Strings(undefinedGlobals)
   261  
   262  		// Make compile jobs to load files to be embedded in the output binary.
   263  		var actionIDDependencies []*compileJob
   264  		allFiles := map[string][]*loader.EmbedFile{}
   265  		for _, files := range pkg.EmbedGlobals {
   266  			for _, file := range files {
   267  				allFiles[file.Name] = append(allFiles[file.Name], file)
   268  			}
   269  		}
   270  		for name, files := range allFiles {
   271  			name := name
   272  			files := files
   273  			job := &compileJob{
   274  				description: "make object file for " + name,
   275  				run: func(job *compileJob) error {
   276  					// Read the file contents in memory.
   277  					path := filepath.Join(pkg.Dir, name)
   278  					data, err := os.ReadFile(path)
   279  					if err != nil {
   280  						return err
   281  					}
   282  
   283  					// Hash the file.
   284  					sum := sha256.Sum256(data)
   285  					hexSum := hex.EncodeToString(sum[:16])
   286  
   287  					for _, file := range files {
   288  						file.Size = uint64(len(data))
   289  						file.Hash = hexSum
   290  						if file.NeedsData {
   291  							file.Data = data
   292  						}
   293  					}
   294  
   295  					job.result, err = createEmbedObjectFile(string(data), hexSum, name, pkg.OriginalDir(), tmpdir, compilerConfig)
   296  					return err
   297  				},
   298  			}
   299  			actionIDDependencies = append(actionIDDependencies, job)
   300  			embedFileObjects = append(embedFileObjects, job)
   301  		}
   302  
   303  		// Action ID jobs need to know the action ID of all the jobs the package
   304  		// imports.
   305  		var importedPackages []*compileJob
   306  		for _, imported := range pkg.Pkg.Imports() {
   307  			job, ok := packageActionIDJobs[imported.Path()]
   308  			if !ok {
   309  				return result, fmt.Errorf("package %s imports %s but couldn't find dependency", pkg.ImportPath, imported.Path())
   310  			}
   311  			importedPackages = append(importedPackages, job)
   312  			actionIDDependencies = append(actionIDDependencies, job)
   313  		}
   314  
   315  		// Create a job that will calculate the action ID for a package compile
   316  		// job. The action ID is the cache key that is used for caching this
   317  		// package.
   318  		packageActionIDJob := &compileJob{
   319  			description:  "calculate cache key for package " + pkg.ImportPath,
   320  			dependencies: actionIDDependencies,
   321  			run: func(job *compileJob) error {
   322  				// Create a cache key: a hash from the action ID below that contains all
   323  				// the parameters for the build.
   324  				actionID := packageAction{
   325  					ImportPath:       pkg.ImportPath,
   326  					CompilerBuildID:  string(compilerBuildID),
   327  					LLVMVersion:      llvm.Version,
   328  					Config:           compilerConfig,
   329  					CFlags:           pkg.CFlags,
   330  					FileHashes:       make(map[string]string, len(pkg.FileHashes)),
   331  					EmbeddedFiles:    make(map[string]string, len(allFiles)),
   332  					Imports:          make(map[string]string, len(pkg.Pkg.Imports())),
   333  					OptLevel:         optLevel,
   334  					UndefinedGlobals: undefinedGlobals,
   335  				}
   336  				for filePath, hash := range pkg.FileHashes {
   337  					actionID.FileHashes[filePath] = hex.EncodeToString(hash)
   338  				}
   339  				for name, files := range allFiles {
   340  					actionID.EmbeddedFiles[name] = files[0].Hash
   341  				}
   342  				for i, imported := range pkg.Pkg.Imports() {
   343  					actionID.Imports[imported.Path()] = importedPackages[i].result
   344  				}
   345  				buf, err := json.Marshal(actionID)
   346  				if err != nil {
   347  					return err // shouldn't happen
   348  				}
   349  				hash := sha512.Sum512_224(buf)
   350  				job.result = hex.EncodeToString(hash[:])
   351  				return nil
   352  			},
   353  		}
   354  		packageActionIDJobs[pkg.ImportPath] = packageActionIDJob
   355  
   356  		// Build the SSA for the given package.
   357  		ssaPkg := program.Package(pkg.Pkg)
   358  		ssaPkg.Build()
   359  
   360  		// Now create the job to actually build the package. It will exit early
   361  		// if the package is already compiled.
   362  		job := &compileJob{
   363  			description:  "compile package " + pkg.ImportPath,
   364  			dependencies: []*compileJob{packageActionIDJob},
   365  			run: func(job *compileJob) error {
   366  				job.result = filepath.Join(cacheDir, "pkg-"+packageActionIDJob.result+".bc")
   367  				// Acquire a lock (if supported).
   368  				unlock := lock(job.result + ".lock")
   369  				defer unlock()
   370  
   371  				if _, err := os.Stat(job.result); err == nil {
   372  					// Already cached, don't recreate this package.
   373  					return nil
   374  				}
   375  
   376  				// Compile AST to IR. The compiler.CompilePackage function will
   377  				// build the SSA as needed.
   378  				mod, errs := compiler.CompilePackage(pkg.ImportPath, pkg, program.Package(pkg.Pkg), machine, compilerConfig, config.DumpSSA())
   379  				defer mod.Context().Dispose()
   380  				defer mod.Dispose()
   381  				if errs != nil {
   382  					return newMultiError(errs)
   383  				}
   384  				if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
   385  					return errors.New("verification error after compiling package " + pkg.ImportPath)
   386  				}
   387  
   388  				// Load bitcode of CGo headers and join the modules together.
   389  				// This may seem vulnerable to cache problems, but this is not
   390  				// the case: the Go code that was just compiled already tracks
   391  				// all C files that are read and hashes them.
   392  				// These headers could be compiled in parallel but the benefit
   393  				// is so small that it's probably not worth parallelizing.
   394  				// Packages are compiled independently anyway.
   395  				for _, cgoHeader := range pkg.CGoHeaders {
   396  					// Store the header text in a temporary file.
   397  					f, err := os.CreateTemp(tmpdir, "cgosnippet-*.c")
   398  					if err != nil {
   399  						return err
   400  					}
   401  					_, err = f.Write([]byte(cgoHeader))
   402  					if err != nil {
   403  						return err
   404  					}
   405  					f.Close()
   406  
   407  					// Compile the code (if there is any) to bitcode.
   408  					flags := append([]string{"-c", "-emit-llvm", "-o", f.Name() + ".bc", f.Name()}, pkg.CFlags...)
   409  					if config.Options.PrintCommands != nil {
   410  						config.Options.PrintCommands("clang", flags...)
   411  					}
   412  					err = runCCompiler(flags...)
   413  					if err != nil {
   414  						return &commandError{"failed to build CGo header", "", err}
   415  					}
   416  
   417  					// Load and link the bitcode.
   418  					// This makes it possible to optimize the functions defined
   419  					// in the header together with the Go code. In particular,
   420  					// this allows inlining. It also ensures there is only one
   421  					// file per package to cache.
   422  					headerMod, err := mod.Context().ParseBitcodeFile(f.Name() + ".bc")
   423  					if err != nil {
   424  						return fmt.Errorf("failed to load bitcode file: %w", err)
   425  					}
   426  					err = llvm.LinkModules(mod, headerMod)
   427  					if err != nil {
   428  						return fmt.Errorf("failed to link module: %w", err)
   429  					}
   430  				}
   431  
   432  				// Erase all globals that are part of the undefinedGlobals list.
   433  				// This list comes from the -ldflags="-X pkg.foo=val" option.
   434  				// Instead of setting the value directly in the AST (which would
   435  				// mean the value, which may be a secret, is stored in the build
   436  				// cache), the global itself is left external (undefined) and is
   437  				// only set at the end of the compilation.
   438  				for _, name := range undefinedGlobals {
   439  					globalName := pkg.Pkg.Path() + "." + name
   440  					global := mod.NamedGlobal(globalName)
   441  					if global.IsNil() {
   442  						return errors.New("global not found: " + globalName)
   443  					}
   444  					name := global.Name()
   445  					newGlobal := llvm.AddGlobal(mod, global.GlobalValueType(), name+".tmp")
   446  					global.ReplaceAllUsesWith(newGlobal)
   447  					global.EraseFromParentAsGlobal()
   448  					newGlobal.SetName(name)
   449  				}
   450  
   451  				// Try to interpret package initializers at compile time.
   452  				// It may only be possible to do this partially, in which case
   453  				// it is completed after all IR files are linked.
   454  				pkgInit := mod.NamedFunction(pkg.Pkg.Path() + ".init")
   455  				if pkgInit.IsNil() {
   456  					panic("init not found for " + pkg.Pkg.Path())
   457  				}
   458  				err := interp.RunFunc(pkgInit, config.Options.InterpTimeout, config.DumpSSA())
   459  				if err != nil {
   460  					return err
   461  				}
   462  				if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
   463  					return errors.New("verification error after interpreting " + pkgInit.Name())
   464  				}
   465  
   466  				transform.OptimizePackage(mod, config)
   467  
   468  				// Serialize the LLVM module as a bitcode file.
   469  				// Write to a temporary path that is renamed to the destination
   470  				// file to avoid race conditions with other TinyGo invocatiosn
   471  				// that might also be compiling this package at the same time.
   472  				f, err := os.CreateTemp(filepath.Dir(job.result), filepath.Base(job.result))
   473  				if err != nil {
   474  					return err
   475  				}
   476  				if runtime.GOOS == "windows" {
   477  					// Work around a problem on Windows.
   478  					// For some reason, WriteBitcodeToFile causes TinyGo to
   479  					// exit with the following message:
   480  					//   LLVM ERROR: IO failure on output stream: Bad file descriptor
   481  					buf := llvm.WriteBitcodeToMemoryBuffer(mod)
   482  					defer buf.Dispose()
   483  					_, err = f.Write(buf.Bytes())
   484  				} else {
   485  					// Otherwise, write bitcode directly to the file (probably
   486  					// faster).
   487  					err = llvm.WriteBitcodeToFile(mod, f)
   488  				}
   489  				if err != nil {
   490  					// WriteBitcodeToFile doesn't produce a useful error on its
   491  					// own, so create a somewhat useful error message here.
   492  					return fmt.Errorf("failed to write bitcode for package %s to file %s", pkg.ImportPath, job.result)
   493  				}
   494  				err = f.Close()
   495  				if err != nil {
   496  					return err
   497  				}
   498  				return os.Rename(f.Name(), job.result)
   499  			},
   500  		}
   501  		packageJobs = append(packageJobs, job)
   502  	}
   503  
   504  	// Add job that links and optimizes all packages together.
   505  	var mod llvm.Module
   506  	defer func() {
   507  		if !mod.IsNil() {
   508  			ctx := mod.Context()
   509  			mod.Dispose()
   510  			ctx.Dispose()
   511  		}
   512  	}()
   513  	var stackSizeLoads []string
   514  	programJob := &compileJob{
   515  		description:  "link+optimize packages (LTO)",
   516  		dependencies: packageJobs,
   517  		run: func(*compileJob) error {
   518  			// Load and link all the bitcode files. This does not yet optimize
   519  			// anything, it only links the bitcode files together.
   520  			ctx := llvm.NewContext()
   521  			mod = ctx.NewModule("main")
   522  			for _, pkgJob := range packageJobs {
   523  				pkgMod, err := ctx.ParseBitcodeFile(pkgJob.result)
   524  				if err != nil {
   525  					return fmt.Errorf("failed to load bitcode file: %w", err)
   526  				}
   527  				err = llvm.LinkModules(mod, pkgMod)
   528  				if err != nil {
   529  					return fmt.Errorf("failed to link module: %w", err)
   530  				}
   531  			}
   532  
   533  			// Create runtime.initAll function that calls the runtime
   534  			// initializer of each package.
   535  			llvmInitFn := mod.NamedFunction("runtime.initAll")
   536  			llvmInitFn.SetLinkage(llvm.InternalLinkage)
   537  			llvmInitFn.SetUnnamedAddr(true)
   538  			transform.AddStandardAttributes(llvmInitFn, config)
   539  			llvmInitFn.Param(0).SetName("context")
   540  			block := mod.Context().AddBasicBlock(llvmInitFn, "entry")
   541  			irbuilder := mod.Context().NewBuilder()
   542  			defer irbuilder.Dispose()
   543  			irbuilder.SetInsertPointAtEnd(block)
   544  			ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
   545  			for _, pkg := range lprogram.Sorted() {
   546  				pkgInit := mod.NamedFunction(pkg.Pkg.Path() + ".init")
   547  				if pkgInit.IsNil() {
   548  					panic("init not found for " + pkg.Pkg.Path())
   549  				}
   550  				irbuilder.CreateCall(pkgInit.GlobalValueType(), pkgInit, []llvm.Value{llvm.Undef(ptrType)}, "")
   551  			}
   552  			irbuilder.CreateRetVoid()
   553  
   554  			// After linking, functions should (as far as possible) be set to
   555  			// private linkage or internal linkage. The compiler package marks
   556  			// non-exported functions by setting the visibility to hidden or
   557  			// (for thunks) to linkonce_odr linkage. Change the linkage here to
   558  			// internal to benefit much more from interprocedural optimizations.
   559  			for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
   560  				if fn.Visibility() == llvm.HiddenVisibility {
   561  					fn.SetVisibility(llvm.DefaultVisibility)
   562  					fn.SetLinkage(llvm.InternalLinkage)
   563  				} else if fn.Linkage() == llvm.LinkOnceODRLinkage {
   564  					fn.SetLinkage(llvm.InternalLinkage)
   565  				}
   566  			}
   567  
   568  			// Do the same for globals.
   569  			for global := mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
   570  				if global.Visibility() == llvm.HiddenVisibility {
   571  					global.SetVisibility(llvm.DefaultVisibility)
   572  					global.SetLinkage(llvm.InternalLinkage)
   573  				} else if global.Linkage() == llvm.LinkOnceODRLinkage {
   574  					global.SetLinkage(llvm.InternalLinkage)
   575  				}
   576  			}
   577  
   578  			if config.Options.PrintIR {
   579  				fmt.Println("; Generated LLVM IR:")
   580  				fmt.Println(mod.String())
   581  			}
   582  
   583  			// Run all optimization passes, which are much more effective now
   584  			// that the optimizer can see the whole program at once.
   585  			err := optimizeProgram(mod, config, globalValues)
   586  			if err != nil {
   587  				return err
   588  			}
   589  
   590  			// Make sure stack sizes are loaded from a separate section so they can be
   591  			// modified after linking.
   592  			if config.AutomaticStackSize() {
   593  				stackSizeLoads = transform.CreateStackSizeLoads(mod, config)
   594  			}
   595  			return nil
   596  		},
   597  	}
   598  
   599  	// Check whether we only need to create an object file.
   600  	// If so, we don't need to link anything and will be finished quickly.
   601  	outext := filepath.Ext(outpath)
   602  	if outext == ".o" || outext == ".bc" || outext == ".ll" {
   603  		// Run jobs to produce the LLVM module.
   604  		err := runJobs(programJob, config.Options.Semaphore)
   605  		if err != nil {
   606  			return result, err
   607  		}
   608  		// Generate output.
   609  		switch outext {
   610  		case ".o":
   611  			llvmBuf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
   612  			if err != nil {
   613  				return result, err
   614  			}
   615  			defer llvmBuf.Dispose()
   616  			return result, os.WriteFile(outpath, llvmBuf.Bytes(), 0666)
   617  		case ".bc":
   618  			buf := llvm.WriteThinLTOBitcodeToMemoryBuffer(mod)
   619  			defer buf.Dispose()
   620  			return result, os.WriteFile(outpath, buf.Bytes(), 0666)
   621  		case ".ll":
   622  			data := []byte(mod.String())
   623  			return result, os.WriteFile(outpath, data, 0666)
   624  		default:
   625  			panic("unreachable")
   626  		}
   627  	}
   628  
   629  	// Act as a compiler driver, as we need to produce a complete executable.
   630  	// First add all jobs necessary to build this object file, then afterwards
   631  	// run all jobs in parallel as far as possible.
   632  
   633  	// Add job to write the output object file.
   634  	objfile := filepath.Join(tmpdir, "main.o")
   635  	outputObjectFileJob := &compileJob{
   636  		description:  "generate output file",
   637  		dependencies: []*compileJob{programJob},
   638  		result:       objfile,
   639  		run: func(*compileJob) error {
   640  			llvmBuf := llvm.WriteThinLTOBitcodeToMemoryBuffer(mod)
   641  			defer llvmBuf.Dispose()
   642  			return os.WriteFile(objfile, llvmBuf.Bytes(), 0666)
   643  		},
   644  	}
   645  
   646  	// Prepare link command.
   647  	linkerDependencies := []*compileJob{outputObjectFileJob}
   648  	result.Executable = filepath.Join(tmpdir, "main")
   649  	if config.GOOS() == "windows" {
   650  		result.Executable += ".exe"
   651  	}
   652  	result.Binary = result.Executable // final file
   653  	ldflags := append(config.LDFlags(), "-o", result.Executable)
   654  
   655  	// Add compiler-rt dependency if needed. Usually this is a simple load from
   656  	// a cache.
   657  	if config.Target.RTLib == "compiler-rt" {
   658  		job, unlock, err := CompilerRT.load(config, tmpdir)
   659  		if err != nil {
   660  			return result, err
   661  		}
   662  		defer unlock()
   663  		linkerDependencies = append(linkerDependencies, job)
   664  	}
   665  
   666  	// Add jobs to compile extra files. These files are in C or assembly and
   667  	// contain things like the interrupt vector table and low level operations
   668  	// such as stack switching.
   669  	for _, path := range config.ExtraFiles() {
   670  		abspath := filepath.Join(root, path)
   671  		job := &compileJob{
   672  			description: "compile extra file " + path,
   673  			run: func(job *compileJob) error {
   674  				result, err := compileAndCacheCFile(abspath, tmpdir, config.CFlags(false), config.Options.PrintCommands)
   675  				job.result = result
   676  				return err
   677  			},
   678  		}
   679  		linkerDependencies = append(linkerDependencies, job)
   680  	}
   681  
   682  	// Add jobs to compile C files in all packages. This is part of CGo.
   683  	// TODO: do this as part of building the package to be able to link the
   684  	// bitcode files together.
   685  	for _, pkg := range lprogram.Sorted() {
   686  		pkg := pkg
   687  		for _, filename := range pkg.CFiles {
   688  			abspath := filepath.Join(pkg.Dir, filename)
   689  			job := &compileJob{
   690  				description: "compile CGo file " + abspath,
   691  				run: func(job *compileJob) error {
   692  					result, err := compileAndCacheCFile(abspath, tmpdir, pkg.CFlags, config.Options.PrintCommands)
   693  					job.result = result
   694  					return err
   695  				},
   696  			}
   697  			linkerDependencies = append(linkerDependencies, job)
   698  		}
   699  	}
   700  
   701  	// Linker flags from CGo lines:
   702  	//     #cgo LDFLAGS: foo
   703  	if len(lprogram.LDFlags) > 0 {
   704  		ldflags = append(ldflags, lprogram.LDFlags...)
   705  	}
   706  
   707  	// Add libc dependencies, if they exist.
   708  	linkerDependencies = append(linkerDependencies, libcDependencies...)
   709  
   710  	// Add embedded files.
   711  	linkerDependencies = append(linkerDependencies, embedFileObjects...)
   712  
   713  	// Determine whether the compilation configuration would result in debug
   714  	// (DWARF) information in the object files.
   715  	var hasDebug = true
   716  	if config.GOOS() == "darwin" {
   717  		// Debug information isn't stored in the binary itself on MacOS but
   718  		// is left in the object files by default. The binary does store the
   719  		// path to these object files though.
   720  		hasDebug = false
   721  	}
   722  
   723  	// Strip debug information with -no-debug.
   724  	if hasDebug && !config.Debug() {
   725  		if config.Target.Linker == "wasm-ld" {
   726  			// Don't just strip debug information, also compress relocations
   727  			// while we're at it. Relocations can only be compressed when debug
   728  			// information is stripped.
   729  			ldflags = append(ldflags, "--strip-debug", "--compress-relocations")
   730  		} else if config.Target.Linker == "ld.lld" {
   731  			// ld.lld is also used on Linux.
   732  			ldflags = append(ldflags, "--strip-debug")
   733  		} else {
   734  			// Other linkers may have different flags.
   735  			return result, errors.New("cannot remove debug information: unknown linker: " + config.Target.Linker)
   736  		}
   737  	}
   738  
   739  	// Create a linker job, which links all object files together and does some
   740  	// extra stuff that can only be done after linking.
   741  	linkJob := &compileJob{
   742  		description:  "link",
   743  		dependencies: linkerDependencies,
   744  		run: func(job *compileJob) error {
   745  			for _, dependency := range job.dependencies {
   746  				if dependency.result == "" {
   747  					return errors.New("dependency without result: " + dependency.description)
   748  				}
   749  				ldflags = append(ldflags, dependency.result)
   750  			}
   751  			ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
   752  			if config.GOOS() == "windows" {
   753  				// Options for the MinGW wrapper for the lld COFF linker.
   754  				ldflags = append(ldflags,
   755  					"-Xlink=/opt:lldlto="+strconv.Itoa(speedLevel),
   756  					"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"))
   757  			} else if config.GOOS() == "darwin" {
   758  				// Options for the ld64-compatible lld linker.
   759  				ldflags = append(ldflags,
   760  					"--lto-O"+strconv.Itoa(speedLevel),
   761  					"-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
   762  			} else {
   763  				// Options for the ELF linker.
   764  				ldflags = append(ldflags,
   765  					"--lto-O"+strconv.Itoa(speedLevel),
   766  					"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
   767  				)
   768  			}
   769  			if config.CodeModel() != "default" {
   770  				ldflags = append(ldflags,
   771  					"-mllvm", "-code-model="+config.CodeModel())
   772  			}
   773  			if sizeLevel >= 2 {
   774  				// Workaround with roughly the same effect as
   775  				// https://reviews.llvm.org/D119342.
   776  				// Can hopefully be removed in LLVM 18.
   777  				ldflags = append(ldflags,
   778  					"-mllvm", "--rotation-max-header-size=0")
   779  			}
   780  			if config.Options.PrintCommands != nil {
   781  				config.Options.PrintCommands(config.Target.Linker, ldflags...)
   782  			}
   783  			err = link(config.Target.Linker, ldflags...)
   784  			if err != nil {
   785  				return &commandError{"failed to link", result.Executable, err}
   786  			}
   787  
   788  			var calculatedStacks []string
   789  			var stackSizes map[string]functionStackSize
   790  			if config.Options.PrintStacks || config.AutomaticStackSize() {
   791  				// Try to determine stack sizes at compile time.
   792  				// Don't do this by default as it usually doesn't work on
   793  				// unsupported architectures.
   794  				calculatedStacks, stackSizes, err = determineStackSizes(mod, result.Executable)
   795  				if err != nil {
   796  					return err
   797  				}
   798  			}
   799  
   800  			// Apply ELF patches
   801  			if config.AutomaticStackSize() {
   802  				// Modify the .tinygo_stacksizes section that contains a stack size
   803  				// for each goroutine.
   804  				err = modifyStackSizes(result.Executable, stackSizeLoads, stackSizes)
   805  				if err != nil {
   806  					return fmt.Errorf("could not modify stack sizes: %w", err)
   807  				}
   808  			}
   809  			if config.RP2040BootPatch() {
   810  				// Patch the second stage bootloader CRC into the .boot2 section
   811  				err = patchRP2040BootCRC(result.Executable)
   812  				if err != nil {
   813  					return fmt.Errorf("could not patch RP2040 second stage boot loader: %w", err)
   814  				}
   815  			}
   816  
   817  			// Run wasm-opt for wasm binaries
   818  			if arch := strings.Split(config.Triple(), "-")[0]; arch == "wasm32" {
   819  				optLevel, _, _ := config.OptLevel()
   820  				opt := "-" + optLevel
   821  
   822  				var args []string
   823  
   824  				if config.Scheduler() == "asyncify" {
   825  					args = append(args, "--asyncify")
   826  				}
   827  
   828  				args = append(args,
   829  					opt,
   830  					"-g",
   831  					result.Executable,
   832  					"--output", result.Executable,
   833  				)
   834  
   835  				cmd := exec.Command(goenv.Get("WASMOPT"), args...)
   836  				cmd.Stdout = os.Stdout
   837  				cmd.Stderr = os.Stderr
   838  
   839  				err := cmd.Run()
   840  				if err != nil {
   841  					return fmt.Errorf("wasm-opt failed: %w", err)
   842  				}
   843  			}
   844  
   845  			// Print code size if requested.
   846  			if config.Options.PrintSizes == "short" || config.Options.PrintSizes == "full" {
   847  				packagePathMap := make(map[string]string, len(lprogram.Packages))
   848  				for _, pkg := range lprogram.Sorted() {
   849  					packagePathMap[pkg.OriginalDir()] = pkg.Pkg.Path()
   850  				}
   851  				sizes, err := loadProgramSize(result.Executable, packagePathMap)
   852  				if err != nil {
   853  					return err
   854  				}
   855  				if config.Options.PrintSizes == "short" {
   856  					fmt.Printf("   code    data     bss |   flash     ram\n")
   857  					fmt.Printf("%7d %7d %7d | %7d %7d\n", sizes.Code+sizes.ROData, sizes.Data, sizes.BSS, sizes.Flash(), sizes.RAM())
   858  				} else {
   859  					if !config.Debug() {
   860  						fmt.Println("warning: data incomplete, remove the -no-debug flag for more detail")
   861  					}
   862  					fmt.Printf("   code  rodata    data     bss |   flash     ram | package\n")
   863  					fmt.Printf("------------------------------- | --------------- | -------\n")
   864  					for _, name := range sizes.sortedPackageNames() {
   865  						pkgSize := sizes.Packages[name]
   866  						fmt.Printf("%7d %7d %7d %7d | %7d %7d | %s\n", pkgSize.Code, pkgSize.ROData, pkgSize.Data, pkgSize.BSS, pkgSize.Flash(), pkgSize.RAM(), name)
   867  					}
   868  					fmt.Printf("------------------------------- | --------------- | -------\n")
   869  					fmt.Printf("%7d %7d %7d %7d | %7d %7d | total\n", sizes.Code, sizes.ROData, sizes.Data, sizes.BSS, sizes.Code+sizes.ROData+sizes.Data, sizes.Data+sizes.BSS)
   870  				}
   871  			}
   872  
   873  			// Print goroutine stack sizes, as far as possible.
   874  			if config.Options.PrintStacks {
   875  				printStacks(calculatedStacks, stackSizes)
   876  			}
   877  
   878  			return nil
   879  		},
   880  	}
   881  
   882  	// Run all jobs to compile and link the program.
   883  	// Do this now (instead of after elf-to-hex and similar conversions) as it
   884  	// is simpler and cannot be parallelized.
   885  	err = runJobs(linkJob, config.Options.Semaphore)
   886  	if err != nil {
   887  		return result, err
   888  	}
   889  
   890  	// Get an Intel .hex file or .bin file from the .elf file.
   891  	outputBinaryFormat := config.BinaryFormat(outext)
   892  	switch outputBinaryFormat {
   893  	case "elf":
   894  		// do nothing, file is already in ELF format
   895  	case "hex", "bin":
   896  		// Extract raw binary, either encoding it as a hex file or as a raw
   897  		// firmware file.
   898  		result.Binary = filepath.Join(tmpdir, "main"+outext)
   899  		err := objcopy(result.Executable, result.Binary, outputBinaryFormat)
   900  		if err != nil {
   901  			return result, err
   902  		}
   903  	case "uf2":
   904  		// Get UF2 from the .elf file.
   905  		result.Binary = filepath.Join(tmpdir, "main"+outext)
   906  		err := convertELFFileToUF2File(result.Executable, result.Binary, config.Target.UF2FamilyID)
   907  		if err != nil {
   908  			return result, err
   909  		}
   910  	case "esp32", "esp32-img", "esp32c3", "esp8266":
   911  		// Special format for the ESP family of chips (parsed by the ROM
   912  		// bootloader).
   913  		result.Binary = filepath.Join(tmpdir, "main"+outext)
   914  		err := makeESPFirmareImage(result.Executable, result.Binary, outputBinaryFormat)
   915  		if err != nil {
   916  			return result, err
   917  		}
   918  	case "nrf-dfu":
   919  		// special format for nrfutil for Nordic chips
   920  		result.Binary = filepath.Join(tmpdir, "main"+outext)
   921  		err = makeDFUFirmwareImage(config.Options, result.Executable, result.Binary)
   922  		if err != nil {
   923  			return result, err
   924  		}
   925  	default:
   926  		return result, fmt.Errorf("unknown output binary format: %s", outputBinaryFormat)
   927  	}
   928  
   929  	return result, nil
   930  }
   931  
   932  // createEmbedObjectFile creates a new object file with the given contents, for
   933  // the embed package.
   934  func createEmbedObjectFile(data, hexSum, sourceFile, sourceDir, tmpdir string, compilerConfig *compiler.Config) (string, error) {
   935  	// TODO: this works for small files, but can be a problem for larger files.
   936  	// For larger files, it seems more appropriate to generate the object file
   937  	// manually without going through LLVM.
   938  	// On the other hand, generating DWARF like we do here can be difficult
   939  	// without assistance from LLVM.
   940  
   941  	// Create new LLVM module just for this file.
   942  	ctx := llvm.NewContext()
   943  	defer ctx.Dispose()
   944  	mod := ctx.NewModule("data")
   945  	defer mod.Dispose()
   946  
   947  	// Create data global.
   948  	value := ctx.ConstString(data, false)
   949  	globalName := "embed/file_" + hexSum
   950  	global := llvm.AddGlobal(mod, value.Type(), globalName)
   951  	global.SetInitializer(value)
   952  	global.SetLinkage(llvm.LinkOnceODRLinkage)
   953  	global.SetGlobalConstant(true)
   954  	global.SetUnnamedAddr(true)
   955  	global.SetAlignment(1)
   956  	if compilerConfig.GOOS != "darwin" {
   957  		// MachO doesn't support COMDATs, while COFF requires it (to avoid
   958  		// "duplicate symbol" errors). ELF works either way.
   959  		// Therefore, only use a COMDAT on non-MachO systems (aka non-MacOS).
   960  		global.SetComdat(mod.Comdat(globalName))
   961  	}
   962  
   963  	// Add DWARF debug information to this global, so that it is
   964  	// correctly counted when compiling with the -size= flag.
   965  	dibuilder := llvm.NewDIBuilder(mod)
   966  	dibuilder.CreateCompileUnit(llvm.DICompileUnit{
   967  		Language:  0xb, // DW_LANG_C99 (0xc, off-by-one?)
   968  		File:      sourceFile,
   969  		Dir:       sourceDir,
   970  		Producer:  "TinyGo",
   971  		Optimized: false,
   972  	})
   973  	ditype := dibuilder.CreateArrayType(llvm.DIArrayType{
   974  		SizeInBits:  uint64(len(data)) * 8,
   975  		AlignInBits: 8,
   976  		ElementType: dibuilder.CreateBasicType(llvm.DIBasicType{
   977  			Name:       "byte",
   978  			SizeInBits: 8,
   979  			Encoding:   llvm.DW_ATE_unsigned_char,
   980  		}),
   981  		Subscripts: []llvm.DISubrange{
   982  			{
   983  				Lo:    0,
   984  				Count: int64(len(data)),
   985  			},
   986  		},
   987  	})
   988  	difile := dibuilder.CreateFile(sourceFile, sourceDir)
   989  	diglobalexpr := dibuilder.CreateGlobalVariableExpression(difile, llvm.DIGlobalVariableExpression{
   990  		Name:        globalName,
   991  		File:        difile,
   992  		Line:        1,
   993  		Type:        ditype,
   994  		Expr:        dibuilder.CreateExpression(nil),
   995  		AlignInBits: 8,
   996  	})
   997  	global.AddMetadata(0, diglobalexpr)
   998  	mod.AddNamedMetadataOperand("llvm.module.flags",
   999  		ctx.MDNode([]llvm.Metadata{
  1000  			llvm.ConstInt(ctx.Int32Type(), 2, false).ConstantAsMetadata(), // Warning on mismatch
  1001  			ctx.MDString("Debug Info Version"),
  1002  			llvm.ConstInt(ctx.Int32Type(), 3, false).ConstantAsMetadata(),
  1003  		}),
  1004  	)
  1005  	mod.AddNamedMetadataOperand("llvm.module.flags",
  1006  		ctx.MDNode([]llvm.Metadata{
  1007  			llvm.ConstInt(ctx.Int32Type(), 7, false).ConstantAsMetadata(), // Max on mismatch
  1008  			ctx.MDString("Dwarf Version"),
  1009  			llvm.ConstInt(ctx.Int32Type(), 4, false).ConstantAsMetadata(),
  1010  		}),
  1011  	)
  1012  	dibuilder.Finalize()
  1013  	dibuilder.Destroy()
  1014  
  1015  	// Write this LLVM module out as an object file.
  1016  	machine, err := compiler.NewTargetMachine(compilerConfig)
  1017  	if err != nil {
  1018  		return "", err
  1019  	}
  1020  	defer machine.Dispose()
  1021  	outfile, err := os.CreateTemp(tmpdir, "embed-"+hexSum+"-*.o")
  1022  	if err != nil {
  1023  		return "", err
  1024  	}
  1025  	defer outfile.Close()
  1026  	buf, err := machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
  1027  	if err != nil {
  1028  		return "", err
  1029  	}
  1030  	defer buf.Dispose()
  1031  	_, err = outfile.Write(buf.Bytes())
  1032  	if err != nil {
  1033  		return "", err
  1034  	}
  1035  	return outfile.Name(), outfile.Close()
  1036  }
  1037  
  1038  // optimizeProgram runs a series of optimizations and transformations that are
  1039  // needed to convert a program to its final form. Some transformations are not
  1040  // optional and must be run as the compiler expects them to run.
  1041  func optimizeProgram(mod llvm.Module, config *compileopts.Config, globalValues map[string]map[string]string) error {
  1042  	err := interp.Run(mod, config.Options.InterpTimeout, config.DumpSSA())
  1043  	if err != nil {
  1044  		return err
  1045  	}
  1046  	if config.VerifyIR() {
  1047  		// Only verify if we really need it.
  1048  		// The IR has already been verified before writing the bitcode to disk
  1049  		// and the interp function above doesn't need to do a lot as most of the
  1050  		// package initializers have already run. Additionally, verifying this
  1051  		// linked IR is _expensive_ because dead code hasn't been removed yet,
  1052  		// easily costing a few hundred milliseconds. Therefore, only do it when
  1053  		// specifically requested.
  1054  		if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
  1055  			return errors.New("verification error after interpreting runtime.initAll")
  1056  		}
  1057  	}
  1058  
  1059  	// Insert values from -ldflags="-X ..." into the IR.
  1060  	err = setGlobalValues(mod, globalValues)
  1061  	if err != nil {
  1062  		return err
  1063  	}
  1064  
  1065  	// Run most of the whole-program optimizations (including the whole
  1066  	// O0/O1/O2/Os/Oz optimization pipeline).
  1067  	errs := transform.Optimize(mod, config)
  1068  	if len(errs) > 0 {
  1069  		return newMultiError(errs)
  1070  	}
  1071  	if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
  1072  		return errors.New("verification failure after LLVM optimization passes")
  1073  	}
  1074  
  1075  	return nil
  1076  }
  1077  
  1078  // setGlobalValues sets the global values from the -ldflags="-X ..." compiler
  1079  // option in the given module. An error may be returned if the global is not of
  1080  // the expected type.
  1081  func setGlobalValues(mod llvm.Module, globals map[string]map[string]string) error {
  1082  	var pkgPaths []string
  1083  	for pkgPath := range globals {
  1084  		pkgPaths = append(pkgPaths, pkgPath)
  1085  	}
  1086  	sort.Strings(pkgPaths)
  1087  	for _, pkgPath := range pkgPaths {
  1088  		pkg := globals[pkgPath]
  1089  		var names []string
  1090  		for name := range pkg {
  1091  			names = append(names, name)
  1092  		}
  1093  		sort.Strings(names)
  1094  		for _, name := range names {
  1095  			value := pkg[name]
  1096  			globalName := pkgPath + "." + name
  1097  			global := mod.NamedGlobal(globalName)
  1098  			if global.IsNil() || !global.Initializer().IsNil() {
  1099  				// The global either does not exist (optimized away?) or has
  1100  				// some value, in which case it has already been initialized at
  1101  				// package init time.
  1102  				continue
  1103  			}
  1104  
  1105  			// A strin is a {ptr, len} pair. We need these types to build the
  1106  			// initializer.
  1107  			initializerType := global.GlobalValueType()
  1108  			if initializerType.TypeKind() != llvm.StructTypeKind || initializerType.StructName() == "" {
  1109  				return fmt.Errorf("%s: not a string", globalName)
  1110  			}
  1111  			elementTypes := initializerType.StructElementTypes()
  1112  			if len(elementTypes) != 2 {
  1113  				return fmt.Errorf("%s: not a string", globalName)
  1114  			}
  1115  
  1116  			// Create a buffer for the string contents.
  1117  			bufInitializer := mod.Context().ConstString(value, false)
  1118  			buf := llvm.AddGlobal(mod, bufInitializer.Type(), ".string")
  1119  			buf.SetInitializer(bufInitializer)
  1120  			buf.SetAlignment(1)
  1121  			buf.SetUnnamedAddr(true)
  1122  			buf.SetLinkage(llvm.PrivateLinkage)
  1123  
  1124  			// Create the string value, which is a {ptr, len} pair.
  1125  			zero := llvm.ConstInt(mod.Context().Int32Type(), 0, false)
  1126  			ptr := llvm.ConstGEP(bufInitializer.Type(), buf, []llvm.Value{zero, zero})
  1127  			if ptr.Type() != elementTypes[0] {
  1128  				return fmt.Errorf("%s: not a string", globalName)
  1129  			}
  1130  			length := llvm.ConstInt(elementTypes[1], uint64(len(value)), false)
  1131  			initializer := llvm.ConstNamedStruct(initializerType, []llvm.Value{
  1132  				ptr,
  1133  				length,
  1134  			})
  1135  
  1136  			// Set the initializer. No initializer should be set at this point.
  1137  			global.SetInitializer(initializer)
  1138  		}
  1139  	}
  1140  	return nil
  1141  }
  1142  
  1143  // functionStackSizes keeps stack size information about a single function
  1144  // (usually a goroutine).
  1145  type functionStackSize struct {
  1146  	humanName        string
  1147  	stackSize        uint64
  1148  	stackSizeType    stacksize.SizeType
  1149  	missingStackSize *stacksize.CallNode
  1150  }
  1151  
  1152  // determineStackSizes tries to determine the stack sizes of all started
  1153  // goroutines and of the reset vector. The LLVM module is necessary to find
  1154  // functions that call a function pointer.
  1155  func determineStackSizes(mod llvm.Module, executable string) ([]string, map[string]functionStackSize, error) {
  1156  	var callsIndirectFunction []string
  1157  	gowrappers := []string{}
  1158  	gowrapperNames := make(map[string]string)
  1159  	for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
  1160  		// Determine which functions call a function pointer.
  1161  		for bb := fn.FirstBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
  1162  			for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
  1163  				if inst.IsACallInst().IsNil() {
  1164  					continue
  1165  				}
  1166  				if callee := inst.CalledValue(); callee.IsAFunction().IsNil() && callee.IsAInlineAsm().IsNil() {
  1167  					callsIndirectFunction = append(callsIndirectFunction, fn.Name())
  1168  				}
  1169  			}
  1170  		}
  1171  
  1172  		// Get a list of "go wrappers", small wrapper functions that decode
  1173  		// parameters when starting a new goroutine.
  1174  		attr := fn.GetStringAttributeAtIndex(-1, "tinygo-gowrapper")
  1175  		if !attr.IsNil() {
  1176  			gowrappers = append(gowrappers, fn.Name())
  1177  			gowrapperNames[fn.Name()] = attr.GetStringValue()
  1178  		}
  1179  	}
  1180  	sort.Strings(gowrappers)
  1181  
  1182  	// Load the ELF binary.
  1183  	f, err := elf.Open(executable)
  1184  	if err != nil {
  1185  		return nil, nil, fmt.Errorf("could not load executable for stack size analysis: %w", err)
  1186  	}
  1187  	defer f.Close()
  1188  
  1189  	// Determine the frame size of each function (if available) and the callgraph.
  1190  	functions, err := stacksize.CallGraph(f, callsIndirectFunction)
  1191  	if err != nil {
  1192  		return nil, nil, fmt.Errorf("could not parse executable for stack size analysis: %w", err)
  1193  	}
  1194  
  1195  	// Goroutines need to be started and finished and take up some stack space
  1196  	// that way. This can be measured by measuing the stack size of
  1197  	// tinygo_startTask.
  1198  	if numFuncs := len(functions["tinygo_startTask"]); numFuncs != 1 {
  1199  		return nil, nil, fmt.Errorf("expected exactly one definition of tinygo_startTask, got %d", numFuncs)
  1200  	}
  1201  	baseStackSize, baseStackSizeType, baseStackSizeFailedAt := functions["tinygo_startTask"][0].StackSize()
  1202  
  1203  	sizes := make(map[string]functionStackSize)
  1204  
  1205  	// Add the reset handler function, for convenience. The reset handler runs
  1206  	// startup code and the scheduler. The listed stack size is not the full
  1207  	// stack size: interrupts are not counted.
  1208  	var resetFunction string
  1209  	switch f.Machine {
  1210  	case elf.EM_ARM:
  1211  		// Note: all interrupts happen on this stack so the real size is bigger.
  1212  		resetFunction = "Reset_Handler"
  1213  	}
  1214  	if resetFunction != "" {
  1215  		funcs := functions[resetFunction]
  1216  		if len(funcs) != 1 {
  1217  			return nil, nil, fmt.Errorf("expected exactly one definition of %s in the callgraph, found %d", resetFunction, len(funcs))
  1218  		}
  1219  		stackSize, stackSizeType, missingStackSize := funcs[0].StackSize()
  1220  		sizes[resetFunction] = functionStackSize{
  1221  			stackSize:        stackSize,
  1222  			stackSizeType:    stackSizeType,
  1223  			missingStackSize: missingStackSize,
  1224  			humanName:        resetFunction,
  1225  		}
  1226  	}
  1227  
  1228  	// Add all goroutine wrapper functions.
  1229  	for _, name := range gowrappers {
  1230  		funcs := functions[name]
  1231  		if len(funcs) != 1 {
  1232  			return nil, nil, fmt.Errorf("expected exactly one definition of %s in the callgraph, found %d", name, len(funcs))
  1233  		}
  1234  		humanName := gowrapperNames[name]
  1235  		if humanName == "" {
  1236  			humanName = name // fallback
  1237  		}
  1238  		stackSize, stackSizeType, missingStackSize := funcs[0].StackSize()
  1239  		if baseStackSizeType != stacksize.Bounded {
  1240  			// It was not possible to determine the stack size at compile time
  1241  			// because tinygo_startTask does not have a fixed stack size. This
  1242  			// can happen when using -opt=1.
  1243  			stackSizeType = baseStackSizeType
  1244  			missingStackSize = baseStackSizeFailedAt
  1245  		} else if stackSize < baseStackSize {
  1246  			// This goroutine has a very small stack, but still needs to fit all
  1247  			// registers to start and suspend the goroutine. Otherwise a stack
  1248  			// overflow will occur even before the goroutine is started.
  1249  			stackSize = baseStackSize
  1250  		}
  1251  		sizes[name] = functionStackSize{
  1252  			stackSize:        stackSize,
  1253  			stackSizeType:    stackSizeType,
  1254  			missingStackSize: missingStackSize,
  1255  			humanName:        humanName,
  1256  		}
  1257  	}
  1258  
  1259  	if resetFunction != "" {
  1260  		return append([]string{resetFunction}, gowrappers...), sizes, nil
  1261  	}
  1262  	return gowrappers, sizes, nil
  1263  }
  1264  
  1265  // modifyStackSizes modifies the .tinygo_stacksizes section with the updated
  1266  // stack size information. Before this modification, all stack sizes in the
  1267  // section assume the default stack size (which is relatively big).
  1268  func modifyStackSizes(executable string, stackSizeLoads []string, stackSizes map[string]functionStackSize) error {
  1269  	data, fileHeader, err := getElfSectionData(executable, ".tinygo_stacksizes")
  1270  	if err != nil {
  1271  		return err
  1272  	}
  1273  
  1274  	if len(stackSizeLoads)*4 != len(data) {
  1275  		// Note: while AVR should use 2 byte stack sizes, even 64-bit platforms
  1276  		// should probably stick to 4 byte stack sizes as a larger than 4GB
  1277  		// stack doesn't make much sense.
  1278  		return errors.New("expected 4 byte stack sizes")
  1279  	}
  1280  
  1281  	// Modify goroutine stack sizes with a compile-time known worst case stack
  1282  	// size.
  1283  	for i, name := range stackSizeLoads {
  1284  		fn, ok := stackSizes[name]
  1285  		if !ok {
  1286  			return fmt.Errorf("could not find symbol %s in ELF file", name)
  1287  		}
  1288  		if fn.stackSizeType == stacksize.Bounded {
  1289  			stackSize := uint32(fn.stackSize)
  1290  
  1291  			// Add stack size used by interrupts.
  1292  			switch fileHeader.Machine {
  1293  			case elf.EM_ARM:
  1294  				if stackSize%8 != 0 {
  1295  					// If the stack isn't a multiple of 8, it means the leaf
  1296  					// function with the biggest stack depth doesn't have an aligned
  1297  					// stack. If the STKALIGN flag is set (which it is by default)
  1298  					// the interrupt controller will forcibly align the stack before
  1299  					// storing in-use registers. This will thus overwrite one word
  1300  					// past the end of the stack (off-by-one).
  1301  					stackSize += 4
  1302  				}
  1303  
  1304  				// On Cortex-M (assumed here), this stack size is 8 words or 32
  1305  				// bytes. This is only to store the registers that the interrupt
  1306  				// may modify, the interrupt will switch to the interrupt stack
  1307  				// (MSP).
  1308  				// Some background:
  1309  				// https://interrupt.memfault.com/blog/cortex-m-rtos-context-switching
  1310  				stackSize += 32
  1311  
  1312  				// Adding 4 for the stack canary, and another 4 to keep the
  1313  				// stack aligned. Even though the size may be automatically
  1314  				// determined, stack overflow checking is still important as the
  1315  				// stack size cannot be determined for all goroutines.
  1316  				stackSize += 8
  1317  			default:
  1318  				return fmt.Errorf("unknown architecture: %s", fileHeader.Machine.String())
  1319  			}
  1320  
  1321  			// Finally write the stack size to the binary.
  1322  			binary.LittleEndian.PutUint32(data[i*4:], stackSize)
  1323  		}
  1324  	}
  1325  
  1326  	return replaceElfSection(executable, ".tinygo_stacksizes", data)
  1327  }
  1328  
  1329  // printStacks prints the maximum stack depth for functions that are started as
  1330  // goroutines. Stack sizes cannot always be determined statically, in particular
  1331  // recursive functions and functions that call interface methods or function
  1332  // pointers may have an unknown stack depth (depending on what the optimizer
  1333  // manages to optimize away).
  1334  //
  1335  // It might print something like the following:
  1336  //
  1337  //	function                         stack usage (in bytes)
  1338  //	Reset_Handler                    316
  1339  //	examples/blinky2.led1            92
  1340  //	runtime.run$1                    300
  1341  func printStacks(calculatedStacks []string, stackSizes map[string]functionStackSize) {
  1342  	// Print the sizes of all stacks.
  1343  	fmt.Printf("%-32s %s\n", "function", "stack usage (in bytes)")
  1344  	for _, name := range calculatedStacks {
  1345  		fn := stackSizes[name]
  1346  		switch fn.stackSizeType {
  1347  		case stacksize.Bounded:
  1348  			fmt.Printf("%-32s %d\n", fn.humanName, fn.stackSize)
  1349  		case stacksize.Unknown:
  1350  			fmt.Printf("%-32s unknown, %s does not have stack frame information\n", fn.humanName, fn.missingStackSize)
  1351  		case stacksize.Recursive:
  1352  			fmt.Printf("%-32s recursive, %s may call itself\n", fn.humanName, fn.missingStackSize)
  1353  		case stacksize.IndirectCall:
  1354  			fmt.Printf("%-32s unknown, %s calls a function pointer\n", fn.humanName, fn.missingStackSize)
  1355  		}
  1356  	}
  1357  }
  1358  
  1359  // RP2040 second stage bootloader CRC32 calculation
  1360  //
  1361  // Spec: https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf
  1362  // Section: 2.8.1.3.1. Checksum
  1363  func patchRP2040BootCRC(executable string) error {
  1364  	bytes, _, err := getElfSectionData(executable, ".boot2")
  1365  	if err != nil {
  1366  		return err
  1367  	}
  1368  
  1369  	if len(bytes) != 256 {
  1370  		return fmt.Errorf("rp2040 .boot2 section must be exactly 256 bytes")
  1371  	}
  1372  
  1373  	// From the 'official' RP2040 checksum script:
  1374  	//
  1375  	//  Our bootrom CRC32 is slightly bass-ackward but it's
  1376  	//  best to work around for now (FIXME)
  1377  	//  100% worth it to save two Thumb instructions
  1378  	revBytes := make([]byte, len(bytes))
  1379  	for i := range bytes {
  1380  		revBytes[i] = bits.Reverse8(bytes[i])
  1381  	}
  1382  
  1383  	// crc32.Update does an initial negate and negates the
  1384  	// result, so to meet RP2040 spec, pass 0x0 as initial
  1385  	// hash and negate returned value.
  1386  	//
  1387  	// Note: checksum is over 252 bytes (256 - 4)
  1388  	hash := bits.Reverse32(crc32.Update(0x0, crc32.IEEETable, revBytes[:252]) ^ 0xFFFFFFFF)
  1389  
  1390  	// Write the CRC to the end of the bootloader.
  1391  	binary.LittleEndian.PutUint32(bytes[252:], hash)
  1392  
  1393  	// Update the .boot2 section to included the CRC
  1394  	return replaceElfSection(executable, ".boot2", bytes)
  1395  }
  1396  
  1397  // lock may acquire a lock at the specified path.
  1398  // It returns a function to release the lock.
  1399  // If flock is not supported, it does nothing.
  1400  func lock(path string) func() {
  1401  	flock := flock.New(path)
  1402  	err := flock.Lock()
  1403  	if err != nil {
  1404  		return func() {}
  1405  	}
  1406  
  1407  	return func() { flock.Close() }
  1408  }