mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-01 01:27:46 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2de0635140 | |||
| 9b91cbb841 | |||
| fa6bc6445c | |||
| 8bf94b9231 | |||
| 488174767b | |||
| 201592d93b | |||
| 476621736c | |||
| 7b44fcd865 | |||
| cfe971d723 | |||
| bad7bfc4d5 | |||
| ad1da7d26f | |||
| 6df303ff19 | |||
| 96b70fd619 | |||
| 4d0dfbd6fd | |||
| 1065f06e57 | |||
| ec27d9fb48 |
@@ -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="-v -short"
|
run: make test GOTESTFLAGS="-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="-v -short"
|
run: make test GOTESTFLAGS="-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 ?= -v
|
GOTESTFLAGS ?=
|
||||||
|
|
||||||
# md5sum binary
|
# md5sum binary
|
||||||
MD5SUM = md5sum
|
MD5SUM = md5sum
|
||||||
@@ -474,6 +474,8 @@ 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
|
||||||
|
|||||||
+124
-81
@@ -169,12 +169,14 @@ 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
|
||||||
@@ -305,7 +307,6 @@ 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,
|
||||||
@@ -452,18 +453,24 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if runtime.GOOS == "windows" {
|
if compilerConfig.LTO {
|
||||||
// Work around a problem on Windows.
|
buf := llvm.WriteThinLTOBitcodeToMemoryBuffer(mod)
|
||||||
// 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 {
|
||||||
// Otherwise, write bitcode directly to the file (probably
|
if runtime.GOOS == "windows" {
|
||||||
// faster).
|
// Work around a problem on Windows.
|
||||||
err = llvm.WriteBitcodeToFile(mod, f)
|
// 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()
|
||||||
|
_, 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
|
||||||
@@ -511,24 +518,11 @@ 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.
|
||||||
llvmInitFn := mod.NamedFunction("runtime.initAll")
|
initAllModule := createInitAll(ctx, config, compilerConfig, lprogram.Sorted())
|
||||||
llvmInitFn.SetLinkage(llvm.InternalLinkage)
|
err := llvm.LinkModules(mod, initAllModule)
|
||||||
llvmInitFn.SetUnnamedAddr(true)
|
if err != nil {
|
||||||
transform.AddStandardAttributes(llvmInitFn, config)
|
return fmt.Errorf("failed to link module: %w", err)
|
||||||
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
|
||||||
@@ -561,7 +555,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
|
||||||
}
|
}
|
||||||
@@ -594,12 +588,7 @@ 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":
|
||||||
var buf llvm.MemoryBuffer
|
buf := llvm.WriteThinLTOBitcodeToMemoryBuffer(mod)
|
||||||
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":
|
||||||
@@ -621,23 +610,44 @@ 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 {
|
||||||
var llvmBuf llvm.MemoryBuffer
|
llvmBuf := llvm.WriteThinLTOBitcodeToMemoryBuffer(mod)
|
||||||
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.
|
||||||
linkerDependencies := []*compileJob{outputObjectFileJob}
|
var linkerDependencies []*compileJob
|
||||||
|
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"
|
||||||
@@ -664,7 +674,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.UseThinLTO(), config.Options.PrintCommands)
|
result, err := compileAndCacheCFile(abspath, tmpdir, config.CFlags(), config.Options.PrintCommands)
|
||||||
job.result = result
|
job.result = result
|
||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
@@ -682,7 +692,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.UseThinLTO(), config.Options.PrintCommands)
|
result, err := compileAndCacheCFile(abspath, tmpdir, pkg.CFlags, config.Options.PrintCommands)
|
||||||
job.result = result
|
job.result = result
|
||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
@@ -741,36 +751,34 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
}
|
}
|
||||||
ldflags = append(ldflags, dependency.result)
|
ldflags = append(ldflags, dependency.result)
|
||||||
}
|
}
|
||||||
if config.UseThinLTO() {
|
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
|
||||||
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
|
if config.GOOS() == "windows" {
|
||||||
if config.GOOS() == "windows" {
|
// Options for the MinGW wrapper for the lld COFF linker.
|
||||||
// Options for the MinGW wrapper for the lld COFF linker.
|
ldflags = append(ldflags,
|
||||||
ldflags = append(ldflags,
|
"-Xlink=/opt:lldlto="+strconv.Itoa(optLevel),
|
||||||
"-Xlink=/opt:lldlto="+strconv.Itoa(optLevel),
|
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"))
|
||||||
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"))
|
} else if config.GOOS() == "darwin" {
|
||||||
} else if config.GOOS() == "darwin" {
|
// Options for the ld64-compatible lld linker.
|
||||||
// Options for the ld64-compatible lld linker.
|
ldflags = append(ldflags,
|
||||||
ldflags = append(ldflags,
|
"--lto-O"+strconv.Itoa(optLevel),
|
||||||
"--lto-O"+strconv.Itoa(optLevel),
|
"-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
|
||||||
"-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
|
} else {
|
||||||
} else {
|
// Options for the ELF linker.
|
||||||
// Options for the ELF linker.
|
ldflags = append(ldflags,
|
||||||
ldflags = append(ldflags,
|
"--lto-O"+strconv.Itoa(optLevel),
|
||||||
"--lto-O"+strconv.Itoa(optLevel),
|
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
|
||||||
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
|
)
|
||||||
)
|
}
|
||||||
}
|
if config.CodeModel() != "default" {
|
||||||
if config.CodeModel() != "default" {
|
ldflags = append(ldflags,
|
||||||
ldflags = append(ldflags,
|
"-mllvm", "-code-model="+config.CodeModel())
|
||||||
"-mllvm", "-code-model="+config.CodeModel())
|
}
|
||||||
}
|
if sizeLevel >= 2 {
|
||||||
if sizeLevel >= 2 {
|
// Workaround with roughly the same effect as
|
||||||
// Workaround with roughly the same effect as
|
// https://reviews.llvm.org/D119342.
|
||||||
// https://reviews.llvm.org/D119342.
|
// Can hopefully be removed in LLVM 15.
|
||||||
// Can hopefully be removed in LLVM 15.
|
ldflags = append(ldflags,
|
||||||
ldflags = append(ldflags,
|
"-mllvm", "--rotation-max-header-size=0")
|
||||||
"-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...)
|
||||||
@@ -1048,6 +1056,45 @@ 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.
|
||||||
@@ -1069,10 +1116,6 @@ 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 {
|
||||||
|
|||||||
+8
-16
@@ -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, thinlto bool, printCommands func(string, ...string)) (string, error) {
|
func compileAndCacheCFile(abspath, tmpdir string, cflags []string, 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,11 +67,6 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool,
|
|||||||
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
|
||||||
@@ -104,7 +99,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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, ext)
|
outpath, err := makeCFileCachePath(dependencies, depfileNameHash)
|
||||||
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
|
||||||
@@ -117,7 +112,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool,
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
objTmpFile, err := os.CreateTemp(goenv.Get("GOCACHE"), "tmp-*"+ext)
|
objTmpFile, err := os.CreateTemp(goenv.Get("GOCACHE"), "tmp-*.bc")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -127,11 +122,8 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool,
|
|||||||
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()) // autogenerate dependencies
|
flags = append(flags, "-MD", "-MV", "-MTdeps", "-MF", depTmpFile.Name(), "-flto=thin") // 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
|
||||||
@@ -189,7 +181,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Move temporary object file to final location.
|
// Move temporary object file to final location.
|
||||||
outpath, err := makeCFileCachePath(dependencySlice, depfileNameHash, ext)
|
outpath, err := makeCFileCachePath(dependencySlice, depfileNameHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -204,7 +196,7 @@ func compileAndCacheCFile(abspath, tmpdir string, cflags []string, thinlto bool,
|
|||||||
// 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, ext string) (string, error) {
|
func makeCFileCachePath(paths []string, depfileNameHash 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 {
|
||||||
@@ -229,7 +221,7 @@ func makeCFileCachePath(paths []string, depfileNameHash, ext string) (string, er
|
|||||||
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+ext)
|
outpath := filepath.Join(goenv.Get("GOCACHE"), "obj-"+cacheKey+".bc")
|
||||||
return outpath, nil
|
return outpath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -165,6 +165,14 @@ 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).
|
||||||
@@ -191,14 +199,6 @@ 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,6 +14,7 @@ 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
|
||||||
@@ -25,6 +26,7 @@ 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
|
||||||
@@ -107,6 +109,12 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -34,7 +34,9 @@ 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) {
|
||||||
b.llvmFn.SetVisibility(llvm.HiddenVisibility)
|
if !b.LTO {
|
||||||
|
b.llvmFn.SetVisibility(llvm.HiddenVisibility)
|
||||||
|
}
|
||||||
b.llvmFn.SetUnnamedAddr(true)
|
b.llvmFn.SetUnnamedAddr(true)
|
||||||
|
|
||||||
if b.Debug {
|
if b.Debug {
|
||||||
|
|||||||
+24
-5
@@ -47,6 +47,7 @@ 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
|
||||||
@@ -54,6 +55,7 @@ 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
|
||||||
@@ -321,6 +323,14 @@ 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()
|
||||||
}
|
}
|
||||||
@@ -888,7 +898,9 @@ 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()))
|
||||||
global.SetVisibility(llvm.HiddenVisibility)
|
if !c.LTO {
|
||||||
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
|
}
|
||||||
if info.section != "" {
|
if info.section != "" {
|
||||||
global.SetSection(info.section)
|
global.SetSection(info.section)
|
||||||
}
|
}
|
||||||
@@ -935,7 +947,9 @@ 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)
|
||||||
global.SetVisibility(llvm.HiddenVisibility)
|
if !c.LTO {
|
||||||
|
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 {
|
||||||
@@ -959,7 +973,9 @@ 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)
|
||||||
global.SetVisibility(llvm.HiddenVisibility)
|
if !c.LTO {
|
||||||
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
|
}
|
||||||
|
|
||||||
case *types.Struct:
|
case *types.Struct:
|
||||||
// Assume this is an embed.FS struct:
|
// Assume this is an embed.FS struct:
|
||||||
@@ -1042,7 +1058,9 @@ 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)
|
||||||
global.SetVisibility(llvm.HiddenVisibility)
|
if !c.LTO {
|
||||||
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
|
}
|
||||||
global.SetAlignment(c.targetData.ABITypeAlignment(globalInitializer.Type()))
|
global.SetAlignment(c.targetData.ABITypeAlignment(globalInitializer.Type()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1089,7 +1107,8 @@ 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,6 +49,7 @@ 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", "", ""})
|
||||||
|
|||||||
+15
-10
@@ -490,17 +490,22 @@ 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 {
|
||||||
globalName := "reflect/types.typeid:" + getTypeCodeName(expr.AssertedType)
|
if b.LTO {
|
||||||
assertedTypeCodeGlobal := b.mod.NamedGlobal(globalName)
|
assertedTypeCodeGlobal := b.getTypeCode(expr.AssertedType)
|
||||||
if assertedTypeCodeGlobal.IsNil() {
|
commaOk = b.CreateICmp(llvm.IntEQ, actualTypeNum, assertedTypeCodeGlobal, "commaok")
|
||||||
// Create a new typecode global.
|
} else {
|
||||||
assertedTypeCodeGlobal = llvm.AddGlobal(b.mod, b.ctx.Int8Type(), globalName)
|
globalName := "reflect/types.typeid:" + getTypeCodeName(expr.AssertedType)
|
||||||
assertedTypeCodeGlobal.SetGlobalConstant(true)
|
assertedTypeCodeGlobal := b.mod.NamedGlobal(globalName)
|
||||||
|
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,7 +45,9 @@ 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)
|
||||||
global.SetVisibility(llvm.HiddenVisibility)
|
if !b.LTO {
|
||||||
|
global.SetVisibility(llvm.HiddenVisibility)
|
||||||
|
}
|
||||||
global.SetGlobalConstant(true)
|
global.SetGlobalConstant(true)
|
||||||
global.SetUnnamedAddr(true)
|
global.SetUnnamedAddr(true)
|
||||||
initializer := llvm.ConstNull(globalLLVMType)
|
initializer := llvm.ConstNull(globalLLVMType)
|
||||||
|
|||||||
+78
-1
@@ -89,6 +89,7 @@ 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, "")
|
||||||
@@ -133,6 +134,7 @@ 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)
|
||||||
@@ -161,6 +163,7 @@ 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)
|
||||||
@@ -240,7 +243,8 @@ 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.
|
// can be compared with runtime.memequal. Note that padding bytes are undef
|
||||||
|
// 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:
|
||||||
@@ -263,3 +267,76 @@ 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
@@ -0,0 +1,37 @@
|
|||||||
|
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
@@ -0,0 +1,170 @@
|
|||||||
|
; 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,6 +1357,7 @@ 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)")
|
||||||
@@ -1459,6 +1460,7 @@ 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,
|
||||||
|
|||||||
+13
-9
@@ -105,6 +105,13 @@ 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)
|
||||||
@@ -180,7 +187,8 @@ 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 work due to type code issues.
|
// Reflect tests do not run correctly, probably because of the
|
||||||
|
// limited amount of memory.
|
||||||
continue
|
continue
|
||||||
|
|
||||||
case "gc.go":
|
case "gc.go":
|
||||||
@@ -188,20 +196,16 @@ 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":
|
||||||
// Breaks interp.
|
// Too big for AVR. Doesn't fit in flash/RAM.
|
||||||
continue
|
continue
|
||||||
|
|
||||||
case "math.go":
|
case "math.go":
|
||||||
// Stuck somewhere, not sure what's happening.
|
// Needs newer picolibc version (for sqrt).
|
||||||
continue
|
continue
|
||||||
|
|
||||||
case "cgo/":
|
case "cgo/":
|
||||||
// CGo does not work on AVR.
|
// CGo function pointers don't work on AVR (needs LLVM 16 and
|
||||||
continue
|
// some compiler changes).
|
||||||
|
|
||||||
case "timers.go":
|
|
||||||
// Doesn't compile:
|
|
||||||
// panic: compiler: could not store type code number inside interface type code
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
//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,7 +2,10 @@
|
|||||||
|
|
||||||
package task
|
package task
|
||||||
|
|
||||||
import "unsafe"
|
import (
|
||||||
|
"runtime/interrupt"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
//go:linkname runtimePanic runtime.runtimePanic
|
//go:linkname runtimePanic runtime.runtimePanic
|
||||||
func runtimePanic(str string)
|
func runtimePanic(str string)
|
||||||
@@ -45,6 +48,9 @@ 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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,18 @@ 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,
|
||||||
@@ -72,4 +84,5 @@ 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 = D4
|
I2C0_SDA_PIN Pin = D2
|
||||||
I2C0_SCL_PIN Pin = D5
|
I2C0_SCL_PIN Pin = D3
|
||||||
|
|
||||||
I2C1_SDA_PIN Pin = NoPin
|
I2C1_SDA_PIN Pin = D4
|
||||||
I2C1_SCL_PIN Pin = NoPin
|
I2C1_SCL_PIN Pin = D5
|
||||||
)
|
)
|
||||||
|
|
||||||
// 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 37: // PB04
|
case 36: // PB04
|
||||||
return uint8(sam.PORT.PINCFG1_4.Get()>>0) & 0xff
|
return uint8(sam.PORT.PINCFG1_4.Get()>>0) & 0xff
|
||||||
case 38: // PB05
|
case 37: // PB05
|
||||||
return uint8(sam.PORT.PINCFG1_4.Get()>>8) & 0xff
|
return uint8(sam.PORT.PINCFG1_4.Get()>>8) & 0xff
|
||||||
case 39: // PB06
|
case 38: // PB06
|
||||||
return uint8(sam.PORT.PINCFG1_4.Get()>>16) & 0xff
|
return uint8(sam.PORT.PINCFG1_4.Get()>>16) & 0xff
|
||||||
case 40: // PB07
|
case 39: // PB07
|
||||||
return uint8(sam.PORT.PINCFG1_4.Get()>>24) & 0xff
|
return uint8(sam.PORT.PINCFG1_4.Get()>>24) & 0xff
|
||||||
case 41: // PB08
|
case 40: // PB08
|
||||||
return uint8(sam.PORT.PINCFG1_8.Get()>>0) & 0xff
|
return uint8(sam.PORT.PINCFG1_8.Get()>>0) & 0xff
|
||||||
case 42: // PB09
|
case 41: // PB09
|
||||||
return uint8(sam.PORT.PINCFG1_8.Get()>>8) & 0xff
|
return uint8(sam.PORT.PINCFG1_8.Get()>>8) & 0xff
|
||||||
case 43: // PB10
|
case 42: // PB10
|
||||||
return uint8(sam.PORT.PINCFG1_8.Get()>>16) & 0xff
|
return uint8(sam.PORT.PINCFG1_8.Get()>>16) & 0xff
|
||||||
case 44: // PB11
|
case 43: // PB11
|
||||||
return uint8(sam.PORT.PINCFG1_8.Get()>>24) & 0xff
|
return uint8(sam.PORT.PINCFG1_8.Get()>>24) & 0xff
|
||||||
case 45: // PB12
|
case 44: // PB12
|
||||||
return uint8(sam.PORT.PINCFG1_12.Get()>>0) & 0xff
|
return uint8(sam.PORT.PINCFG1_12.Get()>>0) & 0xff
|
||||||
case 46: // PB13
|
case 45: // PB13
|
||||||
return uint8(sam.PORT.PINCFG1_12.Get()>>8) & 0xff
|
return uint8(sam.PORT.PINCFG1_12.Get()>>8) & 0xff
|
||||||
case 47: // PB14
|
case 46: // PB14
|
||||||
return uint8(sam.PORT.PINCFG1_12.Get()>>16) & 0xff
|
return uint8(sam.PORT.PINCFG1_12.Get()>>16) & 0xff
|
||||||
case 48: // PB15
|
case 47: // PB15
|
||||||
return uint8(sam.PORT.PINCFG1_12.Get()>>24) & 0xff
|
return uint8(sam.PORT.PINCFG1_12.Get()>>24) & 0xff
|
||||||
case 49: // PB16
|
case 48: // PB16
|
||||||
return uint8(sam.PORT.PINCFG1_16.Get()>>0) & 0xff
|
return uint8(sam.PORT.PINCFG1_16.Get()>>0) & 0xff
|
||||||
case 50: // PB17
|
case 49: // PB17
|
||||||
return uint8(sam.PORT.PINCFG1_16.Get()>>8) & 0xff
|
return uint8(sam.PORT.PINCFG1_16.Get()>>8) & 0xff
|
||||||
case 51: // PB18
|
case 50: // PB18
|
||||||
return uint8(sam.PORT.PINCFG1_16.Get()>>16) & 0xff
|
return uint8(sam.PORT.PINCFG1_16.Get()>>16) & 0xff
|
||||||
case 52: // PB19
|
case 51: // PB19
|
||||||
return uint8(sam.PORT.PINCFG1_16.Get()>>24) & 0xff
|
return uint8(sam.PORT.PINCFG1_16.Get()>>24) & 0xff
|
||||||
case 53: // PB20
|
case 52: // PB20
|
||||||
return uint8(sam.PORT.PINCFG1_20.Get()>>0) & 0xff
|
return uint8(sam.PORT.PINCFG1_20.Get()>>0) & 0xff
|
||||||
case 54: // PB21
|
case 53: // PB21
|
||||||
return uint8(sam.PORT.PINCFG1_20.Get()>>8) & 0xff
|
return uint8(sam.PORT.PINCFG1_20.Get()>>8) & 0xff
|
||||||
case 55: // PB22
|
case 54: // PB22
|
||||||
return uint8(sam.PORT.PINCFG1_20.Get()>>16) & 0xff
|
return uint8(sam.PORT.PINCFG1_20.Get()>>16) & 0xff
|
||||||
case 56: // PB23
|
case 55: // PB23
|
||||||
return uint8(sam.PORT.PINCFG1_20.Get()>>24) & 0xff
|
return uint8(sam.PORT.PINCFG1_20.Get()>>24) & 0xff
|
||||||
case 57: // PB24
|
case 56: // PB24
|
||||||
return uint8(sam.PORT.PINCFG1_24.Get()>>0) & 0xff
|
return uint8(sam.PORT.PINCFG1_24.Get()>>0) & 0xff
|
||||||
case 58: // PB25
|
case 57: // PB25
|
||||||
return uint8(sam.PORT.PINCFG1_24.Get()>>8) & 0xff
|
return uint8(sam.PORT.PINCFG1_24.Get()>>8) & 0xff
|
||||||
case 59: // PB26
|
case 58: // PB26
|
||||||
return uint8(sam.PORT.PINCFG1_24.Get()>>16) & 0xff
|
return uint8(sam.PORT.PINCFG1_24.Get()>>16) & 0xff
|
||||||
case 60: // PB27
|
case 59: // PB27
|
||||||
return uint8(sam.PORT.PINCFG1_24.Get()>>24) & 0xff
|
return uint8(sam.PORT.PINCFG1_24.Get()>>24) & 0xff
|
||||||
case 61: // PB28
|
case 60: // PB28
|
||||||
return uint8(sam.PORT.PINCFG1_28.Get()>>0) & 0xff
|
return uint8(sam.PORT.PINCFG1_28.Get()>>0) & 0xff
|
||||||
case 62: // PB29
|
case 61: // PB29
|
||||||
return uint8(sam.PORT.PINCFG1_28.Get()>>8) & 0xff
|
return uint8(sam.PORT.PINCFG1_28.Get()>>8) & 0xff
|
||||||
case 63: // PB30
|
case 62: // PB30
|
||||||
return uint8(sam.PORT.PINCFG1_28.Get()>>16) & 0xff
|
return uint8(sam.PORT.PINCFG1_28.Get()>>16) & 0xff
|
||||||
case 64: // PB31
|
case 63: // 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,6 +57,8 @@ 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
|
||||||
@@ -90,7 +92,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 {
|
if config.SCL == 0 && config.SDA == 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:
|
||||||
@@ -101,6 +103,23 @@ 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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,240 @@
|
|||||||
|
//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,6 +40,9 @@ 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 {
|
||||||
@@ -162,7 +165,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 {
|
if config.SCK == 0 && config.SDO == 0 && config.SDI == 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:
|
||||||
@@ -175,6 +178,27 @@ 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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Vendored
+34
@@ -129,6 +129,8 @@ func main() {
|
|||||||
floatcmplx()
|
floatcmplx()
|
||||||
|
|
||||||
mapgrow()
|
mapgrow()
|
||||||
|
|
||||||
|
interfacerehash()
|
||||||
}
|
}
|
||||||
|
|
||||||
func floatcmplx() {
|
func floatcmplx() {
|
||||||
@@ -274,3 +276,35 @@ 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,3 +80,4 @@ tested growing of a map
|
|||||||
2
|
2
|
||||||
2
|
2
|
||||||
done
|
done
|
||||||
|
no interface lookup failures
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
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