mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-01 17:47:46 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 96187990e3 |
@@ -89,7 +89,7 @@ jobs:
|
|||||||
run: make wasi-libc
|
run: make wasi-libc
|
||||||
- name: Test TinyGo
|
- name: Test TinyGo
|
||||||
shell: bash
|
shell: bash
|
||||||
run: make test GOTESTFLAGS="-short"
|
run: make test GOTESTFLAGS="-v -short"
|
||||||
- name: Build TinyGo release tarball
|
- name: Build TinyGo release tarball
|
||||||
run: make release -j3
|
run: make release -j3
|
||||||
- name: Test stdlib packages
|
- name: Test stdlib packages
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ jobs:
|
|||||||
scoop install wasmtime
|
scoop install wasmtime
|
||||||
- name: Test TinyGo
|
- name: Test TinyGo
|
||||||
shell: bash
|
shell: bash
|
||||||
run: make test GOTESTFLAGS="-short"
|
run: make test GOTESTFLAGS="-v -short"
|
||||||
- name: Build TinyGo release tarball
|
- name: Build TinyGo release tarball
|
||||||
shell: bash
|
shell: bash
|
||||||
run: make build/release -j4
|
run: make build/release -j4
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ GO ?= go
|
|||||||
export GOROOT = $(shell $(GO) env GOROOT)
|
export GOROOT = $(shell $(GO) env GOROOT)
|
||||||
|
|
||||||
# Flags to pass to go test.
|
# Flags to pass to go test.
|
||||||
GOTESTFLAGS ?=
|
GOTESTFLAGS ?= -v
|
||||||
|
|
||||||
# md5sum binary
|
# md5sum binary
|
||||||
MD5SUM = md5sum
|
MD5SUM = md5sum
|
||||||
@@ -474,8 +474,6 @@ smoketest:
|
|||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/pininterrupt
|
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/pininterrupt
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=nano-rp2040 examples/rtcinterrupt
|
|
||||||
@$(MD5SUM) test.hex
|
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/serial
|
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/serial
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/systick
|
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/systick
|
||||||
|
|||||||
+81
-124
@@ -169,14 +169,12 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
CodeModel: config.CodeModel(),
|
CodeModel: config.CodeModel(),
|
||||||
RelocationModel: config.RelocationModel(),
|
RelocationModel: config.RelocationModel(),
|
||||||
SizeLevel: sizeLevel,
|
SizeLevel: sizeLevel,
|
||||||
TinyGoVersion: goenv.Version,
|
|
||||||
|
|
||||||
Scheduler: config.Scheduler(),
|
Scheduler: config.Scheduler(),
|
||||||
AutomaticStackSize: config.AutomaticStackSize(),
|
AutomaticStackSize: config.AutomaticStackSize(),
|
||||||
DefaultStackSize: config.StackSize(),
|
DefaultStackSize: config.StackSize(),
|
||||||
NeedsStackObjects: config.NeedsStackObjects(),
|
NeedsStackObjects: config.NeedsStackObjects(),
|
||||||
Debug: true,
|
Debug: true,
|
||||||
LTO: config.LTO() != "legacy",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the target machine, which is the LLVM object that contains all
|
// Load the target machine, which is the LLVM object that contains all
|
||||||
@@ -307,6 +305,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
actionID := packageAction{
|
actionID := packageAction{
|
||||||
ImportPath: pkg.ImportPath,
|
ImportPath: pkg.ImportPath,
|
||||||
CompilerBuildID: string(compilerBuildID),
|
CompilerBuildID: string(compilerBuildID),
|
||||||
|
TinyGoVersion: goenv.Version,
|
||||||
LLVMVersion: llvm.Version,
|
LLVMVersion: llvm.Version,
|
||||||
Config: compilerConfig,
|
Config: compilerConfig,
|
||||||
CFlags: pkg.CFlags,
|
CFlags: pkg.CFlags,
|
||||||
@@ -453,24 +452,18 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if compilerConfig.LTO {
|
if runtime.GOOS == "windows" {
|
||||||
buf := llvm.WriteThinLTOBitcodeToMemoryBuffer(mod)
|
// Work around a problem on Windows.
|
||||||
|
// For some reason, WriteBitcodeToFile causes TinyGo to
|
||||||
|
// exit with the following message:
|
||||||
|
// LLVM ERROR: IO failure on output stream: Bad file descriptor
|
||||||
|
buf := llvm.WriteBitcodeToMemoryBuffer(mod)
|
||||||
defer buf.Dispose()
|
defer buf.Dispose()
|
||||||
_, err = f.Write(buf.Bytes())
|
_, err = f.Write(buf.Bytes())
|
||||||
} else {
|
} else {
|
||||||
if runtime.GOOS == "windows" {
|
// Otherwise, write bitcode directly to the file (probably
|
||||||
// Work around a problem on Windows.
|
// faster).
|
||||||
// For some reason, WriteBitcodeToFile causes TinyGo to
|
err = llvm.WriteBitcodeToFile(mod, f)
|
||||||
// exit with the following message:
|
|
||||||
// LLVM ERROR: IO failure on output stream: Bad file descriptor
|
|
||||||
buf := llvm.WriteBitcodeToMemoryBuffer(mod)
|
|
||||||
defer buf.Dispose()
|
|
||||||
_, err = f.Write(buf.Bytes())
|
|
||||||
} else {
|
|
||||||
// Otherwise, write bitcode directly to the file (probably
|
|
||||||
// faster).
|
|
||||||
err = llvm.WriteBitcodeToFile(mod, f)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// WriteBitcodeToFile doesn't produce a useful error on its
|
// WriteBitcodeToFile doesn't produce a useful error on its
|
||||||
@@ -518,11 +511,24 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
|
|
||||||
// Create runtime.initAll function that calls the runtime
|
// Create runtime.initAll function that calls the runtime
|
||||||
// initializer of each package.
|
// initializer of each package.
|
||||||
initAllModule := createInitAll(ctx, config, compilerConfig, lprogram.Sorted())
|
llvmInitFn := mod.NamedFunction("runtime.initAll")
|
||||||
err := llvm.LinkModules(mod, initAllModule)
|
llvmInitFn.SetLinkage(llvm.InternalLinkage)
|
||||||
if err != nil {
|
llvmInitFn.SetUnnamedAddr(true)
|
||||||
return fmt.Errorf("failed to link module: %w", err)
|
transform.AddStandardAttributes(llvmInitFn, config)
|
||||||
|
llvmInitFn.Param(0).SetName("context")
|
||||||
|
block := mod.Context().AddBasicBlock(llvmInitFn, "entry")
|
||||||
|
irbuilder := mod.Context().NewBuilder()
|
||||||
|
defer irbuilder.Dispose()
|
||||||
|
irbuilder.SetInsertPointAtEnd(block)
|
||||||
|
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
||||||
|
for _, pkg := range lprogram.Sorted() {
|
||||||
|
pkgInit := mod.NamedFunction(pkg.Pkg.Path() + ".init")
|
||||||
|
if pkgInit.IsNil() {
|
||||||
|
panic("init not found for " + pkg.Pkg.Path())
|
||||||
|
}
|
||||||
|
irbuilder.CreateCall(pkgInit.GlobalValueType(), pkgInit, []llvm.Value{llvm.Undef(i8ptrType)}, "")
|
||||||
}
|
}
|
||||||
|
irbuilder.CreateRetVoid()
|
||||||
|
|
||||||
// After linking, functions should (as far as possible) be set to
|
// After linking, functions should (as far as possible) be set to
|
||||||
// private linkage or internal linkage. The compiler package marks
|
// private linkage or internal linkage. The compiler package marks
|
||||||
@@ -555,7 +561,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
|
|
||||||
// Run all optimization passes, which are much more effective now
|
// Run all optimization passes, which are much more effective now
|
||||||
// that the optimizer can see the whole program at once.
|
// that the optimizer can see the whole program at once.
|
||||||
err = optimizeProgram(mod, config)
|
err := optimizeProgram(mod, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -588,7 +594,12 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
defer llvmBuf.Dispose()
|
defer llvmBuf.Dispose()
|
||||||
return result, os.WriteFile(outpath, llvmBuf.Bytes(), 0666)
|
return result, os.WriteFile(outpath, llvmBuf.Bytes(), 0666)
|
||||||
case ".bc":
|
case ".bc":
|
||||||
buf := llvm.WriteThinLTOBitcodeToMemoryBuffer(mod)
|
var buf llvm.MemoryBuffer
|
||||||
|
if config.UseThinLTO() {
|
||||||
|
buf = llvm.WriteThinLTOBitcodeToMemoryBuffer(mod)
|
||||||
|
} else {
|
||||||
|
buf = llvm.WriteBitcodeToMemoryBuffer(mod)
|
||||||
|
}
|
||||||
defer buf.Dispose()
|
defer buf.Dispose()
|
||||||
return result, os.WriteFile(outpath, buf.Bytes(), 0666)
|
return result, os.WriteFile(outpath, buf.Bytes(), 0666)
|
||||||
case ".ll":
|
case ".ll":
|
||||||
@@ -610,44 +621,23 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
dependencies: []*compileJob{programJob},
|
dependencies: []*compileJob{programJob},
|
||||||
result: objfile,
|
result: objfile,
|
||||||
run: func(*compileJob) error {
|
run: func(*compileJob) error {
|
||||||
llvmBuf := llvm.WriteThinLTOBitcodeToMemoryBuffer(mod)
|
var llvmBuf llvm.MemoryBuffer
|
||||||
|
if config.UseThinLTO() {
|
||||||
|
llvmBuf = llvm.WriteThinLTOBitcodeToMemoryBuffer(mod)
|
||||||
|
} else {
|
||||||
|
var err error
|
||||||
|
llvmBuf, err = machine.EmitToMemoryBuffer(mod, llvm.ObjectFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
defer llvmBuf.Dispose()
|
defer llvmBuf.Dispose()
|
||||||
return os.WriteFile(objfile, llvmBuf.Bytes(), 0666)
|
return os.WriteFile(objfile, llvmBuf.Bytes(), 0666)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add job to create the runtime.initAll function in a new module.
|
|
||||||
initAllJob := &compileJob{
|
|
||||||
description: "create runtime.initAll",
|
|
||||||
result: filepath.Join(tmpdir, "runtime-initAll.bc"),
|
|
||||||
run: func(job *compileJob) (err error) {
|
|
||||||
// Create module with runtime.initAll.
|
|
||||||
ctx := llvm.NewContext()
|
|
||||||
defer ctx.Dispose()
|
|
||||||
initAllMod := createInitAll(ctx, config, compilerConfig, lprogram.Sorted())
|
|
||||||
defer initAllMod.Dispose()
|
|
||||||
|
|
||||||
// Write module to bitcode file.
|
|
||||||
llvmBuf := llvm.WriteThinLTOBitcodeToMemoryBuffer(initAllMod)
|
|
||||||
defer llvmBuf.Dispose()
|
|
||||||
return os.WriteFile(job.result, llvmBuf.Bytes(), 0666)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare link command.
|
// Prepare link command.
|
||||||
var linkerDependencies []*compileJob
|
linkerDependencies := []*compileJob{outputObjectFileJob}
|
||||||
switch config.LTO() {
|
|
||||||
case "legacy":
|
|
||||||
// Link all Go bitcode files together into a single large module and
|
|
||||||
// then do a ThinLTO link with the resulting large module + extra C
|
|
||||||
// files (from CGo etc).
|
|
||||||
linkerDependencies = append(linkerDependencies, outputObjectFileJob)
|
|
||||||
case "thin":
|
|
||||||
// Do a real thin link, with each Go package in a separate translation
|
|
||||||
// unit. This is faster than merging them into one big LTO module.
|
|
||||||
linkerDependencies = append(linkerDependencies, packageJobs...)
|
|
||||||
linkerDependencies = append(linkerDependencies, initAllJob)
|
|
||||||
}
|
|
||||||
result.Executable = filepath.Join(tmpdir, "main")
|
result.Executable = filepath.Join(tmpdir, "main")
|
||||||
if config.GOOS() == "windows" {
|
if config.GOOS() == "windows" {
|
||||||
result.Executable += ".exe"
|
result.Executable += ".exe"
|
||||||
@@ -674,7 +664,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
job := &compileJob{
|
job := &compileJob{
|
||||||
description: "compile extra file " + path,
|
description: "compile extra file " + path,
|
||||||
run: func(job *compileJob) error {
|
run: func(job *compileJob) error {
|
||||||
result, err := compileAndCacheCFile(abspath, tmpdir, config.CFlags(), config.Options.PrintCommands)
|
result, err := compileAndCacheCFile(abspath, tmpdir, config.CFlags(), config.UseThinLTO(), config.Options.PrintCommands)
|
||||||
job.result = result
|
job.result = result
|
||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
@@ -692,7 +682,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
job := &compileJob{
|
job := &compileJob{
|
||||||
description: "compile CGo file " + abspath,
|
description: "compile CGo file " + abspath,
|
||||||
run: func(job *compileJob) error {
|
run: func(job *compileJob) error {
|
||||||
result, err := compileAndCacheCFile(abspath, tmpdir, pkg.CFlags, config.Options.PrintCommands)
|
result, err := compileAndCacheCFile(abspath, tmpdir, pkg.CFlags, config.UseThinLTO(), config.Options.PrintCommands)
|
||||||
job.result = result
|
job.result = result
|
||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
@@ -751,34 +741,36 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
}
|
}
|
||||||
ldflags = append(ldflags, dependency.result)
|
ldflags = append(ldflags, dependency.result)
|
||||||
}
|
}
|
||||||
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
|
if config.UseThinLTO() {
|
||||||
if config.GOOS() == "windows" {
|
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
|
||||||
// Options for the MinGW wrapper for the lld COFF linker.
|
if config.GOOS() == "windows" {
|
||||||
ldflags = append(ldflags,
|
// Options for the MinGW wrapper for the lld COFF linker.
|
||||||
"-Xlink=/opt:lldlto="+strconv.Itoa(optLevel),
|
ldflags = append(ldflags,
|
||||||
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"))
|
"-Xlink=/opt:lldlto="+strconv.Itoa(optLevel),
|
||||||
} else if config.GOOS() == "darwin" {
|
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"))
|
||||||
// Options for the ld64-compatible lld linker.
|
} else if config.GOOS() == "darwin" {
|
||||||
ldflags = append(ldflags,
|
// Options for the ld64-compatible lld linker.
|
||||||
"--lto-O"+strconv.Itoa(optLevel),
|
ldflags = append(ldflags,
|
||||||
"-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
|
"--lto-O"+strconv.Itoa(optLevel),
|
||||||
} else {
|
"-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
|
||||||
// Options for the ELF linker.
|
} else {
|
||||||
ldflags = append(ldflags,
|
// Options for the ELF linker.
|
||||||
"--lto-O"+strconv.Itoa(optLevel),
|
ldflags = append(ldflags,
|
||||||
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
|
"--lto-O"+strconv.Itoa(optLevel),
|
||||||
)
|
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
|
||||||
}
|
)
|
||||||
if config.CodeModel() != "default" {
|
}
|
||||||
ldflags = append(ldflags,
|
if config.CodeModel() != "default" {
|
||||||
"-mllvm", "-code-model="+config.CodeModel())
|
ldflags = append(ldflags,
|
||||||
}
|
"-mllvm", "-code-model="+config.CodeModel())
|
||||||
if sizeLevel >= 2 {
|
}
|
||||||
// Workaround with roughly the same effect as
|
if sizeLevel >= 2 {
|
||||||
// https://reviews.llvm.org/D119342.
|
// Workaround with roughly the same effect as
|
||||||
// Can hopefully be removed in LLVM 15.
|
// https://reviews.llvm.org/D119342.
|
||||||
ldflags = append(ldflags,
|
// Can hopefully be removed in LLVM 15.
|
||||||
"-mllvm", "--rotation-max-header-size=0")
|
ldflags = append(ldflags,
|
||||||
|
"-mllvm", "--rotation-max-header-size=0")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if config.Options.PrintCommands != nil {
|
if config.Options.PrintCommands != nil {
|
||||||
config.Options.PrintCommands(config.Target.Linker, ldflags...)
|
config.Options.PrintCommands(config.Target.Linker, ldflags...)
|
||||||
@@ -1056,45 +1048,6 @@ func createEmbedObjectFile(data, hexSum, sourceFile, sourceDir, tmpdir string, c
|
|||||||
return outfile.Name(), outfile.Close()
|
return outfile.Name(), outfile.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a module with the runtime.initAll function that calls all package
|
|
||||||
// initializers in initialization order.
|
|
||||||
func createInitAll(ctx llvm.Context, config *compileopts.Config, compilerConfig *compiler.Config, pkgs []*loader.Package) llvm.Module {
|
|
||||||
// Create LLVM module.
|
|
||||||
mod := ctx.NewModule("runtime-initAll")
|
|
||||||
|
|
||||||
// Add datalayout string.
|
|
||||||
machine, err := compiler.NewTargetMachine(compilerConfig)
|
|
||||||
if err != nil {
|
|
||||||
panic(err) // extremely unlikely to fail here (NewTargetMachine is called many times before)
|
|
||||||
}
|
|
||||||
defer machine.Dispose()
|
|
||||||
targetData := machine.CreateTargetData()
|
|
||||||
defer targetData.Dispose()
|
|
||||||
mod.SetTarget(config.Triple())
|
|
||||||
mod.SetDataLayout(targetData.String())
|
|
||||||
|
|
||||||
// Create empty runtime.initAll function.
|
|
||||||
i8ptrType := llvm.PointerType(mod.Context().Int8Type(), 0)
|
|
||||||
fnType := llvm.FunctionType(ctx.VoidType(), []llvm.Type{i8ptrType}, false)
|
|
||||||
fn := llvm.AddFunction(mod, "runtime.initAll", fnType)
|
|
||||||
fn.SetUnnamedAddr(true)
|
|
||||||
transform.AddStandardAttributes(fn, config)
|
|
||||||
|
|
||||||
// Add calls to package initializers.
|
|
||||||
fn.Param(0).SetName("context")
|
|
||||||
block := mod.Context().AddBasicBlock(fn, "entry")
|
|
||||||
irbuilder := mod.Context().NewBuilder()
|
|
||||||
defer irbuilder.Dispose()
|
|
||||||
irbuilder.SetInsertPointAtEnd(block)
|
|
||||||
for _, pkg := range pkgs {
|
|
||||||
pkgInit := llvm.AddFunction(mod, pkg.Pkg.Path()+".init", fnType)
|
|
||||||
irbuilder.CreateCall(fnType, pkgInit, []llvm.Value{llvm.Undef(i8ptrType)}, "")
|
|
||||||
}
|
|
||||||
irbuilder.CreateRetVoid()
|
|
||||||
|
|
||||||
return mod
|
|
||||||
}
|
|
||||||
|
|
||||||
// optimizeProgram runs a series of optimizations and transformations that are
|
// optimizeProgram runs a series of optimizations and transformations that are
|
||||||
// needed to convert a program to its final form. Some transformations are not
|
// needed to convert a program to its final form. Some transformations are not
|
||||||
// optional and must be run as the compiler expects them to run.
|
// optional and must be run as the compiler expects them to run.
|
||||||
@@ -1116,6 +1069,10 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.GOOS() != "darwin" && !config.UseThinLTO() {
|
||||||
|
transform.ApplyFunctionSections(mod) // -ffunction-sections
|
||||||
|
}
|
||||||
|
|
||||||
// Insert values from -ldflags="-X ..." into the IR.
|
// Insert values from -ldflags="-X ..." into the IR.
|
||||||
err = setGlobalValues(mod, config.Options.GlobalValues)
|
err = setGlobalValues(mod, config.Options.GlobalValues)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ func TestClangAttributes(t *testing.T) {
|
|||||||
{GOOS: "linux", GOARCH: "arm", GOARM: "6"},
|
{GOOS: "linux", GOARCH: "arm", GOARM: "6"},
|
||||||
{GOOS: "linux", GOARCH: "arm", GOARM: "7"},
|
{GOOS: "linux", GOARCH: "arm", GOARM: "7"},
|
||||||
{GOOS: "linux", GOARCH: "arm64"},
|
{GOOS: "linux", GOARCH: "arm64"},
|
||||||
|
{GOOS: "linux", GOARCH: "mips"},
|
||||||
{GOOS: "darwin", GOARCH: "amd64"},
|
{GOOS: "darwin", GOARCH: "amd64"},
|
||||||
{GOOS: "darwin", GOARCH: "arm64"},
|
{GOOS: "darwin", GOARCH: "arm64"},
|
||||||
{GOOS: "windows", GOARCH: "amd64"},
|
{GOOS: "windows", GOARCH: "amd64"},
|
||||||
|
|||||||
+16
-8
@@ -56,7 +56,7 @@ import (
|
|||||||
// depfile but without invalidating its name. For this reason, the depfile is
|
// depfile but without invalidating its name. For this reason, the depfile is
|
||||||
// written on each new compilation (even when it seems unnecessary). However, it
|
// written on each new compilation (even when it seems unnecessary). However, it
|
||||||
// could in rare cases lead to a stale file fetched from the cache.
|
// could in rare cases lead to a stale file fetched from the cache.
|
||||||
func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands func(string, ...string)) (string, error) {
|
func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool, printCommands func(string, ...string)) (string, error) {
|
||||||
// Hash input file.
|
// Hash input file.
|
||||||
fileHash, err := hashFile(abspath)
|
fileHash, err := hashFile(abspath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -67,6 +67,11 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
|
|||||||
unlock := lock(filepath.Join(goenv.Get("GOCACHE"), fileHash+".c.lock"))
|
unlock := lock(filepath.Join(goenv.Get("GOCACHE"), fileHash+".c.lock"))
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
|
ext := ".o"
|
||||||
|
if thinlto {
|
||||||
|
ext = ".bc"
|
||||||
|
}
|
||||||
|
|
||||||
// Create cache key for the dependencies file.
|
// Create cache key for the dependencies file.
|
||||||
buf, err := json.Marshal(struct {
|
buf, err := json.Marshal(struct {
|
||||||
Path string
|
Path string
|
||||||
@@ -99,7 +104,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Obtain hashes of all the files listed as a dependency.
|
// Obtain hashes of all the files listed as a dependency.
|
||||||
outpath, err := makeCFileCachePath(dependencies, depfileNameHash)
|
outpath, err := makeCFileCachePath(dependencies, depfileNameHash, ext)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if _, err := os.Stat(outpath); err == nil {
|
if _, err := os.Stat(outpath); err == nil {
|
||||||
return outpath, nil
|
return outpath, nil
|
||||||
@@ -112,7 +117,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
objTmpFile, err := os.CreateTemp(goenv.Get("GOCACHE"), "tmp-*.bc")
|
objTmpFile, err := os.CreateTemp(goenv.Get("GOCACHE"), "tmp-*"+ext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -122,8 +127,11 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
depTmpFile.Close()
|
depTmpFile.Close()
|
||||||
flags := append([]string{}, cflags...) // copy cflags
|
flags := append([]string{}, cflags...) // copy cflags
|
||||||
flags = append(flags, "-MD", "-MV", "-MTdeps", "-MF", depTmpFile.Name(), "-flto=thin") // autogenerate dependencies
|
flags = append(flags, "-MD", "-MV", "-MTdeps", "-MF", depTmpFile.Name()) // autogenerate dependencies
|
||||||
|
if thinlto {
|
||||||
|
flags = append(flags, "-flto=thin")
|
||||||
|
}
|
||||||
flags = append(flags, "-c", "-o", objTmpFile.Name(), abspath)
|
flags = append(flags, "-c", "-o", objTmpFile.Name(), abspath)
|
||||||
if strings.ToLower(filepath.Ext(abspath)) == ".s" {
|
if strings.ToLower(filepath.Ext(abspath)) == ".s" {
|
||||||
// If this is an assembly file (.s or .S, lowercase or uppercase), then
|
// If this is an assembly file (.s or .S, lowercase or uppercase), then
|
||||||
@@ -181,7 +189,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Move temporary object file to final location.
|
// Move temporary object file to final location.
|
||||||
outpath, err := makeCFileCachePath(dependencySlice, depfileNameHash)
|
outpath, err := makeCFileCachePath(dependencySlice, depfileNameHash, ext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -196,7 +204,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, printCommands
|
|||||||
// Create a cache path (a path in GOCACHE) to store the output of a compiler
|
// Create a cache path (a path in GOCACHE) to store the output of a compiler
|
||||||
// job. This path is based on the dep file name (which is a hash of metadata
|
// job. This path is based on the dep file name (which is a hash of metadata
|
||||||
// including compiler flags) and the hash of all input files in the paths slice.
|
// including compiler flags) and the hash of all input files in the paths slice.
|
||||||
func makeCFileCachePath(paths []string, depfileNameHash string) (string, error) {
|
func makeCFileCachePath(paths []string, depfileNameHash, ext string) (string, error) {
|
||||||
// Hash all input files.
|
// Hash all input files.
|
||||||
fileHashes := make(map[string]string, len(paths))
|
fileHashes := make(map[string]string, len(paths))
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
@@ -221,7 +229,7 @@ func makeCFileCachePath(paths []string, depfileNameHash string) (string, error)
|
|||||||
outFileNameBuf := sha512.Sum512_224(buf)
|
outFileNameBuf := sha512.Sum512_224(buf)
|
||||||
cacheKey := hex.EncodeToString(outFileNameBuf[:])
|
cacheKey := hex.EncodeToString(outFileNameBuf[:])
|
||||||
|
|
||||||
outpath := filepath.Join(goenv.Get("GOCACHE"), "obj-"+cacheKey+".bc")
|
outpath := filepath.Join(goenv.Get("GOCACHE"), "obj-"+cacheKey+ext)
|
||||||
return outpath, nil
|
return outpath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -165,14 +165,6 @@ func (c *Config) OptLevels() (optLevel, sizeLevel int, inlinerThreshold uint) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LTO returns one of the possible LTO configurations: legacy or thin.
|
|
||||||
func (c *Config) LTO() string {
|
|
||||||
if c.Options.LTO != "" {
|
|
||||||
return c.Options.LTO
|
|
||||||
}
|
|
||||||
return "legacy"
|
|
||||||
}
|
|
||||||
|
|
||||||
// PanicStrategy returns the panic strategy selected for this target. Valid
|
// PanicStrategy returns the panic strategy selected for this target. Valid
|
||||||
// values are "print" (print the panic value, then exit) or "trap" (issue a trap
|
// values are "print" (print the panic value, then exit) or "trap" (issue a trap
|
||||||
// instruction).
|
// instruction).
|
||||||
@@ -199,6 +191,14 @@ func (c *Config) StackSize() uint64 {
|
|||||||
return c.Target.DefaultStackSize
|
return c.Target.DefaultStackSize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UseThinLTO returns whether ThinLTO should be used for the given target.
|
||||||
|
func (c *Config) UseThinLTO() bool {
|
||||||
|
// All architectures support ThinLTO now. However, this code is kept for the
|
||||||
|
// time being in case there are regressions. The non-ThinLTO code support
|
||||||
|
// should be removed when it is proven to work reliably.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// RP2040BootPatch returns whether the RP2040 boot patch should be applied that
|
// RP2040BootPatch returns whether the RP2040 boot patch should be applied that
|
||||||
// calculates and patches in the checksum for the 2nd stage bootloader.
|
// calculates and patches in the checksum for the 2nd stage bootloader.
|
||||||
func (c *Config) RP2040BootPatch() bool {
|
func (c *Config) RP2040BootPatch() bool {
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ var (
|
|||||||
validPrintSizeOptions = []string{"none", "short", "full"}
|
validPrintSizeOptions = []string{"none", "short", "full"}
|
||||||
validPanicStrategyOptions = []string{"print", "trap"}
|
validPanicStrategyOptions = []string{"print", "trap"}
|
||||||
validOptOptions = []string{"none", "0", "1", "2", "s", "z"}
|
validOptOptions = []string{"none", "0", "1", "2", "s", "z"}
|
||||||
validLTOOptions = []string{"legacy", "thin"}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Options contains extra options to give to the compiler. These options are
|
// Options contains extra options to give to the compiler. These options are
|
||||||
@@ -26,7 +25,6 @@ type Options struct {
|
|||||||
GOARM string // environment variable (only used with GOARCH=arm)
|
GOARM string // environment variable (only used with GOARCH=arm)
|
||||||
Target string
|
Target string
|
||||||
Opt string
|
Opt string
|
||||||
LTO string
|
|
||||||
GC string
|
GC string
|
||||||
PanicStrategy string
|
PanicStrategy string
|
||||||
Scheduler string
|
Scheduler string
|
||||||
@@ -109,12 +107,6 @@ func (o *Options) Verify() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.LTO != "" {
|
|
||||||
if !isInArray(validLTOOptions, o.LTO) {
|
|
||||||
return fmt.Errorf("invalid -lto=%s: valid values are %s", o.LTO, strings.Join(validLTOOptions, ", "))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -278,6 +278,9 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
case "arm64":
|
case "arm64":
|
||||||
spec.CPU = "generic"
|
spec.CPU = "generic"
|
||||||
spec.Features = "+neon"
|
spec.Features = "+neon"
|
||||||
|
case "mips":
|
||||||
|
spec.CPU = "mips32r2"
|
||||||
|
spec.Features = "+mips32r2,-noabicalls"
|
||||||
}
|
}
|
||||||
if goos == "darwin" {
|
if goos == "darwin" {
|
||||||
spec.Linker = "ld.lld"
|
spec.Linker = "ld.lld"
|
||||||
@@ -350,6 +353,8 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
|||||||
spec.Emulator = "qemu-arm {}"
|
spec.Emulator = "qemu-arm {}"
|
||||||
case "arm64":
|
case "arm64":
|
||||||
spec.Emulator = "qemu-aarch64 {}"
|
spec.Emulator = "qemu-aarch64 {}"
|
||||||
|
case "mips":
|
||||||
|
spec.Emulator = "qemu-mips {}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-3
@@ -34,9 +34,7 @@ var stdlibAliases = map[string]string{
|
|||||||
// createAlias implements the function (in the builder) as a call to the alias
|
// createAlias implements the function (in the builder) as a call to the alias
|
||||||
// function.
|
// function.
|
||||||
func (b *builder) createAlias(alias llvm.Value) {
|
func (b *builder) createAlias(alias llvm.Value) {
|
||||||
if !b.LTO {
|
b.llvmFn.SetVisibility(llvm.HiddenVisibility)
|
||||||
b.llvmFn.SetVisibility(llvm.HiddenVisibility)
|
|
||||||
}
|
|
||||||
b.llvmFn.SetUnnamedAddr(true)
|
b.llvmFn.SetUnnamedAddr(true)
|
||||||
|
|
||||||
if b.Debug {
|
if b.Debug {
|
||||||
|
|||||||
+5
-24
@@ -47,7 +47,6 @@ type Config struct {
|
|||||||
CodeModel string
|
CodeModel string
|
||||||
RelocationModel string
|
RelocationModel string
|
||||||
SizeLevel int
|
SizeLevel int
|
||||||
TinyGoVersion string // for llvm.ident
|
|
||||||
|
|
||||||
// Various compiler options that determine how code is generated.
|
// Various compiler options that determine how code is generated.
|
||||||
Scheduler string
|
Scheduler string
|
||||||
@@ -55,7 +54,6 @@ type Config struct {
|
|||||||
DefaultStackSize uint64
|
DefaultStackSize uint64
|
||||||
NeedsStackObjects bool
|
NeedsStackObjects bool
|
||||||
Debug bool // Whether to emit debug information in the LLVM module.
|
Debug bool // Whether to emit debug information in the LLVM module.
|
||||||
LTO bool // non-legacy LTO (meaning: package bitcode is merged by the linker)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// compilerContext contains function-independent data that should still be
|
// compilerContext contains function-independent data that should still be
|
||||||
@@ -323,14 +321,6 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package,
|
|||||||
llvm.ConstInt(c.ctx.Int32Type(), 4, false).ConstantAsMetadata(),
|
llvm.ConstInt(c.ctx.Int32Type(), 4, false).ConstantAsMetadata(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
if c.TinyGoVersion != "" {
|
|
||||||
// It is necessary to set llvm.ident, otherwise debugging on MacOS
|
|
||||||
// won't work.
|
|
||||||
c.mod.AddNamedMetadataOperand("llvm.ident",
|
|
||||||
c.ctx.MDNode(([]llvm.Metadata{
|
|
||||||
c.ctx.MDString("TinyGo version " + c.TinyGoVersion),
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
c.dibuilder.Finalize()
|
c.dibuilder.Finalize()
|
||||||
c.dibuilder.Destroy()
|
c.dibuilder.Destroy()
|
||||||
}
|
}
|
||||||
@@ -898,9 +888,7 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
|||||||
c.createEmbedGlobal(member, global, files)
|
c.createEmbedGlobal(member, global, files)
|
||||||
} else if !info.extern {
|
} else if !info.extern {
|
||||||
global.SetInitializer(llvm.ConstNull(global.GlobalValueType()))
|
global.SetInitializer(llvm.ConstNull(global.GlobalValueType()))
|
||||||
if !c.LTO {
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
global.SetVisibility(llvm.HiddenVisibility)
|
|
||||||
}
|
|
||||||
if info.section != "" {
|
if info.section != "" {
|
||||||
global.SetSection(info.section)
|
global.SetSection(info.section)
|
||||||
}
|
}
|
||||||
@@ -947,9 +935,7 @@ func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Valu
|
|||||||
}
|
}
|
||||||
strObj := c.getEmbedFileString(files[0])
|
strObj := c.getEmbedFileString(files[0])
|
||||||
global.SetInitializer(strObj)
|
global.SetInitializer(strObj)
|
||||||
if !c.LTO {
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
global.SetVisibility(llvm.HiddenVisibility)
|
|
||||||
}
|
|
||||||
|
|
||||||
case *types.Slice:
|
case *types.Slice:
|
||||||
if typ.Elem().Underlying().(*types.Basic).Kind() != types.Byte {
|
if typ.Elem().Underlying().(*types.Basic).Kind() != types.Byte {
|
||||||
@@ -973,9 +959,7 @@ func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Valu
|
|||||||
sliceLen := llvm.ConstInt(c.uintptrType, file.Size, false)
|
sliceLen := llvm.ConstInt(c.uintptrType, file.Size, false)
|
||||||
sliceObj := c.ctx.ConstStruct([]llvm.Value{slicePtr, sliceLen, sliceLen}, false)
|
sliceObj := c.ctx.ConstStruct([]llvm.Value{slicePtr, sliceLen, sliceLen}, false)
|
||||||
global.SetInitializer(sliceObj)
|
global.SetInitializer(sliceObj)
|
||||||
if !c.LTO {
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
global.SetVisibility(llvm.HiddenVisibility)
|
|
||||||
}
|
|
||||||
|
|
||||||
case *types.Struct:
|
case *types.Struct:
|
||||||
// Assume this is an embed.FS struct:
|
// Assume this is an embed.FS struct:
|
||||||
@@ -1058,9 +1042,7 @@ func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Valu
|
|||||||
globalInitializer := llvm.ConstNull(c.getLLVMType(member.Type().(*types.Pointer).Elem()))
|
globalInitializer := llvm.ConstNull(c.getLLVMType(member.Type().(*types.Pointer).Elem()))
|
||||||
globalInitializer = c.builder.CreateInsertValue(globalInitializer, sliceGlobal, 0, "")
|
globalInitializer = c.builder.CreateInsertValue(globalInitializer, sliceGlobal, 0, "")
|
||||||
global.SetInitializer(globalInitializer)
|
global.SetInitializer(globalInitializer)
|
||||||
if !c.LTO {
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
global.SetVisibility(llvm.HiddenVisibility)
|
|
||||||
}
|
|
||||||
global.SetAlignment(c.targetData.ABITypeAlignment(globalInitializer.Type()))
|
global.SetAlignment(c.targetData.ABITypeAlignment(globalInitializer.Type()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1107,8 +1089,7 @@ func (b *builder) createFunctionStart(intrinsic bool) {
|
|||||||
// assertion error in llvm-project/llvm/include/llvm/IR/GlobalValue.h:236
|
// assertion error in llvm-project/llvm/include/llvm/IR/GlobalValue.h:236
|
||||||
// is thrown.
|
// is thrown.
|
||||||
if b.llvmFn.Linkage() != llvm.InternalLinkage &&
|
if b.llvmFn.Linkage() != llvm.InternalLinkage &&
|
||||||
b.llvmFn.Linkage() != llvm.PrivateLinkage &&
|
b.llvmFn.Linkage() != llvm.PrivateLinkage {
|
||||||
!b.LTO {
|
|
||||||
b.llvmFn.SetVisibility(llvm.HiddenVisibility)
|
b.llvmFn.SetVisibility(llvm.HiddenVisibility)
|
||||||
}
|
}
|
||||||
b.llvmFn.SetUnnamedAddr(true)
|
b.llvmFn.SetUnnamedAddr(true)
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ func TestCompiler(t *testing.T) {
|
|||||||
{"goroutine.go", "cortex-m-qemu", "tasks"},
|
{"goroutine.go", "cortex-m-qemu", "tasks"},
|
||||||
{"channel.go", "", ""},
|
{"channel.go", "", ""},
|
||||||
{"gc.go", "", ""},
|
{"gc.go", "", ""},
|
||||||
{"zeromap.go", "", ""},
|
|
||||||
}
|
}
|
||||||
if goMinor >= 20 {
|
if goMinor >= 20 {
|
||||||
tests = append(tests, testCase{"go1.20.go", "", ""})
|
tests = append(tests, testCase{"go1.20.go", "", ""})
|
||||||
|
|||||||
+1
-1
@@ -31,7 +31,7 @@ func (b *builder) supportsRecover() bool {
|
|||||||
// proposal of WebAssembly:
|
// proposal of WebAssembly:
|
||||||
// https://github.com/WebAssembly/exception-handling
|
// https://github.com/WebAssembly/exception-handling
|
||||||
return false
|
return false
|
||||||
case "riscv64", "xtensa":
|
case "riscv64", "xtensa", "mips":
|
||||||
// TODO: add support for these architectures
|
// TODO: add support for these architectures
|
||||||
return false
|
return false
|
||||||
default:
|
default:
|
||||||
|
|||||||
+10
-15
@@ -490,22 +490,17 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
|||||||
commaOk = b.CreateCall(fn.GlobalValueType(), fn, []llvm.Value{actualTypeNum}, "")
|
commaOk = b.CreateCall(fn.GlobalValueType(), fn, []llvm.Value{actualTypeNum}, "")
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if b.LTO {
|
globalName := "reflect/types.typeid:" + getTypeCodeName(expr.AssertedType)
|
||||||
assertedTypeCodeGlobal := b.getTypeCode(expr.AssertedType)
|
assertedTypeCodeGlobal := b.mod.NamedGlobal(globalName)
|
||||||
commaOk = b.CreateICmp(llvm.IntEQ, actualTypeNum, assertedTypeCodeGlobal, "commaok")
|
if assertedTypeCodeGlobal.IsNil() {
|
||||||
} else {
|
// Create a new typecode global.
|
||||||
globalName := "reflect/types.typeid:" + getTypeCodeName(expr.AssertedType)
|
assertedTypeCodeGlobal = llvm.AddGlobal(b.mod, b.ctx.Int8Type(), globalName)
|
||||||
assertedTypeCodeGlobal := b.mod.NamedGlobal(globalName)
|
assertedTypeCodeGlobal.SetGlobalConstant(true)
|
||||||
if assertedTypeCodeGlobal.IsNil() {
|
|
||||||
// Create a new typecode global.
|
|
||||||
assertedTypeCodeGlobal = llvm.AddGlobal(b.mod, b.ctx.Int8Type(), globalName)
|
|
||||||
assertedTypeCodeGlobal.SetGlobalConstant(true)
|
|
||||||
}
|
|
||||||
// Type assert on concrete type.
|
|
||||||
// Call runtime.typeAssert, which will be lowered to a simple icmp
|
|
||||||
// or const false in the interface lowering pass.
|
|
||||||
commaOk = b.createRuntimeCall("typeAssert", []llvm.Value{actualTypeNum, assertedTypeCodeGlobal}, "typecode")
|
|
||||||
}
|
}
|
||||||
|
// Type assert on concrete type.
|
||||||
|
// Call runtime.typeAssert, which will be lowered to a simple icmp or
|
||||||
|
// const false in the interface lowering pass.
|
||||||
|
commaOk = b.createRuntimeCall("typeAssert", []llvm.Value{actualTypeNum, assertedTypeCodeGlobal}, "typecode")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add 2 new basic blocks (that should get optimized away): one for the
|
// Add 2 new basic blocks (that should get optimized away): one for the
|
||||||
|
|||||||
@@ -45,9 +45,7 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
|
|||||||
globalLLVMType := b.getLLVMType(globalType)
|
globalLLVMType := b.getLLVMType(globalType)
|
||||||
globalName := b.fn.Package().Pkg.Path() + "$interrupt" + strconv.FormatInt(id.Int64(), 10)
|
globalName := b.fn.Package().Pkg.Path() + "$interrupt" + strconv.FormatInt(id.Int64(), 10)
|
||||||
global := llvm.AddGlobal(b.mod, globalLLVMType, globalName)
|
global := llvm.AddGlobal(b.mod, globalLLVMType, globalName)
|
||||||
if !b.LTO {
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
global.SetVisibility(llvm.HiddenVisibility)
|
|
||||||
}
|
|
||||||
global.SetGlobalConstant(true)
|
global.SetGlobalConstant(true)
|
||||||
global.SetUnnamedAddr(true)
|
global.SetUnnamedAddr(true)
|
||||||
initializer := llvm.ConstNull(globalLLVMType)
|
initializer := llvm.ConstNull(globalLLVMType)
|
||||||
|
|||||||
+1
-78
@@ -89,7 +89,6 @@ func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Val
|
|||||||
// growth.
|
// growth.
|
||||||
mapKeyAlloca, mapKeyPtr, mapKeySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
mapKeyAlloca, mapKeyPtr, mapKeySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
||||||
b.CreateStore(key, mapKeyAlloca)
|
b.CreateStore(key, mapKeyAlloca)
|
||||||
b.zeroUndefBytes(b.getLLVMType(keyType), mapKeyAlloca)
|
|
||||||
// Fetch the value from the hashmap.
|
// Fetch the value from the hashmap.
|
||||||
params := []llvm.Value{m, mapKeyPtr, mapValuePtr, mapValueSize}
|
params := []llvm.Value{m, mapKeyPtr, mapValuePtr, mapValueSize}
|
||||||
commaOkValue = b.createRuntimeCall("hashmapBinaryGet", params, "")
|
commaOkValue = b.createRuntimeCall("hashmapBinaryGet", params, "")
|
||||||
@@ -134,7 +133,6 @@ func (b *builder) createMapUpdate(keyType types.Type, m, key, value llvm.Value,
|
|||||||
// key can be compared with runtime.memequal
|
// key can be compared with runtime.memequal
|
||||||
keyAlloca, keyPtr, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
keyAlloca, keyPtr, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
||||||
b.CreateStore(key, keyAlloca)
|
b.CreateStore(key, keyAlloca)
|
||||||
b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca)
|
|
||||||
params := []llvm.Value{m, keyPtr, valuePtr}
|
params := []llvm.Value{m, keyPtr, valuePtr}
|
||||||
b.createRuntimeCall("hashmapBinarySet", params, "")
|
b.createRuntimeCall("hashmapBinarySet", params, "")
|
||||||
b.emitLifetimeEnd(keyPtr, keySize)
|
b.emitLifetimeEnd(keyPtr, keySize)
|
||||||
@@ -163,7 +161,6 @@ func (b *builder) createMapDelete(keyType types.Type, m, key llvm.Value, pos tok
|
|||||||
} else if hashmapIsBinaryKey(keyType) {
|
} else if hashmapIsBinaryKey(keyType) {
|
||||||
keyAlloca, keyPtr, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
keyAlloca, keyPtr, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
||||||
b.CreateStore(key, keyAlloca)
|
b.CreateStore(key, keyAlloca)
|
||||||
b.zeroUndefBytes(b.getLLVMType(keyType), keyAlloca)
|
|
||||||
params := []llvm.Value{m, keyPtr}
|
params := []llvm.Value{m, keyPtr}
|
||||||
b.createRuntimeCall("hashmapBinaryDelete", params, "")
|
b.createRuntimeCall("hashmapBinaryDelete", params, "")
|
||||||
b.emitLifetimeEnd(keyPtr, keySize)
|
b.emitLifetimeEnd(keyPtr, keySize)
|
||||||
@@ -243,8 +240,7 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if this key type does not contain strings, interfaces etc., so
|
// Returns true if this key type does not contain strings, interfaces etc., so
|
||||||
// can be compared with runtime.memequal. Note that padding bytes are undef
|
// can be compared with runtime.memequal.
|
||||||
// and can alter two "equal" structs being equal when compared with memequal.
|
|
||||||
func hashmapIsBinaryKey(keyType types.Type) bool {
|
func hashmapIsBinaryKey(keyType types.Type) bool {
|
||||||
switch keyType := keyType.(type) {
|
switch keyType := keyType.(type) {
|
||||||
case *types.Basic:
|
case *types.Basic:
|
||||||
@@ -267,76 +263,3 @@ func hashmapIsBinaryKey(keyType types.Type) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *builder) zeroUndefBytes(llvmType llvm.Type, ptr llvm.Value) error {
|
|
||||||
// We know that hashmapIsBinaryKey is true, so we only have to handle those types that can show up there.
|
|
||||||
// To zero all undefined bytes, we iterate over all the fields in the type. For each element, compute the
|
|
||||||
// offset of that element. If it's Basic type, there are no internal padding bytes. For compound types, we recurse to ensure
|
|
||||||
// we handle nested types. Next, we determine if there are any padding bytes before the next
|
|
||||||
// element and zero those as well.
|
|
||||||
|
|
||||||
zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
|
|
||||||
|
|
||||||
switch llvmType.TypeKind() {
|
|
||||||
case llvm.IntegerTypeKind:
|
|
||||||
// no padding bytes
|
|
||||||
return nil
|
|
||||||
case llvm.PointerTypeKind:
|
|
||||||
// mo padding bytes
|
|
||||||
return nil
|
|
||||||
case llvm.ArrayTypeKind:
|
|
||||||
llvmArrayType := llvmType
|
|
||||||
llvmElemType := llvmType.ElementType()
|
|
||||||
|
|
||||||
for i := 0; i < llvmArrayType.ArrayLength(); i++ {
|
|
||||||
idx := llvm.ConstInt(b.uintptrType, uint64(i), false)
|
|
||||||
elemPtr := b.CreateInBoundsGEP(llvmArrayType, ptr, []llvm.Value{zero, idx}, "")
|
|
||||||
|
|
||||||
// zero any padding bytes in this element
|
|
||||||
b.zeroUndefBytes(llvmElemType, elemPtr)
|
|
||||||
}
|
|
||||||
|
|
||||||
case llvm.StructTypeKind:
|
|
||||||
llvmStructType := llvmType
|
|
||||||
numFields := llvmStructType.StructElementTypesCount()
|
|
||||||
llvmElementTypes := llvmStructType.StructElementTypes()
|
|
||||||
|
|
||||||
for i := 0; i < numFields; i++ {
|
|
||||||
idx := llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)
|
|
||||||
elemPtr := b.CreateInBoundsGEP(llvmStructType, ptr, []llvm.Value{zero, idx}, "")
|
|
||||||
|
|
||||||
// zero any padding bytes in this field
|
|
||||||
llvmElemType := llvmElementTypes[i]
|
|
||||||
b.zeroUndefBytes(llvmElemType, elemPtr)
|
|
||||||
|
|
||||||
// zero any padding bytes before the next field, if any
|
|
||||||
offset := b.targetData.ElementOffset(llvmStructType, i)
|
|
||||||
storeSize := b.targetData.TypeStoreSize(llvmElemType)
|
|
||||||
fieldEndOffset := offset + storeSize
|
|
||||||
|
|
||||||
var nextOffset uint64
|
|
||||||
if i < numFields-1 {
|
|
||||||
nextOffset = b.targetData.ElementOffset(llvmStructType, i+1)
|
|
||||||
} else {
|
|
||||||
// Last field? Next offset is the total size of the allcoate struct.
|
|
||||||
nextOffset = b.targetData.TypeAllocSize(llvmStructType)
|
|
||||||
}
|
|
||||||
|
|
||||||
if fieldEndOffset != nextOffset {
|
|
||||||
n := llvm.ConstInt(b.uintptrType, nextOffset-fieldEndOffset, false)
|
|
||||||
llvmStoreSize := llvm.ConstInt(b.uintptrType, storeSize, false)
|
|
||||||
gepPtr := elemPtr
|
|
||||||
if gepPtr.Type() != b.i8ptrType {
|
|
||||||
gepPtr = b.CreateBitCast(gepPtr, b.i8ptrType, "") // LLVM 14
|
|
||||||
}
|
|
||||||
paddingStart := b.CreateInBoundsGEP(b.ctx.Int8Type(), gepPtr, []llvm.Value{llvmStoreSize}, "")
|
|
||||||
if paddingStart.Type() != b.i8ptrType {
|
|
||||||
paddingStart = b.CreateBitCast(paddingStart, b.i8ptrType, "") // LLVM 14
|
|
||||||
}
|
|
||||||
b.createRuntimeCall("memzero", []llvm.Value{paddingStart, n}, "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
Vendored
-37
@@ -1,37 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
type hasPadding struct {
|
|
||||||
b1 bool
|
|
||||||
i int
|
|
||||||
b2 bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type nestedPadding struct {
|
|
||||||
b bool
|
|
||||||
hasPadding
|
|
||||||
i int
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:noinline
|
|
||||||
func testZeroGet(m map[hasPadding]int, s hasPadding) int {
|
|
||||||
return m[s]
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:noinline
|
|
||||||
func testZeroSet(m map[hasPadding]int, s hasPadding) {
|
|
||||||
m[s] = 5
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:noinline
|
|
||||||
func testZeroArrayGet(m map[[2]hasPadding]int, s [2]hasPadding) int {
|
|
||||||
return m[s]
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:noinline
|
|
||||||
func testZeroArraySet(m map[[2]hasPadding]int, s [2]hasPadding) {
|
|
||||||
m[s] = 5
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
}
|
|
||||||
Vendored
-170
@@ -1,170 +0,0 @@
|
|||||||
; ModuleID = 'zeromap.go'
|
|
||||||
source_filename = "zeromap.go"
|
|
||||||
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
|
|
||||||
target triple = "wasm32-unknown-wasi"
|
|
||||||
|
|
||||||
%main.hasPadding = type { i1, i32, i1 }
|
|
||||||
|
|
||||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
|
||||||
|
|
||||||
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
|
||||||
define hidden void @main.init(ptr %context) unnamed_addr #1 {
|
|
||||||
entry:
|
|
||||||
ret void
|
|
||||||
}
|
|
||||||
|
|
||||||
; Function Attrs: noinline nounwind
|
|
||||||
define hidden i32 @main.testZeroGet(ptr dereferenceable_or_null(40) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #2 {
|
|
||||||
entry:
|
|
||||||
%hashmap.key = alloca %main.hasPadding, align 8
|
|
||||||
%hashmap.value = alloca i32, align 4
|
|
||||||
%s = alloca %main.hasPadding, align 8
|
|
||||||
%0 = insertvalue %main.hasPadding zeroinitializer, i1 %s.b1, 0
|
|
||||||
%1 = insertvalue %main.hasPadding %0, i32 %s.i, 1
|
|
||||||
%2 = insertvalue %main.hasPadding %1, i1 %s.b2, 2
|
|
||||||
%stackalloc = alloca i8, align 1
|
|
||||||
store %main.hasPadding zeroinitializer, ptr %s, align 8
|
|
||||||
call void @runtime.trackPointer(ptr nonnull %s, ptr nonnull %stackalloc, ptr undef) #4
|
|
||||||
store %main.hasPadding %2, ptr %s, align 8
|
|
||||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
|
|
||||||
call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key)
|
|
||||||
store %main.hasPadding %2, ptr %hashmap.key, align 8
|
|
||||||
%3 = getelementptr inbounds i8, ptr %hashmap.key, i32 1
|
|
||||||
call void @runtime.memzero(ptr nonnull %3, i32 3, ptr undef) #4
|
|
||||||
%4 = getelementptr inbounds i8, ptr %hashmap.key, i32 9
|
|
||||||
call void @runtime.memzero(ptr nonnull %4, i32 3, ptr undef) #4
|
|
||||||
%5 = call i1 @runtime.hashmapBinaryGet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, i32 4, ptr undef) #4
|
|
||||||
call void @llvm.lifetime.end.p0(i64 12, ptr nonnull %hashmap.key)
|
|
||||||
%6 = load i32, ptr %hashmap.value, align 4
|
|
||||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
|
|
||||||
ret i32 %6
|
|
||||||
}
|
|
||||||
|
|
||||||
; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn
|
|
||||||
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #3
|
|
||||||
|
|
||||||
declare void @runtime.memzero(ptr, i32, ptr) #0
|
|
||||||
|
|
||||||
declare i1 @runtime.hashmapBinaryGet(ptr dereferenceable_or_null(40), ptr, ptr, i32, ptr) #0
|
|
||||||
|
|
||||||
; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn
|
|
||||||
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #3
|
|
||||||
|
|
||||||
; Function Attrs: noinline nounwind
|
|
||||||
define hidden void @main.testZeroSet(ptr dereferenceable_or_null(40) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #2 {
|
|
||||||
entry:
|
|
||||||
%hashmap.key = alloca %main.hasPadding, align 8
|
|
||||||
%hashmap.value = alloca i32, align 4
|
|
||||||
%s = alloca %main.hasPadding, align 8
|
|
||||||
%0 = insertvalue %main.hasPadding zeroinitializer, i1 %s.b1, 0
|
|
||||||
%1 = insertvalue %main.hasPadding %0, i32 %s.i, 1
|
|
||||||
%2 = insertvalue %main.hasPadding %1, i1 %s.b2, 2
|
|
||||||
%stackalloc = alloca i8, align 1
|
|
||||||
store %main.hasPadding zeroinitializer, ptr %s, align 8
|
|
||||||
call void @runtime.trackPointer(ptr nonnull %s, ptr nonnull %stackalloc, ptr undef) #4
|
|
||||||
store %main.hasPadding %2, ptr %s, align 8
|
|
||||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
|
|
||||||
store i32 5, ptr %hashmap.value, align 4
|
|
||||||
call void @llvm.lifetime.start.p0(i64 12, ptr nonnull %hashmap.key)
|
|
||||||
store %main.hasPadding %2, ptr %hashmap.key, align 8
|
|
||||||
%3 = getelementptr inbounds i8, ptr %hashmap.key, i32 1
|
|
||||||
call void @runtime.memzero(ptr nonnull %3, i32 3, ptr undef) #4
|
|
||||||
%4 = getelementptr inbounds i8, ptr %hashmap.key, i32 9
|
|
||||||
call void @runtime.memzero(ptr nonnull %4, i32 3, ptr undef) #4
|
|
||||||
call void @runtime.hashmapBinarySet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #4
|
|
||||||
call void @llvm.lifetime.end.p0(i64 12, ptr nonnull %hashmap.key)
|
|
||||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
|
|
||||||
ret void
|
|
||||||
}
|
|
||||||
|
|
||||||
declare void @runtime.hashmapBinarySet(ptr dereferenceable_or_null(40), ptr, ptr, ptr) #0
|
|
||||||
|
|
||||||
; Function Attrs: noinline nounwind
|
|
||||||
define hidden i32 @main.testZeroArrayGet(ptr dereferenceable_or_null(40) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #2 {
|
|
||||||
entry:
|
|
||||||
%hashmap.key = alloca [2 x %main.hasPadding], align 8
|
|
||||||
%hashmap.value = alloca i32, align 4
|
|
||||||
%s1 = alloca [2 x %main.hasPadding], align 8
|
|
||||||
%stackalloc = alloca i8, align 1
|
|
||||||
store %main.hasPadding zeroinitializer, ptr %s1, align 8
|
|
||||||
%s1.repack2 = getelementptr inbounds [2 x %main.hasPadding], ptr %s1, i32 0, i32 1
|
|
||||||
store %main.hasPadding zeroinitializer, ptr %s1.repack2, align 4
|
|
||||||
call void @runtime.trackPointer(ptr nonnull %s1, ptr nonnull %stackalloc, ptr undef) #4
|
|
||||||
%s.elt = extractvalue [2 x %main.hasPadding] %s, 0
|
|
||||||
store %main.hasPadding %s.elt, ptr %s1, align 8
|
|
||||||
%s1.repack3 = getelementptr inbounds [2 x %main.hasPadding], ptr %s1, i32 0, i32 1
|
|
||||||
%s.elt4 = extractvalue [2 x %main.hasPadding] %s, 1
|
|
||||||
store %main.hasPadding %s.elt4, ptr %s1.repack3, align 4
|
|
||||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
|
|
||||||
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %hashmap.key)
|
|
||||||
%s.elt7 = extractvalue [2 x %main.hasPadding] %s, 0
|
|
||||||
store %main.hasPadding %s.elt7, ptr %hashmap.key, align 8
|
|
||||||
%hashmap.key.repack8 = getelementptr inbounds [2 x %main.hasPadding], ptr %hashmap.key, i32 0, i32 1
|
|
||||||
%s.elt9 = extractvalue [2 x %main.hasPadding] %s, 1
|
|
||||||
store %main.hasPadding %s.elt9, ptr %hashmap.key.repack8, align 4
|
|
||||||
%0 = getelementptr inbounds i8, ptr %hashmap.key, i32 1
|
|
||||||
call void @runtime.memzero(ptr nonnull %0, i32 3, ptr undef) #4
|
|
||||||
%1 = getelementptr inbounds i8, ptr %hashmap.key, i32 9
|
|
||||||
call void @runtime.memzero(ptr nonnull %1, i32 3, ptr undef) #4
|
|
||||||
%2 = getelementptr inbounds i8, ptr %hashmap.key, i32 13
|
|
||||||
call void @runtime.memzero(ptr nonnull %2, i32 3, ptr undef) #4
|
|
||||||
%3 = getelementptr inbounds i8, ptr %hashmap.key, i32 21
|
|
||||||
call void @runtime.memzero(ptr nonnull %3, i32 3, ptr undef) #4
|
|
||||||
%4 = call i1 @runtime.hashmapBinaryGet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, i32 4, ptr undef) #4
|
|
||||||
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %hashmap.key)
|
|
||||||
%5 = load i32, ptr %hashmap.value, align 4
|
|
||||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
|
|
||||||
ret i32 %5
|
|
||||||
}
|
|
||||||
|
|
||||||
; Function Attrs: noinline nounwind
|
|
||||||
define hidden void @main.testZeroArraySet(ptr dereferenceable_or_null(40) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #2 {
|
|
||||||
entry:
|
|
||||||
%hashmap.key = alloca [2 x %main.hasPadding], align 8
|
|
||||||
%hashmap.value = alloca i32, align 4
|
|
||||||
%s1 = alloca [2 x %main.hasPadding], align 8
|
|
||||||
%stackalloc = alloca i8, align 1
|
|
||||||
store %main.hasPadding zeroinitializer, ptr %s1, align 8
|
|
||||||
%s1.repack2 = getelementptr inbounds [2 x %main.hasPadding], ptr %s1, i32 0, i32 1
|
|
||||||
store %main.hasPadding zeroinitializer, ptr %s1.repack2, align 4
|
|
||||||
call void @runtime.trackPointer(ptr nonnull %s1, ptr nonnull %stackalloc, ptr undef) #4
|
|
||||||
%s.elt = extractvalue [2 x %main.hasPadding] %s, 0
|
|
||||||
store %main.hasPadding %s.elt, ptr %s1, align 8
|
|
||||||
%s1.repack3 = getelementptr inbounds [2 x %main.hasPadding], ptr %s1, i32 0, i32 1
|
|
||||||
%s.elt4 = extractvalue [2 x %main.hasPadding] %s, 1
|
|
||||||
store %main.hasPadding %s.elt4, ptr %s1.repack3, align 4
|
|
||||||
call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %hashmap.value)
|
|
||||||
store i32 5, ptr %hashmap.value, align 4
|
|
||||||
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %hashmap.key)
|
|
||||||
%s.elt7 = extractvalue [2 x %main.hasPadding] %s, 0
|
|
||||||
store %main.hasPadding %s.elt7, ptr %hashmap.key, align 8
|
|
||||||
%hashmap.key.repack8 = getelementptr inbounds [2 x %main.hasPadding], ptr %hashmap.key, i32 0, i32 1
|
|
||||||
%s.elt9 = extractvalue [2 x %main.hasPadding] %s, 1
|
|
||||||
store %main.hasPadding %s.elt9, ptr %hashmap.key.repack8, align 4
|
|
||||||
%0 = getelementptr inbounds i8, ptr %hashmap.key, i32 1
|
|
||||||
call void @runtime.memzero(ptr nonnull %0, i32 3, ptr undef) #4
|
|
||||||
%1 = getelementptr inbounds i8, ptr %hashmap.key, i32 9
|
|
||||||
call void @runtime.memzero(ptr nonnull %1, i32 3, ptr undef) #4
|
|
||||||
%2 = getelementptr inbounds i8, ptr %hashmap.key, i32 13
|
|
||||||
call void @runtime.memzero(ptr nonnull %2, i32 3, ptr undef) #4
|
|
||||||
%3 = getelementptr inbounds i8, ptr %hashmap.key, i32 21
|
|
||||||
call void @runtime.memzero(ptr nonnull %3, i32 3, ptr undef) #4
|
|
||||||
call void @runtime.hashmapBinarySet(ptr %m, ptr nonnull %hashmap.key, ptr nonnull %hashmap.value, ptr undef) #4
|
|
||||||
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %hashmap.key)
|
|
||||||
call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %hashmap.value)
|
|
||||||
ret void
|
|
||||||
}
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
|
||||||
define hidden void @main.main(ptr %context) unnamed_addr #1 {
|
|
||||||
entry:
|
|
||||||
ret void
|
|
||||||
}
|
|
||||||
|
|
||||||
attributes #0 = { "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
|
||||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
|
||||||
attributes #2 = { noinline nounwind "target-features"="+bulk-memory,+nontrapping-fptoint,+sign-ext" }
|
|
||||||
attributes #3 = { argmemonly nocallback nofree nosync nounwind willreturn }
|
|
||||||
attributes #4 = { nounwind }
|
|
||||||
@@ -1357,7 +1357,6 @@ func main() {
|
|||||||
command := os.Args[1]
|
command := os.Args[1]
|
||||||
|
|
||||||
opt := flag.String("opt", "z", "optimization level: 0, 1, 2, s, z")
|
opt := flag.String("opt", "z", "optimization level: 0, 1, 2, s, z")
|
||||||
lto := flag.String("lto", "legacy", "LTO mode: legacy or thin")
|
|
||||||
gc := flag.String("gc", "", "garbage collector to use (none, leaking, conservative)")
|
gc := flag.String("gc", "", "garbage collector to use (none, leaking, conservative)")
|
||||||
panicStrategy := flag.String("panic", "print", "panic strategy (print, trap)")
|
panicStrategy := flag.String("panic", "print", "panic strategy (print, trap)")
|
||||||
scheduler := flag.String("scheduler", "", "which scheduler to use (none, tasks, asyncify)")
|
scheduler := flag.String("scheduler", "", "which scheduler to use (none, tasks, asyncify)")
|
||||||
@@ -1460,7 +1459,6 @@ func main() {
|
|||||||
Target: *target,
|
Target: *target,
|
||||||
StackSize: stackSize,
|
StackSize: stackSize,
|
||||||
Opt: *opt,
|
Opt: *opt,
|
||||||
LTO: *lto,
|
|
||||||
GC: *gc,
|
GC: *gc,
|
||||||
PanicStrategy: *panicStrategy,
|
PanicStrategy: *panicStrategy,
|
||||||
Scheduler: *scheduler,
|
Scheduler: *scheduler,
|
||||||
|
|||||||
+10
-13
@@ -34,6 +34,7 @@ var supportedLinuxArches = map[string]string{
|
|||||||
"X86Linux": "linux/386",
|
"X86Linux": "linux/386",
|
||||||
"ARMLinux": "linux/arm/6",
|
"ARMLinux": "linux/arm/6",
|
||||||
"ARM64Linux": "linux/arm64",
|
"ARM64Linux": "linux/arm64",
|
||||||
|
"MIPSLinux": "linux/mips",
|
||||||
}
|
}
|
||||||
|
|
||||||
var sema = make(chan struct{}, runtime.NumCPU())
|
var sema = make(chan struct{}, runtime.NumCPU())
|
||||||
@@ -105,13 +106,6 @@ func TestBuild(t *testing.T) {
|
|||||||
runTestWithConfig("print.go", t, opts, nil, nil)
|
runTestWithConfig("print.go", t, opts, nil, nil)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("lto=thin", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
opts := optionsFromTarget("", sema)
|
|
||||||
opts.LTO = "thin"
|
|
||||||
runTestWithConfig("init.go", t, opts, nil, nil)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("ldflags", func(t *testing.T) {
|
t.Run("ldflags", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
opts := optionsFromTarget("", sema)
|
opts := optionsFromTarget("", sema)
|
||||||
@@ -187,8 +181,7 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
|
|||||||
// Skip the ones that aren't.
|
// Skip the ones that aren't.
|
||||||
switch name {
|
switch name {
|
||||||
case "reflect.go":
|
case "reflect.go":
|
||||||
// Reflect tests do not run correctly, probably because of the
|
// Reflect tests do not work due to type code issues.
|
||||||
// limited amount of memory.
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
case "gc.go":
|
case "gc.go":
|
||||||
@@ -196,16 +189,20 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
case "json.go", "stdlib.go", "testing.go":
|
case "json.go", "stdlib.go", "testing.go":
|
||||||
// Too big for AVR. Doesn't fit in flash/RAM.
|
// Breaks interp.
|
||||||
continue
|
continue
|
||||||
|
|
||||||
case "math.go":
|
case "math.go":
|
||||||
// Needs newer picolibc version (for sqrt).
|
// Stuck somewhere, not sure what's happening.
|
||||||
continue
|
continue
|
||||||
|
|
||||||
case "cgo/":
|
case "cgo/":
|
||||||
// CGo function pointers don't work on AVR (needs LLVM 16 and
|
// CGo does not work on AVR.
|
||||||
// some compiler changes).
|
continue
|
||||||
|
|
||||||
|
case "timers.go":
|
||||||
|
// Doesn't compile:
|
||||||
|
// panic: compiler: could not store type code number inside interface type code
|
||||||
continue
|
continue
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
//go:build rp2040
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
// This example demonstrates scheduling a delayed interrupt by real time clock.
|
|
||||||
//
|
|
||||||
// An interrupt may execute user callback function or used for its side effects
|
|
||||||
// like waking up from sleep or dormant states.
|
|
||||||
//
|
|
||||||
// The interrupt can be configured to repeat.
|
|
||||||
//
|
|
||||||
// There is no separate method to disable interrupt, use 0 delay for that.
|
|
||||||
//
|
|
||||||
// Unfortunately, it is not possible to use time.Duration to work with RTC directly,
|
|
||||||
// that would introduce a circular dependency between "machine" and "time" packages.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"machine"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
// Schedule and enable recurring interrupt.
|
|
||||||
// The callback function is executed in the context of an interrupt handler,
|
|
||||||
// so regular restructions for this sort of code apply: no blocking, no memory allocation, etc.
|
|
||||||
delay := time.Minute + 12*time.Second
|
|
||||||
machine.RTC.SetInterrupt(uint32(delay.Seconds()), true, func() { println("Peekaboo!") })
|
|
||||||
|
|
||||||
for {
|
|
||||||
fmt.Printf("%v\r\n", time.Now().Format(time.RFC3339))
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,10 +2,7 @@
|
|||||||
|
|
||||||
package task
|
package task
|
||||||
|
|
||||||
import (
|
import "unsafe"
|
||||||
"runtime/interrupt"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:linkname runtimePanic runtime.runtimePanic
|
//go:linkname runtimePanic runtime.runtimePanic
|
||||||
func runtimePanic(str string)
|
func runtimePanic(str string)
|
||||||
@@ -48,9 +45,6 @@ func Pause() {
|
|||||||
if *currentTask.state.canaryPtr != stackCanary {
|
if *currentTask.state.canaryPtr != stackCanary {
|
||||||
runtimePanic("goroutine stack overflow")
|
runtimePanic("goroutine stack overflow")
|
||||||
}
|
}
|
||||||
if interrupt.In() {
|
|
||||||
runtimePanic("blocked inside interrupt")
|
|
||||||
}
|
|
||||||
currentTask.state.pause()
|
currentTask.state.pause()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
.section .text.tinygo_startTask
|
||||||
|
.global tinygo_startTask
|
||||||
|
.type tinygo_startTask, %function
|
||||||
|
tinygo_startTask:
|
||||||
|
// Small assembly stub for starting a goroutine. This is already run on the
|
||||||
|
// new stack, with the callee-saved registers already loaded.
|
||||||
|
// Most importantly, s0 contains the pc of the to-be-started function and s1
|
||||||
|
// contains the only argument it is given. Multiple arguments are packed
|
||||||
|
// into one by storing them in a new allocation.
|
||||||
|
|
||||||
|
// Set the first argument of the goroutine start wrapper, which contains all
|
||||||
|
// the arguments.
|
||||||
|
move $a0, $s1
|
||||||
|
|
||||||
|
// Branch to the "goroutine start" function. Use jalr to write the return
|
||||||
|
// address to ra so we'll return here after the goroutine exits.
|
||||||
|
jalr $s0
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
|
||||||
|
// After return, exit this goroutine. This is a tail call.
|
||||||
|
j tinygo_pause
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
|
||||||
|
.section .text.tinygo_swapTask
|
||||||
|
.global tinygo_swapTask
|
||||||
|
.type tinygo_swapTask, %function
|
||||||
|
tinygo_swapTask:
|
||||||
|
// This function gets the following parameters:
|
||||||
|
// a0 = newStack uintptr
|
||||||
|
// a1 = oldStack *uintptr
|
||||||
|
|
||||||
|
// Push all callee-saved registers.
|
||||||
|
addiu $sp, $sp, -40
|
||||||
|
sw $ra, 36($sp)
|
||||||
|
sw $s8, 32($sp)
|
||||||
|
sw $s7, 28($sp)
|
||||||
|
sw $s6, 24($sp)
|
||||||
|
sw $s5, 20($sp)
|
||||||
|
sw $s4, 16($sp)
|
||||||
|
sw $s3, 12($sp)
|
||||||
|
sw $s2, 8($sp)
|
||||||
|
sw $s1, 4($sp)
|
||||||
|
sw $s0, ($sp)
|
||||||
|
|
||||||
|
// Save the current stack pointer in oldStack.
|
||||||
|
sw $sp, 0($a1)
|
||||||
|
|
||||||
|
// Switch to the new stack pointer.
|
||||||
|
move $sp, $a0
|
||||||
|
|
||||||
|
// Pop all saved registers from this new stack.
|
||||||
|
lw $ra, 36($sp)
|
||||||
|
lw $s8, 32($sp)
|
||||||
|
lw $s7, 28($sp)
|
||||||
|
lw $s6, 24($sp)
|
||||||
|
lw $s5, 20($sp)
|
||||||
|
lw $s4, 16($sp)
|
||||||
|
lw $s3, 12($sp)
|
||||||
|
lw $s2, 8($sp)
|
||||||
|
lw $s1, 4($sp)
|
||||||
|
lw $s0, ($sp)
|
||||||
|
addiu $sp, $sp, 40
|
||||||
|
|
||||||
|
// Return into the task.
|
||||||
|
jalr $ra
|
||||||
|
nop
|
||||||
|
nop
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
//go:build scheduler.tasks && mips
|
||||||
|
|
||||||
|
package task
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
var systemStack uintptr
|
||||||
|
|
||||||
|
// calleeSavedRegs is the list of registers that must be saved and restored when
|
||||||
|
// switching between tasks. Also see task_stack_mips.S that relies on the exact
|
||||||
|
// layout of this struct.
|
||||||
|
type calleeSavedRegs struct {
|
||||||
|
s0 uintptr
|
||||||
|
s1 uintptr
|
||||||
|
s2 uintptr
|
||||||
|
s3 uintptr
|
||||||
|
s4 uintptr
|
||||||
|
s5 uintptr
|
||||||
|
s6 uintptr
|
||||||
|
s7 uintptr
|
||||||
|
s8 uintptr
|
||||||
|
ra uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// archInit runs architecture-specific setup for the goroutine startup.
|
||||||
|
func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
|
||||||
|
// Store the initial sp for the startTask function (implemented in assembly).
|
||||||
|
s.sp = uintptr(unsafe.Pointer(r))
|
||||||
|
|
||||||
|
// Initialize the registers.
|
||||||
|
// These will be popped off of the stack on the first resume of the goroutine.
|
||||||
|
|
||||||
|
// Start the function at tinygo_startTask (defined in src/internal/task/task_stack_mips.S).
|
||||||
|
// This assembly code calls a function (passed in s0) with a single argument
|
||||||
|
// (passed in s1). After the function returns, it calls Pause().
|
||||||
|
r.ra = uintptr(unsafe.Pointer(&startTask))
|
||||||
|
|
||||||
|
// Pass the function to call in s0.
|
||||||
|
// This function is a compiler-generated wrapper which loads arguments out of a struct pointer.
|
||||||
|
// See createGoroutineStartWrapper (defined in compiler/goroutine.go) for more information.
|
||||||
|
r.s0 = fn
|
||||||
|
|
||||||
|
// Pass the pointer to the arguments struct in s1.
|
||||||
|
r.s1 = uintptr(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *state) resume() {
|
||||||
|
swapTask(s.sp, &systemStack)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *state) pause() {
|
||||||
|
newStack := systemStack
|
||||||
|
systemStack = 0
|
||||||
|
swapTask(newStack, &s.sp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SystemStack returns the system stack pointer when called from a task stack.
|
||||||
|
// When called from the system stack, it returns 0.
|
||||||
|
func SystemStack() uintptr {
|
||||||
|
return systemStack
|
||||||
|
}
|
||||||
@@ -56,18 +56,6 @@ var (
|
|||||||
}
|
}
|
||||||
DefaultUART = UART0
|
DefaultUART = UART0
|
||||||
|
|
||||||
// Since we treat UART1 as zero, let's also call it by the real name
|
|
||||||
UART1 = UART0
|
|
||||||
|
|
||||||
// UART2
|
|
||||||
UART2 = &_UART2
|
|
||||||
_UART2 = UART{
|
|
||||||
Buffer: NewRingBuffer(),
|
|
||||||
Bus: stm32.USART2,
|
|
||||||
TxAltFuncSelector: AF7_USART1_2,
|
|
||||||
RxAltFuncSelector: AF7_USART1_2,
|
|
||||||
}
|
|
||||||
|
|
||||||
// I2C Busses
|
// I2C Busses
|
||||||
I2C1 = &I2C{
|
I2C1 = &I2C{
|
||||||
Bus: stm32.I2C1,
|
Bus: stm32.I2C1,
|
||||||
@@ -84,5 +72,4 @@ var (
|
|||||||
func init() {
|
func init() {
|
||||||
// Enable UARTs Interrupts
|
// Enable UARTs Interrupts
|
||||||
UART0.Interrupt = interrupt.New(stm32.IRQ_USART1, _UART0.handleInterrupt)
|
UART0.Interrupt = interrupt.New(stm32.IRQ_USART1, _UART0.handleInterrupt)
|
||||||
UART2.Interrupt = interrupt.New(stm32.IRQ_USART2, _UART2.handleInterrupt)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,11 +50,11 @@ const (
|
|||||||
|
|
||||||
// I2C pins
|
// I2C pins
|
||||||
const (
|
const (
|
||||||
I2C0_SDA_PIN Pin = D2
|
I2C0_SDA_PIN Pin = D4
|
||||||
I2C0_SCL_PIN Pin = D3
|
I2C0_SCL_PIN Pin = D5
|
||||||
|
|
||||||
I2C1_SDA_PIN Pin = D4
|
I2C1_SDA_PIN Pin = NoPin
|
||||||
I2C1_SCL_PIN Pin = D5
|
I2C1_SCL_PIN Pin = NoPin
|
||||||
)
|
)
|
||||||
|
|
||||||
// SPI pins
|
// SPI pins
|
||||||
|
|||||||
@@ -406,61 +406,61 @@ func (p Pin) getPinCfg() uint8 {
|
|||||||
return uint8(sam.PORT.PINCFG1_0.Get()>>16) & 0xff
|
return uint8(sam.PORT.PINCFG1_0.Get()>>16) & 0xff
|
||||||
case 35: // PB03
|
case 35: // PB03
|
||||||
return uint8(sam.PORT.PINCFG1_0.Get()>>24) & 0xff
|
return uint8(sam.PORT.PINCFG1_0.Get()>>24) & 0xff
|
||||||
case 36: // PB04
|
case 37: // PB04
|
||||||
return uint8(sam.PORT.PINCFG1_4.Get()>>0) & 0xff
|
return uint8(sam.PORT.PINCFG1_4.Get()>>0) & 0xff
|
||||||
case 37: // PB05
|
case 38: // PB05
|
||||||
return uint8(sam.PORT.PINCFG1_4.Get()>>8) & 0xff
|
return uint8(sam.PORT.PINCFG1_4.Get()>>8) & 0xff
|
||||||
case 38: // PB06
|
case 39: // PB06
|
||||||
return uint8(sam.PORT.PINCFG1_4.Get()>>16) & 0xff
|
return uint8(sam.PORT.PINCFG1_4.Get()>>16) & 0xff
|
||||||
case 39: // PB07
|
case 40: // PB07
|
||||||
return uint8(sam.PORT.PINCFG1_4.Get()>>24) & 0xff
|
return uint8(sam.PORT.PINCFG1_4.Get()>>24) & 0xff
|
||||||
case 40: // PB08
|
case 41: // PB08
|
||||||
return uint8(sam.PORT.PINCFG1_8.Get()>>0) & 0xff
|
return uint8(sam.PORT.PINCFG1_8.Get()>>0) & 0xff
|
||||||
case 41: // PB09
|
case 42: // PB09
|
||||||
return uint8(sam.PORT.PINCFG1_8.Get()>>8) & 0xff
|
return uint8(sam.PORT.PINCFG1_8.Get()>>8) & 0xff
|
||||||
case 42: // PB10
|
case 43: // PB10
|
||||||
return uint8(sam.PORT.PINCFG1_8.Get()>>16) & 0xff
|
return uint8(sam.PORT.PINCFG1_8.Get()>>16) & 0xff
|
||||||
case 43: // PB11
|
case 44: // PB11
|
||||||
return uint8(sam.PORT.PINCFG1_8.Get()>>24) & 0xff
|
return uint8(sam.PORT.PINCFG1_8.Get()>>24) & 0xff
|
||||||
case 44: // PB12
|
case 45: // PB12
|
||||||
return uint8(sam.PORT.PINCFG1_12.Get()>>0) & 0xff
|
return uint8(sam.PORT.PINCFG1_12.Get()>>0) & 0xff
|
||||||
case 45: // PB13
|
case 46: // PB13
|
||||||
return uint8(sam.PORT.PINCFG1_12.Get()>>8) & 0xff
|
return uint8(sam.PORT.PINCFG1_12.Get()>>8) & 0xff
|
||||||
case 46: // PB14
|
case 47: // PB14
|
||||||
return uint8(sam.PORT.PINCFG1_12.Get()>>16) & 0xff
|
return uint8(sam.PORT.PINCFG1_12.Get()>>16) & 0xff
|
||||||
case 47: // PB15
|
case 48: // PB15
|
||||||
return uint8(sam.PORT.PINCFG1_12.Get()>>24) & 0xff
|
return uint8(sam.PORT.PINCFG1_12.Get()>>24) & 0xff
|
||||||
case 48: // PB16
|
case 49: // PB16
|
||||||
return uint8(sam.PORT.PINCFG1_16.Get()>>0) & 0xff
|
return uint8(sam.PORT.PINCFG1_16.Get()>>0) & 0xff
|
||||||
case 49: // PB17
|
case 50: // PB17
|
||||||
return uint8(sam.PORT.PINCFG1_16.Get()>>8) & 0xff
|
return uint8(sam.PORT.PINCFG1_16.Get()>>8) & 0xff
|
||||||
case 50: // PB18
|
case 51: // PB18
|
||||||
return uint8(sam.PORT.PINCFG1_16.Get()>>16) & 0xff
|
return uint8(sam.PORT.PINCFG1_16.Get()>>16) & 0xff
|
||||||
case 51: // PB19
|
case 52: // PB19
|
||||||
return uint8(sam.PORT.PINCFG1_16.Get()>>24) & 0xff
|
return uint8(sam.PORT.PINCFG1_16.Get()>>24) & 0xff
|
||||||
case 52: // PB20
|
case 53: // PB20
|
||||||
return uint8(sam.PORT.PINCFG1_20.Get()>>0) & 0xff
|
return uint8(sam.PORT.PINCFG1_20.Get()>>0) & 0xff
|
||||||
case 53: // PB21
|
case 54: // PB21
|
||||||
return uint8(sam.PORT.PINCFG1_20.Get()>>8) & 0xff
|
return uint8(sam.PORT.PINCFG1_20.Get()>>8) & 0xff
|
||||||
case 54: // PB22
|
case 55: // PB22
|
||||||
return uint8(sam.PORT.PINCFG1_20.Get()>>16) & 0xff
|
return uint8(sam.PORT.PINCFG1_20.Get()>>16) & 0xff
|
||||||
case 55: // PB23
|
case 56: // PB23
|
||||||
return uint8(sam.PORT.PINCFG1_20.Get()>>24) & 0xff
|
return uint8(sam.PORT.PINCFG1_20.Get()>>24) & 0xff
|
||||||
case 56: // PB24
|
case 57: // PB24
|
||||||
return uint8(sam.PORT.PINCFG1_24.Get()>>0) & 0xff
|
return uint8(sam.PORT.PINCFG1_24.Get()>>0) & 0xff
|
||||||
case 57: // PB25
|
case 58: // PB25
|
||||||
return uint8(sam.PORT.PINCFG1_24.Get()>>8) & 0xff
|
return uint8(sam.PORT.PINCFG1_24.Get()>>8) & 0xff
|
||||||
case 58: // PB26
|
case 59: // PB26
|
||||||
return uint8(sam.PORT.PINCFG1_24.Get()>>16) & 0xff
|
return uint8(sam.PORT.PINCFG1_24.Get()>>16) & 0xff
|
||||||
case 59: // PB27
|
case 60: // PB27
|
||||||
return uint8(sam.PORT.PINCFG1_24.Get()>>24) & 0xff
|
return uint8(sam.PORT.PINCFG1_24.Get()>>24) & 0xff
|
||||||
case 60: // PB28
|
case 61: // PB28
|
||||||
return uint8(sam.PORT.PINCFG1_28.Get()>>0) & 0xff
|
return uint8(sam.PORT.PINCFG1_28.Get()>>0) & 0xff
|
||||||
case 61: // PB29
|
case 62: // PB29
|
||||||
return uint8(sam.PORT.PINCFG1_28.Get()>>8) & 0xff
|
return uint8(sam.PORT.PINCFG1_28.Get()>>8) & 0xff
|
||||||
case 62: // PB30
|
case 63: // PB30
|
||||||
return uint8(sam.PORT.PINCFG1_28.Get()>>16) & 0xff
|
return uint8(sam.PORT.PINCFG1_28.Get()>>16) & 0xff
|
||||||
case 63: // PB31
|
case 64: // PB31
|
||||||
return uint8(sam.PORT.PINCFG1_28.Get()>>24) & 0xff
|
return uint8(sam.PORT.PINCFG1_28.Get()>>24) & 0xff
|
||||||
default:
|
default:
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@@ -57,8 +57,6 @@ var (
|
|||||||
ErrInvalidTgtAddr = errors.New("invalid target i2c address not in 0..0x80 or is reserved")
|
ErrInvalidTgtAddr = errors.New("invalid target i2c address not in 0..0x80 or is reserved")
|
||||||
ErrI2CGeneric = errors.New("i2c error")
|
ErrI2CGeneric = errors.New("i2c error")
|
||||||
ErrRP2040I2CDisable = errors.New("i2c rp2040 peripheral timeout in disable")
|
ErrRP2040I2CDisable = errors.New("i2c rp2040 peripheral timeout in disable")
|
||||||
errInvalidI2CSDA = errors.New("invalid I2C SDA pin")
|
|
||||||
errInvalidI2CSCL = errors.New("invalid I2C SCL pin")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Tx performs a write and then a read transfer placing the result in
|
// Tx performs a write and then a read transfer placing the result in
|
||||||
@@ -92,7 +90,7 @@ func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
|
|||||||
// SCL: 3, 7, 11, 15, 19, 27
|
// SCL: 3, 7, 11, 15, 19, 27
|
||||||
func (i2c *I2C) Configure(config I2CConfig) error {
|
func (i2c *I2C) Configure(config I2CConfig) error {
|
||||||
const defaultBaud uint32 = 100_000 // 100kHz standard mode
|
const defaultBaud uint32 = 100_000 // 100kHz standard mode
|
||||||
if config.SCL == 0 && config.SDA == 0 {
|
if config.SCL == 0 {
|
||||||
// If config pins are zero valued or clock pin is invalid then we set default values.
|
// If config pins are zero valued or clock pin is invalid then we set default values.
|
||||||
switch i2c.Bus {
|
switch i2c.Bus {
|
||||||
case rp.I2C0:
|
case rp.I2C0:
|
||||||
@@ -103,23 +101,6 @@ func (i2c *I2C) Configure(config I2CConfig) error {
|
|||||||
config.SDA = I2C1_SDA_PIN
|
config.SDA = I2C1_SDA_PIN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var okSCL, okSDA bool
|
|
||||||
switch i2c.Bus {
|
|
||||||
case rp.I2C0:
|
|
||||||
okSCL = (config.SCL+3)%4 == 0
|
|
||||||
okSDA = (config.SDA+4)%4 == 0
|
|
||||||
case rp.I2C1:
|
|
||||||
okSCL = (config.SCL+1)%4 == 0
|
|
||||||
okSDA = (config.SDA+2)%4 == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case !okSCL:
|
|
||||||
return errInvalidI2CSCL
|
|
||||||
case !okSDA:
|
|
||||||
return errInvalidI2CSDA
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.Frequency == 0 {
|
if config.Frequency == 0 {
|
||||||
config.Frequency = defaultBaud
|
config.Frequency = defaultBaud
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,240 +0,0 @@
|
|||||||
//go:build rp2040
|
|
||||||
|
|
||||||
// Implementation based on code located here:
|
|
||||||
// https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/hardware_rtc/rtc.c
|
|
||||||
|
|
||||||
package machine
|
|
||||||
|
|
||||||
import (
|
|
||||||
"device/rp"
|
|
||||||
"errors"
|
|
||||||
"runtime/interrupt"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
type rtcType rp.RTC_Type
|
|
||||||
|
|
||||||
type rtcTime struct {
|
|
||||||
Year int16
|
|
||||||
Month int8
|
|
||||||
Day int8
|
|
||||||
Dotw int8
|
|
||||||
Hour int8
|
|
||||||
Min int8
|
|
||||||
Sec int8
|
|
||||||
}
|
|
||||||
|
|
||||||
var RTC = (*rtcType)(unsafe.Pointer(rp.RTC))
|
|
||||||
|
|
||||||
const (
|
|
||||||
second = 1
|
|
||||||
minute = 60 * second
|
|
||||||
hour = 60 * minute
|
|
||||||
day = 24 * hour
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
rtcAlarmRepeats bool
|
|
||||||
rtcCallback func()
|
|
||||||
rtcEpoch = rtcTime{
|
|
||||||
Year: 1970, Month: 1, Day: 1, Dotw: 4, Hour: 0, Min: 0, Sec: 0,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrRtcDelayTooSmall = errors.New("RTC interrupt deplay is too small, shall be at least 1 second")
|
|
||||||
ErrRtcDelayTooLarge = errors.New("RTC interrupt deplay is too large, shall be no more than 1 day")
|
|
||||||
)
|
|
||||||
|
|
||||||
// SetInterrupt configures delayed and optionally recurring interrupt by real time clock.
|
|
||||||
//
|
|
||||||
// Delay is specified in whole seconds, allowed range depends on platform.
|
|
||||||
// Zero delay disables previously configured interrupt, if any.
|
|
||||||
//
|
|
||||||
// RP2040 implementation allows delay to be up to 1 day, otherwise a respective error is emitted.
|
|
||||||
func (rtc *rtcType) SetInterrupt(delay uint32, repeat bool, callback func()) error {
|
|
||||||
|
|
||||||
// Verify delay range
|
|
||||||
if delay > day {
|
|
||||||
return ErrRtcDelayTooLarge
|
|
||||||
}
|
|
||||||
|
|
||||||
// De-configure delayed interrupt if delay is zero
|
|
||||||
if delay == 0 {
|
|
||||||
rtc.disableInterruptMatch()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure delayed interrupt
|
|
||||||
rtc.setDivider()
|
|
||||||
|
|
||||||
rtcAlarmRepeats = repeat
|
|
||||||
rtcCallback = callback
|
|
||||||
|
|
||||||
err := rtc.setTime(rtcEpoch)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
rtc.setAlarm(toAlarmTime(delay), callback)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func toAlarmTime(delay uint32) rtcTime {
|
|
||||||
result := rtcEpoch
|
|
||||||
remainder := delay + 1 // needed "+1", otherwise alarm fires one second too early
|
|
||||||
if remainder >= hour {
|
|
||||||
result.Hour = int8(remainder / hour)
|
|
||||||
remainder %= hour
|
|
||||||
}
|
|
||||||
if remainder >= minute {
|
|
||||||
result.Min = int8(remainder / minute)
|
|
||||||
remainder %= minute
|
|
||||||
}
|
|
||||||
result.Sec = int8(remainder)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rtc *rtcType) setDivider() {
|
|
||||||
// Get clk_rtc freq and make sure it is running
|
|
||||||
rtcFreq := configuredFreq[clkRTC]
|
|
||||||
if rtcFreq == 0 {
|
|
||||||
panic("can not set RTC divider, clock is not running")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take rtc out of reset now that we know clk_rtc is running
|
|
||||||
resetBlock(rp.RESETS_RESET_RTC)
|
|
||||||
unresetBlockWait(rp.RESETS_RESET_RTC)
|
|
||||||
|
|
||||||
// Set up the 1 second divider.
|
|
||||||
// If rtc_freq is 400 then clkdiv_m1 should be 399
|
|
||||||
rtcFreq -= 1
|
|
||||||
|
|
||||||
// Check the freq is not too big to divide
|
|
||||||
if rtcFreq > rp.RTC_CLKDIV_M1_CLKDIV_M1_Msk {
|
|
||||||
panic("can not set RTC divider, clock frequency is too big to divide")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write divide value
|
|
||||||
rtc.CLKDIV_M1.Set(rtcFreq)
|
|
||||||
}
|
|
||||||
|
|
||||||
// setTime configures RTC with supplied time, initialises and activates it.
|
|
||||||
func (rtc *rtcType) setTime(t rtcTime) error {
|
|
||||||
|
|
||||||
// Disable RTC and wait while it is still running
|
|
||||||
rtc.CTRL.Set(0)
|
|
||||||
for rtc.isActive() {
|
|
||||||
}
|
|
||||||
|
|
||||||
rtc.SETUP_0.Set((uint32(t.Year) << rp.RTC_SETUP_0_YEAR_Pos) |
|
|
||||||
(uint32(t.Month) << rp.RTC_SETUP_0_MONTH_Pos) |
|
|
||||||
(uint32(t.Day) << rp.RTC_SETUP_0_DAY_Pos))
|
|
||||||
|
|
||||||
rtc.SETUP_1.Set((uint32(t.Dotw) << rp.RTC_SETUP_1_DOTW_Pos) |
|
|
||||||
(uint32(t.Hour) << rp.RTC_SETUP_1_HOUR_Pos) |
|
|
||||||
(uint32(t.Min) << rp.RTC_SETUP_1_MIN_Pos) |
|
|
||||||
(uint32(t.Sec) << rp.RTC_SETUP_1_SEC_Pos))
|
|
||||||
|
|
||||||
// Load setup values into RTC clock domain
|
|
||||||
rtc.CTRL.SetBits(rp.RTC_CTRL_LOAD)
|
|
||||||
|
|
||||||
// Enable RTC and wait for it to be running
|
|
||||||
rtc.CTRL.SetBits(rp.RTC_CTRL_RTC_ENABLE)
|
|
||||||
for !rtc.isActive() {
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rtc *rtcType) isActive() bool {
|
|
||||||
return rtc.CTRL.HasBits(rp.RTC_CTRL_RTC_ACTIVE)
|
|
||||||
}
|
|
||||||
|
|
||||||
// setAlarm configures alarm in RTC and arms it.
|
|
||||||
// The callback is executed in the context of an interrupt handler,
|
|
||||||
// so regular restructions for this sort of code apply: no blocking, no memory allocation, etc.
|
|
||||||
func (rtc *rtcType) setAlarm(t rtcTime, callback func()) {
|
|
||||||
|
|
||||||
rtc.disableInterruptMatch()
|
|
||||||
|
|
||||||
// Clear all match enable bits
|
|
||||||
rtc.IRQ_SETUP_0.ClearBits(rp.RTC_IRQ_SETUP_0_YEAR_ENA | rp.RTC_IRQ_SETUP_0_MONTH_ENA | rp.RTC_IRQ_SETUP_0_DAY_ENA)
|
|
||||||
rtc.IRQ_SETUP_1.ClearBits(rp.RTC_IRQ_SETUP_1_DOTW_ENA | rp.RTC_IRQ_SETUP_1_HOUR_ENA | rp.RTC_IRQ_SETUP_1_MIN_ENA | rp.RTC_IRQ_SETUP_1_SEC_ENA)
|
|
||||||
|
|
||||||
// Only add to setup if it isn't -1 and set the match enable bits for things we care about
|
|
||||||
if t.Year >= 0 {
|
|
||||||
rtc.IRQ_SETUP_0.SetBits(uint32(t.Year) << rp.RTC_SETUP_0_YEAR_Pos)
|
|
||||||
rtc.IRQ_SETUP_0.SetBits(rp.RTC_IRQ_SETUP_0_YEAR_ENA)
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.Month >= 0 {
|
|
||||||
rtc.IRQ_SETUP_0.SetBits(uint32(t.Month) << rp.RTC_SETUP_0_MONTH_Pos)
|
|
||||||
rtc.IRQ_SETUP_0.SetBits(rp.RTC_IRQ_SETUP_0_MONTH_ENA)
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.Day >= 0 {
|
|
||||||
rtc.IRQ_SETUP_0.SetBits(uint32(t.Day) << rp.RTC_SETUP_0_DAY_Pos)
|
|
||||||
rtc.IRQ_SETUP_0.SetBits(rp.RTC_IRQ_SETUP_0_DAY_ENA)
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.Dotw >= 0 {
|
|
||||||
rtc.IRQ_SETUP_1.SetBits(uint32(t.Dotw) << rp.RTC_SETUP_1_DOTW_Pos)
|
|
||||||
rtc.IRQ_SETUP_1.SetBits(rp.RTC_IRQ_SETUP_1_DOTW_ENA)
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.Hour >= 0 {
|
|
||||||
rtc.IRQ_SETUP_1.SetBits(uint32(t.Hour) << rp.RTC_SETUP_1_HOUR_Pos)
|
|
||||||
rtc.IRQ_SETUP_1.SetBits(rp.RTC_IRQ_SETUP_1_HOUR_ENA)
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.Min >= 0 {
|
|
||||||
rtc.IRQ_SETUP_1.SetBits(uint32(t.Min) << rp.RTC_SETUP_1_MIN_Pos)
|
|
||||||
rtc.IRQ_SETUP_1.SetBits(rp.RTC_IRQ_SETUP_1_MIN_ENA)
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.Sec >= 0 {
|
|
||||||
rtc.IRQ_SETUP_1.SetBits(uint32(t.Sec) << rp.RTC_SETUP_1_SEC_Pos)
|
|
||||||
rtc.IRQ_SETUP_1.SetBits(rp.RTC_IRQ_SETUP_1_SEC_ENA)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable the IRQ at the proc
|
|
||||||
interrupt.New(rp.IRQ_RTC_IRQ, rtcHandleInterrupt).Enable()
|
|
||||||
|
|
||||||
// Enable the IRQ at the peri
|
|
||||||
rtc.INTE.Set(rp.RTC_INTE_RTC)
|
|
||||||
|
|
||||||
rtc.enableInterruptMatch()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rtc *rtcType) enableInterruptMatch() {
|
|
||||||
// Set matching and wait for it to be enabled
|
|
||||||
rtc.IRQ_SETUP_0.SetBits(rp.RTC_IRQ_SETUP_0_MATCH_ENA)
|
|
||||||
for !rtc.IRQ_SETUP_0.HasBits(rp.RTC_IRQ_SETUP_0_MATCH_ACTIVE) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rtc *rtcType) disableInterruptMatch() {
|
|
||||||
// Disable matching and wait for it to stop being active
|
|
||||||
rtc.IRQ_SETUP_0.ClearBits(rp.RTC_IRQ_SETUP_0_MATCH_ENA)
|
|
||||||
for rtc.IRQ_SETUP_0.HasBits(rp.RTC_IRQ_SETUP_0_MATCH_ACTIVE) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func rtcHandleInterrupt(itr interrupt.Interrupt) {
|
|
||||||
// Always disable the alarm to clear the current IRQ.
|
|
||||||
// Even if it is a repeatable alarm, we don't want it to keep firing.
|
|
||||||
// If it matches on a second it can keep firing for that second.
|
|
||||||
RTC.disableInterruptMatch()
|
|
||||||
|
|
||||||
// Call user callback function
|
|
||||||
if rtcCallback != nil {
|
|
||||||
rtcCallback()
|
|
||||||
}
|
|
||||||
|
|
||||||
if rtcAlarmRepeats {
|
|
||||||
// If it is a repeatable alarm, reset time and re-enable the alarm.
|
|
||||||
RTC.setTime(rtcEpoch)
|
|
||||||
RTC.enableInterruptMatch()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -40,9 +40,6 @@ var (
|
|||||||
ErrLSBNotSupported = errors.New("SPI LSB unsupported on PL022")
|
ErrLSBNotSupported = errors.New("SPI LSB unsupported on PL022")
|
||||||
ErrSPITimeout = errors.New("SPI timeout")
|
ErrSPITimeout = errors.New("SPI timeout")
|
||||||
ErrSPIBaud = errors.New("SPI baud too low or above 66.5Mhz")
|
ErrSPIBaud = errors.New("SPI baud too low or above 66.5Mhz")
|
||||||
errSPIInvalidSDI = errors.New("invalid SPI SDI pin")
|
|
||||||
errSPIInvalidSDO = errors.New("invalid SPI SDO pin")
|
|
||||||
errSPIInvalidSCK = errors.New("invalid SPI SCK pin")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SPI struct {
|
type SPI struct {
|
||||||
@@ -165,7 +162,7 @@ func (spi SPI) GetBaudRate() uint32 {
|
|||||||
// No pin configuration is needed of SCK, SDO and SDI needed after calling Configure.
|
// No pin configuration is needed of SCK, SDO and SDI needed after calling Configure.
|
||||||
func (spi SPI) Configure(config SPIConfig) error {
|
func (spi SPI) Configure(config SPIConfig) error {
|
||||||
const defaultBaud uint32 = 115200
|
const defaultBaud uint32 = 115200
|
||||||
if config.SCK == 0 && config.SDO == 0 && config.SDI == 0 {
|
if config.SCK == 0 {
|
||||||
// set default pins if config zero valued or invalid clock pin supplied.
|
// set default pins if config zero valued or invalid clock pin supplied.
|
||||||
switch spi.Bus {
|
switch spi.Bus {
|
||||||
case rp.SPI0:
|
case rp.SPI0:
|
||||||
@@ -178,27 +175,6 @@ func (spi SPI) Configure(config SPIConfig) error {
|
|||||||
config.SDI = SPI1_SDI_PIN
|
config.SDI = SPI1_SDI_PIN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var okSDI, okSDO, okSCK bool
|
|
||||||
switch spi.Bus {
|
|
||||||
case rp.SPI0:
|
|
||||||
okSDI = config.SDI == 0 || config.SDI == 4 || config.SDI == 16
|
|
||||||
okSDO = config.SDO == 3 || config.SDO == 7 || config.SDO == 19
|
|
||||||
okSCK = config.SCK == 2 || config.SCK == 6 || config.SCK == 18
|
|
||||||
case rp.SPI1:
|
|
||||||
okSDI = config.SDI == 8 || config.SDI == 12 || config.SDI == 28
|
|
||||||
okSDO = config.SDO == 11 || config.SDO == 15 || config.SDO == 27
|
|
||||||
okSCK = config.SCK == 10 || config.SCK == 14 || config.SCK == 26
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case !okSDI:
|
|
||||||
return errSPIInvalidSDI
|
|
||||||
case !okSDO:
|
|
||||||
return errSPIInvalidSDO
|
|
||||||
case !okSCK:
|
|
||||||
return errSPIInvalidSCK
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.DataBits < 4 || config.DataBits > 16 {
|
if config.DataBits < 4 || config.DataBits > 16 {
|
||||||
config.DataBits = 8
|
config.DataBits = 8
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
const GOARCH = "mips"
|
||||||
|
|
||||||
|
// The bitness of the CPU (e.g. 8, 32, 64).
|
||||||
|
const TargetBits = 32
|
||||||
|
|
||||||
|
const deferExtraRegs = 0
|
||||||
|
|
||||||
|
const flag_linux_MAP_ANONYMOUS = 0x800
|
||||||
|
|
||||||
|
// Align on the maximum alignment for this platform (double).
|
||||||
|
func align(ptr uintptr) uintptr {
|
||||||
|
return (ptr + 7) &^ 7
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCurrentStackPointer() uintptr {
|
||||||
|
return uintptr(stacksave())
|
||||||
|
}
|
||||||
@@ -563,7 +563,7 @@ func hashmapInterfaceHash(itf interface{}, seed uintptr) uint32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func hashmapInterfacePtrHash(iptr unsafe.Pointer, size uintptr, seed uintptr) uint32 {
|
func hashmapInterfacePtrHash(iptr unsafe.Pointer, size uintptr, seed uintptr) uint32 {
|
||||||
_i := *(*interface{})(iptr)
|
_i := *(*_interface)(iptr)
|
||||||
return hashmapInterfaceHash(_i, seed)
|
return hashmapInterfaceHash(_i, seed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const (
|
|||||||
flag_PROT_READ = 0x1
|
flag_PROT_READ = 0x1
|
||||||
flag_PROT_WRITE = 0x2
|
flag_PROT_WRITE = 0x2
|
||||||
flag_MAP_PRIVATE = 0x2
|
flag_MAP_PRIVATE = 0x2
|
||||||
flag_MAP_ANONYMOUS = 0x20
|
flag_MAP_ANONYMOUS = flag_linux_MAP_ANONYMOUS
|
||||||
)
|
)
|
||||||
|
|
||||||
// Source: https://github.com/torvalds/linux/blob/master/include/uapi/linux/time.h
|
// Source: https://github.com/torvalds/linux/blob/master/include/uapi/linux/time.h
|
||||||
|
|||||||
Vendored
-34
@@ -129,8 +129,6 @@ func main() {
|
|||||||
floatcmplx()
|
floatcmplx()
|
||||||
|
|
||||||
mapgrow()
|
mapgrow()
|
||||||
|
|
||||||
interfacerehash()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func floatcmplx() {
|
func floatcmplx() {
|
||||||
@@ -276,35 +274,3 @@ func mapgrow() {
|
|||||||
}
|
}
|
||||||
println("done")
|
println("done")
|
||||||
}
|
}
|
||||||
|
|
||||||
type Counter interface {
|
|
||||||
count() int
|
|
||||||
}
|
|
||||||
|
|
||||||
type counter struct {
|
|
||||||
i int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *counter) count() int {
|
|
||||||
return c.i
|
|
||||||
}
|
|
||||||
|
|
||||||
func interfacerehash() {
|
|
||||||
m := make(map[Counter]int)
|
|
||||||
|
|
||||||
for i := 0; i < 20; i++ {
|
|
||||||
c := &counter{i}
|
|
||||||
m[c] = i
|
|
||||||
}
|
|
||||||
|
|
||||||
var failures int
|
|
||||||
for k, v := range m {
|
|
||||||
if got := m[k]; got != v {
|
|
||||||
println("lookup failure got", got, "want", v)
|
|
||||||
failures++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if failures == 0 {
|
|
||||||
println("no interface lookup failures")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Vendored
-1
@@ -80,4 +80,3 @@ tested growing of a map
|
|||||||
2
|
2
|
||||||
2
|
2
|
||||||
done
|
done
|
||||||
no interface lookup failures
|
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package transform
|
||||||
|
|
||||||
|
import "tinygo.org/x/go-llvm"
|
||||||
|
|
||||||
|
// This file implements small transformations on globals (functions and global
|
||||||
|
// variables) for specific ABIs/architectures.
|
||||||
|
|
||||||
|
// ApplyFunctionSections puts every function in a separate section. This makes
|
||||||
|
// it possible for the linker to remove dead code. It is the equivalent of
|
||||||
|
// passing -ffunction-sections to a C compiler.
|
||||||
|
func ApplyFunctionSections(mod llvm.Module) {
|
||||||
|
llvmFn := mod.FirstFunction()
|
||||||
|
for !llvmFn.IsNil() {
|
||||||
|
if !llvmFn.IsDeclaration() && llvmFn.Section() == "" {
|
||||||
|
name := llvmFn.Name()
|
||||||
|
llvmFn.SetSection(".text." + name)
|
||||||
|
}
|
||||||
|
llvmFn = llvm.NextFunction(llvmFn)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package transform_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/tinygo-org/tinygo/transform"
|
||||||
|
"tinygo.org/x/go-llvm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestApplyFunctionSections(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
testTransform(t, "testdata/globals-function-sections", func(mod llvm.Module) {
|
||||||
|
transform.ApplyFunctionSections(mod)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||||
|
target triple = "armv7em-none-eabi"
|
||||||
|
|
||||||
|
declare void @foo()
|
||||||
|
|
||||||
|
define void @bar() {
|
||||||
|
ret void
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||||
|
target triple = "armv7em-none-eabi"
|
||||||
|
|
||||||
|
declare void @foo()
|
||||||
|
|
||||||
|
define void @bar() section ".text.bar" {
|
||||||
|
ret void
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user