Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 103 additions & 28 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,13 @@ jobs:
os:
- ubuntu-latest
- macos-latest
- windows-2022
steps:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.26.7'
go-version: '1.27.0'

- name: Check out LLGo
uses: actions/checkout@v4
Expand All @@ -54,15 +53,23 @@ jobs:
- name: Set up LLGo dependencies
uses: ./.llgo/.github/actions/setup-deps
with:
llvm-version: '19'
llvm-version: '22'

- name: Build LLGo
if: runner.os != 'Windows'
working-directory: .llgo
run: go build -tags=dev -o "$RUNNER_TEMP/llgo" ./cmd/llgo
run: |
# Stable LLGo predates the default-version switch, but its source can
# build against system LLVM 22 through the released binding package.
llvm_version="$(llvm-config --version)"
test "${llvm_version%%.*}" = 22
export CGO_CPPFLAGS="$(llvm-config --cflags)"
export CGO_CXXFLAGS="$(llvm-config --cxxflags)"
export CGO_LDFLAGS="$(llvm-config --ldflags --libs --system-libs)"
go mod edit -require=github.com/xgo-dev/llvm@v0.9.8
go mod download github.com/xgo-dev/llvm@v0.9.8
go build -tags=dev,byollvm -o "$RUNNER_TEMP/llgo" ./cmd/llgo

- name: Test LLGo native bindings
if: runner.os != 'Windows'
env:
LLGO_BUILD_CACHE: 'off'
LLGO_ROOT: ${{ github.workspace }}/.llgo
Expand All @@ -80,32 +87,100 @@ jobs:
run: |
"$RUNNER_TEMP/llgo" test -v ./cpp/llvm

- name: Build LLGo on Windows
if: runner.os == 'Windows'
working-directory: .llgo
shell: pwsh
run: go build -tags=dev -o "$env:RUNNER_TEMP\llgo.exe" ./cmd/llgo
windows-llvm-demangle:
name: Windows LLVM 22 demangle bridge
runs-on: windows-2022
steps:
- uses: actions/checkout@v4

- name: Cache LLVM 22 development archive
id: llvm-cache
uses: actions/cache/restore@v4
with:
path: ${{ runner.tool_cache }}\lib-llvm\clang+llvm-22.1.8-x86_64-pc-windows-msvc.tar.xz
key: lib-llvm-22.1.8-windows-msvc-d96c2cc1736f4eb7fa43cb9bbdf56d93551a9ae0a9aadb9c99c3c3b2b712a234-v1

- name: Test LLGo Windows bindings
if: runner.os == 'Windows'
- name: Install LLVM 22 development archive
shell: pwsh
env:
LLGO_BUILD_CACHE: 'off'
LLGO_REQUIRE_MSVC: '1'
LLGO_ROOT: ${{ github.workspace }}/.llgo
LLVM_VERSION: '22.1.8'
LLVM_SHA256: d96c2cc1736f4eb7fa43cb9bbdf56d93551a9ae0a9aadb9c99c3c3b2b712a234
run: |
$ErrorActionPreference = "Stop"
$archiveName = "clang+llvm-$env:LLVM_VERSION-x86_64-pc-windows-msvc.tar.xz"
$archiveDir = Join-Path $env:RUNNER_TOOL_CACHE "lib-llvm"
$archive = Join-Path $archiveDir $archiveName
if ("${{ steps.llvm-cache.outputs.cache-hit }}" -ne "true") {
New-Item -ItemType Directory -Force $archiveDir | Out-Null
$asset = $archiveName.Replace("+", "%2B")
$url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-$env:LLVM_VERSION/$asset"
& curl.exe --fail --location --retry 5 --retry-all-errors --output $archive $url
if ($LASTEXITCODE -ne 0) {
throw "Downloading LLVM failed with exit code $LASTEXITCODE"
}
}
$actual = (Get-FileHash -Algorithm SHA256 $archive).Hash
if (-not $actual.Equals($env:LLVM_SHA256, [StringComparison]::OrdinalIgnoreCase)) {
throw "LLVM archive SHA-256 is $actual, want $env:LLVM_SHA256"
}

$extractRoot = Join-Path $env:RUNNER_TEMP "lib-llvm-extract"
$sevenZip = Join-Path $env:ProgramFiles "7-Zip\7z.exe"
New-Item -ItemType Directory -Force $extractRoot | Out-Null
& $sevenZip x -y "-o$extractRoot" $archive
if ($LASTEXITCODE -ne 0) {
throw "Decompressing the LLVM archive failed with exit code $LASTEXITCODE"
}
$tar = Get-ChildItem -LiteralPath $extractRoot -Filter "*.tar" | Select-Object -First 1
& $sevenZip x -y "-o$extractRoot" $tar.FullName
if ($LASTEXITCODE -ne 0) {
throw "Extracting the LLVM archive failed with exit code $LASTEXITCODE"
}
$llvmRoot = Get-ChildItem -LiteralPath $extractRoot -Directory |
Where-Object { Test-Path (Join-Path $_.FullName "bin\llvm-config.exe") } |
Select-Object -First 1 -ExpandProperty FullName
if (-not $llvmRoot) {
throw "The LLVM development archive is incomplete"
}
Add-Content -Encoding utf8 $env:GITHUB_ENV "LLVM_ROOT=$llvmRoot"

- name: Save LLVM 22 development archive
if: steps.llvm-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: ${{ runner.tool_cache }}\lib-llvm\clang+llvm-22.1.8-x86_64-pc-windows-msvc.tar.xz
key: lib-llvm-22.1.8-windows-msvc-d96c2cc1736f4eb7fa43cb9bbdf56d93551a9ae0a9aadb9c99c3c3b2b712a234-v1

- name: Activate MSVC SDK
uses: ilammy/msvc-dev-cmd@v1

- name: Compile and run LLVM 22 demangle bridge
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$llgo = Join-Path $env:RUNNER_TEMP "llgo.exe"

& $llgo test -count=1 -v `
./c `
./c/math/rand `
./c/net `
./c/os `
./c/setjmp `
./c/time `
./cpp/llvm `
./cpp/std
$llvmConfig = Join-Path $env:LLVM_ROOT "bin\llvm-config.exe"
$version = ((& $llvmConfig --version) -join "").Trim()
if ($LASTEXITCODE -ne 0 -or -not $version.StartsWith("22.")) {
throw "Expected LLVM 22, got '$version'"
}
$clang = Join-Path $env:LLVM_ROOT "bin\clang++.exe"
$include = Join-Path $env:LLVM_ROOT "include"
$demangle = Join-Path $env:LLVM_ROOT "lib\LLVMDemangle.lib"
$test = Join-Path $env:RUNNER_TEMP "demangle_windows_test.exe"
& $clang `
--target=x86_64-pc-windows-msvc `
-fuse-ld=lld `
-fms-runtime-lib=static `
-std=c++17 `
"-I$include" `
cpp/llvm/_wrap/demangle_windows.cpp `
cpp/llvm/_wrap/demangle_windows_test.cpp `
$demangle `
-o $test
if ($LASTEXITCODE -ne 0 -or -not (Test-Path $test)) {
throw "Compiling the LLVM demangle bridge failed with exit code $LASTEXITCODE"
}
& $test
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
throw "The LLVM demangle bridge smoke test failed with exit code $LASTEXITCODE"
}
74 changes: 74 additions & 0 deletions cpp/llvm/_wrap/demangle_windows_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2026 The GoPlus Authors (goplus.org). 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.
*/

#include <cstdlib>
#include <iostream>
#include <string>

extern "C" {

char *llgoLLVMItaniumDemangle(const char *data, size_t size, int parseParams);
char *llgoLLVMMicrosoftDemangle(const char *data, size_t size, size_t *nRead,
int *status, int flags);
char *llgoLLVMRustDemangle(const char *data, size_t size);

} // extern "C"

static bool expectEqual(const char *name, char *value, const char *want) {
if (value == nullptr) {
std::cerr << name << " returned null\n";
return false;
}
const std::string got(value);
std::free(value);
if (got != want) {
std::cerr << name << " returned " << got << ", want " << want << '\n';
return false;
}
return true;
}

int main() {
constexpr char itaniumName[] = "_Z3foov";
bool ok = expectEqual(
"Itanium",
llgoLLVMItaniumDemangle(itaniumName, sizeof(itaniumName) - 1, 1),
"foo()");

constexpr char microsoftName[] = "?foo@@YAHH@Z";
size_t nRead = 0;
int status = -1;
char *microsoft = llgoLLVMMicrosoftDemangle(
microsoftName, sizeof(microsoftName) - 1, &nRead, &status, 0);
if (microsoft == nullptr) {
std::cerr << "Microsoft returned null\n";
ok = false;
} else {
const std::string got(microsoft);
std::free(microsoft);
if (status != 0 || nRead == 0 || got.find("foo") == std::string::npos) {
std::cerr << "Microsoft returned " << got << ", nRead " << nRead
<< ", status " << status << '\n';
ok = false;
}
}

constexpr char rustName[] = "_RNvC6_123foo3bar";
ok = expectEqual("Rust", llgoLLVMRustDemangle(rustName, sizeof(rustName) - 1),
"123foo::bar") &&
ok;
return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
8 changes: 4 additions & 4 deletions cpp/llvm/config_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ const (
// the bridge for the ABI selected by its host clang++, then expose only C
// functions to the LLGo target. This avoids leaking that C++ ABI across the
// package boundary while retaining the target's native C ABI.
LLGoFiles = "$(pkg-config --cflags llvm-19) -std=c++17 --target=$(llvm-config --host-target): _wrap/demangle_windows.cpp"
// The llvm-19 package preserves LLVM's library order and required Windows
// system libraries.
LLGoPackage = "link: $(pkg-config --libs llvm-19)"
LLGoFiles = "$(pkg-config --cflags llvm-22) -std=c++17 --target=$(llvm-config --host-target): _wrap/demangle_windows.cpp"
// The explicitly versioned LLVM package preserves LLVM's library order and
// required Windows system libraries.
LLGoPackage = "link: $(pkg-config --libs llvm-22)"
)
5 changes: 2 additions & 3 deletions cpp/std/_wrap/string.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
#if defined(_WIN32) && defined(__clang__) && __clang_major__ < 20
// The current Visual Studio STL rejects older Clang releases by default.
// LLGo intentionally supports LLVM 19, whose MSVC ABI is sufficient for this
// wrapper; use the STL's documented escape hatch instead of changing the
// compiler or C++ ABI selected by the caller.
// Legacy Clang consumers may still select the STL's documented compatibility
// escape hatch without changing the C++ ABI selected by the caller.
#define _ALLOW_COMPILER_AND_STL_VERSION_MISMATCH
#endif

Expand Down
Loading