Compare commits

...
Sign in to create a new pull request.

2 commits

Author SHA1 Message Date
Elliott Sales de Andrade
db2de4ca7d Allow an older version of golang.org/x/tools
The requirement is really only on 0.9, as that changed some internals.
2023-11-05 03:45:44 -05:00
Elliott Sales de Andrade
47ec68f853 Backport support for LLVM17 2023-11-05 03:45:28 -05:00
8 changed files with 937 additions and 4 deletions

View file

@ -1,7 +1,7 @@
From ee856392109e39cf60e83022441c94adee205447 Mon Sep 17 00:00:00 2001
From: Elliott Sales de Andrade <quantum.analyst@gmail.com>
Date: Tue, 15 Dec 2020 05:06:04 -0500
Subject: [PATCH 1/3] Skip WASI tests.
Subject: [PATCH 1/7] Skip WASI tests.
We do not have wasmtime available.

View file

@ -1,7 +1,7 @@
From 1f60829a1fffdcbe268c1998a560fdeb6029407e Mon Sep 17 00:00:00 2001
From: Elliott Sales de Andrade <quantum.analyst@gmail.com>
Date: Mon, 3 Jan 2022 22:39:31 -0500
Subject: [PATCH 2/3] Skip some cross Linux tests where qemu is broken
Subject: [PATCH 2/7] Skip some cross Linux tests where qemu is broken
The upstream issues will hopefully be fixed soon:

View file

@ -1,7 +1,7 @@
From 3f6a6f241ab37976f07e792986b5c47fdde20c8a Mon Sep 17 00:00:00 2001
From: Elliott Sales de Andrade <quantum.analyst@gmail.com>
Date: Sun, 6 Feb 2022 03:49:16 -0500
Subject: [PATCH 3/3] Suggest optional packages to install if missing
Subject: [PATCH 3/7] Suggest optional packages to install if missing
Signed-off-by: Elliott Sales de Andrade <quantum.analyst@gmail.com>
---

View file

@ -0,0 +1,121 @@
From bf51015656ff3b1bbe6b41ea1155610178c2961d Mon Sep 17 00:00:00 2001
From: Ayke van Laethem <aykevanlaethem@gmail.com>
Date: Thu, 21 Sep 2023 15:39:06 +0200
Subject: [PATCH 4/7] transform: fix bug in StringToBytes optimization pass
Previously, this pass would convert any read-only use of a
runtime.stringToBytes call to use the original string buffer instead.
This is incorrect: if there are any writes to the resulting buffer, none
of the slice buffer pointers can be converted to use the original
read-only string buffer.
This commit fixes that bug and adds a test to prove the new (correct)
behavior.
Signed-off-by: Elliott Sales de Andrade <quantum.analyst@gmail.com>
---
transform/rtcalls.go | 24 ++++++++++++++++++------
transform/testdata/stringtobytes.ll | 16 ++++++++++++++++
transform/testdata/stringtobytes.out.ll | 10 ++++++++++
3 files changed, 44 insertions(+), 6 deletions(-)
diff --git a/transform/rtcalls.go b/transform/rtcalls.go
index 36d2853b..0b6feff2 100644
--- a/transform/rtcalls.go
+++ b/transform/rtcalls.go
@@ -28,32 +28,44 @@ func OptimizeStringToBytes(mod llvm.Module) {
// strptr is always constant because strings are always constant.
- convertedAllUses := true
+ var pointerUses []llvm.Value
+ canConvertPointer := true
for _, use := range getUses(call) {
if use.IsAExtractValueInst().IsNil() {
// Expected an extractvalue, but this is something else.
- convertedAllUses = false
+ canConvertPointer = false
continue
}
switch use.Type().TypeKind() {
case llvm.IntegerTypeKind:
// A length (len or cap). Propagate the length value.
+ // This can always be done because the byte slice is always the
+ // same length as the original string.
use.ReplaceAllUsesWith(strlen)
use.EraseFromParentAsInstruction()
case llvm.PointerTypeKind:
// The string pointer itself.
if !isReadOnly(use) {
- convertedAllUses = false
+ // There is a store to the byte slice. This means that none
+ // of the pointer uses can't be propagated.
+ canConvertPointer = false
continue
}
- use.ReplaceAllUsesWith(strptr)
- use.EraseFromParentAsInstruction()
+ // It may be that the pointer value can be propagated, if all of
+ // the pointer uses are readonly.
+ pointerUses = append(pointerUses, use)
default:
// should not happen
panic("unknown return type of runtime.stringToBytes: " + use.Type().String())
}
}
- if convertedAllUses {
+ if canConvertPointer {
+ // All pointer uses are readonly, so they can be converted.
+ for _, use := range pointerUses {
+ use.ReplaceAllUsesWith(strptr)
+ use.EraseFromParentAsInstruction()
+ }
+
// Call to runtime.stringToBytes can be eliminated: both the input
// and the output is constant.
call.EraseFromParentAsInstruction()
diff --git a/transform/testdata/stringtobytes.ll b/transform/testdata/stringtobytes.ll
index fa43f3d0..06373a51 100644
--- a/transform/testdata/stringtobytes.ll
+++ b/transform/testdata/stringtobytes.ll
@@ -30,3 +30,19 @@ entry:
call fastcc void @writeToSlice(ptr %1, i64 %2, i64 %3)
ret void
}
+
+; Test that pointer values are never propagated if there is even a single write
+; to the pointer value (but len/cap values still can be).
+define void @testReadSome() {
+entry:
+ %s = call fastcc { ptr, i64, i64 } @runtime.stringToBytes(ptr @str, i64 6)
+ %s.ptr = extractvalue { ptr, i64, i64 } %s, 0
+ %s.len = extractvalue { ptr, i64, i64 } %s, 1
+ %s.cap = extractvalue { ptr, i64, i64 } %s, 2
+ call fastcc void @writeToSlice(ptr %s.ptr, i64 %s.len, i64 %s.cap)
+ %s.ptr2 = extractvalue { ptr, i64, i64 } %s, 0
+ %s.len2 = extractvalue { ptr, i64, i64 } %s, 1
+ %s.cap2 = extractvalue { ptr, i64, i64 } %s, 2
+ call fastcc void @printSlice(ptr %s.ptr2, i64 %s.len2, i64 %s.cap2)
+ ret void
+}
diff --git a/transform/testdata/stringtobytes.out.ll b/transform/testdata/stringtobytes.out.ll
index 30aa520a..b33a1755 100644
--- a/transform/testdata/stringtobytes.out.ll
+++ b/transform/testdata/stringtobytes.out.ll
@@ -22,3 +22,13 @@ entry:
call fastcc void @writeToSlice(ptr %1, i64 6, i64 6)
ret void
}
+
+define void @testReadSome() {
+entry:
+ %s = call fastcc { ptr, i64, i64 } @runtime.stringToBytes(ptr @str, i64 6)
+ %s.ptr = extractvalue { ptr, i64, i64 } %s, 0
+ call fastcc void @writeToSlice(ptr %s.ptr, i64 6, i64 6)
+ %s.ptr2 = extractvalue { ptr, i64, i64 } %s, 0
+ call fastcc void @printSlice(ptr %s.ptr2, i64 6, i64 6)
+ ret void
+}
--
2.41.0

View file

@ -0,0 +1,424 @@
From 3dcb55f940011bb9bcb75d98182290da60642e4d Mon Sep 17 00:00:00 2001
From: Ayke van Laethem <aykevanlaethem@gmail.com>
Date: Tue, 19 Sep 2023 22:37:44 +0200
Subject: [PATCH 5/7] all: use the new LLVM pass manager
The old LLVM pass manager is deprecated and should not be used anymore.
Moreover, the pass manager builder (which we used to set up a pass
pipeline) is actually removed from LLVM entirely in LLVM 17:
https://reviews.llvm.org/D145387
https://reviews.llvm.org/D145835
The new pass manager does change the binary size in many cases: both
growing and shrinking it. However, on average the binary size remains
more or less the same.
This is needed as a preparation for LLVM 17.
Signed-off-by: Elliott Sales de Andrade <quantum.analyst@gmail.com>
---
builder/build.go | 19 +++--
builder/sizes_test.go | 6 +-
compileopts/config.go | 12 ++--
compiler/compiler_test.go | 12 ++--
interp/interp_test.go | 9 +--
transform/allocs_test.go | 11 +--
transform/interface-lowering_test.go | 10 +--
transform/maps_test.go | 11 +--
transform/optimizer.go | 102 +++++++++------------------
transform/transform.go | 2 +-
10 files changed, 76 insertions(+), 118 deletions(-)
diff --git a/builder/build.go b/builder/build.go
index dc360b92..d920a598 100644
--- a/builder/build.go
+++ b/builder/build.go
@@ -83,8 +83,7 @@ type packageAction struct {
FileHashes map[string]string // hash of every file that's part of the package
EmbeddedFiles map[string]string // hash of all the //go:embed files in the package
Imports map[string]string // map from imported package to action ID hash
- OptLevel int // LLVM optimization level (0-3)
- SizeLevel int // LLVM optimization for size level (0-2)
+ OptLevel string // LLVM optimization level (O0, O1, O2, Os, Oz)
UndefinedGlobals []string // globals that are left as external globals (no initializer)
}
@@ -158,7 +157,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
return BuildResult{}, fmt.Errorf("unknown libc: %s", config.Target.Libc)
}
- optLevel, sizeLevel, _ := config.OptLevels()
+ optLevel, speedLevel, sizeLevel := config.OptLevel()
compilerConfig := &compiler.Config{
Triple: config.Triple(),
CPU: config.CPU(),
@@ -321,7 +320,6 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
EmbeddedFiles: make(map[string]string, len(allFiles)),
Imports: make(map[string]string, len(pkg.Pkg.Imports())),
OptLevel: optLevel,
- SizeLevel: sizeLevel,
UndefinedGlobals: undefinedGlobals,
}
for filePath, hash := range pkg.FileHashes {
@@ -743,17 +741,17 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
if config.GOOS() == "windows" {
// Options for the MinGW wrapper for the lld COFF linker.
ldflags = append(ldflags,
- "-Xlink=/opt:lldlto="+strconv.Itoa(optLevel),
+ "-Xlink=/opt:lldlto="+strconv.Itoa(speedLevel),
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"))
} else if config.GOOS() == "darwin" {
// Options for the ld64-compatible lld linker.
ldflags = append(ldflags,
- "--lto-O"+strconv.Itoa(optLevel),
+ "--lto-O"+strconv.Itoa(speedLevel),
"-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
} else {
// Options for the ELF linker.
ldflags = append(ldflags,
- "--lto-O"+strconv.Itoa(optLevel),
+ "--lto-O"+strconv.Itoa(speedLevel),
"--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"),
)
}
@@ -1066,10 +1064,9 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config) error {
return err
}
- // Optimization levels here are roughly the same as Clang, but probably not
- // exactly.
- optLevel, sizeLevel, inlinerThreshold := config.OptLevels()
- errs := transform.Optimize(mod, config, optLevel, sizeLevel, inlinerThreshold)
+ // Run most of the whole-program optimizations (including the whole
+ // O0/O1/O2/Os/Oz optimization pipeline).
+ errs := transform.Optimize(mod, config)
if len(errs) > 0 {
return newMultiError(errs)
}
diff --git a/builder/sizes_test.go b/builder/sizes_test.go
index 7aaab78a..dc45898e 100644
--- a/builder/sizes_test.go
+++ b/builder/sizes_test.go
@@ -41,9 +41,9 @@ func TestBinarySize(t *testing.T) {
// This is a small number of very diverse targets that we want to test.
tests := []sizeTest{
// microcontrollers
- {"hifive1b", "examples/echo", 4568, 280, 0, 2252},
- {"microbit", "examples/serial", 2728, 388, 8, 2256},
- {"wioterminal", "examples/pininterrupt", 5996, 1484, 116, 6816},
+ {"hifive1b", "examples/echo", 4484, 280, 0, 2252},
+ {"microbit", "examples/serial", 2724, 388, 8, 2256},
+ {"wioterminal", "examples/pininterrupt", 6000, 1484, 116, 6816},
// TODO: also check wasm. Right now this is difficult, because
// wasm binaries are run through wasm-opt and therefore the
diff --git a/compileopts/config.go b/compileopts/config.go
index 39fc4f2a..5ad45c60 100644
--- a/compileopts/config.go
+++ b/compileopts/config.go
@@ -145,18 +145,18 @@ func (c *Config) Serial() string {
// OptLevels returns the optimization level (0-2), size level (0-2), and inliner
// threshold as used in the LLVM optimization pipeline.
-func (c *Config) OptLevels() (optLevel, sizeLevel int, inlinerThreshold uint) {
+func (c *Config) OptLevel() (level string, speedLevel, sizeLevel int) {
switch c.Options.Opt {
case "none", "0":
- return 0, 0, 0 // -O0
+ return "O0", 0, 0
case "1":
- return 1, 0, 0 // -O1
+ return "O1", 1, 0
case "2":
- return 2, 0, 225 // -O2
+ return "O2", 2, 0
case "s":
- return 2, 1, 225 // -Os
+ return "Os", 2, 1
case "z":
- return 2, 2, 5 // -Oz, default
+ return "Oz", 2, 2 // default
default:
// This is not shown to the user: valid choices are already checked as
// part of Options.Verify(). It is here as a sanity check.
diff --git a/compiler/compiler_test.go b/compiler/compiler_test.go
index 92ce31b0..147e622a 100644
--- a/compiler/compiler_test.go
+++ b/compiler/compiler_test.go
@@ -91,14 +91,12 @@ func TestCompiler(t *testing.T) {
}
// Optimize IR a little.
- funcPasses := llvm.NewFunctionPassManagerForModule(mod)
- defer funcPasses.Dispose()
- funcPasses.AddInstructionCombiningPass()
- funcPasses.InitializeFunc()
- for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
- funcPasses.RunFunc(fn)
+ passOptions := llvm.NewPassBuilderOptions()
+ defer passOptions.Dispose()
+ err = mod.RunPasses("instcombine", llvm.TargetMachine{}, passOptions)
+ if err != nil {
+ t.Error(err)
}
- funcPasses.FinalizeFunc()
outFilePrefix := tc.file[:len(tc.file)-3]
if tc.target != "" {
diff --git a/interp/interp_test.go b/interp/interp_test.go
index fc567af2..cac56508 100644
--- a/interp/interp_test.go
+++ b/interp/interp_test.go
@@ -77,12 +77,9 @@ func runTest(t *testing.T, pathPrefix string) {
}
// Run some cleanup passes to get easy-to-read outputs.
- pm := llvm.NewPassManager()
- defer pm.Dispose()
- pm.AddGlobalOptimizerPass()
- pm.AddDeadStoreEliminationPass()
- pm.AddAggressiveDCEPass()
- pm.Run(mod)
+ to := llvm.NewPassBuilderOptions()
+ defer to.Dispose()
+ mod.RunPasses("globalopt,dse,adce", llvm.TargetMachine{}, to)
// Read the expected output IR.
out, err := os.ReadFile(pathPrefix + ".out.ll")
diff --git a/transform/allocs_test.go b/transform/allocs_test.go
index 27bb9706..59a5b14e 100644
--- a/transform/allocs_test.go
+++ b/transform/allocs_test.go
@@ -38,11 +38,12 @@ func TestAllocs2(t *testing.T) {
mod := compileGoFileForTesting(t, "./testdata/allocs2.go")
// Run functionattrs pass, which is necessary for escape analysis.
- pm := llvm.NewPassManager()
- defer pm.Dispose()
- pm.AddInstructionCombiningPass()
- pm.AddFunctionAttrsPass()
- pm.Run(mod)
+ po := llvm.NewPassBuilderOptions()
+ defer po.Dispose()
+ err := mod.RunPasses("function(instcombine),function-attrs", llvm.TargetMachine{}, po)
+ if err != nil {
+ t.Error("failed to run passes:", err)
+ }
// Run heap to stack transform.
var testOutputs []allocsTestOutput
diff --git a/transform/interface-lowering_test.go b/transform/interface-lowering_test.go
index 7bcce605..65f14dd9 100644
--- a/transform/interface-lowering_test.go
+++ b/transform/interface-lowering_test.go
@@ -15,9 +15,11 @@ func TestInterfaceLowering(t *testing.T) {
t.Error(err)
}
- pm := llvm.NewPassManager()
- defer pm.Dispose()
- pm.AddGlobalDCEPass()
- pm.Run(mod)
+ po := llvm.NewPassBuilderOptions()
+ defer po.Dispose()
+ err = mod.RunPasses("globaldce", llvm.TargetMachine{}, po)
+ if err != nil {
+ t.Error("failed to run passes:", err)
+ }
})
}
diff --git a/transform/maps_test.go b/transform/maps_test.go
index e8b11133..329de698 100644
--- a/transform/maps_test.go
+++ b/transform/maps_test.go
@@ -15,10 +15,11 @@ func TestOptimizeMaps(t *testing.T) {
// Run an optimization pass, to clean up the result.
// This shows that all code related to the map is really eliminated.
- pm := llvm.NewPassManager()
- defer pm.Dispose()
- pm.AddDeadStoreEliminationPass()
- pm.AddAggressiveDCEPass()
- pm.Run(mod)
+ po := llvm.NewPassBuilderOptions()
+ defer po.Dispose()
+ err := mod.RunPasses("dse,adce", llvm.TargetMachine{}, po)
+ if err != nil {
+ t.Error("failed to run passes:", err)
+ }
})
}
diff --git a/transform/optimizer.go b/transform/optimizer.go
index 20258ef4..42acc2dd 100644
--- a/transform/optimizer.go
+++ b/transform/optimizer.go
@@ -14,54 +14,22 @@ import (
// OptimizePackage runs optimization passes over the LLVM module for the given
// Go package.
func OptimizePackage(mod llvm.Module, config *compileopts.Config) {
- optLevel, sizeLevel, _ := config.OptLevels()
-
- // Run function passes for each function in the module.
- // These passes are intended to be run on each function right
- // after they're created to reduce IR size (and maybe also for
- // cache locality to improve performance), but for now they're
- // run here for each function in turn. Maybe this can be
- // improved in the future.
- builder := llvm.NewPassManagerBuilder()
- defer builder.Dispose()
- builder.SetOptLevel(optLevel)
- builder.SetSizeLevel(sizeLevel)
- funcPasses := llvm.NewFunctionPassManagerForModule(mod)
- defer funcPasses.Dispose()
- builder.PopulateFunc(funcPasses)
- funcPasses.InitializeFunc()
- for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
- if fn.IsDeclaration() {
- continue
- }
- funcPasses.RunFunc(fn)
- }
- funcPasses.FinalizeFunc()
+ _, speedLevel, _ := config.OptLevel()
// Run TinyGo-specific optimization passes.
- if optLevel > 0 {
+ if speedLevel > 0 {
OptimizeMaps(mod)
}
}
// Optimize runs a number of optimization and transformation passes over the
// given module. Some passes are specific to TinyGo, others are generic LLVM
-// passes. You can set a preferred performance (0-3) and size (0-2) level and
-// control the limits of the inliner (higher numbers mean more inlining, set it
-// to 0 to disable entirely).
+// passes.
//
// Please note that some optimizations are not optional, thus Optimize must
-// alwasy be run before emitting machine code. Set all controls (optLevel,
-// sizeLevel, inlinerThreshold) to 0 to reduce the number of optimizations to a
-// minimum.
-func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel int, inlinerThreshold uint) []error {
- builder := llvm.NewPassManagerBuilder()
- defer builder.Dispose()
- builder.SetOptLevel(optLevel)
- builder.SetSizeLevel(sizeLevel)
- if inlinerThreshold != 0 {
- builder.UseInlinerWithThreshold(inlinerThreshold)
- }
+// alwasy be run before emitting machine code.
+func Optimize(mod llvm.Module, config *compileopts.Config) []error {
+ optLevel, speedLevel, _ := config.OptLevel()
// Make sure these functions are kept in tact during TinyGo transformation passes.
for _, name := range functionsUsedInTransforms {
@@ -84,23 +52,20 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i
}
}
- if optLevel > 0 {
+ if speedLevel > 0 {
// Run some preparatory passes for the Go optimizer.
- goPasses := llvm.NewPassManager()
- defer goPasses.Dispose()
- goPasses.AddGlobalDCEPass()
- goPasses.AddGlobalOptimizerPass()
- goPasses.AddIPSCCPPass()
- goPasses.AddInstructionCombiningPass() // necessary for OptimizeReflectImplements
- goPasses.AddAggressiveDCEPass()
- goPasses.AddFunctionAttrsPass()
- goPasses.Run(mod)
+ po := llvm.NewPassBuilderOptions()
+ defer po.Dispose()
+ err := mod.RunPasses("globaldce,globalopt,ipsccp,instcombine,adce,function-attrs", llvm.TargetMachine{}, po)
+ if err != nil {
+ return []error{fmt.Errorf("could not build pass pipeline: %w", err)}
+ }
// Run TinyGo-specific optimization passes.
OptimizeStringToBytes(mod)
OptimizeReflectImplements(mod)
OptimizeAllocs(mod, nil, nil)
- err := LowerInterfaces(mod, config)
+ err = LowerInterfaces(mod, config)
if err != nil {
return []error{err}
}
@@ -113,7 +78,10 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i
// After interfaces are lowered, there are many more opportunities for
// interprocedural optimizations. To get them to work, function
// attributes have to be updated first.
- goPasses.Run(mod)
+ err = mod.RunPasses("globaldce,globalopt,ipsccp,instcombine,adce,function-attrs", llvm.TargetMachine{}, po)
+ if err != nil {
+ return []error{fmt.Errorf("could not build pass pipeline: %w", err)}
+ }
// Run TinyGo-specific interprocedural optimizations.
OptimizeAllocs(mod, config.Options.PrintAllocs, func(pos token.Position, msg string) {
@@ -134,10 +102,12 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i
}
// Clean up some leftover symbols of the previous transformations.
- goPasses := llvm.NewPassManager()
- defer goPasses.Dispose()
- goPasses.AddGlobalDCEPass()
- goPasses.Run(mod)
+ po := llvm.NewPassBuilderOptions()
+ defer po.Dispose()
+ err = mod.RunPasses("globaldce", llvm.TargetMachine{}, po)
+ if err != nil {
+ return []error{fmt.Errorf("could not build pass pipeline: %w", err)}
+ }
}
if config.Scheduler() == "none" {
@@ -169,23 +139,15 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i
fn.SetLinkage(llvm.InternalLinkage)
}
- // Run function passes again, because without it, llvm.coro.size.i32()
- // doesn't get lowered.
- funcPasses := llvm.NewFunctionPassManagerForModule(mod)
- defer funcPasses.Dispose()
- builder.PopulateFunc(funcPasses)
- funcPasses.InitializeFunc()
- for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
- funcPasses.RunFunc(fn)
+ // Run the default pass pipeline.
+ // TODO: set the PrepareForThinLTO flag somehow.
+ po := llvm.NewPassBuilderOptions()
+ defer po.Dispose()
+ passes := fmt.Sprintf("default<%s>", optLevel)
+ err := mod.RunPasses(passes, llvm.TargetMachine{}, po)
+ if err != nil {
+ return []error{fmt.Errorf("could not build pass pipeline: %w", err)}
}
- funcPasses.FinalizeFunc()
-
- // Run module passes.
- // TODO: somehow set the PrepareForThinLTO flag in the pass manager builder.
- modPasses := llvm.NewPassManager()
- defer modPasses.Dispose()
- builder.Populate(modPasses)
- modPasses.Run(mod)
hasGCPass := MakeGCStackSlots(mod)
if hasGCPass {
diff --git a/transform/transform.go b/transform/transform.go
index ab08317e..429cbd5f 100644
--- a/transform/transform.go
+++ b/transform/transform.go
@@ -22,7 +22,7 @@ import (
// the -opt= compiler flag.
func AddStandardAttributes(fn llvm.Value, config *compileopts.Config) {
ctx := fn.Type().Context()
- _, sizeLevel, _ := config.OptLevels()
+ _, _, sizeLevel := config.OptLevel()
if sizeLevel >= 1 {
fn.AddFunctionAttr(ctx.CreateEnumAttribute(llvm.AttributeKindID("optsize"), 0))
}
--
2.41.0

View file

@ -0,0 +1,222 @@
From 0321eecc6d29d5b571930ed97f392623fe80a2e5 Mon Sep 17 00:00:00 2001
From: Ayke van Laethem <aykevanlaethem@gmail.com>
Date: Sat, 23 Sep 2023 14:57:25 +0200
Subject: [PATCH 6/7] avr: don't compile large parts of picolibc (math, stdio)
These parts aren't critical and lead to crashes on small chips without
long jumps (like the attiny85) with LLVM 17. (Older LLVM versions would
emit long jumps regardless, even if the chip didn't support those).
For more information, see: https://github.com/llvm/llvm-project/issues/67042
Signed-off-by: Elliott Sales de Andrade <quantum.analyst@gmail.com>
---
builder/picolibc.go | 172 ++++++++++++++++++++++++--------------------
1 file changed, 93 insertions(+), 79 deletions(-)
diff --git a/builder/picolibc.go b/builder/picolibc.go
index 1b7c748b..91ad27be 100644
--- a/builder/picolibc.go
+++ b/builder/picolibc.go
@@ -3,6 +3,7 @@ package builder
import (
"os"
"path/filepath"
+ "strings"
"github.com/tinygo-org/tinygo/goenv"
)
@@ -41,91 +42,23 @@ var Picolibc = Library{
},
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib") },
librarySources: func(target string) ([]string, error) {
- return picolibcSources, nil
+ sources := append([]string(nil), picolibcSources...)
+ if !strings.HasPrefix(target, "avr") {
+ // Small chips without long jumps can't compile many files (printf,
+ // pow, etc). Therefore exclude those source files for those chips.
+ // Unfortunately it's difficult to exclude only some chips, so this
+ // excludes those files on all AVR chips for now.
+ // More information:
+ // https://github.com/llvm/llvm-project/issues/67042
+ sources = append(sources, picolibcSourcesLarge...)
+ }
+ return sources, nil
},
}
var picolibcSources = []string{
"../../picolibc-stdio.c",
- // srcs_tinystdio
- "libc/tinystdio/asprintf.c",
- "libc/tinystdio/bufio.c",
- "libc/tinystdio/clearerr.c",
- "libc/tinystdio/ecvt_r.c",
- "libc/tinystdio/ecvt.c",
- "libc/tinystdio/ecvtf_r.c",
- "libc/tinystdio/ecvtf.c",
- "libc/tinystdio/fcvt.c",
- "libc/tinystdio/fcvt_r.c",
- "libc/tinystdio/fcvtf.c",
- "libc/tinystdio/fcvtf_r.c",
- "libc/tinystdio/gcvt.c",
- "libc/tinystdio/gcvtf.c",
- "libc/tinystdio/fclose.c",
- "libc/tinystdio/fdevopen.c",
- "libc/tinystdio/feof.c",
- "libc/tinystdio/ferror.c",
- "libc/tinystdio/fflush.c",
- "libc/tinystdio/fgetc.c",
- "libc/tinystdio/fgets.c",
- "libc/tinystdio/fileno.c",
- "libc/tinystdio/filestrget.c",
- "libc/tinystdio/filestrput.c",
- "libc/tinystdio/filestrputalloc.c",
- "libc/tinystdio/fmemopen.c",
- "libc/tinystdio/fprintf.c",
- "libc/tinystdio/fputc.c",
- "libc/tinystdio/fputs.c",
- "libc/tinystdio/fread.c",
- //"libc/tinystdio/freopen.c", // crashes with AVR, see: https://github.com/picolibc/picolibc/pull/369
- "libc/tinystdio/fscanf.c",
- "libc/tinystdio/fseek.c",
- "libc/tinystdio/fseeko.c",
- "libc/tinystdio/ftell.c",
- "libc/tinystdio/ftello.c",
- "libc/tinystdio/fwrite.c",
- "libc/tinystdio/getchar.c",
- "libc/tinystdio/gets.c",
- "libc/tinystdio/matchcaseprefix.c",
- "libc/tinystdio/mktemp.c",
- "libc/tinystdio/perror.c",
- "libc/tinystdio/printf.c",
- "libc/tinystdio/putchar.c",
- "libc/tinystdio/puts.c",
- "libc/tinystdio/rewind.c",
- "libc/tinystdio/scanf.c",
- "libc/tinystdio/setbuf.c",
- "libc/tinystdio/setbuffer.c",
- "libc/tinystdio/setlinebuf.c",
- "libc/tinystdio/setvbuf.c",
- "libc/tinystdio/snprintf.c",
- "libc/tinystdio/sprintf.c",
- "libc/tinystdio/snprintfd.c",
- "libc/tinystdio/snprintff.c",
- "libc/tinystdio/sprintff.c",
- "libc/tinystdio/sprintfd.c",
- "libc/tinystdio/sscanf.c",
- "libc/tinystdio/strfromf.c",
- "libc/tinystdio/strfromd.c",
- "libc/tinystdio/strtof.c",
- "libc/tinystdio/strtof_l.c",
- "libc/tinystdio/strtod.c",
- "libc/tinystdio/strtod_l.c",
- "libc/tinystdio/ungetc.c",
- "libc/tinystdio/vasprintf.c",
- "libc/tinystdio/vfiprintf.c",
- "libc/tinystdio/vfprintf.c",
- "libc/tinystdio/vfprintff.c",
- "libc/tinystdio/vfscanf.c",
- "libc/tinystdio/vfiscanf.c",
- "libc/tinystdio/vfscanff.c",
- "libc/tinystdio/vprintf.c",
- "libc/tinystdio/vscanf.c",
- "libc/tinystdio/vsscanf.c",
- "libc/tinystdio/vsnprintf.c",
- "libc/tinystdio/vsprintf.c",
-
"libc/string/bcmp.c",
"libc/string/bcopy.c",
"libc/string/bzero.c",
@@ -229,6 +162,87 @@ var picolibcSources = []string{
"libc/string/wmempcpy.c",
"libc/string/wmemset.c",
"libc/string/xpg_strerror_r.c",
+}
+
+// Parts of picolibc that are too large for small AVRs.
+var picolibcSourcesLarge = []string{
+ // srcs_tinystdio
+ "libc/tinystdio/asprintf.c",
+ "libc/tinystdio/bufio.c",
+ "libc/tinystdio/clearerr.c",
+ "libc/tinystdio/ecvt_r.c",
+ "libc/tinystdio/ecvt.c",
+ "libc/tinystdio/ecvtf_r.c",
+ "libc/tinystdio/ecvtf.c",
+ "libc/tinystdio/fcvt.c",
+ "libc/tinystdio/fcvt_r.c",
+ "libc/tinystdio/fcvtf.c",
+ "libc/tinystdio/fcvtf_r.c",
+ "libc/tinystdio/gcvt.c",
+ "libc/tinystdio/gcvtf.c",
+ "libc/tinystdio/fclose.c",
+ "libc/tinystdio/fdevopen.c",
+ "libc/tinystdio/feof.c",
+ "libc/tinystdio/ferror.c",
+ "libc/tinystdio/fflush.c",
+ "libc/tinystdio/fgetc.c",
+ "libc/tinystdio/fgets.c",
+ "libc/tinystdio/fileno.c",
+ "libc/tinystdio/filestrget.c",
+ "libc/tinystdio/filestrput.c",
+ "libc/tinystdio/filestrputalloc.c",
+ "libc/tinystdio/fmemopen.c",
+ "libc/tinystdio/fprintf.c",
+ "libc/tinystdio/fputc.c",
+ "libc/tinystdio/fputs.c",
+ "libc/tinystdio/fread.c",
+ //"libc/tinystdio/freopen.c", // crashes with AVR, see: https://github.com/picolibc/picolibc/pull/369
+ "libc/tinystdio/fscanf.c",
+ "libc/tinystdio/fseek.c",
+ "libc/tinystdio/fseeko.c",
+ "libc/tinystdio/ftell.c",
+ "libc/tinystdio/ftello.c",
+ "libc/tinystdio/fwrite.c",
+ "libc/tinystdio/getchar.c",
+ "libc/tinystdio/gets.c",
+ "libc/tinystdio/matchcaseprefix.c",
+ "libc/tinystdio/mktemp.c",
+ "libc/tinystdio/perror.c",
+ "libc/tinystdio/printf.c",
+ "libc/tinystdio/putchar.c",
+ "libc/tinystdio/puts.c",
+ "libc/tinystdio/rewind.c",
+ "libc/tinystdio/scanf.c",
+ "libc/tinystdio/setbuf.c",
+ "libc/tinystdio/setbuffer.c",
+ "libc/tinystdio/setlinebuf.c",
+ "libc/tinystdio/setvbuf.c",
+ "libc/tinystdio/snprintf.c",
+ "libc/tinystdio/sprintf.c",
+ "libc/tinystdio/snprintfd.c",
+ "libc/tinystdio/snprintff.c",
+ "libc/tinystdio/sprintff.c",
+ "libc/tinystdio/sprintfd.c",
+ "libc/tinystdio/sscanf.c",
+ "libc/tinystdio/strfromf.c",
+ "libc/tinystdio/strfromd.c",
+ "libc/tinystdio/strtof.c",
+ "libc/tinystdio/strtof_l.c",
+ "libc/tinystdio/strtod.c",
+ "libc/tinystdio/strtod_l.c",
+ "libc/tinystdio/ungetc.c",
+ "libc/tinystdio/vasprintf.c",
+ "libc/tinystdio/vfiprintf.c",
+ "libc/tinystdio/vfprintf.c",
+ "libc/tinystdio/vfprintff.c",
+ "libc/tinystdio/vfscanf.c",
+ "libc/tinystdio/vfiscanf.c",
+ "libc/tinystdio/vfscanff.c",
+ "libc/tinystdio/vprintf.c",
+ "libc/tinystdio/vscanf.c",
+ "libc/tinystdio/vsscanf.c",
+ "libc/tinystdio/vsnprintf.c",
+ "libc/tinystdio/vsprintf.c",
"libm/common/sf_finite.c",
"libm/common/sf_copysign.c",
--
2.41.0

View file

@ -0,0 +1,157 @@
From 55fd04af5a6a815224b436ee491ed05b0ef4ee7c Mon Sep 17 00:00:00 2001
From: Ayke van Laethem <aykevanlaethem@gmail.com>
Date: Sat, 23 Sep 2023 15:03:24 +0200
Subject: [PATCH 7/7] all: add initial LLVM 17 support
This allows us to test LLVM 17 already, before it's available in
Homebrew etc.
Full support for LLVM 17 will have to wait until Espressif rebases their
Xtensa fork of LLVM.
Signed-off-by: Elliott Sales de Andrade <quantum.analyst@gmail.com>
---
.circleci/config.yml | 13 +++++++++++--
.github/workflows/build-macos.yml | 16 +++++++++++++---
cgo/libclang_config_llvm16.go | 2 +-
cgo/libclang_config_llvm17.go | 15 +++++++++++++++
go.mod | 2 +-
go.sum | 4 ++--
6 files changed, 43 insertions(+), 9 deletions(-)
create mode 100644 cgo/libclang_config_llvm17.go
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 1dff3ba7..86da321d 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -55,7 +55,7 @@ commands:
- run:
name: "Install apt dependencies"
command: |
- echo 'deb https://apt.llvm.org/buster/ llvm-toolchain-buster-<<parameters.llvm>> main' > /etc/apt/sources.list.d/llvm.list
+ echo 'deb https://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-<<parameters.llvm>> main' > /etc/apt/sources.list.d/llvm.list
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
apt-get update
apt-get install --no-install-recommends -y \
@@ -100,11 +100,18 @@ commands:
jobs:
test-llvm14-go118:
docker:
- - image: golang:1.18-buster
+ - image: golang:1.18-bullseye
steps:
- test-linux:
llvm: "14"
resource_class: large
+ test-llvm17-go121:
+ docker:
+ - image: golang:1.21-bullseye
+ steps:
+ - test-linux:
+ llvm: "17"
+ resource_class: large
workflows:
test-all:
@@ -112,3 +119,5 @@ workflows:
# This tests our lowest supported versions of Go and LLVM, to make sure at
# least the smoke tests still pass.
- test-llvm14-go118
+ # This tests the upcoming LLVM 17 support.
+ - test-llvm17-go121
diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml
index 1284a4ed..be8bdb03 100644
--- a/.github/workflows/build-macos.yml
+++ b/.github/workflows/build-macos.yml
@@ -116,11 +116,15 @@ jobs:
test-macos-homebrew:
name: homebrew-install
runs-on: macos-latest
+ strategy:
+ matrix:
+ version: [16, 17]
steps:
+ - name: Update Homebrew
+ run: brew update
- name: Install LLVM
- shell: bash
run: |
- HOMEBREW_NO_AUTO_UPDATE=1 brew install llvm@16
+ HOMEBREW_NO_AUTO_UPDATE=1 brew install llvm@${{ matrix.version }}
- name: Checkout
uses: actions/checkout@v3
- name: Install Go
@@ -128,7 +132,13 @@ jobs:
with:
go-version: '1.21'
cache: true
- - name: Build TinyGo
+ - name: Build TinyGo (LLVM ${{ matrix.version }})
+ run: go install -tags=llvm${{ matrix.version }}
+ - name: Check binary
+ run: tinygo version
+ - name: Build TinyGo (default LLVM)
+ if: matrix.version == 16
run: go install
- name: Check binary
+ if: matrix.version == 16
run: tinygo version
diff --git a/cgo/libclang_config_llvm16.go b/cgo/libclang_config_llvm16.go
index 79aacd2f..28091701 100644
--- a/cgo/libclang_config_llvm16.go
+++ b/cgo/libclang_config_llvm16.go
@@ -1,4 +1,4 @@
-//go:build !byollvm && !llvm14 && !llvm15
+//go:build !byollvm && !llvm14 && !llvm15 && !llvm17
package cgo
diff --git a/cgo/libclang_config_llvm17.go b/cgo/libclang_config_llvm17.go
new file mode 100644
index 00000000..fd6d1480
--- /dev/null
+++ b/cgo/libclang_config_llvm17.go
@@ -0,0 +1,15 @@
+//go:build !byollvm && llvm17
+
+package cgo
+
+/*
+#cgo linux CFLAGS: -I/usr/include/llvm-17 -I/usr/include/llvm-c-17 -I/usr/lib/llvm-17/include
+#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@17/include
+#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@17/include
+#cgo freebsd CFLAGS: -I/usr/local/llvm17/include
+#cgo linux LDFLAGS: -L/usr/lib/llvm-17/lib -lclang
+#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@17/lib -lclang -lffi
+#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@17/lib -lclang -lffi
+#cgo freebsd LDFLAGS: -L/usr/local/llvm17/lib -lclang
+*/
+import "C"
diff --git a/go.mod b/go.mod
index 9af3402b..36d601f8 100644
--- a/go.mod
+++ b/go.mod
@@ -18,7 +18,7 @@ require (
golang.org/x/sys v0.11.0
golang.org/x/tools v0.12.0
gopkg.in/yaml.v2 v2.4.0
- tinygo.org/x/go-llvm v0.0.0-20230918183930-9edb6403d0bc
+ tinygo.org/x/go-llvm v0.0.0-20230923132128-bba3c7009bfd
)
require (
diff --git a/go.sum b/go.sum
index 587efdf2..1b5ad074 100644
--- a/go.sum
+++ b/go.sum
@@ -65,5 +65,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
-tinygo.org/x/go-llvm v0.0.0-20230918183930-9edb6403d0bc h1:IVX1dqCX3c88P7iEMBtz1xCAM4UIqCMgbqHdSefBaWE=
-tinygo.org/x/go-llvm v0.0.0-20230918183930-9edb6403d0bc/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
+tinygo.org/x/go-llvm v0.0.0-20230923132128-bba3c7009bfd h1:iSVvs8r3ooxYTsmcf7FKCHq83eOSUkWKtmQhnDadDQU=
+tinygo.org/x/go-llvm v0.0.0-20230923132128-bba3c7009bfd/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=
--
2.41.0

View file

@ -77,6 +77,15 @@ Patch0002: 0002-Skip-some-cross-Linux-tests-where-qemu-is-broken.patch
# Add Fedora specific dnf instructions
Patch0003: 0003-Suggest-optional-packages-to-install-if-missing.patch
# Support LLVM 17.
# https://github.com/tinygo-org/tinygo/pull/3923
Patch0004: 0004-transform-fix-bug-in-StringToBytes-optimization-pass.patch
# https://github.com/tinygo-org/tinygo/pull/3918
Patch0005: 0005-all-use-the-new-LLVM-pass-manager.patch
# https://github.com/tinygo-org/tinygo/pull/3913
Patch0006: 0006-avr-don-t-compile-large-parts-of-picolibc-math-stdio.patch
Patch0007: 0007-all-add-initial-LLVM-17-support.patch
# Not supported upstream yet.
ExcludeArch: ppc64le s390x
# https://fedoraproject.org/wiki/Changes/EncourageI686LeafRemoval
@ -97,7 +106,7 @@ BuildRequires: golang(github.com/mattn/go-tty) >= 0.0.4
BuildRequires: golang(github.com/sigurn/crc16)
BuildRequires: golang(go.bug.st/serial) >= 1.6.0
BuildRequires: golang(golang.org/x/tools/go/ast/astutil)
BuildRequires: golang(golang.org/x/tools/go/ssa) >= 0.12
BuildRequires: golang(golang.org/x/tools/go/ssa) >= 0.11
BuildRequires: golang(gopkg.in/yaml.v2) >= 2.4.0
BuildRequires: golang(tinygo.org/x/go-llvm)
BuildRequires: golang-tests