diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index f457cc687c..09ca5d69ee 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -40,7 +40,7 @@ jobs: - name: Install Go uses: actions/setup-go@v6 with: - go-version: '1.26.4' + go-version: '1.27.0-rc.2' cache: true - name: Restore LLVM source cache uses: actions/cache/restore@v5 @@ -71,7 +71,7 @@ jobs: uses: actions/cache/restore@v5 id: cache-llvm-build with: - key: llvm-build-20-${{ matrix.os }}-v2 + key: llvm-build-20-${{ matrix.os }}-v3 path: llvm-build - name: Build LLVM if: steps.cache-llvm-build.outputs.cache-hit != 'true' @@ -131,7 +131,7 @@ jobs: - name: Install Go uses: actions/setup-go@v6 with: - go-version: '1.26.4' + go-version: '1.27.0-rc.2' cache: true - name: Build TinyGo (LLVM ${{ matrix.version }}) run: go install -tags=llvm${{ matrix.version }} diff --git a/.github/workflows/compat.yml b/.github/workflows/compat.yml index 7ba585dc78..42f6826507 100644 --- a/.github/workflows/compat.yml +++ b/.github/workflows/compat.yml @@ -20,7 +20,7 @@ jobs: env: # Oldest versions currently supported by TinyGo LLVM: "15" - Go: "1.24" # when updating this, also update minorMin in builder/config.go + Go: "1.25" # when updating this, also update minorMin in builder/config.go steps: - name: Checkout uses: actions/checkout@v6 diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index fc519c07dd..d4721ee4c8 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -23,7 +23,7 @@ jobs: - name: Install Go uses: actions/setup-go@v6 with: - go-version: '1.26.4' + go-version: '1.27.0-rc.2' cache: true - name: Run go mod tidy run: go mod tidy @@ -36,7 +36,7 @@ jobs: # statically linked binary. runs-on: ubuntu-latest container: - image: golang:1.26-alpine + image: golang:1.27rc2-alpine outputs: version: ${{ steps.version.outputs.version }} steps: @@ -91,7 +91,7 @@ jobs: uses: actions/cache/restore@v5 id: cache-llvm-build with: - key: llvm-build-20-linux-alpine-v2 + key: llvm-build-20-linux-alpine-v3 path: llvm-build - name: Build LLVM if: steps.cache-llvm-build.outputs.cache-hit != 'true' @@ -115,7 +115,7 @@ jobs: uses: actions/cache@v5 id: cache-binaryen with: - key: binaryen-linux-alpine-v2 + key: binaryen-linux-alpine-v3 path: build/wasm-opt - name: Build Binaryen if: steps.cache-binaryen.outputs.cache-hit != 'true' @@ -160,7 +160,7 @@ jobs: - name: Install Go uses: actions/setup-go@v6 with: - go-version: '1.26.4' + go-version: '1.27.0-rc.2' cache: true - name: Install wasmtime uses: bytecodealliance/actions/wasmtime/setup@v1 @@ -204,7 +204,7 @@ jobs: - name: Install Go uses: actions/setup-go@v6 with: - go-version: '1.26.4' + go-version: '1.27.0-rc.2' cache: true - name: Install Node.js uses: actions/setup-node@v6 @@ -245,7 +245,7 @@ jobs: uses: actions/cache/restore@v5 id: cache-llvm-build with: - key: llvm-build-20-linux-asserts-v1 + key: llvm-build-20-linux-asserts-v2 path: llvm-build - name: Build LLVM if: steps.cache-llvm-build.outputs.cache-hit != 'true' @@ -323,7 +323,7 @@ jobs: - name: Install Go uses: actions/setup-go@v6 with: - go-version: '1.26.4' + go-version: '1.27.0-rc.2' cache: true - name: Restore LLVM source cache uses: actions/cache/restore@v5 @@ -354,7 +354,7 @@ jobs: uses: actions/cache/restore@v5 id: cache-llvm-build with: - key: llvm-build-20-linux-${{ matrix.goarch }}-v1 + key: llvm-build-20-linux-${{ matrix.goarch }}-v2 path: llvm-build - name: Build LLVM if: steps.cache-llvm-build.outputs.cache-hit != 'true' @@ -378,7 +378,7 @@ jobs: uses: actions/cache@v5 id: cache-binaryen with: - key: binaryen-linux-${{ matrix.goarch }}-v4 + key: binaryen-linux-${{ matrix.goarch }}-v5 path: build/wasm-opt - name: Build Binaryen if: steps.cache-binaryen.outputs.cache-hit != 'true' diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 5e76e207d2..77aa5d8d6c 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -34,7 +34,7 @@ jobs: - name: Install Go uses: actions/setup-go@v6 with: - go-version: '1.26.4' + go-version: '1.27.0-rc.2' cache: true - name: Restore cached LLVM source uses: actions/cache/restore@v5 @@ -65,7 +65,7 @@ jobs: uses: actions/cache/restore@v5 id: cache-llvm-build with: - key: llvm-build-20-windows-v5 + key: llvm-build-20-windows-v6 path: llvm-build - name: Build LLVM if: steps.cache-llvm-build.outputs.cache-hit != 'true' @@ -129,7 +129,7 @@ jobs: - name: Install Go uses: actions/setup-go@v6 with: - go-version: '1.26.4' + go-version: '1.27.0-rc.2' cache: true - name: Download TinyGo build uses: actions/download-artifact@v8 @@ -150,7 +150,7 @@ jobs: - name: Install Go uses: actions/setup-go@v6 with: - go-version: '1.26.4' + go-version: '1.27.0-rc.2' cache: true - name: Download TinyGo build uses: actions/download-artifact@v8 @@ -176,7 +176,7 @@ jobs: - name: Install Go uses: actions/setup-go@v6 with: - go-version: '1.26.4' + go-version: '1.27.0-rc.2' cache: true - name: Download TinyGo build uses: actions/download-artifact@v8 diff --git a/Dockerfile b/Dockerfile index 1fe6d2d74e..4103d00770 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # tinygo-llvm stage obtains the llvm source for TinyGo -FROM golang:1.26 AS tinygo-llvm +FROM golang:1.27rc2 AS tinygo-llvm RUN apt-get update && \ apt-get install -y apt-utils make cmake clang-17 ninja-build && \ @@ -33,7 +33,7 @@ RUN cd /tinygo/ && \ # tinygo-compiler copies the compiler build over to a base Go container (without # all the build tools etc). -FROM golang:1.26 AS tinygo-compiler +FROM golang:1.27rc2 AS tinygo-compiler # Copy tinygo build. COPY --from=tinygo-compiler-build /tinygo/build/release/tinygo /tinygo diff --git a/GNUmakefile b/GNUmakefile index ec4c6a1a5c..42f87b6f7f 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -387,7 +387,9 @@ TEST_PACKAGES_FAST = \ # archive/zip requires os.ReadAt, which is not yet supported on windows # bytes requires mmap -# compress/flate appears to hang on wasi +# compress/flate appears to hang on wasi; on all targets Go 1.27's new +# compress/flate.indexTokens returns a ~262KB struct by value which crashes +# LLVM's X86 instruction selector during LTO codegen # crypto/aes needs reflect.Type.Method(), not yet implemented # crypto/des fails on wasi, needs panic()/recover() # crypto/hmac fails on wasi, it exits with a "slice out of range" panic @@ -409,7 +411,6 @@ TEST_PACKAGES_FAST = \ # Additional standard library packages that pass tests on individual platforms TEST_PACKAGES_LINUX := \ archive/zip \ - compress/flate \ context \ crypto/aes \ crypto/des \ @@ -437,7 +438,6 @@ TEST_PACKAGES_DARWIN := $(TEST_PACKAGES_LINUX) # os/user requires t.Skip() support TEST_PACKAGES_WINDOWS := \ - compress/flate \ crypto/des \ crypto/hmac \ image \ diff --git a/builder/builtins.go b/builder/builtins.go index f135c83dd1..1feeb5d5a3 100644 --- a/builder/builtins.go +++ b/builder/builtins.go @@ -207,6 +207,16 @@ var windowsI386Builtins = []string{ "i386/chkstk.S", // also _alloca } +// Builtins needed specifically for windows/amd64. +var windowsAMD64Builtins = []string{ + "x86_64/chkstk.S", +} + +// Builtins needed specifically for windows/arm64. +var windowsARM64Builtins = []string{ + "aarch64/chkstk.S", +} + // libCompilerRT is a library with symbols required by programs compiled with // LLVM. These symbols are for operations that cannot be emitted with a single // instruction or a short sequence of instructions for that target. @@ -233,13 +243,28 @@ var libCompilerRT = Library{ builtins = append(builtins, aeabiBuiltins...) case "avr": builtins = append(builtins, avrBuiltins...) - case "x86_64", "aarch64", "riscv64": // any 64-bit arch + case "x86_64": + builtins = append(builtins, genericBuiltins128...) + if isWindowsTriple(target) { + builtins = append(builtins, windowsAMD64Builtins...) + } + case "aarch64": + builtins = append(builtins, genericBuiltins128...) + if isWindowsTriple(target) { + builtins = append(builtins, windowsARM64Builtins...) + } + case "riscv64": builtins = append(builtins, genericBuiltins128...) case "i386": - if strings.Split(target, "-")[2] == "windows" { + if isWindowsTriple(target) { builtins = append(builtins, windowsI386Builtins...) } } return builtins, nil }, } + +func isWindowsTriple(target string) bool { + parts := strings.Split(target, "-") + return len(parts) > 2 && parts[2] == "windows" +} diff --git a/builder/config.go b/builder/config.go index afada914c9..4dcc2786e8 100644 --- a/builder/config.go +++ b/builder/config.go @@ -25,8 +25,8 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) { } // Version range supported by TinyGo. - const minorMin = 24 // when updating the min version, also update .github/workflows/compat.yml - const minorMax = 26 + const minorMin = 25 // when updating the min version, also update .github/workflows/compat.yml + const minorMax = 27 // Check that we support this Go toolchain version. gorootMajor, gorootMinor, err := goenv.GetGorootVersion() diff --git a/builder/sizes_test.go b/builder/sizes_test.go index c73d653c0e..ee769ee9d9 100644 --- a/builder/sizes_test.go +++ b/builder/sizes_test.go @@ -42,8 +42,8 @@ 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", 3771, 309, 0, 2260}, - {"microbit", "examples/serial", 2832, 368, 8, 2256}, + {"hifive1b", "examples/echo", 3769, 307, 0, 2260}, + {"microbit", "examples/serial", 2828, 368, 8, 2256}, {"wioterminal", "examples/pininterrupt", 8053, 1663, 132, 7488}, // TODO: also check wasm. Right now this is difficult, because diff --git a/compileopts/target.go b/compileopts/target.go index 42e3734870..92b011b830 100644 --- a/compileopts/target.go +++ b/compileopts/target.go @@ -475,10 +475,12 @@ func defaultTarget(options *Options) (*TargetSpec, error) { "-m", "i386pep", "--image-base", "0x400000", ) + spec.RTLib = "compiler-rt" case "arm64": spec.LDFlags = append(spec.LDFlags, "-m", "arm64pe", ) + spec.RTLib = "compiler-rt" } spec.LDFlags = append(spec.LDFlags, "-Bdynamic", diff --git a/compiler/symbol.go b/compiler/symbol.go index 9076e870de..2b8d13b6f3 100644 --- a/compiler/symbol.go +++ b/compiler/symbol.go @@ -322,6 +322,26 @@ func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo { // Pick the default linkName. linkName: f.RelString(nil), } + // If this is an instantiation of a generic function, disambiguate the link + // name using the unaliased type arguments. Without this, two different + // instantiations that happen to spell their type argument the same way + // (e.g. via distinct function-local `type F = float32` and `type F = float64` + // aliases, as used by internal/strconv.ftoa32/ftoa64 in Go 1.27) share the + // same RelString and collide on mod.NamedFunction, causing the second + // instantiation to silently reuse the first one's code. + if targs := f.TypeArgs(); len(targs) > 0 { + var sb strings.Builder + sb.WriteString(f.RelString(nil)) + sb.WriteString("{") + for i, t := range targs { + if i > 0 { + sb.WriteString(",") + } + sb.WriteString(types.Unalias(t).String()) + } + sb.WriteString("}") + info.linkName = sb.String() + } // Check for a few runtime functions that are treated specially. if info.linkName == "runtime.wasmEntryReactor" && c.BuildMode == "c-shared" { @@ -444,7 +464,7 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) { info.inline = inlineHint case "//go:noinline": info.inline = inlineNone - case "//go:linkname": + case "//go:linkname", "//go:linknamestd": if len(parts) != 3 || parts[1] != f.Name() { continue } diff --git a/compiler/testdata/pragma.go b/compiler/testdata/pragma.go index ebb3a2a67b..1445937df0 100644 --- a/compiler/testdata/pragma.go +++ b/compiler/testdata/pragma.go @@ -35,6 +35,12 @@ func withLinkageName1() { //go:linkname withLinkageName2 somepkg.someFunction2 func withLinkageName2() +// Import a function from a different package using go:linknamestd (the standard +// library variant of go:linkname introduced in Go 1.27). +// +//go:linknamestd withLinkageNameStd somepkg.someFunctionStd +func withLinkageNameStd() + // Function has an 'inline hint', similar to the inline keyword in C. // //go:inline diff --git a/compiler/testdata/pragma.ll b/compiler/testdata/pragma.ll index c2c06b3d49..6722e56f82 100644 --- a/compiler/testdata/pragma.ll +++ b/compiler/testdata/pragma.ll @@ -33,6 +33,8 @@ entry: declare void @somepkg.someFunction2(ptr) #0 +declare void @somepkg.someFunctionStd(ptr) #0 + ; Function Attrs: inlinehint nounwind define hidden void @main.inlineFunc(ptr %context) unnamed_addr #3 { entry: diff --git a/go.mod b/go.mod index a33d3dd7cb..4a6ee01fd0 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/tinygo-org/tinygo -go 1.24.0 +go 1.25.0 require ( github.com/aykevl/go-wasm v0.0.2-0.20250317121156-42b86c494139 @@ -18,9 +18,9 @@ require ( go.bug.st/serial v1.6.4 go.bytecodealliance.org v0.6.2 go.bytecodealliance.org/cm v0.2.2 - golang.org/x/net v0.50.0 - golang.org/x/sys v0.41.0 - golang.org/x/tools v0.42.0 + golang.org/x/net v0.56.0 + golang.org/x/sys v0.46.0 + golang.org/x/tools v0.47.0 gopkg.in/yaml.v2 v2.4.0 tinygo.org/x/espflasher v0.6.1 tinygo.org/x/go-llvm v0.0.0-20260422095634-06c6725fe5e6 @@ -48,6 +48,6 @@ require ( github.com/spf13/afero v1.11.0 // indirect github.com/ulikunitz/xz v0.5.12 // indirect github.com/urfave/cli/v3 v3.0.0-beta1 // indirect - golang.org/x/mod v0.33.0 // indirect - golang.org/x/text v0.34.0 // indirect + golang.org/x/mod v0.37.0 // indirect + golang.org/x/text v0.38.0 // indirect ) diff --git a/go.sum b/go.sum index 0b179750d5..1f5c9b3ccf 100644 --- a/go.sum +++ b/go.sum @@ -95,24 +95,24 @@ go.bytecodealliance.org v0.6.2 h1:Jy4u5DVmSkXgsnwojBhJ+AD/YsJsR3VzVnxF0xRCqTQ= go.bytecodealliance.org v0.6.2/go.mod h1:gqjTJm0y9NSksG4py/lSjIQ/SNuIlOQ+hCIEPQwtJgA= go.bytecodealliance.org/cm v0.2.2 h1:M9iHS6qs884mbQbIjtLX1OifgyPG9DuMs2iwz8G4WQA= go.bytecodealliance.org/cm v0.2.2/go.mod h1:JD5vtVNZv7sBoQQkvBvAAVKJPhR/bqBH7yYXTItMfZI= -golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= -golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= -golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= -golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= -golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= +golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= +golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= +golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= +golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= +golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= -golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= -golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= -golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= -golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= +golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= +golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE= +golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4= +golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q= +golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= diff --git a/main_test.go b/main_test.go index b7dfa6b0f5..4316c2daee 100644 --- a/main_test.go +++ b/main_test.go @@ -273,6 +273,10 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) { isWASI := strings.HasPrefix(options.Target, "wasi") isWebAssembly := isWASI || strings.HasPrefix(options.Target, "wasm") || (options.Target == "" && strings.HasPrefix(options.GOARCH, "wasm")) isBaremetal := options.Target == "simavr" || options.Target == "cortex-m-qemu" || options.Target == "riscv-qemu" + _, goMinor, err := goenv.GetGorootVersion() + if err != nil { + t.Fatal("could not get version:", goMinor) + } for _, name := range tests { if options.GOOS == "linux" && (options.GOARCH == "arm" || options.GOARCH == "386") { @@ -296,6 +300,11 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) { continue } } + if options.Target == "cortex-m-qemu" && goMinor >= 27 && name == "json.go" { + // Go 1.27 jsonv2 exceeds the LM3S6965's 256KiB flash. json.go + // is still covered by larger targets such as riscv-qemu. + continue + } if options.Target == "simavr" { // Not all tests are currently supported on AVR. // Skip the ones that aren't. diff --git a/src/runtime/os_darwin_go126.go b/src/runtime/os_darwin_go126.go index d676482ca8..c013f02a87 100644 --- a/src/runtime/os_darwin_go126.go +++ b/src/runtime/os_darwin_go126.go @@ -1,4 +1,4 @@ -//go:build darwin && go1.26 +//go:build darwin && (go1.26 || go1.27) package runtime