From e74c37b974b952a49530cc5302e71f054418a4ac Mon Sep 17 00:00:00 2001 From: ZhouGuangyuan Date: Sat, 15 Aug 2026 14:43:02 +0800 Subject: [PATCH 1/4] build: prepare LLVM 21 compatibility --- internal/build/build.go | 20 +++++++ internal/littest/littest.go | 12 ++++- internal/littest/littest_test.go | 10 ++++ internal/lto/lto.go | 2 +- ltoplugin/CMakeLists.txt | 6 +-- ssa/ssa_test.go | 31 +++++------ .../llvm/llvm_config_darwin_amd64_llvm19.go | 2 +- .../llvm/llvm_config_darwin_amd64_llvm21.go | 21 ++++++++ xtool/env/llvm/llvm_config_darwin_llvm19.go | 2 +- xtool/env/llvm/llvm_config_darwin_llvm21.go | 21 ++++++++ xtool/env/llvm/llvm_config_linux_llvm19.go | 2 +- xtool/env/llvm/llvm_config_linux_llvm21.go | 21 ++++++++ xtool/env/llvm/llvm_test.go | 48 +++++++++++++++++ xtool/env/llvm/version.go | 54 +++++++++++++++++++ 14 files changed, 229 insertions(+), 23 deletions(-) create mode 100644 xtool/env/llvm/llvm_config_darwin_amd64_llvm21.go create mode 100644 xtool/env/llvm/llvm_config_darwin_llvm21.go create mode 100644 xtool/env/llvm/llvm_config_linux_llvm21.go create mode 100644 xtool/env/llvm/version.go diff --git a/internal/build/build.go b/internal/build/build.go index 876f303d71..022a088ef0 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -63,6 +63,7 @@ import ( "github.com/goplus/llgo/internal/typepatch" "github.com/goplus/llgo/ssa/abi" xenv "github.com/goplus/llgo/xtool/env" + envllvm "github.com/goplus/llgo/xtool/env/llvm" gllvm "github.com/xgo-dev/llvm" llruntime "github.com/goplus/llgo/runtime" @@ -411,6 +412,9 @@ func Build(inv Invocation) ([]Package, error) { if err != nil { return nil, fmt.Errorf("failed to setup crosscompile: %w", err) } + if err := validateLLVMToolchain(export); err != nil { + return nil, fmt.Errorf("invalid LLVM toolchain: %w", err) + } applyBuildModeCompileFlags(conf.BuildMode, &export) // Update GOOS/GOARCH from export if target was used if conf.Target != "" && export.GOOS != "" { @@ -798,6 +802,22 @@ func Build(inv Invocation) ([]Package, error) { return allPkgs, nil } +func validateLLVMToolchain(export crosscompile.Export) error { + if export.ClangRoot != "" { + binDir := filepath.Join(export.ClangRoot, "bin") + return envllvm.ValidateToolchainMajor(gllvm.Version, + filepath.Join(binDir, "llvm-config"), + filepath.Join(binDir, "clang"), + filepath.Join(binDir, "ld.lld"), + ) + } + compiler := filepath.Base(export.CC) + if compiler != "clang" && compiler != "clang++" { + return nil + } + return envllvm.ValidateToolchainMajor(gllvm.Version, "llvm-config", export.CC, "ld.lld") +} + // cHeaderPackages excludes the patched standard runtime implementation. Its // //export callbacks are linker implementation details and may use internal C // types that are deliberately not representable in a public generated header. diff --git a/internal/littest/littest.go b/internal/littest/littest.go index 0147603631..de11bad3c2 100644 --- a/internal/littest/littest.go +++ b/internal/littest/littest.go @@ -63,13 +63,14 @@ func LoadSpec(pkgDir string) (Spec, error) { } func Check(spec Spec, actual string) error { + actual = CanonicalizeLLVMIR(actual) switch spec.Mode { case ModeSkip: return nil case ModeFileCheck: return filecheck.Match(spec.Path, actual) case ModeLiteral: - if actual != spec.Text { + if actual != CanonicalizeLLVMIR(spec.Text) { return fmt.Errorf("%s: literal LLVM IR mismatch", spec.Path) } return nil @@ -78,6 +79,15 @@ func Check(spec Spec, actual string) error { } } +// CanonicalizeLLVMIR removes LLVM-version-specific spellings that are not the +// semantic contract of LLGo's IR tests. Verifier, object, link, and runtime +// tests still consume the original IR. +func CanonicalizeLLVMIR(ir string) string { + ir = strings.ReplaceAll(ir, "getelementptr inbounds nuw ", "getelementptr inbounds ") + ir = strings.ReplaceAll(ir, " captures(none)", " nocapture") + return ir +} + func loadSourceSpec(pkgDir string) (Spec, bool, error) { marked, ok, err := FindMarkedSourceFile(pkgDir) if err != nil { diff --git a/internal/littest/littest_test.go b/internal/littest/littest_test.go index d02d899375..b7da0668d5 100644 --- a/internal/littest/littest_test.go +++ b/internal/littest/littest_test.go @@ -110,6 +110,16 @@ package main } } +func TestCanonicalizeLLVMIRVersionSpellings(t *testing.T) { + input := " %1 = getelementptr inbounds nuw { ptr }, ptr %0, i32 0\n" + + "declare void @f(ptr captures(none) readonly)\n" + want := " %1 = getelementptr inbounds { ptr }, ptr %0, i32 0\n" + + "declare void @f(ptr nocapture readonly)\n" + if got := CanonicalizeLLVMIR(input); got != want { + t.Fatalf("CanonicalizeLLVMIR() = %q, want %q", got, want) + } +} + func TestLoadSpecFallsBackToOutLLWithoutMarker(t *testing.T) { dir := t.TempDir() err := os.WriteFile(filepath.Join(dir, "in.go"), []byte(`// CHECK: ret void diff --git a/internal/lto/lto.go b/internal/lto/lto.go index 06967124d4..47fc3f579e 100644 --- a/internal/lto/lto.go +++ b/internal/lto/lto.go @@ -65,7 +65,7 @@ func (p PassPlugin) LinkerFlags(goos string) ([]string, error) { return nil, nil } if goos == "darwin" { - return nil, fmt.Errorf("LTO pass plugins are not supported on darwin by LLVM 19 ld64.lld or Apple ld64") + return nil, fmt.Errorf("LTO pass plugins are not supported on darwin by the bundled ld64.lld or Apple ld64") } return []string{"-Wl,--load-pass-plugin=" + p.Path}, nil } diff --git a/ltoplugin/CMakeLists.txt b/ltoplugin/CMakeLists.txt index 67e9c3ce1e..10e3213e61 100644 --- a/ltoplugin/CMakeLists.txt +++ b/ltoplugin/CMakeLists.txt @@ -1,11 +1,11 @@ cmake_minimum_required(VERSION 3.20) -project(LLGOLTOPlugin LANGUAGES CXX) +project(LLGOLTOPlugin LANGUAGES C CXX) find_package(LLVM REQUIRED CONFIG) -if(NOT LLVM_VERSION_MAJOR EQUAL 19) - message(FATAL_ERROR "LLGo LTO plugin requires LLVM 19.x, found ${LLVM_PACKAGE_VERSION}") +if(NOT LLVM_VERSION_MAJOR EQUAL 19 AND NOT LLVM_VERSION_MAJOR EQUAL 21) + message(FATAL_ERROR "LLGo LTO plugin requires LLVM 19.x or 21.x, found ${LLVM_PACKAGE_VERSION}") endif() message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") diff --git a/ssa/ssa_test.go b/ssa/ssa_test.go index 1374662138..744e6d765a 100644 --- a/ssa/ssa_test.go +++ b/ssa/ssa_test.go @@ -34,6 +34,7 @@ import ( "unsafe" "github.com/goplus/gogen/packages" + "github.com/goplus/llgo/internal/littest" rtabi "github.com/goplus/llgo/runtime/abi" "github.com/xgo-dev/llvm" ) @@ -1985,8 +1986,8 @@ func TestAny(t *testing.T) { func assertPkg(t *testing.T, p Package, expected string) { t.Helper() - got := StripModuleTarget(p.String()) - want := StripModuleTarget(expected) + got := littest.CanonicalizeLLVMIR(StripModuleTarget(p.String())) + want := littest.CanonicalizeLLVMIR(StripModuleTarget(expected)) if got != want { t.Fatalf("\n==> got:\n%s\n==> expected:\n%s\n", got, want) } @@ -2587,15 +2588,14 @@ attributes #1 = { returns_twice } func TestTargetMachineAndDataLayout(t *testing.T) { tests := []struct { - goos string - goarch string - dataLayout string - triple string + goos string + goarch string + triple string }{ - {"linux", "amd64", "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", "x86_64-unknown-linux"}, - {"linux", "arm64", "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32", "aarch64-unknown-linux"}, - {"darwin", "amd64", "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", "x86_64-apple-macosx"}, - {"darwin", "arm64", "e-m:o-i64:64-i128:128-n32:64-S128-Fn32", "arm64-apple-macosx"}, + {"linux", "amd64", "x86_64-unknown-linux"}, + {"linux", "arm64", "aarch64-unknown-linux"}, + {"darwin", "amd64", "x86_64-apple-macosx"}, + {"darwin", "arm64", "arm64-apple-macosx"}, } for _, tt := range tests { prog := NewProgram(&Target{GOOS: tt.goos, GOARCH: tt.goarch}) @@ -2612,14 +2612,15 @@ func TestTargetMachineAndDataLayout(t *testing.T) { t.Fatalf("%s/%s TargetData() returned nil", tt.goos, tt.goarch) } - // Test DataLayout() returns the expected data layout string - if dl := prog.DataLayout(); dl != tt.dataLayout { - t.Fatalf("%s/%s DataLayout mismatch: got %q, want %q", tt.goos, tt.goarch, dl, tt.dataLayout) + // Test DataLayout() returns a valid layout and is propagated to modules. + dl := prog.DataLayout() + if dl == "" || !strings.HasPrefix(dl, "e-") { + t.Fatalf("%s/%s DataLayout is invalid: %q", tt.goos, tt.goarch, dl) } pkg := prog.NewPackage("foo", "foo/bar") - if dl := pkg.Module().DataLayout(); dl != tt.dataLayout { - t.Fatalf("%s/%s module DataLayout mismatch: got %q, want %q", tt.goos, tt.goarch, dl, tt.dataLayout) + if moduleLayout := pkg.Module().DataLayout(); moduleLayout != dl { + t.Fatalf("%s/%s module DataLayout mismatch: got %q, want %q", tt.goos, tt.goarch, moduleLayout, dl) } // Test Target().Spec().Triple returns the expected triple diff --git a/xtool/env/llvm/llvm_config_darwin_amd64_llvm19.go b/xtool/env/llvm/llvm_config_darwin_amd64_llvm19.go index c1ef8cd433..e330bbd2c0 100644 --- a/xtool/env/llvm/llvm_config_darwin_amd64_llvm19.go +++ b/xtool/env/llvm/llvm_config_darwin_amd64_llvm19.go @@ -1,4 +1,4 @@ -//go:build !byollvm && darwin && amd64 && !llvm14 && !llvm15 && !llvm16 && !llvm17 && !llvm18 +//go:build !byollvm && darwin && amd64 && !llvm14 && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm20 && !llvm21 && !llvm22 /* * Copyright (c) 2024 The XGo Authors (xgo.dev). All rights reserved. diff --git a/xtool/env/llvm/llvm_config_darwin_amd64_llvm21.go b/xtool/env/llvm/llvm_config_darwin_amd64_llvm21.go new file mode 100644 index 0000000000..c815343500 --- /dev/null +++ b/xtool/env/llvm/llvm_config_darwin_amd64_llvm21.go @@ -0,0 +1,21 @@ +//go:build !byollvm && darwin && amd64 && llvm21 + +/* + * Copyright (c) 2024 The XGo Authors (xgo.dev). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package llvm + +const ldLLVMConfigBin = "/usr/local/opt/llvm@21/bin/llvm-config" diff --git a/xtool/env/llvm/llvm_config_darwin_llvm19.go b/xtool/env/llvm/llvm_config_darwin_llvm19.go index 08baa210c9..829203508b 100644 --- a/xtool/env/llvm/llvm_config_darwin_llvm19.go +++ b/xtool/env/llvm/llvm_config_darwin_llvm19.go @@ -1,4 +1,4 @@ -//go:build !byollvm && darwin && !amd64 && !llvm14 && !llvm15 && !llvm16 && !llvm17 && !llvm18 +//go:build !byollvm && darwin && !amd64 && !llvm14 && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm20 && !llvm21 && !llvm22 /* * Copyright (c) 2024 The XGo Authors (xgo.dev). All rights reserved. diff --git a/xtool/env/llvm/llvm_config_darwin_llvm21.go b/xtool/env/llvm/llvm_config_darwin_llvm21.go new file mode 100644 index 0000000000..757611e6b2 --- /dev/null +++ b/xtool/env/llvm/llvm_config_darwin_llvm21.go @@ -0,0 +1,21 @@ +//go:build !byollvm && darwin && !amd64 && llvm21 + +/* + * Copyright (c) 2024 The XGo Authors (xgo.dev). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package llvm + +const ldLLVMConfigBin = "/opt/homebrew/opt/llvm@21/bin/llvm-config" diff --git a/xtool/env/llvm/llvm_config_linux_llvm19.go b/xtool/env/llvm/llvm_config_linux_llvm19.go index 6fca306d4d..e9eabea377 100644 --- a/xtool/env/llvm/llvm_config_linux_llvm19.go +++ b/xtool/env/llvm/llvm_config_linux_llvm19.go @@ -1,4 +1,4 @@ -//go:build !byollvm && linux && !llvm14 && !llvm15 && !llvm16 && !llvm17 && !llvm18 +//go:build !byollvm && linux && !llvm14 && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm20 && !llvm21 && !llvm22 /* * Copyright (c) 2024 The XGo Authors (xgo.dev). All rights reserved. diff --git a/xtool/env/llvm/llvm_config_linux_llvm21.go b/xtool/env/llvm/llvm_config_linux_llvm21.go new file mode 100644 index 0000000000..d10082059f --- /dev/null +++ b/xtool/env/llvm/llvm_config_linux_llvm21.go @@ -0,0 +1,21 @@ +//go:build !byollvm && linux && llvm21 + +/* + * Copyright (c) 2024 The XGo Authors (xgo.dev). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package llvm + +const ldLLVMConfigBin = "/usr/lib/llvm-21/bin/llvm-config" diff --git a/xtool/env/llvm/llvm_test.go b/xtool/env/llvm/llvm_test.go index 16a45005fe..73effcb2e0 100644 --- a/xtool/env/llvm/llvm_test.go +++ b/xtool/env/llvm/llvm_test.go @@ -6,6 +6,7 @@ import ( "os" "path/filepath" "runtime" + "strings" "testing" ) @@ -54,3 +55,50 @@ func TestSetupPathIgnoresMissingBinDir(t *testing.T) { t.Fatalf("PATH changed from %q to %q without an LLVM bin directory", before, got) } } + +func TestParseMajorVersion(t *testing.T) { + tests := []struct { + version string + want int + }{ + {version: "21.1.8", want: 21}, + {version: "Homebrew clang version 21.1.8", want: 21}, + {version: "Homebrew LLD 21.1.8 (compatible with GNU linkers)", want: 21}, + } + for _, test := range tests { + got, err := parseMajorVersion(test.version) + if err != nil { + t.Fatalf("parseMajorVersion(%q): %v", test.version, err) + } + if got != test.want { + t.Fatalf("parseMajorVersion(%q) = %d, want %d", test.version, got, test.want) + } + } +} + +func TestValidateToolchainMajor(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("test helper uses a shell script") + } + + matching := writeVersionTool(t, "clang 21.1.3") + if err := ValidateToolchainMajor("21.1.8", matching); err != nil { + t.Fatalf("matching major rejected: %v", err) + } + + mismatched := writeVersionTool(t, "clang 19.1.7") + err := ValidateToolchainMajor("21.1.8", mismatched) + if err == nil || !strings.Contains(err.Error(), "LLVM major version mismatch") { + t.Fatalf("mismatched major error = %v", err) + } +} + +func writeVersionTool(t *testing.T, version string) string { + t.Helper() + tool := filepath.Join(t.TempDir(), "llvm-tool") + contents := "#!/bin/sh\nprintf '%s\\n' '" + version + "'\n" + if err := os.WriteFile(tool, []byte(contents), 0o755); err != nil { + t.Fatal(err) + } + return tool +} diff --git a/xtool/env/llvm/version.go b/xtool/env/llvm/version.go new file mode 100644 index 0000000000..60c0c4bfee --- /dev/null +++ b/xtool/env/llvm/version.go @@ -0,0 +1,54 @@ +package llvm + +import ( + "fmt" + "os/exec" + "regexp" + "strconv" + "strings" +) + +var dottedVersion = regexp.MustCompile(`(?:^|[^0-9])([0-9]+)\.[0-9]+(?:\.[0-9]+)?`) + +// ValidateToolchainMajor checks that every named LLVM tool has the same major +// version as the LLVM library linked into the current LLGo process. +func ValidateToolchainMajor(linkedVersion string, tools ...string) error { + linkedMajor, err := parseMajorVersion(linkedVersion) + if err != nil { + return fmt.Errorf("parse linked LLVM version %q: %w", linkedVersion, err) + } + for _, tool := range tools { + output, err := exec.Command(tool, "--version").CombinedOutput() + if err != nil { + return fmt.Errorf("query LLVM tool %q: %w", tool, err) + } + toolVersion := strings.TrimSpace(string(output)) + toolMajor, err := parseMajorVersion(toolVersion) + if err != nil { + return fmt.Errorf("parse LLVM tool %q version %q: %w", tool, toolVersion, err) + } + if toolMajor != linkedMajor { + return fmt.Errorf("LLVM major version mismatch: linked LLVM %s, %s reports %s", linkedVersion, tool, firstLine(toolVersion)) + } + } + return nil +} + +func parseMajorVersion(version string) (int, error) { + match := dottedVersion.FindStringSubmatch(version) + if len(match) != 2 { + return 0, fmt.Errorf("no dotted version found") + } + major, err := strconv.Atoi(match[1]) + if err != nil { + return 0, err + } + return major, nil +} + +func firstLine(value string) string { + if i := strings.IndexByte(value, '\n'); i >= 0 { + return value[:i] + } + return value +} From 2e1027134c446ef3a53bfcac3f2987b83acfe756 Mon Sep 17 00:00:00 2001 From: ZhouGuangyuan Date: Sat, 15 Aug 2026 14:44:05 +0800 Subject: [PATCH 2/4] build: centralize the LLVM payload contract --- .github/actions/setup-goreleaser/action.yml | 7 +- .github/workflows/download_esp_clang.sh | 63 ++++++++-- .github/workflows/release-build.yml | 2 + .goreleaser.yaml | 8 +- internal/crosscompile/crosscompile.go | 46 +++---- internal/crosscompile/fetch.go | 37 +++++- internal/crosscompile/fetch_test.go | 51 ++++++-- internal/llvmpayload/cmd/llvmpayload/main.go | 55 +++++++++ internal/llvmpayload/payload.go | 121 +++++++++++++++++++ internal/llvmpayload/payload_test.go | 71 +++++++++++ 10 files changed, 403 insertions(+), 58 deletions(-) create mode 100644 internal/llvmpayload/cmd/llvmpayload/main.go create mode 100644 internal/llvmpayload/payload.go create mode 100644 internal/llvmpayload/payload_test.go diff --git a/.github/actions/setup-goreleaser/action.yml b/.github/actions/setup-goreleaser/action.yml index feb2cff60f..03dd185a4c 100644 --- a/.github/actions/setup-goreleaser/action.yml +++ b/.github/actions/setup-goreleaser/action.yml @@ -17,6 +17,9 @@ runs: steps: - name: Set up Go uses: ./.github/actions/setup-go + - name: Load LLVM payload contract + run: go run ./internal/llvmpayload/cmd/llvmpayload >> "$GITHUB_ENV" + shell: bash - name: Restore Linux sysroot cache id: cache-linux-sysroot uses: actions/cache/restore@v5 @@ -31,7 +34,7 @@ runs: uses: actions/cache/restore@v5 with: path: ${{ inputs.esp-clang-cache-path }} - key: esp-clang-${{ hashFiles('.github/workflows/download_esp_clang.sh') }} + key: esp-clang-${{ hashFiles('.github/workflows/download_esp_clang.sh', 'internal/llvmpayload/**') }} - name: Download ESP Clang (if cache miss) if: steps.cache-esp-clang.outputs.cache-hit != 'true' run: bash .github/workflows/download_esp_clang.sh @@ -41,7 +44,7 @@ runs: uses: actions/cache/save@v5 with: path: ${{ inputs.esp-clang-cache-path }} - key: esp-clang-${{ hashFiles('.github/workflows/download_esp_clang.sh') }} + key: esp-clang-${{ hashFiles('.github/workflows/download_esp_clang.sh', 'internal/llvmpayload/**') }} - name: Check file run: tree .sysroot shell: bash diff --git a/.github/workflows/download_esp_clang.sh b/.github/workflows/download_esp_clang.sh index 5ea6a764db..cf9ad1bee2 100755 --- a/.github/workflows/download_esp_clang.sh +++ b/.github/workflows/download_esp_clang.sh @@ -1,8 +1,19 @@ #!/bin/bash -set -e +set -euo pipefail -ESP_CLANG_VERSION="19.1.2_20250905-3" -BASE_URL="https://github.com/goplus/espressif-llvm-project-prebuilt/releases/download/${ESP_CLANG_VERSION}" +payload_env=$(mktemp) +archive_file="" +cleanup() { + rm -f "${payload_env}" + if [[ -n "${archive_file}" ]]; then + rm -f "${archive_file}" + fi +} +trap cleanup EXIT + +go run ./internal/llvmpayload/cmd/llvmpayload > "${payload_env}" +# shellcheck disable=SC1090 +source "${payload_env}" get_esp_clang_platform() { local platform="$1" @@ -33,22 +44,56 @@ get_esp_clang_platform() { get_filename() { local platform="$1" - local platform_suffix=$(get_esp_clang_platform "${platform}") + local platform_suffix + platform_suffix=$(get_esp_clang_platform "${platform}") echo "clang-esp-${ESP_CLANG_VERSION}-${platform_suffix}.tar.xz" } +get_checksum() { + case "$1" in + "darwin-amd64") echo "${ESP_CLANG_SHA256_DARWIN_AMD64}" ;; + "darwin-arm64") echo "${ESP_CLANG_SHA256_DARWIN_ARM64}" ;; + "linux-amd64") echo "${ESP_CLANG_SHA256_LINUX_AMD64}" ;; + "linux-arm64") echo "${ESP_CLANG_SHA256_LINUX_ARM64}" ;; + *) echo "Error: Unsupported checksum platform: $1" >&2; exit 1 ;; + esac +} + +verify_checksum() { + local filename="$1" + local expected="$2" + local actual + if command -v sha256sum >/dev/null 2>&1; then + actual=$(sha256sum "${filename}" | awk '{print $1}') + else + actual=$(shasum -a 256 "${filename}" | awk '{print $1}') + fi + if [[ "${actual}" != "${expected}" ]]; then + echo "Error: checksum mismatch for ${filename}: got ${actual}, want ${expected}" >&2 + exit 1 + fi +} + download_and_extract() { local platform="$1" local os="${platform%-*}" local arch="${platform##*-}" - local filename=$(get_filename "${platform}") - local download_url="${BASE_URL}/${filename}" + local filename + local checksum + filename=$(get_filename "${platform}") + checksum=$(get_checksum "${platform}") + local download_url="${ESP_CLANG_BASE_URL}/${filename}" echo "Downloading ESP Clang for ${platform}..." echo " URL: ${download_url}" + archive_file=$(mktemp) + curl -fsSL "${download_url}" -o "${archive_file}" + verify_checksum "${archive_file}" "${checksum}" mkdir -p ".sysroot/${os}/${arch}/crosscompile/clang" - curl -fsSL "${download_url}" | tar -xJ -C ".sysroot/${os}/${arch}/crosscompile/clang" --strip-components=1 + tar -xJf "${archive_file}" -C ".sysroot/${os}/${arch}/crosscompile/clang" --strip-components=1 + rm -f "${archive_file}" + archive_file="" if [[ ! -f ".sysroot/${os}/${arch}/crosscompile/clang/bin/clang++" ]]; then echo "Error: clang++ not found in ${platform} toolchain" @@ -60,6 +105,10 @@ download_and_extract() { echo "Downloading ESP Clang toolchain version ${ESP_CLANG_VERSION}..." +if [[ -n "${GITHUB_ENV:-}" ]]; then + echo "LLGO_LLVM_MAJOR=${ESP_CLANG_LLVM_MAJOR}" >> "${GITHUB_ENV}" +fi + for platform in "darwin-amd64" "darwin-arm64" "linux-amd64" "linux-arm64"; do download_and_extract "${platform}" done diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index 7f367be4d4..16b5bd2d4e 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -81,6 +81,7 @@ jobs: docker run \ --rm \ -e GITHUB_TOKEN=${GITHUB_TOKEN} \ + -e LLGO_LLVM_MAJOR=${LLGO_LLVM_MAJOR} \ -v /var/run/docker.sock:/var/run/docker.sock \ -v $(pwd):/go/src/llgo \ -w /go/src/llgo \ @@ -208,6 +209,7 @@ jobs: docker run \ --rm \ -e GITHUB_TOKEN=${GITHUB_TOKEN} \ + -e LLGO_LLVM_MAJOR=${LLGO_LLVM_MAJOR} \ -v /var/run/docker.sock:/var/run/docker.sock \ -v $(pwd):/go/src/llgo \ -w /go/src/llgo \ diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 4c3d6c41a7..9e008b455e 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -27,7 +27,7 @@ builds: - CC=o64-clang - CXX=o64-clang++ - CGO_CPPFLAGS=-I{{.Env.SYSROOT_DARWIN_AMD64}}/crosscompile/clang/include -mmacosx-version-min=10.13 -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS - - CGO_LDFLAGS=-L{{.Env.SYSROOT_DARWIN_AMD64}}/crosscompile/clang/lib -mmacosx-version-min=10.13 -Wl,-search_paths_first -Wl,-headerpad_max_install_names -lLLVM-19 -lz -lm -Wl,-rpath,@executable_path/../crosscompile/clang/lib + - CGO_LDFLAGS=-L{{.Env.SYSROOT_DARWIN_AMD64}}/crosscompile/clang/lib -mmacosx-version-min=10.13 -Wl,-search_paths_first -Wl,-headerpad_max_install_names -lLLVM-{{.Env.LLGO_LLVM_MAJOR}} -lz -lm -Wl,-rpath,@executable_path/../crosscompile/clang/lib targets: - darwin_amd64 mod_timestamp: "{{.CommitTimestamp}}" @@ -43,7 +43,7 @@ builds: - CC=oa64-clang - CXX=oa64-clang++ - CGO_CPPFLAGS=-I{{.Env.SYSROOT_DARWIN_ARM64}}/crosscompile/clang/include -mmacosx-version-min=10.13 -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS - - CGO_LDFLAGS=-L{{.Env.SYSROOT_DARWIN_ARM64}}/crosscompile/clang/lib -mmacosx-version-min=10.13 -Wl,-search_paths_first -Wl,-headerpad_max_install_names -lLLVM-19 -lz -lm -Wl,-rpath,@executable_path/../crosscompile/clang/lib + - CGO_LDFLAGS=-L{{.Env.SYSROOT_DARWIN_ARM64}}/crosscompile/clang/lib -mmacosx-version-min=10.13 -Wl,-search_paths_first -Wl,-headerpad_max_install_names -lLLVM-{{.Env.LLGO_LLVM_MAJOR}} -lz -lm -Wl,-rpath,@executable_path/../crosscompile/clang/lib targets: - darwin_arm64 mod_timestamp: "{{.CommitTimestamp}}" @@ -62,7 +62,7 @@ builds: - CXX={{.Env.SYSROOT_LINUX_AMD64}}/crosscompile/clang/bin/clang++ - CGO_CPPFLAGS=--target=x86_64-linux-gnu --gcc-toolchain={{.Env.SYSROOT_LINUX_AMD64}}/usr --sysroot={{.Env.SYSROOT_LINUX_AMD64}} -I{{.Env.SYSROOT_LINUX_AMD64}}/crosscompile/clang/include -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS - CGO_CXXFLAGS=-std=c++17 -nostdinc++ -isystem {{.Env.SYSROOT_LINUX_AMD64}}/usr/include/c++/10 -isystem {{.Env.SYSROOT_LINUX_AMD64}}/usr/include/x86_64-linux-gnu/c++/10 -isystem {{.Env.SYSROOT_LINUX_AMD64}}/usr/include/c++/10/backward - - CGO_LDFLAGS=--target=x86_64-linux-gnu --gcc-toolchain={{.Env.SYSROOT_LINUX_AMD64}}/usr --sysroot={{.Env.SYSROOT_LINUX_AMD64}} -L{{.Env.SYSROOT_LINUX_AMD64}}/crosscompile/clang/lib -L{{.Env.SYSROOT_LINUX_AMD64}}/usr/lib/gcc/x86_64-linux-gnu/10 -L{{.Env.SYSROOT_LINUX_AMD64}}/usr/lib/x86_64-linux-gnu -L{{.Env.SYSROOT_LINUX_AMD64}}/lib/x86_64-linux-gnu -lLLVM-19 -lz + - CGO_LDFLAGS=--target=x86_64-linux-gnu --gcc-toolchain={{.Env.SYSROOT_LINUX_AMD64}}/usr --sysroot={{.Env.SYSROOT_LINUX_AMD64}} -L{{.Env.SYSROOT_LINUX_AMD64}}/crosscompile/clang/lib -L{{.Env.SYSROOT_LINUX_AMD64}}/usr/lib/gcc/x86_64-linux-gnu/10 -L{{.Env.SYSROOT_LINUX_AMD64}}/usr/lib/x86_64-linux-gnu -L{{.Env.SYSROOT_LINUX_AMD64}}/lib/x86_64-linux-gnu -lLLVM-{{.Env.LLGO_LLVM_MAJOR}} -lz - CGO_LDFLAGS_ALLOW=(--target=.*|--gcc-toolchain=.*|--sysroot.*) targets: - linux_amd64 @@ -82,7 +82,7 @@ builds: - CXX={{.Env.SYSROOT_LINUX_AMD64}}/crosscompile/clang/bin/clang++ - CGO_CPPFLAGS=--target=aarch64-linux-gnu --gcc-toolchain={{.Env.SYSROOT_LINUX_ARM64}}/usr --sysroot={{.Env.SYSROOT_LINUX_ARM64}} -I{{.Env.SYSROOT_LINUX_ARM64}}/crosscompile/clang/include -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS - CGO_CXXFLAGS=-std=c++17 -nostdinc++ -isystem {{.Env.SYSROOT_LINUX_ARM64}}/usr/include/c++/10 -isystem {{.Env.SYSROOT_LINUX_ARM64}}/usr/include/aarch64-linux-gnu/c++/10 -isystem {{.Env.SYSROOT_LINUX_ARM64}}/usr/include/c++/10/backward - - CGO_LDFLAGS=--target=aarch64-linux-gnu --gcc-toolchain={{.Env.SYSROOT_LINUX_ARM64}}/usr --sysroot={{.Env.SYSROOT_LINUX_ARM64}} -L{{.Env.SYSROOT_LINUX_ARM64}}/crosscompile/clang/lib -L{{.Env.SYSROOT_LINUX_ARM64}}/usr/lib/gcc/aarch64-linux-gnu/10 -L{{.Env.SYSROOT_LINUX_ARM64}}/usr/lib/aarch64-linux-gnu -L{{.Env.SYSROOT_LINUX_ARM64}}/lib/aarch64-linux-gnu -lLLVM-19 -lz + - CGO_LDFLAGS=--target=aarch64-linux-gnu --gcc-toolchain={{.Env.SYSROOT_LINUX_ARM64}}/usr --sysroot={{.Env.SYSROOT_LINUX_ARM64}} -L{{.Env.SYSROOT_LINUX_ARM64}}/crosscompile/clang/lib -L{{.Env.SYSROOT_LINUX_ARM64}}/usr/lib/gcc/aarch64-linux-gnu/10 -L{{.Env.SYSROOT_LINUX_ARM64}}/usr/lib/aarch64-linux-gnu -L{{.Env.SYSROOT_LINUX_ARM64}}/lib/aarch64-linux-gnu -lLLVM-{{.Env.LLGO_LLVM_MAJOR}} -lz - CGO_LDFLAGS_ALLOW=(--target=.*|--gcc-toolchain=.*|--sysroot.*) targets: - linux_arm64 diff --git a/internal/crosscompile/crosscompile.go b/internal/crosscompile/crosscompile.go index c017c7e5f4..d4121caa80 100644 --- a/internal/crosscompile/crosscompile.go +++ b/internal/crosscompile/crosscompile.go @@ -13,11 +13,13 @@ import ( "github.com/goplus/llgo/internal/crosscompile/compile" "github.com/goplus/llgo/internal/env" "github.com/goplus/llgo/internal/flash" + "github.com/goplus/llgo/internal/llvmpayload" "github.com/goplus/llgo/internal/lto" "github.com/goplus/llgo/internal/optlevel" "github.com/goplus/llgo/internal/targets" "github.com/goplus/llgo/internal/xtool/llvm" envllvm "github.com/goplus/llgo/xtool/env/llvm" + gllvm "github.com/xgo-dev/llvm" ) type Export struct { @@ -70,11 +72,6 @@ var ( wasiMacosSubdir = "wasi-sdk-25.0-x86_64-macos" ) -var ( - espClangBaseUrl = "https://github.com/goplus/espressif-llvm-project-prebuilt/releases/download/19.1.2_20250905-3" - espClangVersion = "19.1.2_20250905-3" -) - // cacheRoot can be overridden for testing var cacheRoot = env.LLGoCacheDir @@ -141,16 +138,25 @@ func getESPClangRoot(forceEspClang bool) (clangRoot string, err error) { return "", nil } + payload, err := llvmpayload.ForLLVMVersion(gllvm.Version) + if err != nil { + return "", err + } + // Try to download ESP Clang if platform is supported platformSuffix := getESPClangPlatform(runtime.GOOS, runtime.GOARCH) if platformSuffix != "" { - cacheClangDir := filepath.Join(cacheRoot(), "crosscompile", "esp-clang-"+espClangVersion) + artifact, artifactErr := payload.Artifact(platformSuffix) + if artifactErr != nil { + return "", artifactErr + } + cacheClangDir := filepath.Join(cacheRoot(), "crosscompile", "esp-clang-"+payload.Version()) if _, err = os.Stat(cacheClangDir); err != nil { if !errors.Is(err, fs.ErrNotExist) { return } fmt.Fprintln(os.Stderr, "ESP Clang not found in LLGO_ROOT or cache, will download.") - if err = checkDownloadAndExtractESPClang(platformSuffix, cacheClangDir); err != nil { + if err = checkDownloadAndExtractESPClang(artifact, cacheClangDir); err != nil { return } } @@ -164,30 +170,8 @@ func getESPClangRoot(forceEspClang bool) (clangRoot string, err error) { // getESPClangPlatform returns the platform suffix for ESP Clang downloads func getESPClangPlatform(goos, goarch string) string { - switch goos { - case "darwin": - switch goarch { - case "amd64": - return "x86_64-apple-darwin" - case "arm64": - return "aarch64-apple-darwin" - } - case "linux": - switch goarch { - case "amd64": - return "x86_64-linux-gnu" - case "arm64": - return "aarch64-linux-gnu" - case "arm": - return "arm-linux-gnueabihf" - } - case "windows": - switch goarch { - case "amd64": - return "x86_64-w64-mingw32" - } - } - return "" + platform, _ := llvmpayload.PlatformSuffix(goos, goarch) + return platform } // ldFlagsFromFileName extracts the library name from a filename for use in linker flags diff --git a/internal/crosscompile/fetch.go b/internal/crosscompile/fetch.go index d32003bacc..b788bf945e 100644 --- a/internal/crosscompile/fetch.go +++ b/internal/crosscompile/fetch.go @@ -4,6 +4,8 @@ import ( "archive/tar" "archive/zip" "compress/gzip" + "crypto/sha256" + "encoding/hex" "fmt" "io" "net/http" @@ -13,6 +15,8 @@ import ( "path/filepath" "strings" "syscall" + + "github.com/goplus/llgo/internal/llvmpayload" ) // checkDownloadAndExtractWasiSDK downloads and extracts WASI SDK @@ -42,7 +46,7 @@ func checkDownloadAndExtractWasiSDK(dir string) (wasiSdkRoot string, err error) } // checkDownloadAndExtractESPClang downloads and extracts ESP Clang binaries and libraries -func checkDownloadAndExtractESPClang(platformSuffix, dir string) error { +func checkDownloadAndExtractESPClang(artifact llvmpayload.Artifact, dir string) error { // Check if already exists if _, err := os.Stat(dir); err == nil { return nil @@ -61,12 +65,11 @@ func checkDownloadAndExtractESPClang(platformSuffix, dir string) error { return nil } - clangUrl := fmt.Sprintf("%s/clang-esp-%s-%s.tar.xz", espClangBaseUrl, espClangVersion, platformSuffix) - description := fmt.Sprintf("ESP Clang %s-%s", espClangVersion, platformSuffix) + description := fmt.Sprintf("ESP Clang %s-%s", artifact.Version, artifact.Platform) // Use temporary extraction directory for ESP Clang special handling tempExtractDir := dir + ".extract" - if err := downloadAndExtractArchive(clangUrl, tempExtractDir, description); err != nil { + if err := downloadAndExtractArchiveWithChecksum(artifact.URL, tempExtractDir, description, artifact.SHA256); err != nil { return err } defer os.RemoveAll(tempExtractDir) @@ -154,6 +157,10 @@ func releaseLock(lockFile *os.File) error { // downloadAndExtractArchive downloads and extracts an archive to the destination directory (without locking) func downloadAndExtractArchive(url, destDir, description string) error { + return downloadAndExtractArchiveWithChecksum(url, destDir, description, "") +} + +func downloadAndExtractArchiveWithChecksum(url, destDir, description, expectedSHA256 string) error { fmt.Fprintf(os.Stderr, "Downloading %s...\n", description) // Use temporary extraction directory @@ -171,6 +178,15 @@ func downloadAndExtractArchive(url, destDir, description string) error { if err := downloadFile(url, localFile); err != nil { return fmt.Errorf("failed to download %s from %s: %w", description, url, err) } + if expectedSHA256 != "" { + actualSHA256, err := fileSHA256(localFile) + if err != nil { + return fmt.Errorf("calculate %s checksum: %w", description, err) + } + if !strings.EqualFold(actualSHA256, expectedSHA256) { + return fmt.Errorf("%s checksum mismatch: got %s, want %s", description, actualSHA256, expectedSHA256) + } + } // Extract the archive fmt.Fprintf(os.Stderr, "Extracting %s...\n", description) @@ -201,6 +217,19 @@ func downloadAndExtractArchive(url, destDir, description string) error { return nil } +func fileSHA256(filename string) (string, error) { + file, err := os.Open(filename) + if err != nil { + return "", err + } + defer file.Close() + hash := sha256.New() + if _, err := io.Copy(hash, file); err != nil { + return "", err + } + return hex.EncodeToString(hash.Sum(nil)), nil +} + func downloadFile(url, filepath string) error { out, err := os.Create(filepath) if err != nil { diff --git a/internal/crosscompile/fetch_test.go b/internal/crosscompile/fetch_test.go index 9be771dd16..ae98642df7 100644 --- a/internal/crosscompile/fetch_test.go +++ b/internal/crosscompile/fetch_test.go @@ -17,6 +17,8 @@ import ( "sync" "testing" "time" + + "github.com/goplus/llgo/internal/llvmpayload" ) // Helper function to create a test tar.gz archive @@ -513,7 +515,7 @@ func TestESPClangExtractionLogic(t *testing.T) { } // Test that function skips download for existing directory - err = checkDownloadAndExtractESPClang("linux", espClangDir) + err = checkDownloadAndExtractESPClang(llvmpayload.Artifact{}, espClangDir) if err != nil { t.Fatalf("checkDownloadAndExtractESPClang failed: %v", err) } @@ -604,9 +606,8 @@ func TestESPClangDownloadWhenNotExists(t *testing.T) { t.Fatalf("Failed to read test archive: %v", err) } - server := createTestServer(t, map[string]string{ - fmt.Sprintf("clang-esp-%s-linux.tar.xz", espClangVersion): string(archiveContent), - }) + const filename = "clang-esp-test-linux.tar.xz" + server := createTestServer(t, map[string]string{filename: string(archiveContent)}) defer server.Close() // Override cacheRoot to use a temporary directory @@ -615,16 +616,21 @@ func TestESPClangDownloadWhenNotExists(t *testing.T) { cacheRoot = func() string { return tempCacheRoot } defer func() { cacheRoot = originalCacheRoot }() - // Override espClangBaseUrl to use our test server - originalEspClangBaseUrl := espClangBaseUrl - espClangBaseUrl = server.URL - defer func() { espClangBaseUrl = originalEspClangBaseUrl }() - // Use a fresh temp directory that doesn't have ESP Clang espClangDir := filepath.Join(tempCacheRoot, "esp-clang-test") + checksum, err := fileSHA256(archivePath) + if err != nil { + t.Fatal(err) + } + artifact := llvmpayload.Artifact{ + Platform: "linux", + Version: "test", + URL: server.URL + "/" + filename, + SHA256: checksum, + } // Test download and extract when directory doesn't exist - err = checkDownloadAndExtractESPClang("linux", espClangDir) + err = checkDownloadAndExtractESPClang(artifact, espClangDir) if err != nil { t.Fatalf("checkDownloadAndExtractESPClang failed: %v", err) } @@ -650,6 +656,31 @@ func TestESPClangDownloadWhenNotExists(t *testing.T) { } } +func TestESPClangRejectsChecksumMismatch(t *testing.T) { + archivePath := createTestTarGz(t, map[string]string{"esp-clang/bin/clang": "fake"}) + archiveContent, err := os.ReadFile(archivePath) + if err != nil { + t.Fatal(err) + } + server := createTestServer(t, map[string]string{"clang-esp-test-linux.tar.xz": string(archiveContent)}) + defer server.Close() + + destination := filepath.Join(t.TempDir(), "esp-clang") + artifact := llvmpayload.Artifact{ + Platform: "linux", + Version: "test", + URL: server.URL + "/clang-esp-test-linux.tar.xz", + SHA256: strings.Repeat("0", 64), + } + err = checkDownloadAndExtractESPClang(artifact, destination) + if err == nil || !strings.Contains(err.Error(), "checksum mismatch") { + t.Fatalf("checksum mismatch error = %v", err) + } + if _, statErr := os.Stat(destination); !os.IsNotExist(statErr) { + t.Fatalf("destination exists after rejected download: %v", statErr) + } +} + func TestExtractZip(t *testing.T) { // Create temporary test directory tempDir := t.TempDir() diff --git a/internal/llvmpayload/cmd/llvmpayload/main.go b/internal/llvmpayload/cmd/llvmpayload/main.go new file mode 100644 index 0000000000..32894d8864 --- /dev/null +++ b/internal/llvmpayload/cmd/llvmpayload/main.go @@ -0,0 +1,55 @@ +// Command llvmpayload prints the checked-in LLVM payload contract for release +// scripts that cannot import Go constants directly. +package main + +import ( + "flag" + "fmt" + "os" + "strings" + + "github.com/goplus/llgo/internal/llvmpayload" +) + +func main() { + major := flag.Int("major", 0, "LLVM major (defaults to the release payload)") + flag.Parse() + + var ( + manifest llvmpayload.Manifest + err error + ) + if *major == 0 { + manifest, err = llvmpayload.Default() + } else { + manifest, err = llvmpayload.ForMajor(*major) + } + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + fmt.Printf("LLGO_LLVM_MAJOR=%s\n", fmt.Sprint(manifest.LLVMMajor())) + fmt.Printf("ESP_CLANG_LLVM_MAJOR=%s\n", fmt.Sprint(manifest.LLVMMajor())) + fmt.Printf("ESP_CLANG_VERSION=%s\n", manifest.Version()) + fmt.Printf("ESP_CLANG_BASE_URL=%s\n", manifest.BaseURL()) + for _, host := range []struct { + name, goos, goarch string + }{ + {name: "DARWIN_AMD64", goos: "darwin", goarch: "amd64"}, + {name: "DARWIN_ARM64", goos: "darwin", goarch: "arm64"}, + {name: "LINUX_AMD64", goos: "linux", goarch: "amd64"}, + {name: "LINUX_ARM64", goos: "linux", goarch: "arm64"}, + } { + platform, ok := llvmpayload.PlatformSuffix(host.goos, host.goarch) + if !ok { + panic("missing platform mapping for " + host.goos + "/" + host.goarch) + } + artifact, err := manifest.Artifact(platform) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + fmt.Printf("ESP_CLANG_SHA256_%s=%s\n", strings.ToUpper(host.name), artifact.SHA256) + } +} diff --git a/internal/llvmpayload/payload.go b/internal/llvmpayload/payload.go new file mode 100644 index 0000000000..750314b46d --- /dev/null +++ b/internal/llvmpayload/payload.go @@ -0,0 +1,121 @@ +// Package llvmpayload defines the revision-locked LLVM toolchains distributed +// with and downloaded by LLGo. +package llvmpayload + +import ( + "fmt" + "regexp" + "sort" + "strconv" +) + +const releaseBaseURL = "https://github.com/goplus/espressif-llvm-project-prebuilt/releases/download" + +// DefaultMajor is the LLVM payload bundled into LLGo release archives. +const DefaultMajor = 19 + +var llvmMajorPattern = regexp.MustCompile(`(?:^|[^0-9])([0-9]+)\.[0-9]+`) + +type manifest struct { + llvmMajor int + version string + sha256 map[string]string +} + +// Artifact identifies one host-specific LLVM payload archive. +type Artifact struct { + Platform string + Version string + URL string + SHA256 string +} + +var manifests = map[int]manifest{ + 19: { + llvmMajor: 19, + version: "19.1.2_20250905-3", + sha256: map[string]string{ + "aarch64-apple-darwin": "4f15d18c93eabdace3eab901582e528ac334d328fb8f19f153ee55b2208d101b", + "aarch64-linux-gnu": "b2d8e77bbf3394c6a1f0d66e59385d78d2b49b97ebe782e612cba7f93dcb2337", + "x86_64-apple-darwin": "e4f329a911e813ee825984f039578614dc0fe69001c2afe3e61edf27821be3ad", + "x86_64-linux-gnu": "e2e0c48cd76e45ceba910917a2a97988dc80e3bb6040ea262bfe9293d5d9ac57", + }, + }, +} + +// ForLLVMVersion returns the payload compatible with an in-process LLVM +// version such as "21.1.8". +func ForLLVMVersion(version string) (Manifest, error) { + match := llvmMajorPattern.FindStringSubmatch(version) + if len(match) != 2 { + return Manifest{}, fmt.Errorf("parse LLVM version %q", version) + } + major, err := strconv.Atoi(match[1]) + if err != nil { + return Manifest{}, fmt.Errorf("parse LLVM version %q: %w", version, err) + } + return ForMajor(major) +} + +// ForMajor returns the published payload for one LLVM major version. +func ForMajor(major int) (Manifest, error) { + payload, ok := manifests[major] + if !ok { + return Manifest{}, fmt.Errorf("no LLGo LLVM payload for major version %d", major) + } + return Manifest{payload: payload}, nil +} + +func Default() (Manifest, error) { return ForMajor(DefaultMajor) } + +// Manifest provides read-only access to one payload release. +type Manifest struct { + payload manifest +} + +func (m Manifest) LLVMMajor() int { return m.payload.llvmMajor } + +func (m Manifest) Version() string { return m.payload.version } + +func (m Manifest) BaseURL() string { + return releaseBaseURL + "/" + m.payload.version +} + +func (m Manifest) Platforms() []string { + platforms := make([]string, 0, len(m.payload.sha256)) + for platform := range m.payload.sha256 { + platforms = append(platforms, platform) + } + sort.Strings(platforms) + return platforms +} + +func (m Manifest) Artifact(platform string) (Artifact, error) { + checksum, ok := m.payload.sha256[platform] + if !ok { + return Artifact{}, fmt.Errorf("LLVM %d payload %s is unavailable for %s", m.LLVMMajor(), m.Version(), platform) + } + filename := fmt.Sprintf("clang-esp-%s-%s.tar.xz", m.Version(), platform) + return Artifact{ + Platform: platform, + Version: m.Version(), + URL: m.BaseURL() + "/" + filename, + SHA256: checksum, + }, nil +} + +// PlatformSuffix maps the host platform to the suffix used by payload assets. +func PlatformSuffix(goos, goarch string) (string, bool) { + switch goos + "/" + goarch { + case "darwin/amd64": + return "x86_64-apple-darwin", true + case "darwin/arm64": + return "aarch64-apple-darwin", true + case "linux/amd64": + return "x86_64-linux-gnu", true + case "linux/arm64": + return "aarch64-linux-gnu", true + default: + return "", false + } +} diff --git a/internal/llvmpayload/payload_test.go b/internal/llvmpayload/payload_test.go new file mode 100644 index 0000000000..9eb588545a --- /dev/null +++ b/internal/llvmpayload/payload_test.go @@ -0,0 +1,71 @@ +package llvmpayload + +import ( + "encoding/hex" + "strings" + "testing" +) + +func TestLLVM19Manifest(t *testing.T) { + manifest, err := ForLLVMVersion("LLVM 19.1.7") + if err != nil { + t.Fatal(err) + } + if manifest.LLVMMajor() != 19 || manifest.Version() != "19.1.2_20250905-3" { + t.Fatalf("manifest identity = LLVM %d %s", manifest.LLVMMajor(), manifest.Version()) + } + platforms := manifest.Platforms() + if len(platforms) != 4 { + t.Fatalf("platform count = %d, want 4: %v", len(platforms), platforms) + } + for _, platform := range platforms { + artifact, err := manifest.Artifact(platform) + if err != nil { + t.Fatal(err) + } + if !strings.HasSuffix(artifact.URL, "clang-esp-"+manifest.Version()+"-"+platform+".tar.xz") { + t.Errorf("artifact URL = %q", artifact.URL) + } + checksum, err := hex.DecodeString(artifact.SHA256) + if err != nil || len(checksum) != 32 { + t.Errorf("artifact checksum = %q, err %v", artifact.SHA256, err) + } + } +} + +func TestPayloadErrors(t *testing.T) { + if _, err := ForLLVMVersion("development"); err == nil { + t.Fatal("invalid LLVM version accepted") + } + if _, err := ForMajor(20); err == nil { + t.Fatal("unpublished LLVM major accepted") + } + manifest, err := ForMajor(19) + if err != nil { + t.Fatal(err) + } + if _, err := manifest.Artifact("arm-linux-gnueabihf"); err == nil { + t.Fatal("unpublished platform accepted") + } +} + +func TestPlatformSuffix(t *testing.T) { + tests := []struct { + goos, goarch string + want string + ok bool + }{ + {goos: "darwin", goarch: "amd64", want: "x86_64-apple-darwin", ok: true}, + {goos: "darwin", goarch: "arm64", want: "aarch64-apple-darwin", ok: true}, + {goos: "linux", goarch: "amd64", want: "x86_64-linux-gnu", ok: true}, + {goos: "linux", goarch: "arm64", want: "aarch64-linux-gnu", ok: true}, + {goos: "linux", goarch: "arm", ok: false}, + {goos: "windows", goarch: "amd64", ok: false}, + } + for _, test := range tests { + got, ok := PlatformSuffix(test.goos, test.goarch) + if got != test.want || ok != test.ok { + t.Errorf("PlatformSuffix(%q, %q) = %q, %v; want %q, %v", test.goos, test.goarch, got, ok, test.want, test.ok) + } + } +} From bf6d6ef3a382de694d0f3f1613849c72d77101ca Mon Sep 17 00:00:00 2001 From: ZhouGuangyuan Date: Sat, 15 Aug 2026 16:23:20 +0800 Subject: [PATCH 3/4] build: isolate cross libraries by LLVM toolchain --- internal/crosscompile/crosscompile.go | 50 +++++++++++-- internal/crosscompile/crosscompile_test.go | 29 ++++++++ internal/crosscompile/libc.go | 82 +++++++++++++++++----- internal/crosscompile/libc_test.go | 61 +++++++++++++--- targets/esp32.json | 2 +- targets/esp32c3-basic.json | 2 +- targets/esp8266.json | 2 +- targets/fe310.json | 2 +- targets/k210.json | 2 +- targets/riscv-qemu.json | 2 +- 10 files changed, 198 insertions(+), 36 deletions(-) diff --git a/internal/crosscompile/crosscompile.go b/internal/crosscompile/crosscompile.go index d4121caa80..6ce43f56a0 100644 --- a/internal/crosscompile/crosscompile.go +++ b/internal/crosscompile/crosscompile.go @@ -39,6 +39,8 @@ type Export struct { ClangBinPath string // Path to clang binary directory LLVMTarget string // LLVM Target + CPU string // LLVM target CPU used by external code generation and cache identity + Features string // LLVM target features used by external code generation and cache identity TargetABI string // RISC-V Target ABI (e.g., "lp64", "lp64d") BinaryFormat string // Binary format (e.g., "elf", "esp", "uf2") FormatDetail string // For uf2, it's uf2FamilyID @@ -180,12 +182,28 @@ func ldFlagsFromFileName(fileName string) string { return strings.TrimPrefix(strings.TrimSuffix(fileName, ".a"), "lib") } +func lldLTOOptFlag(level optlevel.Level) (string, error) { + switch level { + case optlevel.O0, optlevel.O1, optlevel.O2, optlevel.O3: + return "--lto-" + level.Name(), nil + case optlevel.Os, optlevel.Oz: + // ld.lld only accepts numeric LTO optimization levels. Clang maps its + // size-oriented modes to O2 for the link-time optimization pipeline. + return "--lto-O2", nil + default: + return "", fmt.Errorf("invalid LTO optimization level %q", level) + } +} + // compileWithConfig compiles libraries according to the provided configuration // and returns the necessary linker flags for linking against the compiled libraries func compileWithConfig( compileConfig compile.CompileConfig, outputDir string, options compile.CompileOptions, ) (ldflags []string, err error) { + if err = os.MkdirAll(outputDir, 0o755); err != nil { + return nil, fmt.Errorf("create compiled library cache %q: %w", outputDir, err) + } ldflags = append(ldflags, "-nostdlib", "-L"+outputDir) for _, group := range compileConfig.Groups { @@ -236,7 +254,12 @@ func use(goos, goarch string, wasiThreads, forceEspClang bool, level optlevel.Le "-Wl,--icf=none", } if ltoMode.Enabled() { - export.LDFLAGS = append(export.LDFLAGS, ltoMode.ClangFlag(), "-Wl,--lto"+level.Flag()) + var ltoOptFlag string + ltoOptFlag, err = lldLTOOptFlag(level) + if err != nil { + return + } + export.LDFLAGS = append(export.LDFLAGS, ltoMode.ClangFlag(), "-Wl,"+ltoOptFlag) } if clangRoot != "" { clangLib := filepath.Join(clangRoot, "lib") @@ -484,6 +507,8 @@ func UseTarget(targetName string, level optlevel.Level, ltoMode lto.Mode) (expor export.GOARCH = config.GOARCH export.ExtraFiles = config.ExtraFiles export.LLVMTarget = config.LLVMTarget + export.CPU = config.CPU + export.Features = config.Features export.TargetABI = config.TargetABI export.BinaryFormat = config.BinaryFormat export.FormatDetail = config.FormatDetail() @@ -527,7 +552,12 @@ func UseTarget(targetName string, level optlevel.Level, ltoMode lto.Mode) (expor cflags = append(cflags, expandedCFlags...) if config.Linker == "ld.lld" && ltoMode.Enabled() { - ldflags = append(ldflags, "--lto"+level.Flag()) + var ltoOptFlag string + ltoOptFlag, err = lldLTOOptFlag(level) + if err != nil { + return + } + ldflags = append(ldflags, ltoOptFlag) cflags = append(cflags, ltoMode.ClangFlag()) ccflags = append(ccflags, ltoMode.ClangFlag()) } @@ -637,9 +667,17 @@ func UseTarget(targetName string, level optlevel.Level, ltoMode lto.Mode) (expor if config.LinkerScript != "" { ldflags = append(ldflags, "-T", config.LinkerScript) } - ldflags = append(ldflags, "-L", env.LLGoROOT()) // search targets/*.ld - var libcIncludeDir []string + var compiledLibraryKey string + if config.Libc != "" || config.RTLib != "" { + var compilerKey string + compilerKey, err = compilerCacheKey(export.CC) + if err != nil { + return + } + compiledLibraryKey = compiledLibraryCacheKey(compilerKey, ccflags, ldflags) + } + ldflags = append(ldflags, "-L", env.LLGoROOT()) // search targets/*.ld if config.Libc != "" { var outputDir string @@ -647,7 +685,7 @@ func UseTarget(targetName string, level optlevel.Level, ltoMode lto.Mode) (expor var compileConfig compile.CompileConfig baseDir := filepath.Join(cacheRoot(), "crosscompile") - outputDir, compileConfig, err = getLibcCompileConfigByName(baseDir, config.Libc, config.LLVMTarget, config.CPU) + outputDir, compileConfig, err = getLibcCompileConfigByName(baseDir, config.Libc, config.LLVMTarget, config.CPU, compiledLibraryKey) if err != nil { return } @@ -673,7 +711,7 @@ func UseTarget(targetName string, level optlevel.Level, ltoMode lto.Mode) (expor var compileConfig compile.CompileConfig baseDir := filepath.Join(cacheRoot(), "crosscompile") - outputDir, compileConfig, err = getRTCompileConfigByName(baseDir, config.RTLib, config.LLVMTarget) + outputDir, compileConfig, err = getRTCompileConfigByName(baseDir, config.RTLib, config.LLVMTarget, compiledLibraryKey) if err != nil { return } diff --git a/internal/crosscompile/crosscompile_test.go b/internal/crosscompile/crosscompile_test.go index ea89a9596a..6f2dbef614 100644 --- a/internal/crosscompile/crosscompile_test.go +++ b/internal/crosscompile/crosscompile_test.go @@ -254,6 +254,9 @@ func TestUseTarget(t *testing.T) { if !slices.Contains(export.LDFLAGS, "-S") { t.Fatalf("target %s declares AlwaysOmit without linker -S: %v", tc.targetName, export.LDFLAGS) } + if export.CPU != tc.expectCPU { + t.Fatalf("target %s exported CPU = %q, want %q", tc.targetName, export.CPU, tc.expectCPU) + } // Check if LLVM target is in CCFLAGS if tc.expectLLVM != "" { @@ -477,6 +480,29 @@ func hasFlagValue(flags []string, flag, value string) bool { return false } +func TestLLDLTOOptFlag(t *testing.T) { + tests := []struct { + level optlevel.Level + want string + }{ + {optlevel.O0, "--lto-O0"}, + {optlevel.O1, "--lto-O1"}, + {optlevel.O2, "--lto-O2"}, + {optlevel.O3, "--lto-O3"}, + {optlevel.Os, "--lto-O2"}, + {optlevel.Oz, "--lto-O2"}, + } + for _, test := range tests { + got, err := lldLTOOptFlag(test.level) + if err != nil || got != test.want { + t.Errorf("lldLTOOptFlag(%v) = %q, %v; want %q", test.level, got, err, test.want) + } + } + if _, err := lldLTOOptFlag(optlevel.Unset); err == nil { + t.Fatal("lldLTOOptFlag accepted an unset level") + } +} + func TestUseTargetCodegenFlagsOnlyAddedToLDFlagsWithLTO(t *testing.T) { const target = "k210" @@ -504,6 +530,9 @@ func TestUseTargetCodegenFlagsOnlyAddedToLDFlagsWithLTO(t *testing.T) { if !slices.Contains(withLTO.CCFLAGS, "-flto=thin") { t.Fatalf("missing thin LTO ccflag: %v", withLTO.CCFLAGS) } + if !slices.Contains(withLTO.LDFLAGS, "--lto-O2") { + t.Fatalf("missing numeric LTO optimization flag for Oz: %v", withLTO.LDFLAGS) + } if !hasMllvmOption(withLTO.LDFLAGS, "-code-model=medium") { t.Fatalf("missing -mllvm -code-model=medium in LDFLAGS when LTO enabled: %v", withLTO.LDFLAGS) } diff --git a/internal/crosscompile/libc.go b/internal/crosscompile/libc.go index 225a4a56c4..787350b1ec 100644 --- a/internal/crosscompile/libc.go +++ b/internal/crosscompile/libc.go @@ -1,8 +1,14 @@ package crosscompile import ( + "crypto/sha256" + "errors" "fmt" + "os" + "os/exec" "path/filepath" + "regexp" + "strings" "github.com/goplus/llgo/internal/crosscompile/compile" "github.com/goplus/llgo/internal/crosscompile/compile/libc" @@ -12,67 +18,111 @@ import ( // for testing, in testing env, we use fake path, it will cause downloading failure var needSkipDownload = false +var llvmVersionPattern = regexp.MustCompile(`[0-9]+\.[0-9]+(?:\.[0-9]+)?`) + +func compilerVersionCacheKey(versionOutput string, payloadContract []byte) (string, error) { + versionLine := strings.TrimSpace(versionOutput) + if i := strings.IndexByte(versionLine, '\n'); i >= 0 { + versionLine = versionLine[:i] + } + version := llvmVersionPattern.FindString(versionLine) + if version == "" { + return "", fmt.Errorf("parse compiler version from %q", versionLine) + } + identity := append([]byte(versionLine+"\x00"), payloadContract...) + digest := sha256.Sum256(identity) + return fmt.Sprintf("llvm-%s-%x", version, digest[:6]), nil +} + +func compilerCacheKey(cc string) (string, error) { + output, err := exec.Command(cc, "--version").CombinedOutput() + if err != nil { + return "", fmt.Errorf("query compiler %q version: %w: %s", cc, err, strings.TrimSpace(string(output))) + } + payloadContract, err := os.ReadFile(filepath.Join(filepath.Dir(filepath.Dir(cc)), "LLGO-LLVM-MANIFEST.txt")) + if err != nil && !errors.Is(err, os.ErrNotExist) { + return "", fmt.Errorf("read compiler payload contract: %w", err) + } + return compilerVersionCacheKey(string(output), payloadContract) +} + +func compiledLibraryCacheKey(compilerKey string, flagGroups ...[]string) string { + identity := compilerKey + for _, flags := range flagGroups { + identity += "\x00" + strings.Join(flags, "\x00") + "\x01" + } + digest := sha256.Sum256([]byte(identity)) + return fmt.Sprintf("%s-%x", compilerKey, digest[:6]) +} + +func compiledLibraryDir(baseDir string, config compile.LibConfig, compilerKey string) string { + return filepath.Join(baseDir, config.String()+"-"+compilerKey) +} + // getLibcCompileConfigByName retrieves libc compilation configuration by name // Returns the actual libc output dir, compilation config and err -func getLibcCompileConfigByName(baseDir, libcName, target, mcpu string) (outputDir string, cfg compile.CompileConfig, err error) { +func getLibcCompileConfigByName(baseDir, libcName, target, mcpu, compilerKey string) (outputDir string, cfg compile.CompileConfig, err error) { if libcName == "" { err = fmt.Errorf("libc name cannot be empty") return } - var libcDir string + var sourceDir string var config compile.LibConfig var compileConfig compile.CompileConfig switch libcName { case "picolibc": config = libc.GetPicolibcConfig() - libcDir = filepath.Join(baseDir, config.String()) - compileConfig = libc.GetPicolibcCompileConfig(libcDir, target) + sourceDir = filepath.Join(baseDir, config.String()) + compileConfig = libc.GetPicolibcCompileConfig(sourceDir, target) case "newlib-esp32": config = libc.GetNewlibESP32Config() - libcDir = filepath.Join(baseDir, config.String()) - compileConfig = libc.GetNewlibESP32CompileConfig(libcDir, target, mcpu) + sourceDir = filepath.Join(baseDir, config.String()) + compileConfig = libc.GetNewlibESP32CompileConfig(sourceDir, target, mcpu) default: err = fmt.Errorf("unsupported libc: %s", libcName) return } + outputDir = compiledLibraryDir(baseDir, config, compilerKey) if needSkipDownload { - return libcDir, compileConfig, err + return outputDir, compileConfig, err } - if err = checkDownloadAndExtractLib(config.Url, libcDir, config.ResourceSubDir); err != nil { + if err = checkDownloadAndExtractLib(config.Url, sourceDir, config.ResourceSubDir); err != nil { return } - return libcDir, compileConfig, nil + return outputDir, compileConfig, nil } // getRTCompileConfigByName retrieves runtime library compilation configuration by name // Returns the actual libc output dir, compilation config and err -func getRTCompileConfigByName(baseDir, rtName, target string) (outputDir string, cfg compile.CompileConfig, err error) { +func getRTCompileConfigByName(baseDir, rtName, target, compilerKey string) (outputDir string, cfg compile.CompileConfig, err error) { if rtName == "" { err = fmt.Errorf("rt name cannot be empty") return } - var rtDir string + var sourceDir string var config compile.LibConfig var compileConfig compile.CompileConfig switch rtName { case "compiler-rt": config = rtlib.GetCompilerRTConfig() - rtDir = filepath.Join(baseDir, config.String()) - compileConfig = rtlib.GetCompilerRTCompileConfig(rtDir, target) + sourceDir = filepath.Join(baseDir, config.String()) + compileConfig = rtlib.GetCompilerRTCompileConfig(sourceDir, target) default: err = fmt.Errorf("unsupported rt: %s", rtName) + return } + outputDir = compiledLibraryDir(baseDir, config, compilerKey) if needSkipDownload { - return rtDir, compileConfig, err + return outputDir, compileConfig, err } - if err = checkDownloadAndExtractLib(config.Url, rtDir, config.ResourceSubDir); err != nil { + if err = checkDownloadAndExtractLib(config.Url, sourceDir, config.ResourceSubDir); err != nil { return } - return rtDir, compileConfig, nil + return outputDir, compileConfig, nil } diff --git a/internal/crosscompile/libc_test.go b/internal/crosscompile/libc_test.go index f03a46467a..bdfe3de4e5 100644 --- a/internal/crosscompile/libc_test.go +++ b/internal/crosscompile/libc_test.go @@ -13,6 +13,39 @@ import ( "github.com/goplus/llgo/internal/crosscompile/compile/rtlib" ) +const testCompilerKey = "llvm-21.1.3-deadbeef" + +func TestCompilerVersionCacheKey(t *testing.T) { + first, err := compilerVersionCacheKey("clang version 21.1.3 (https://example.test/llvm abc123)\nInstalledDir: /one", nil) + if err != nil { + t.Fatal(err) + } + second, err := compilerVersionCacheKey("clang version 21.1.3 (https://example.test/llvm abc123)\nInstalledDir: /two", nil) + if err != nil { + t.Fatal(err) + } + if first != second { + t.Fatalf("cache key depends on install directory: %q != %q", first, second) + } + different, err := compilerVersionCacheKey("clang version 21.1.3 (https://example.test/llvm def456)", nil) + if err != nil { + t.Fatal(err) + } + if different == first { + t.Fatalf("cache key ignores compiler revision: %q", first) + } + patched, err := compilerVersionCacheKey("clang version 21.1.3 (https://example.test/llvm abc123)", []byte("llvm_source_patch_sha256=abc")) + if err != nil { + t.Fatal(err) + } + if patched == first { + t.Fatalf("cache key ignores payload contract: %q", first) + } + if a, b := compiledLibraryCacheKey(first, []string{"-Oz", "-flto=thin"}), compiledLibraryCacheKey(first, []string{"-Oz"}); a == b { + t.Fatalf("compiled library cache key ignores code-generation flags: %q", a) + } +} + func TestGetLibcCompileConfigByName(t *testing.T) { baseDir := "/test/base" target := "armv7" @@ -20,21 +53,21 @@ func TestGetLibcCompileConfigByName(t *testing.T) { needSkipDownload = true t.Run("EmptyName", func(t *testing.T) { - _, _, err := getLibcCompileConfigByName(baseDir, "", target, mcpu) + _, _, err := getLibcCompileConfigByName(baseDir, "", target, mcpu, testCompilerKey) if err == nil || err.Error() != "libc name cannot be empty" { t.Errorf("Expected empty name error, got: %v", err) } }) t.Run("UnsupportedLibc", func(t *testing.T) { - _, _, err := getLibcCompileConfigByName(baseDir, "invalid", target, mcpu) + _, _, err := getLibcCompileConfigByName(baseDir, "invalid", target, mcpu, testCompilerKey) if err == nil || err.Error() != "unsupported libc: invalid" { t.Errorf("Expected unsupported libc error, got: %v", err) } }) t.Run("Picolibc", func(t *testing.T) { - _, cfg, err := getLibcCompileConfigByName(baseDir, "picolibc", target, mcpu) + outputDir, cfg, err := getLibcCompileConfigByName(baseDir, "picolibc", target, mcpu, testCompilerKey) if err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -44,19 +77,23 @@ func TestGetLibcCompileConfigByName(t *testing.T) { } group := cfg.Groups[0] + expectedDir := compiledLibraryDir(baseDir, libc.GetPicolibcConfig(), testCompilerKey) + if outputDir != expectedDir { + t.Fatalf("output dir = %q, want %q", outputDir, expectedDir) + } expectedFile := filepath.Join(baseDir, libc.GetPicolibcConfig().String(), "newlib", "libc", "string", "memmem.c") if !slices.Contains(group.Files, expectedFile) { t.Errorf("Expected files [%s], got: %v", expectedFile, group.Files) } - expectedFlag := "-I" + filepath.Join("/test", "base", libc.GetPicolibcConfig().String()) + expectedFlag := "-I" + filepath.Join(baseDir, libc.GetPicolibcConfig().String()) if !slices.Contains(group.CFlags, expectedFlag) { t.Errorf("Expected flags [%s], got: %v", expectedFlag, group.CFlags) } }) t.Run("NewlibESP32", func(t *testing.T) { - _, cfg, err := getLibcCompileConfigByName(baseDir, "newlib-esp32", target, mcpu) + outputDir, cfg, err := getLibcCompileConfigByName(baseDir, "newlib-esp32", target, mcpu, testCompilerKey) if err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -66,6 +103,10 @@ func TestGetLibcCompileConfigByName(t *testing.T) { } group := cfg.Groups[0] + expectedDir := compiledLibraryDir(baseDir, libc.GetNewlibESP32Config(), testCompilerKey) + if outputDir != expectedDir { + t.Fatalf("output dir = %q, want %q", outputDir, expectedDir) + } expectedFile := filepath.Join(baseDir, libc.GetNewlibESP32Config().String(), "libgloss", "xtensa", "crt1-boards.S") if !slices.Contains(group.Files, expectedFile) { t.Errorf("Expected files [%s], got: %v", expectedFile, group.Files) @@ -84,21 +125,21 @@ func TestGetRTCompileConfigByName(t *testing.T) { needSkipDownload = true t.Run("EmptyName", func(t *testing.T) { - _, _, err := getRTCompileConfigByName(baseDir, "", target) + _, _, err := getRTCompileConfigByName(baseDir, "", target, testCompilerKey) if err == nil || err.Error() != "rt name cannot be empty" { t.Errorf("Expected empty name error, got: %v", err) } }) t.Run("UnsupportedRT", func(t *testing.T) { - _, _, err := getRTCompileConfigByName(baseDir, "invalid", target) + _, _, err := getRTCompileConfigByName(baseDir, "invalid", target, testCompilerKey) if err == nil || err.Error() != "unsupported rt: invalid" { t.Errorf("Expected unsupported rt error, got: %v", err) } }) t.Run("CompilerRT", func(t *testing.T) { - _, cfg, err := getRTCompileConfigByName(baseDir, "compiler-rt", target) + outputDir, cfg, err := getRTCompileConfigByName(baseDir, "compiler-rt", target, testCompilerKey) if err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -108,6 +149,10 @@ func TestGetRTCompileConfigByName(t *testing.T) { } group := cfg.Groups[0] + expectedDir := compiledLibraryDir(baseDir, rtlib.GetCompilerRTConfig(), testCompilerKey) + if outputDir != expectedDir { + t.Fatalf("output dir = %q, want %q", outputDir, expectedDir) + } expectedFile := filepath.Join(baseDir, rtlib.GetCompilerRTConfig().String(), "lib", "builtins", "absvdi2.c") if !slices.Contains(group.Files, expectedFile) { t.Errorf("Expected files [%s], got: %v", expectedFile, group.Files) diff --git a/targets/esp32.json b/targets/esp32.json index c88e5c4791..393cc8970b 100644 --- a/targets/esp32.json +++ b/targets/esp32.json @@ -3,7 +3,7 @@ "xtensa" ], "cpu": "esp32", - "features": "+atomctl,+bool,+clamps,+coprocessor,+debug,+density,+dfpaccel,+div32,+exception,+fp,+highpriinterrupts,+interrupt,+loop,+mac16,+memctl,+minmax,+miscsr,+mul32,+mul32high,+nsa,+prid,+regprotect,+rvector,+s32c1i,+sext,+threadptr,+timerint,+windowed", + "features": "+bool,+clamps,+coprocessor,+debug,+density,+dfpaccel,+div32,+exception,+fp,+highpriinterrupts,+interrupt,+loop,+mac16,+minmax,+miscsr,+mul32,+mul32high,+nsa,+prid,+regprotect,+rvector,+s32c1i,+sext,+threadptr,+windowed", "build-tags": [ "esp32", "esp" diff --git a/targets/esp32c3-basic.json b/targets/esp32c3-basic.json index 1478b326c7..671974940d 100644 --- a/targets/esp32c3-basic.json +++ b/targets/esp32c3-basic.json @@ -2,7 +2,7 @@ "inherits": [ "riscv32-esp" ], - "features": "+32bit,+c,+m,+zmmul,-a,-b,-d,-e,-experimental-smmpm,-experimental-smnpm,-experimental-ssnpm,-experimental-sspm,-experimental-ssqosid,-experimental-supm,-experimental-zacas,-experimental-zalasr,-experimental-zicfilp,-experimental-zicfiss,-f,-h,-relax,-shcounterenw,-shgatpa,-shtvala,-shvsatpa,-shvstvala,-shvstvecd,-smaia,-smcdeleg,-smcsrind,-smepmp,-smstateen,-ssaia,-ssccfg,-ssccptr,-sscofpmf,-sscounterenw,-sscsrind,-ssstateen,-ssstrict,-sstc,-sstvala,-sstvecd,-ssu64xl,-svade,-svadu,-svbare,-svinval,-svnapot,-svpbmt,-v,-xcvalu,-xcvbi,-xcvbitmanip,-xcvelw,-xcvmac,-xcvmem,-xcvsimd,-xesppie,-xsfcease,-xsfvcp,-xsfvfnrclipxfqf,-xsfvfwmaccqqq,-xsfvqmaccdod,-xsfvqmaccqoq,-xsifivecdiscarddlone,-xsifivecflushdlone,-xtheadba,-xtheadbb,-xtheadbs,-xtheadcmo,-xtheadcondmov,-xtheadfmemidx,-xtheadmac,-xtheadmemidx,-xtheadmempair,-xtheadsync,-xtheadvdot,-xventanacondops,-xwchc,-za128rs,-za64rs,-zaamo,-zabha,-zalrsc,-zama16b,-zawrs,-zba,-zbb,-zbc,-zbkb,-zbkc,-zbkx,-zbs,-zca,-zcb,-zcd,-zce,-zcf,-zcmop,-zcmp,-zcmt,-zdinx,-zfa,-zfbfmin,-zfh,-zfhmin,-zfinx,-zhinx,-zhinxmin,-zic64b,-zicbom,-zicbop,-zicboz,-ziccamoa,-ziccif,-zicclsm,-ziccrse,-zicntr,-zicond,-zicsr,-zifencei,-zihintntl,-zihintpause,-zihpm,-zimop,-zk,-zkn,-zknd,-zkne,-zknh,-zkr,-zks,-zksed,-zksh,-zkt,-ztso,-zvbb,-zvbc,-zve32f,-zve32x,-zve64d,-zve64f,-zve64x,-zvfbfmin,-zvfbfwma,-zvfh,-zvfhmin,-zvkb,-zvkg,-zvkn,-zvknc,-zvkned,-zvkng,-zvknha,-zvknhb,-zvks,-zvksc,-zvksed,-zvksg,-zvksh,-zvkt,-zvl1024b,-zvl128b,-zvl16384b,-zvl2048b,-zvl256b,-zvl32768b,-zvl32b,-zvl4096b,-zvl512b,-zvl64b,-zvl65536b,-zvl8192b", + "features": "+32bit,+c,+m,+zmmul,-a,-b,-d,-e,-experimental-zalasr,-experimental-zicfilp,-experimental-zicfiss,-f,-h,-relax,-shcounterenw,-shgatpa,-shtvala,-shvsatpa,-shvstvala,-shvstvecd,-smaia,-smcdeleg,-smcsrind,-smepmp,-smstateen,-ssaia,-ssccfg,-ssccptr,-sscofpmf,-sscounterenw,-sscsrind,-ssstateen,-ssstrict,-sstc,-sstvala,-sstvecd,-ssu64xl,-svade,-svadu,-svbare,-svinval,-svnapot,-svpbmt,-v,-xcvalu,-xcvbi,-xcvbitmanip,-xcvelw,-xcvmac,-xcvmem,-xcvsimd,-xsfcease,-xsfvcp,-xsfvfnrclipxfqf,-xsfvfwmaccqqq,-xsfvqmaccdod,-xsfvqmaccqoq,-xsifivecdiscarddlone,-xsifivecflushdlone,-xtheadba,-xtheadbb,-xtheadbs,-xtheadcmo,-xtheadcondmov,-xtheadfmemidx,-xtheadmac,-xtheadmemidx,-xtheadmempair,-xtheadsync,-xtheadvdot,-xventanacondops,-xwchc,-za128rs,-za64rs,-zaamo,-zabha,-zalrsc,-zama16b,-zawrs,-zba,-zbb,-zbc,-zbkb,-zbkc,-zbkx,-zbs,-zca,-zcb,-zcd,-zce,-zcf,-zcmop,-zcmp,-zcmt,-zdinx,-zfa,-zfbfmin,-zfh,-zfhmin,-zfinx,-zhinx,-zhinxmin,-zic64b,-zicbom,-zicbop,-zicboz,-ziccamoa,-ziccif,-zicclsm,-ziccrse,-zicntr,-zicond,-zicsr,-zifencei,-zihintntl,-zihintpause,-zihpm,-zimop,-zk,-zkn,-zknd,-zkne,-zknh,-zkr,-zks,-zksed,-zksh,-zkt,-ztso,-zvbb,-zvbc,-zve32f,-zve32x,-zve64d,-zve64f,-zve64x,-zvfbfmin,-zvfbfwma,-zvfh,-zvfhmin,-zvkb,-zvkg,-zvkn,-zvknc,-zvkned,-zvkng,-zvknha,-zvknhb,-zvks,-zvksc,-zvksed,-zvksg,-zvksh,-zvkt,-zvl1024b,-zvl128b,-zvl16384b,-zvl2048b,-zvl256b,-zvl32768b,-zvl32b,-zvl4096b,-zvl512b,-zvl64b,-zvl65536b,-zvl8192b", "build-tags": [ "esp32c3", "esp" diff --git a/targets/esp8266.json b/targets/esp8266.json index 390ce61c9c..59b18a120c 100644 --- a/targets/esp8266.json +++ b/targets/esp8266.json @@ -1,7 +1,7 @@ { "inherits": ["xtensa"], "cpu": "esp8266", - "features": "+debug,+density,+exception,+extendedl32r,+highpriinterrupts,+interrupt,+mul32,+nsa,+prid,+regprotect,+rvector,+timerint", + "features": "+debug,+density,+exception,+extendedl32r,+highpriinterrupts,+interrupt,+mul32,+nsa,+prid,+regprotect,+rvector", "build-tags": ["esp8266", "esp"], "scheduler": "tasks", "linker": "ld.lld", diff --git a/targets/fe310.json b/targets/fe310.json index cd92c4fb1b..370d547a20 100644 --- a/targets/fe310.json +++ b/targets/fe310.json @@ -1,6 +1,6 @@ { "inherits": ["riscv32"], "cpu": "sifive-e31", - "features": "+32bit,+a,+c,+m,+zmmul,-b,-d,-e,-experimental-smmpm,-experimental-smnpm,-experimental-ssnpm,-experimental-sspm,-experimental-ssqosid,-experimental-supm,-experimental-zacas,-experimental-zalasr,-experimental-zicfilp,-experimental-zicfiss,-f,-h,-relax,-shcounterenw,-shgatpa,-shtvala,-shvsatpa,-shvstvala,-shvstvecd,-smaia,-smcdeleg,-smcsrind,-smepmp,-smstateen,-ssaia,-ssccfg,-ssccptr,-sscofpmf,-sscounterenw,-sscsrind,-ssstateen,-ssstrict,-sstc,-sstvala,-sstvecd,-ssu64xl,-svade,-svadu,-svbare,-svinval,-svnapot,-svpbmt,-v,-xcvalu,-xcvbi,-xcvbitmanip,-xcvelw,-xcvmac,-xcvmem,-xcvsimd,-xesppie,-xsfcease,-xsfvcp,-xsfvfnrclipxfqf,-xsfvfwmaccqqq,-xsfvqmaccdod,-xsfvqmaccqoq,-xsifivecdiscarddlone,-xsifivecflushdlone,-xtheadba,-xtheadbb,-xtheadbs,-xtheadcmo,-xtheadcondmov,-xtheadfmemidx,-xtheadmac,-xtheadmemidx,-xtheadmempair,-xtheadsync,-xtheadvdot,-xventanacondops,-xwchc,-za128rs,-za64rs,-zaamo,-zabha,-zalrsc,-zama16b,-zawrs,-zba,-zbb,-zbc,-zbkb,-zbkc,-zbkx,-zbs,-zca,-zcb,-zcd,-zce,-zcf,-zcmop,-zcmp,-zcmt,-zdinx,-zfa,-zfbfmin,-zfh,-zfhmin,-zfinx,-zhinx,-zhinxmin,-zic64b,-zicbom,-zicbop,-zicboz,-ziccamoa,-ziccif,-zicclsm,-ziccrse,-zicntr,-zicond,-zicsr,-zifencei,-zihintntl,-zihintpause,-zihpm,-zimop,-zk,-zkn,-zknd,-zkne,-zknh,-zkr,-zks,-zksed,-zksh,-zkt,-ztso,-zvbb,-zvbc,-zve32f,-zve32x,-zve64d,-zve64f,-zve64x,-zvfbfmin,-zvfbfwma,-zvfh,-zvfhmin,-zvkb,-zvkg,-zvkn,-zvknc,-zvkned,-zvkng,-zvknha,-zvknhb,-zvks,-zvksc,-zvksed,-zvksg,-zvksh,-zvkt,-zvl1024b,-zvl128b,-zvl16384b,-zvl2048b,-zvl256b,-zvl32768b,-zvl32b,-zvl4096b,-zvl512b,-zvl64b,-zvl65536b,-zvl8192b", + "features": "+32bit,+a,+c,+m,+zmmul,-b,-d,-e,-experimental-zalasr,-experimental-zicfilp,-experimental-zicfiss,-f,-h,-relax,-shcounterenw,-shgatpa,-shtvala,-shvsatpa,-shvstvala,-shvstvecd,-smaia,-smcdeleg,-smcsrind,-smepmp,-smstateen,-ssaia,-ssccfg,-ssccptr,-sscofpmf,-sscounterenw,-sscsrind,-ssstateen,-ssstrict,-sstc,-sstvala,-sstvecd,-ssu64xl,-svade,-svadu,-svbare,-svinval,-svnapot,-svpbmt,-v,-xcvalu,-xcvbi,-xcvbitmanip,-xcvelw,-xcvmac,-xcvmem,-xcvsimd,-xsfcease,-xsfvcp,-xsfvfnrclipxfqf,-xsfvfwmaccqqq,-xsfvqmaccdod,-xsfvqmaccqoq,-xsifivecdiscarddlone,-xsifivecflushdlone,-xtheadba,-xtheadbb,-xtheadbs,-xtheadcmo,-xtheadcondmov,-xtheadfmemidx,-xtheadmac,-xtheadmemidx,-xtheadmempair,-xtheadsync,-xtheadvdot,-xventanacondops,-xwchc,-za128rs,-za64rs,-zaamo,-zabha,-zalrsc,-zama16b,-zawrs,-zba,-zbb,-zbc,-zbkb,-zbkc,-zbkx,-zbs,-zca,-zcb,-zcd,-zce,-zcf,-zcmop,-zcmp,-zcmt,-zdinx,-zfa,-zfbfmin,-zfh,-zfhmin,-zfinx,-zhinx,-zhinxmin,-zic64b,-zicbom,-zicbop,-zicboz,-ziccamoa,-ziccif,-zicclsm,-ziccrse,-zicntr,-zicond,-zicsr,-zifencei,-zihintntl,-zihintpause,-zihpm,-zimop,-zk,-zkn,-zknd,-zkne,-zknh,-zkr,-zks,-zksed,-zksh,-zkt,-ztso,-zvbb,-zvbc,-zve32f,-zve32x,-zve64d,-zve64f,-zve64x,-zvfbfmin,-zvfbfwma,-zvfh,-zvfhmin,-zvkb,-zvkg,-zvkn,-zvknc,-zvkned,-zvkng,-zvknha,-zvknhb,-zvks,-zvksc,-zvksed,-zvksg,-zvksh,-zvkt,-zvl1024b,-zvl128b,-zvl16384b,-zvl2048b,-zvl256b,-zvl32768b,-zvl32b,-zvl4096b,-zvl512b,-zvl64b,-zvl65536b,-zvl8192b", "build-tags": ["fe310", "sifive"] } diff --git a/targets/k210.json b/targets/k210.json index 2140f459e4..b2c64e6489 100644 --- a/targets/k210.json +++ b/targets/k210.json @@ -1,6 +1,6 @@ { "inherits": ["riscv64"], - "features": "+64bit,+a,+c,+d,+f,+m,+zicsr,+zifencei,+zmmul,-b,-e,-experimental-smmpm,-experimental-smnpm,-experimental-ssnpm,-experimental-sspm,-experimental-ssqosid,-experimental-supm,-experimental-zacas,-experimental-zalasr,-experimental-zicfilp,-experimental-zicfiss,-h,-relax,-shcounterenw,-shgatpa,-shtvala,-shvsatpa,-shvstvala,-shvstvecd,-smaia,-smcdeleg,-smcsrind,-smepmp,-smstateen,-ssaia,-ssccfg,-ssccptr,-sscofpmf,-sscounterenw,-sscsrind,-ssstateen,-ssstrict,-sstc,-sstvala,-sstvecd,-ssu64xl,-svade,-svadu,-svbare,-svinval,-svnapot,-svpbmt,-v,-xcvalu,-xcvbi,-xcvbitmanip,-xcvelw,-xcvmac,-xcvmem,-xcvsimd,-xesppie,-xsfcease,-xsfvcp,-xsfvfnrclipxfqf,-xsfvfwmaccqqq,-xsfvqmaccdod,-xsfvqmaccqoq,-xsifivecdiscarddlone,-xsifivecflushdlone,-xtheadba,-xtheadbb,-xtheadbs,-xtheadcmo,-xtheadcondmov,-xtheadfmemidx,-xtheadmac,-xtheadmemidx,-xtheadmempair,-xtheadsync,-xtheadvdot,-xventanacondops,-xwchc,-za128rs,-za64rs,-zaamo,-zabha,-zalrsc,-zama16b,-zawrs,-zba,-zbb,-zbc,-zbkb,-zbkc,-zbkx,-zbs,-zca,-zcb,-zcd,-zce,-zcf,-zcmop,-zcmp,-zcmt,-zdinx,-zfa,-zfbfmin,-zfh,-zfhmin,-zfinx,-zhinx,-zhinxmin,-zic64b,-zicbom,-zicbop,-zicboz,-ziccamoa,-ziccif,-zicclsm,-ziccrse,-zicntr,-zicond,-zihintntl,-zihintpause,-zihpm,-zimop,-zk,-zkn,-zknd,-zkne,-zknh,-zkr,-zks,-zksed,-zksh,-zkt,-ztso,-zvbb,-zvbc,-zve32f,-zve32x,-zve64d,-zve64f,-zve64x,-zvfbfmin,-zvfbfwma,-zvfh,-zvfhmin,-zvkb,-zvkg,-zvkn,-zvknc,-zvkned,-zvkng,-zvknha,-zvknhb,-zvks,-zvksc,-zvksed,-zvksg,-zvksh,-zvkt,-zvl1024b,-zvl128b,-zvl16384b,-zvl2048b,-zvl256b,-zvl32768b,-zvl32b,-zvl4096b,-zvl512b,-zvl64b,-zvl65536b,-zvl8192b", + "features": "+64bit,+a,+c,+d,+f,+m,+zicsr,+zifencei,+zmmul,-b,-e,-experimental-zalasr,-experimental-zicfilp,-experimental-zicfiss,-h,-relax,-shcounterenw,-shgatpa,-shtvala,-shvsatpa,-shvstvala,-shvstvecd,-smaia,-smcdeleg,-smcsrind,-smepmp,-smstateen,-ssaia,-ssccfg,-ssccptr,-sscofpmf,-sscounterenw,-sscsrind,-ssstateen,-ssstrict,-sstc,-sstvala,-sstvecd,-ssu64xl,-svade,-svadu,-svbare,-svinval,-svnapot,-svpbmt,-v,-xcvalu,-xcvbi,-xcvbitmanip,-xcvelw,-xcvmac,-xcvmem,-xcvsimd,-xsfcease,-xsfvcp,-xsfvfnrclipxfqf,-xsfvfwmaccqqq,-xsfvqmaccdod,-xsfvqmaccqoq,-xsifivecdiscarddlone,-xsifivecflushdlone,-xtheadba,-xtheadbb,-xtheadbs,-xtheadcmo,-xtheadcondmov,-xtheadfmemidx,-xtheadmac,-xtheadmemidx,-xtheadmempair,-xtheadsync,-xtheadvdot,-xventanacondops,-xwchc,-za128rs,-za64rs,-zaamo,-zabha,-zalrsc,-zama16b,-zawrs,-zba,-zbb,-zbc,-zbkb,-zbkc,-zbkx,-zbs,-zca,-zcb,-zcd,-zce,-zcf,-zcmop,-zcmp,-zcmt,-zdinx,-zfa,-zfbfmin,-zfh,-zfhmin,-zfinx,-zhinx,-zhinxmin,-zic64b,-zicbom,-zicbop,-zicboz,-ziccamoa,-ziccif,-zicclsm,-ziccrse,-zicntr,-zicond,-zihintntl,-zihintpause,-zihpm,-zimop,-zk,-zkn,-zknd,-zkne,-zknh,-zkr,-zks,-zksed,-zksh,-zkt,-ztso,-zvbb,-zvbc,-zve32f,-zve32x,-zve64d,-zve64f,-zve64x,-zvfbfmin,-zvfbfwma,-zvfh,-zvfhmin,-zvkb,-zvkg,-zvkn,-zvknc,-zvkned,-zvkng,-zvknha,-zvknhb,-zvks,-zvksc,-zvksed,-zvksg,-zvksh,-zvkt,-zvl1024b,-zvl128b,-zvl16384b,-zvl2048b,-zvl256b,-zvl32768b,-zvl32b,-zvl4096b,-zvl512b,-zvl64b,-zvl65536b,-zvl8192b", "build-tags": ["k210", "kendryte"], "code-model": "medium" } diff --git a/targets/riscv-qemu.json b/targets/riscv-qemu.json index 318089332b..1f56796836 100644 --- a/targets/riscv-qemu.json +++ b/targets/riscv-qemu.json @@ -2,7 +2,7 @@ "inherits": [ "riscv32" ], - "features": "+32bit,+a,+c,+m,+zihintpause,+zmmul,-b,-d,-e,-experimental-smmpm,-experimental-smnpm,-experimental-ssnpm,-experimental-sspm,-experimental-ssqosid,-experimental-supm,-experimental-zacas,-experimental-zalasr,-experimental-zicfilp,-experimental-zicfiss,-f,-h,-relax,-shcounterenw,-shgatpa,-shtvala,-shvsatpa,-shvstvala,-shvstvecd,-smaia,-smcdeleg,-smcsrind,-smepmp,-smstateen,-ssaia,-ssccfg,-ssccptr,-sscofpmf,-sscounterenw,-sscsrind,-ssstateen,-ssstrict,-sstc,-sstvala,-sstvecd,-ssu64xl,-svade,-svadu,-svbare,-svinval,-svnapot,-svpbmt,-v,-xcvalu,-xcvbi,-xcvbitmanip,-xcvelw,-xcvmac,-xcvmem,-xcvsimd,-xesppie,-xsfcease,-xsfvcp,-xsfvfnrclipxfqf,-xsfvfwmaccqqq,-xsfvqmaccdod,-xsfvqmaccqoq,-xsifivecdiscarddlone,-xsifivecflushdlone,-xtheadba,-xtheadbb,-xtheadbs,-xtheadcmo,-xtheadcondmov,-xtheadfmemidx,-xtheadmac,-xtheadmemidx,-xtheadmempair,-xtheadsync,-xtheadvdot,-xventanacondops,-xwchc,-za128rs,-za64rs,-zaamo,-zabha,-zalrsc,-zama16b,-zawrs,-zba,-zbb,-zbc,-zbkb,-zbkc,-zbkx,-zbs,-zca,-zcb,-zcd,-zce,-zcf,-zcmop,-zcmp,-zcmt,-zdinx,-zfa,-zfbfmin,-zfh,-zfhmin,-zfinx,-zhinx,-zhinxmin,-zic64b,-zicbom,-zicbop,-zicboz,-ziccamoa,-ziccif,-zicclsm,-ziccrse,-zicntr,-zicond,-zicsr,-zifencei,-zihintntl,-zihpm,-zimop,-zk,-zkn,-zknd,-zkne,-zknh,-zkr,-zks,-zksed,-zksh,-zkt,-ztso,-zvbb,-zvbc,-zve32f,-zve32x,-zve64d,-zve64f,-zve64x,-zvfbfmin,-zvfbfwma,-zvfh,-zvfhmin,-zvkb,-zvkg,-zvkn,-zvknc,-zvkned,-zvkng,-zvknha,-zvknhb,-zvks,-zvksc,-zvksed,-zvksg,-zvksh,-zvkt,-zvl1024b,-zvl128b,-zvl16384b,-zvl2048b,-zvl256b,-zvl32768b,-zvl32b,-zvl4096b,-zvl512b,-zvl64b,-zvl65536b,-zvl8192b", + "features": "+32bit,+a,+c,+m,+zihintpause,+zmmul,-b,-d,-e,-experimental-zalasr,-experimental-zicfilp,-experimental-zicfiss,-f,-h,-relax,-shcounterenw,-shgatpa,-shtvala,-shvsatpa,-shvstvala,-shvstvecd,-smaia,-smcdeleg,-smcsrind,-smepmp,-smstateen,-ssaia,-ssccfg,-ssccptr,-sscofpmf,-sscounterenw,-sscsrind,-ssstateen,-ssstrict,-sstc,-sstvala,-sstvecd,-ssu64xl,-svade,-svadu,-svbare,-svinval,-svnapot,-svpbmt,-v,-xcvalu,-xcvbi,-xcvbitmanip,-xcvelw,-xcvmac,-xcvmem,-xcvsimd,-xsfcease,-xsfvcp,-xsfvfnrclipxfqf,-xsfvfwmaccqqq,-xsfvqmaccdod,-xsfvqmaccqoq,-xsifivecdiscarddlone,-xsifivecflushdlone,-xtheadba,-xtheadbb,-xtheadbs,-xtheadcmo,-xtheadcondmov,-xtheadfmemidx,-xtheadmac,-xtheadmemidx,-xtheadmempair,-xtheadsync,-xtheadvdot,-xventanacondops,-xwchc,-za128rs,-za64rs,-zaamo,-zabha,-zalrsc,-zama16b,-zawrs,-zba,-zbb,-zbc,-zbkb,-zbkc,-zbkx,-zbs,-zca,-zcb,-zcd,-zce,-zcf,-zcmop,-zcmp,-zcmt,-zdinx,-zfa,-zfbfmin,-zfh,-zfhmin,-zfinx,-zhinx,-zhinxmin,-zic64b,-zicbom,-zicbop,-zicboz,-ziccamoa,-ziccif,-zicclsm,-ziccrse,-zicntr,-zicond,-zicsr,-zifencei,-zihintntl,-zihpm,-zimop,-zk,-zkn,-zknd,-zkne,-zknh,-zkr,-zks,-zksed,-zksh,-zkt,-ztso,-zvbb,-zvbc,-zve32f,-zve32x,-zve64d,-zve64f,-zve64x,-zvfbfmin,-zvfbfwma,-zvfh,-zvfhmin,-zvkb,-zvkg,-zvkn,-zvknc,-zvkned,-zvkng,-zvknha,-zvknhb,-zvks,-zvksc,-zvksed,-zvksg,-zvksh,-zvkt,-zvl1024b,-zvl128b,-zvl16384b,-zvl2048b,-zvl256b,-zvl32768b,-zvl32b,-zvl4096b,-zvl512b,-zvl64b,-zvl65536b,-zvl8192b", "build-tags": [ "virt", "qemu" From 29eb1309bad4fd08d716c2c2f5cc0c68d38b2857 Mon Sep 17 00:00:00 2001 From: ZhouGuangyuan Date: Sun, 16 Aug 2026 23:38:03 +0800 Subject: [PATCH 4/4] build: register the LLVM 21 payload release --- internal/llvmpayload/payload.go | 10 ++++++++++ internal/llvmpayload/payload_test.go | 15 ++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/internal/llvmpayload/payload.go b/internal/llvmpayload/payload.go index 750314b46d..ec30446e8a 100644 --- a/internal/llvmpayload/payload.go +++ b/internal/llvmpayload/payload.go @@ -41,6 +41,16 @@ var manifests = map[int]manifest{ "x86_64-linux-gnu": "e2e0c48cd76e45ceba910917a2a97988dc80e3bb6040ea262bfe9293d5d9ac57", }, }, + 21: { + llvmMajor: 21, + version: "21.1.3_20260816", + sha256: map[string]string{ + "aarch64-apple-darwin": "a8c46104501c38a8a7359ec24bc4e9d646f9fec2bdb2b122cbbee78e060400d1", + "aarch64-linux-gnu": "77f49d832e5f309ecd6baaf169c62e3b064b27f9bee5aedddb6e66c981d56f44", + "x86_64-apple-darwin": "21159a4edb8948d83e1f73dfef394bca6941d0c4035da02f8c90ac59799893fa", + "x86_64-linux-gnu": "582b787057c9e36e7d4db20aaed7bbba74c7ad0481489f034f09476703befbd5", + }, + }, } // ForLLVMVersion returns the payload compatible with an in-process LLVM diff --git a/internal/llvmpayload/payload_test.go b/internal/llvmpayload/payload_test.go index 9eb588545a..ba7aef8333 100644 --- a/internal/llvmpayload/payload_test.go +++ b/internal/llvmpayload/payload_test.go @@ -6,12 +6,13 @@ import ( "testing" ) -func TestLLVM19Manifest(t *testing.T) { - manifest, err := ForLLVMVersion("LLVM 19.1.7") +func testManifest(t *testing.T, llvmVersion, payloadVersion string, wantMajor int) { + t.Helper() + manifest, err := ForLLVMVersion(llvmVersion) if err != nil { t.Fatal(err) } - if manifest.LLVMMajor() != 19 || manifest.Version() != "19.1.2_20250905-3" { + if manifest.LLVMMajor() != wantMajor || manifest.Version() != payloadVersion { t.Fatalf("manifest identity = LLVM %d %s", manifest.LLVMMajor(), manifest.Version()) } platforms := manifest.Platforms() @@ -33,6 +34,14 @@ func TestLLVM19Manifest(t *testing.T) { } } +func TestLLVM19Manifest(t *testing.T) { + testManifest(t, "LLVM 19.1.7", "19.1.2_20250905-3", 19) +} + +func TestLLVM21Manifest(t *testing.T) { + testManifest(t, "LLVM 21.1.8", "21.1.3_20260816", 21) +} + func TestPayloadErrors(t *testing.T) { if _, err := ForLLVMVersion("development"); err == nil { t.Fatal("invalid LLVM version accepted")