From 63e3c3ec24ba4cebfa04899dcc91530452139866 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Tue, 16 Sep 2025 16:45:56 +0200 Subject: [PATCH] all: initial support for LLVM 21 --- .circleci/config.yml | 2 +- .github/workflows/build-macos.yml | 6 +++--- .github/workflows/sizediff-install-pkgs.sh | 10 +++++----- cgo/libclang.go | 4 ++++ cgo/libclang_config_llvm20.go | 2 +- cgo/libclang_config_llvm21.go | 15 +++++++++++++++ compiler/compiler.go | 8 ++++++++ compiler/symbol.go | 21 +++++++++------------ go.mod | 2 +- go.sum | 4 ++-- 10 files changed, 49 insertions(+), 25 deletions(-) create mode 100644 cgo/libclang_config_llvm21.go diff --git a/.circleci/config.yml b/.circleci/config.yml index e8654f3e9f..a3844ff44f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -108,7 +108,7 @@ jobs: - image: golang:1.25-bullseye steps: - test-linux: - llvm: "20" + llvm: "21" resource_class: large workflows: diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 8dc15ddde9..0379dbc04a 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -117,7 +117,7 @@ jobs: runs-on: macos-latest strategy: matrix: - version: [16, 17, 18, 19, 20] + version: [16, 17, 18, 19, 20, 21] steps: - name: Set up Homebrew uses: Homebrew/actions/setup-homebrew@master @@ -141,8 +141,8 @@ jobs: - name: Check binary run: tinygo version - name: Build TinyGo (default LLVM) - if: matrix.version == 20 + if: matrix.version == 21 run: go install - name: Check binary - if: matrix.version == 20 + if: matrix.version == 21 run: tinygo version diff --git a/.github/workflows/sizediff-install-pkgs.sh b/.github/workflows/sizediff-install-pkgs.sh index fd65887df8..b98cd70c1a 100755 --- a/.github/workflows/sizediff-install-pkgs.sh +++ b/.github/workflows/sizediff-install-pkgs.sh @@ -2,11 +2,11 @@ # still works after checking out the dev branch (that is, when going from LLVM # 16 to LLVM 17 for example, both Clang 16 and Clang 17 are installed). -echo 'deb https://apt.llvm.org/noble/ llvm-toolchain-noble-20 main' | sudo tee /etc/apt/sources.list.d/llvm.list +echo 'deb https://apt.llvm.org/noble/ llvm-toolchain-noble-21 main' | sudo tee /etc/apt/sources.list.d/llvm.list wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - sudo apt-get update sudo apt-get install --no-install-recommends -y \ - llvm-20-dev \ - clang-20 \ - libclang-20-dev \ - lld-20 + llvm-21-dev \ + clang-21 \ + libclang-21-dev \ + lld-21 diff --git a/cgo/libclang.go b/cgo/libclang.go index 3772ca5073..1cee550b9c 100644 --- a/cgo/libclang.go +++ b/cgo/libclang.go @@ -21,6 +21,10 @@ import ( ) /* +// Hide a warning in LLVM 21 that doesn't apply to us (appears to be a side +// effect of how CGo processes C header files). +#cgo CFLAGS: -Wno-deprecated-declarations + #include // If this fails, libclang headers aren't available. Please take a look here: https://tinygo.org/docs/guides/build/ #include #include diff --git a/cgo/libclang_config_llvm20.go b/cgo/libclang_config_llvm20.go index faa2e54d55..14902a2957 100644 --- a/cgo/libclang_config_llvm20.go +++ b/cgo/libclang_config_llvm20.go @@ -1,4 +1,4 @@ -//go:build !byollvm && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm19 +//go:build !byollvm && llvm20 package cgo diff --git a/cgo/libclang_config_llvm21.go b/cgo/libclang_config_llvm21.go new file mode 100644 index 0000000000..b26a69e6f2 --- /dev/null +++ b/cgo/libclang_config_llvm21.go @@ -0,0 +1,15 @@ +//go:build !byollvm && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm19 && !llvm20 + +package cgo + +/* +#cgo linux CFLAGS: -I/usr/include/llvm-21 -I/usr/include/llvm-c-21 -I/usr/lib/llvm-21/include +#cgo darwin,amd64 CFLAGS: -I/usr/local/opt/llvm@21/include +#cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/llvm@21/include +#cgo freebsd CFLAGS: -I/usr/local/llvm21/include +#cgo linux LDFLAGS: -L/usr/lib/llvm-21/lib -lclang +#cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@21/lib -lclang +#cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@21/lib -lclang +#cgo freebsd LDFLAGS: -L/usr/local/llvm21/lib -lclang +*/ +import "C" diff --git a/compiler/compiler.go b/compiler/compiler.go index 61d7e89335..f506634408 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -84,6 +84,7 @@ type compilerContext struct { funcPtrType llvm.Type // pointer in function address space (1 for AVR, 0 elsewhere) funcPtrAddrSpace int uintptrType llvm.Type + nocaptureAttr llvm.Attribute program *ssa.Program diagnostics []error functionInfos map[*ssa.Function]functionInfo @@ -135,6 +136,13 @@ func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *C c.funcPtrType = dummyFunc.Type() dummyFunc.EraseFromParentAsFunction() + // The attribute "nocapture" changed to "captures(none)" in LLVM 21. + if llvmutil.Version() < 21 { + c.nocaptureAttr = c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0) + } else { + c.nocaptureAttr = c.ctx.CreateEnumAttribute(llvm.AttributeKindID("captures"), 0) + } + return c } diff --git a/compiler/symbol.go b/compiler/symbol.go index 749ad8f1b5..e33985463c 100644 --- a/compiler/symbol.go +++ b/compiler/symbol.go @@ -139,8 +139,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value) // not. // (It may be safe to add the nocapture parameter to the context // parameter, but I'd like to stay on the safe side here). - nocapture := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0) - llvmFn.AddAttributeAtIndex(i+1, nocapture) + llvmFn.AddAttributeAtIndex(i+1, c.nocaptureAttr) } } @@ -153,7 +152,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value) // Mark it as noreturn so LLVM can optimize away code. llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("noreturn"), 0)) case "internal/abi.NoEscape": - llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)) + llvmFn.AddAttributeAtIndex(1, c.nocaptureAttr) case "runtime.alloc": // Tell the optimizer that runtime.alloc is an allocator, meaning that it // returns values that are never null and never alias to an existing value. @@ -175,25 +174,25 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value) case "runtime.sliceAppend": // Appending a slice will only read the to-be-appended slice, it won't // be modified. - llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)) + llvmFn.AddAttributeAtIndex(2, c.nocaptureAttr) llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0)) case "runtime.sliceCopy": // Copying a slice won't capture any of the parameters. llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("writeonly"), 0)) - llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)) + llvmFn.AddAttributeAtIndex(1, c.nocaptureAttr) llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0)) - llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)) + llvmFn.AddAttributeAtIndex(2, c.nocaptureAttr) case "runtime.stringFromBytes": - llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)) + llvmFn.AddAttributeAtIndex(1, c.nocaptureAttr) llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0)) case "runtime.stringFromRunes": - llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)) + llvmFn.AddAttributeAtIndex(1, c.nocaptureAttr) llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0)) case "runtime.trackPointer": // This function is necessary for tracking pointers on the stack in a // portable way (see gc_stack_portable.go). Indicate to the optimizer // that the only thing we'll do is read the pointer. - llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)) + llvmFn.AddAttributeAtIndex(1, c.nocaptureAttr) llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0)) case "__mulsi3", "__divmodsi4", "__udivmodsi4": if strings.Split(c.Triple, "-")[0] == "avr" { @@ -228,11 +227,9 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value) llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-name", info.wasmName)) } - nocaptureKind := llvm.AttributeKindID("nocapture") - nocapture := c.ctx.CreateEnumAttribute(nocaptureKind, 0) for i, typ := range paramTypes { if typ.TypeKind() == llvm.PointerTypeKind { - llvmFn.AddAttributeAtIndex(i+1, nocapture) + llvmFn.AddAttributeAtIndex(i+1, c.nocaptureAttr) } } } diff --git a/go.mod b/go.mod index 7d4ea5f189..3031c47140 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( golang.org/x/sys v0.30.0 golang.org/x/tools v0.30.0 gopkg.in/yaml.v2 v2.4.0 - tinygo.org/x/go-llvm v0.0.0-20250422114502-b8f170971e74 + tinygo.org/x/go-llvm v0.0.0-20250916101410-63740cfada08 ) require ( diff --git a/go.sum b/go.sum index 8c2330c3c5..b98a818c4e 100644 --- a/go.sum +++ b/go.sum @@ -58,5 +58,5 @@ 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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -tinygo.org/x/go-llvm v0.0.0-20250422114502-b8f170971e74 h1:ovavgTdIBWCH8YWlcfq9gkpoyT1+IxMKSn+Df27QwE8= -tinygo.org/x/go-llvm v0.0.0-20250422114502-b8f170971e74/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0= +tinygo.org/x/go-llvm v0.0.0-20250916101410-63740cfada08 h1:vvEVYF4Qb38C25U8Ae/1QUWlCSp4pIE0PR+/BwNPvBU= +tinygo.org/x/go-llvm v0.0.0-20250916101410-63740cfada08/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0=