From e850314bb24fb85a8055d6c2dd4e08bb908cf7ff Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Tue, 21 Apr 2026 11:13:22 +0200 Subject: [PATCH 1/8] Add paths for older LLVM versions on Fedora --- .github/workflows/test.yml | 23 +++++++++++++++++++++++ llvm_config_llvm19.go | 4 ++-- llvm_config_llvm20.go | 4 ++-- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8946fd3..6d004c5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -55,3 +55,26 @@ jobs: if: matrix.llvm == 21 run: go test -v + test-linux-fedora: + # Fedora uses different paths than other systems, so testing it separately. + runs-on: ubuntu-24.04 + strategy: + matrix: + llvm: [19, 20, 21] + container: fedora:43 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Install dependencies (default LLVM) + if: matrix.llvm == 21 + run: dnf install --assumeyes g++ golang llvm-devel + - name: Install dependencies (older LLVM) + if: matrix.llvm != 21 + run: dnf install --assumeyes g++ golang llvm${{ matrix.llvm }}-devel + - name: Test LLVM ${{ matrix.llvm }} + run: + go test -v -tags=llvm${{ matrix.llvm }} + - name: Test default LLVM + if: matrix.llvm == 21 + run: + go test -v diff --git a/llvm_config_llvm19.go b/llvm_config_llvm19.go index e540f57..f5493ef 100644 --- a/llvm_config_llvm19.go +++ b/llvm_config_llvm19.go @@ -11,9 +11,9 @@ package llvm // #cgo freebsd CPPFLAGS: -I/usr/local/llvm19/include -I/usr/local/llvm19/include/llvm-c -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS // #cgo freebsd CXXFLAGS: -std=c++17 // #cgo freebsd LDFLAGS: -L/usr/local/llvm19/lib -lLLVM -// #cgo linux CPPFLAGS: -I/usr/include/llvm-19 -I/usr/include/llvm-c-19 -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS +// #cgo linux CPPFLAGS: -I/usr/include/llvm-19 -I/usr/include/llvm-c-19 -I/usr/lib64/llvm19/include -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS // #cgo linux CXXFLAGS: -std=c++17 -// #cgo linux LDFLAGS: -L/usr/lib/llvm-19/lib -lLLVM-19 +// #cgo linux LDFLAGS: -L/usr/lib/llvm-19/lib -L/usr/lib64/llvm19/lib -lLLVM-19 import "C" type run_build_sh int diff --git a/llvm_config_llvm20.go b/llvm_config_llvm20.go index 090bdd6..9c0a350 100644 --- a/llvm_config_llvm20.go +++ b/llvm_config_llvm20.go @@ -11,9 +11,9 @@ package llvm // #cgo freebsd CPPFLAGS: -I/usr/local/llvm20/include -I/usr/local/llvm20/include/llvm-c -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS // #cgo freebsd CXXFLAGS: -std=c++17 // #cgo freebsd LDFLAGS: -L/usr/local/llvm20/lib -lLLVM -// #cgo linux CPPFLAGS: -I/usr/include/llvm-20 -I/usr/include/llvm-c-20 -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS +// #cgo linux CPPFLAGS: -I/usr/include/llvm-20 -I/usr/include/llvm-c-20 -I/usr/lib64/llvm20/include -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS // #cgo linux CXXFLAGS: -std=c++17 -// #cgo linux LDFLAGS: -L/usr/lib/llvm-20/lib -lLLVM-20 +// #cgo linux LDFLAGS: -L/usr/lib/llvm-20/lib -L/usr/lib64/llvm20/lib64 -lLLVM-20 import "C" type run_build_sh int From 4cf4dc9e12f4d23ba2e8d1458702178803b6b070 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Wed, 22 Apr 2026 11:14:49 +0200 Subject: [PATCH 2/8] Use LLVM 20 by default for now We still haven't updated TinyGo, so this change is needed so we can use https://github.com/tinygo-org/go-llvm/pull/73 in TinyGo. --- .github/workflows/test.yml | 6 +++--- llvm_config_llvm20.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6d004c5..9432e6a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,7 +27,7 @@ jobs: run: go test -v -tags=llvm${{ matrix.llvm }} - name: Test default LLVM - if: matrix.llvm == 21 + if: matrix.llvm == 20 run: go test -v test-linux: @@ -52,7 +52,7 @@ jobs: run: go test -v -tags=llvm${{ matrix.llvm }} - name: Test default LLVM - if: matrix.llvm == 21 + if: matrix.llvm == 20 run: go test -v test-linux-fedora: @@ -75,6 +75,6 @@ jobs: run: go test -v -tags=llvm${{ matrix.llvm }} - name: Test default LLVM - if: matrix.llvm == 21 + if: matrix.llvm == 20 run: go test -v diff --git a/llvm_config_llvm20.go b/llvm_config_llvm20.go index 9c0a350..6741b28 100644 --- a/llvm_config_llvm20.go +++ b/llvm_config_llvm20.go @@ -1,4 +1,4 @@ -//go:build !byollvm && llvm20 +//go:build !byollvm && !llvm14 && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm19 && !llvm21 package llvm From cec52f666ed507a8a489cc34e95619b9a06ef827 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Tue, 7 Jul 2026 13:03:25 -0700 Subject: [PATCH 3/8] Add LLVM 22 build support (#75) * Add LLVM 22 build support Written entirely by Claude (Anthropic's Claude Code), at the request of and under the direction of dgryski, as part of a broader effort to get TinyGo building against upcoming LLVM releases. Adds the llvm22 build-tag config file (mirroring the existing llvm21 one) and a CI matrix entry for it. Also adds a regression test confirming that LLVM 21+'s 'captures' parameter attribute (which replaced the boolean 'nocapture' enum attribute) round-trips correctly through the existing generic enum-attribute API, with value 0 corresponding to captures(none) -- no binding/C++ shim changes were needed for this, since 'captures' is an IntAttr using the same wire format as e.g. 'align' or 'dereferenceable'. Verified by running the full test suite against real LLVM 22.1.8 headers and libraries (via Homebrew). * limit captures_test to llvm 21 and above. * Fix switch-instruction case-value access for LLVM 22 Written entirely by Claude (Anthropic's Claude Code), at the request of and under the direction of dgryski, on the dgryski/llvm23 branch (LLVM 22 support, in preparation for the eventual LLVM 23 release). LLVM 22 stopped exposing SwitchInst case values as regular instruction operands -- only the condition and destination-block operands remain as operands now. Case values must be read via the new LLVMGetSwitchCaseValue C API added in the same release. This was discovered while chasing a real-world regression in TinyGo's interp package (see the paired tinygo commit on the same branch), which manually walked switch operands assuming the old layout; verified independently here first via a minimal standalone .ll repro against real LLVM 20/21/22 headers and libraries, isolating the exact version cutoff (21 -> 22) before touching any tinygo code. Adds: - Value.SuccessorsCount()/Value.Successor(i) in ir.go, using the stable, version-independent LLVMGetNumSuccessors/LLVMGetSuccessor API to enumerate a switch's destination blocks (successor 0 is always the default destination; successors 1..N-1 are the cases). - Value.GetSwitchCaseValue(i), version-gated: switch_llvm22.go uses the new LLVMGetSwitchCaseValue API, switch_pre22.go falls back to the old operand-based read for LLVM <22. - switch_test.go, a regression test verifying case values and destinations round-trip correctly; passes against real LLVM 20/21/22. --- .github/workflows/test.yml | 4 +- captures_test.go | 47 +++++++++++++++++++++++ ir.go | 12 ++++++ llvm_config_llvm20.go | 2 +- llvm_config_llvm22.go | 19 ++++++++++ switch_llvm22.go | 21 +++++++++++ switch_pre22.go | 15 ++++++++ switch_test.go | 76 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 193 insertions(+), 3 deletions(-) create mode 100644 captures_test.go create mode 100644 llvm_config_llvm22.go create mode 100644 switch_llvm22.go create mode 100644 switch_pre22.go create mode 100644 switch_test.go diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9432e6a..9294ab5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,7 +10,7 @@ jobs: runs-on: macos-latest strategy: matrix: - llvm: [14, 15, 16, 17, 18, 19, 20, 21] + llvm: [14, 15, 16, 17, 18, 19, 20, 21, 22] steps: - name: Checkout uses: actions/checkout@v4 @@ -34,7 +34,7 @@ jobs: runs-on: ubuntu-22.04 strategy: matrix: - llvm: [14, 15, 16, 17, 18, 19, 20, 21] + llvm: [14, 15, 16, 17, 18, 19, 20, 21, 22] steps: - name: Checkout uses: actions/checkout@v4 diff --git a/captures_test.go b/captures_test.go new file mode 100644 index 0000000..6baaa96 --- /dev/null +++ b/captures_test.go @@ -0,0 +1,47 @@ +package llvm + +import ( + "strconv" + "strings" + "testing" +) + +// TestCapturesAttribute checks that the 'captures' parameter attribute +// (which replaced the boolean 'nocapture' enum attribute starting with +// LLVM 21) round-trips through the generic enum-attribute API, and that a +// value of 0 corresponds to CaptureInfo::none(), i.e. captures(none). +func TestCapturesAttribute(t *testing.T) { + majorVersion, _ := strconv.Atoi(strings.SplitN(Version, ".", 2)[0]) + if majorVersion < 21 { + t.Skip("not llvm 21") + } + + ctx := NewContext() + mod := ctx.NewModule("") + defer mod.Dispose() + + ptrType := PointerType(ctx.Int8Type(), 0) + ftyp := FunctionType(ctx.VoidType(), []Type{ptrType}, false) + fn := AddFunction(mod, "foo", ftyp) + + kind := AttributeKindID("captures") + if kind == 0 { + t.Fatal("captures kind id not found") + } + + attr := ctx.CreateEnumAttribute(kind, 0) + fn.AddAttributeAtIndex(1, attr) + + got := fn.GetEnumAttributeAtIndex(1, kind) + if got.IsNil() { + t.Fatal("expected captures attribute on param 1, got nil") + } + if val := got.GetEnumValue(); val != 0 { + t.Errorf("expected captures value 0 (none), got %d", val) + } + + text := mod.String() + if !strings.Contains(text, "captures(none)") { + t.Errorf("expected 'captures(none)' in output, got:\n%s", text) + } +} diff --git a/ir.go b/ir.go index efb0de1..47bf413 100644 --- a/ir.go +++ b/ir.go @@ -792,6 +792,18 @@ func (v Value) Operand(i int) (rv Value) { rv.C = C.LLVMGetOperand(v.C, C.unsi func (v Value) SetOperand(i int, op Value) { C.LLVMSetOperand(v.C, C.unsigned(i), op.C) } func (v Value) OperandsCount() int { return int(C.LLVMGetNumOperands(v.C)) } +// Operations on terminator instructions (br, switch, etc). Unlike operands, +// the number and meaning of successors has been stable across LLVM versions, +// making these a safe, version-independent way to enumerate the destination +// blocks of a switch instruction: successor 0 is the default destination, +// and successors 1..N-1 correspond to case 0..N-2 (see GetSwitchCaseValue for +// the matching case value). +func (v Value) SuccessorsCount() int { return int(C.LLVMGetNumSuccessors(v.C)) } +func (v Value) Successor(i int) (bb BasicBlock) { + bb.C = C.LLVMGetSuccessor(v.C, C.unsigned(i)) + return +} + // Operations on constants of any type func ConstNull(t Type) (v Value) { v.C = C.LLVMConstNull(t.C); return } func ConstAllOnes(t Type) (v Value) { v.C = C.LLVMConstAllOnes(t.C); return } diff --git a/llvm_config_llvm20.go b/llvm_config_llvm20.go index 6741b28..2132c5a 100644 --- a/llvm_config_llvm20.go +++ b/llvm_config_llvm20.go @@ -1,4 +1,4 @@ -//go:build !byollvm && !llvm14 && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm19 && !llvm21 +//go:build !byollvm && !llvm14 && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm19 && !llvm21 && !llvm22 package llvm diff --git a/llvm_config_llvm22.go b/llvm_config_llvm22.go new file mode 100644 index 0000000..103e930 --- /dev/null +++ b/llvm_config_llvm22.go @@ -0,0 +1,19 @@ +//go:build !byollvm && llvm22 + +package llvm + +// #cgo darwin,amd64 CPPFLAGS: -I/usr/local/opt/llvm@22/include -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS +// #cgo darwin,amd64 CXXFLAGS: -std=c++17 +// #cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@22/lib -Wl,-search_paths_first -Wl,-headerpad_max_install_names -lLLVM -lz -lm +// #cgo darwin,arm64 CPPFLAGS: -I/opt/homebrew/opt/llvm@22/include -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS +// #cgo darwin,arm64 CXXFLAGS: -std=c++17 +// #cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@22/lib -Wl,-search_paths_first -Wl,-headerpad_max_install_names -lLLVM -lz -lm +// #cgo freebsd CPPFLAGS: -I/usr/local/llvm22/include -I/usr/local/llvm22/include/llvm-c -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS +// #cgo freebsd CXXFLAGS: -std=c++17 +// #cgo freebsd LDFLAGS: -L/usr/local/llvm22/lib -lLLVM +// #cgo linux CPPFLAGS: -I/usr/include/llvm-22 -I/usr/include/llvm-c-22 -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS +// #cgo linux CXXFLAGS: -std=c++17 +// #cgo linux LDFLAGS: -L/usr/lib/llvm-22/lib -lLLVM-22 +import "C" + +type run_build_sh int diff --git a/switch_llvm22.go b/switch_llvm22.go new file mode 100644 index 0000000..0c70b08 --- /dev/null +++ b/switch_llvm22.go @@ -0,0 +1,21 @@ +//go:build llvm22 + +package llvm + +/* +#include "llvm-c/Core.h" +*/ +import "C" + +// GetSwitchCaseValue obtains the case value for a successor of a switch +// instruction. i corresponds to the successor index; the first successor (0) +// is the default destination, so i must be greater than zero. +// +// LLVM 22 stopped exposing switch case values as regular instruction +// operands (only the condition and destination-block operands remain), so +// this is implemented via the new LLVMGetSwitchCaseValue C API added in the +// same release. +func (v Value) GetSwitchCaseValue(i int) (rv Value) { + rv.C = C.LLVMGetSwitchCaseValue(v.C, C.unsigned(i)) + return +} diff --git a/switch_pre22.go b/switch_pre22.go new file mode 100644 index 0000000..cfa381c --- /dev/null +++ b/switch_pre22.go @@ -0,0 +1,15 @@ +//go:build !llvm22 + +package llvm + +// GetSwitchCaseValue obtains the case value for a successor of a switch +// instruction. i corresponds to the successor index; the first successor (0) +// is the default destination, so i must be greater than zero. +// +// Before LLVM 22, switch case values were stored as regular instruction +// operands (alternating with their destination blocks, after the leading +// condition/default-destination pair), so this is implemented via Operand +// access instead of the LLVM 22+-only LLVMGetSwitchCaseValue. +func (v Value) GetSwitchCaseValue(i int) Value { + return v.Operand(2 * i) +} diff --git a/switch_test.go b/switch_test.go new file mode 100644 index 0000000..aa7ca26 --- /dev/null +++ b/switch_test.go @@ -0,0 +1,76 @@ +package llvm + +import ( + "os" + "testing" +) + +// TestSwitchCaseValue checks that GetSwitchCaseValue/SuccessorsCount/Successor +// correctly read a switch instruction's cases across LLVM versions. LLVM 22 +// stopped exposing case values as regular instruction operands (only the +// condition and destination-block operands remain), instead requiring the +// new LLVMGetSwitchCaseValue API; code that assumed the old operand layout +// silently reads a destination block where it expects a case value. +func TestSwitchCaseValue(t *testing.T) { + src := ` +define void @foo(i64 %callback) { +entry: + switch i64 %callback, label %default [ + i64 0, label %case0 + i64 5, label %case1 + ] +default: + ret void +case0: + ret void +case1: + ret void +} +` + f, err := os.CreateTemp("", "switchcase-*.ll") + if err != nil { + t.Fatal(err) + } + defer os.Remove(f.Name()) + if _, err := f.WriteString(src); err != nil { + t.Fatal(err) + } + f.Close() + + ctx := NewContext() + defer ctx.Dispose() + + buf, err := NewMemoryBufferFromFile(f.Name()) + if err != nil { + t.Fatal(err) + } + + m, err := ctx.ParseIR(buf) + if err != nil { + t.Fatal(err) + } + defer m.Dispose() + + fn := m.NamedFunction("foo") + sw := fn.EntryBasicBlock().FirstInstruction() + + if n := sw.SuccessorsCount(); n != 3 { + t.Fatalf("expected 3 successors (default + 2 cases), got %d", n) + } + if got := sw.Successor(0).AsValue().Name(); got != "default" { + t.Errorf("expected default destination %q, got %q", "default", got) + } + + wantCaseValues := []uint64{0, 5} + wantCaseDests := []string{"case0", "case1"} + for i, want := range wantCaseValues { + successor := i + 1 + val := sw.GetSwitchCaseValue(successor) + if got := val.ZExtValue(); got != want { + t.Errorf("case %d: expected value %d, got %d", i, want, got) + } + if got := sw.Successor(successor).AsValue().Name(); got != wantCaseDests[i] { + t.Errorf("case %d: expected destination %q, got %q", i, wantCaseDests[i], got) + } + } +} From 3d122f322d4b72b6fc1ea0ba3950c360b76c2dff Mon Sep 17 00:00:00 2001 From: deadprogram Date: Mon, 20 Jul 2026 15:30:47 +0200 Subject: [PATCH 4/8] go-llvm: use context-aware LLVM C APIs for functions deprecated in LLVM 22 LLVM 22 deprecated 8 global-context C API functions in favor of their context-aware counterparts. Instead of suppressing the warnings, call the non-deprecated InContext APIs directly. The non-context Go functions (MDKindID, StructType, ConstString, ConstStruct, AddBasicBlock, InsertBasicBlock) now delegate to GlobalContext().(), which already calls the InContext C APIs. IntPtrType calls LLVMIntPtrTypeInContext directly. Only LLVMGetGlobalContext still needs a warning-suppression wrapper in deprecated.c since it has no non-deprecated replacement. The context-aware C APIs have been available since well before LLVM 14, so this works across all supported LLVM versions. Signed-off-by: deadprogram --- deprecated.c | 24 ++++++++++++++++++++++++ deprecated.h | 30 ++++++++++++++++++++++++++++++ ir.go | 37 ++++++++----------------------------- target.go | 6 +++++- 4 files changed, 67 insertions(+), 30 deletions(-) create mode 100644 deprecated.c create mode 100644 deprecated.h diff --git a/deprecated.c b/deprecated.c new file mode 100644 index 0000000..b5a2c54 --- /dev/null +++ b/deprecated.c @@ -0,0 +1,24 @@ +//===- deprecated.c - Wrappers for deprecated LLVM C API --------*- C -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file wraps LLVMGetGlobalContext, which was deprecated in LLVM 22 but +// has no non-deprecated replacement. All other wrapped functions are +// handled by calling their context-aware counterparts from Go. +// +//===----------------------------------------------------------------------===// + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + +#include "deprecated.h" + +LLVMContextRef LLVMGetGlobalContext_wrap(void) { + return LLVMGetGlobalContext(); +} + +#pragma GCC diagnostic pop diff --git a/deprecated.h b/deprecated.h new file mode 100644 index 0000000..f2cba8d --- /dev/null +++ b/deprecated.h @@ -0,0 +1,30 @@ +//===- deprecated.h - Wrappers for deprecated LLVM C API --------*- C -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file wraps LLVMGetGlobalContext, which was deprecated in LLVM 22 but +// has no non-deprecated replacement. All other wrapped functions are +// handled by calling their context-aware counterparts from Go. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_BINDINGS_GO_LLVM_DEPRECATED_H +#define LLVM_BINDINGS_GO_LLVM_DEPRECATED_H + +#include "llvm-c/Core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +LLVMContextRef LLVMGetGlobalContext_wrap(void); + +#ifdef __cplusplus +} +#endif + +#endif // LLVM_BINDINGS_GO_LLVM_DEPRECATED_H diff --git a/ir.go b/ir.go index 47bf413..986748d 100644 --- a/ir.go +++ b/ir.go @@ -16,6 +16,7 @@ package llvm #include "llvm-c/Core.h" #include "llvm-c/Comdat.h" #include "IRBindings.h" +#include "deprecated.h" #include */ import "C" @@ -357,7 +358,7 @@ const ( //------------------------------------------------------------------------- func NewContext() Context { return Context{C.LLVMContextCreate()} } -func GlobalContext() Context { return Context{C.LLVMGetGlobalContext()} } +func GlobalContext() Context { return Context{C.LLVMGetGlobalContext_wrap()} } func (c Context) Dispose() { C.LLVMContextDispose(c.C) } func (c Context) MDKindID(name string) (id int) { @@ -368,10 +369,7 @@ func (c Context) MDKindID(name string) (id int) { } func MDKindID(name string) (id int) { - cname := C.CString(name) - defer C.free(unsafe.Pointer(cname)) - id = int(C.LLVMGetMDKindID(cname, C.unsigned(len(name)))) - return + return GlobalContext().MDKindID(name) } //------------------------------------------------------------------------- @@ -610,14 +608,7 @@ func (c Context) StructType(elementTypes []Type, packed bool) (t Type) { } func StructType(elementTypes []Type, packed bool) (t Type) { - var pt *C.LLVMTypeRef - var ptlen C.unsigned - if len(elementTypes) > 0 { - pt = llvmTypeRefPtr(&elementTypes[0]) - ptlen = C.unsigned(len(elementTypes)) - } - t.C = C.LLVMStructType(pt, ptlen, boolToLLVMBool(packed)) - return + return GlobalContext().StructType(elementTypes, packed) } func (c Context) StructCreateNamed(name string) (t Type) { @@ -882,11 +873,7 @@ func ConstNamedStruct(t Type, constVals []Value) (v Value) { return } func ConstString(str string, addnull bool) (v Value) { - cstr := C.CString(str) - defer C.free(unsafe.Pointer(cstr)) - v.C = C.LLVMConstString(cstr, - C.unsigned(len(str)), boolToLLVMBool(!addnull)) - return + return GlobalContext().ConstString(str, addnull) } func ConstArray(t Type, constVals []Value) (v Value) { ptr, nvals := llvmValueRefs(constVals) @@ -894,9 +881,7 @@ func ConstArray(t Type, constVals []Value) (v Value) { return } func ConstStruct(constVals []Value, packed bool) (v Value) { - ptr, nvals := llvmValueRefs(constVals) - v.C = C.LLVMConstStruct(ptr, nvals, boolToLLVMBool(packed)) - return + return GlobalContext().ConstStruct(constVals, packed) } func ConstVector(scalarConstVals []Value, packed bool) (v Value) { ptr, nvals := llvmValueRefs(scalarConstVals) @@ -1203,16 +1188,10 @@ func (c Context) InsertBasicBlock(ref BasicBlock, name string) (bb BasicBlock) { return } func AddBasicBlock(f Value, name string) (bb BasicBlock) { - cname := C.CString(name) - defer C.free(unsafe.Pointer(cname)) - bb.C = C.LLVMAppendBasicBlock(f.C, cname) - return + return GlobalContext().AddBasicBlock(f, name) } func InsertBasicBlock(ref BasicBlock, name string) (bb BasicBlock) { - cname := C.CString(name) - defer C.free(unsafe.Pointer(cname)) - bb.C = C.LLVMInsertBasicBlock(ref.C, cname) - return + return GlobalContext().InsertBasicBlock(ref, name) } func (bb BasicBlock) EraseFromParent() { C.LLVMDeleteBasicBlock(bb.C) } func (bb BasicBlock) MoveBefore(pos BasicBlock) { C.LLVMMoveBasicBlockBefore(bb.C, pos.C) } diff --git a/target.go b/target.go index 5075d14..b15827a 100644 --- a/target.go +++ b/target.go @@ -16,6 +16,7 @@ package llvm #include "llvm-c/Core.h" #include "llvm-c/Target.h" #include "llvm-c/TargetMachine.h" +#include "deprecated.h" #include */ import "C" @@ -143,7 +144,10 @@ func (td TargetData) PointerSize() int { return int(C.LLVMPointerSize(td.C)) } // Returns the integer type that is the same size as a pointer on a target. // See the method llvm::TargetData::getIntPtrType. -func (td TargetData) IntPtrType() (t Type) { t.C = C.LLVMIntPtrType(td.C); return } +func (td TargetData) IntPtrType() (t Type) { + t.C = C.LLVMIntPtrTypeInContext(C.LLVMGetGlobalContext_wrap(), td.C) + return +} // Computes the size of a type in bytes for a target. // See the method llvm::TargetData::getTypeSizeInBits. From 1b722f8cbe7ce52490e1f9cefb6762acb9c99110 Mon Sep 17 00:00:00 2001 From: ZhouGuangyuan Date: Mon, 27 Jul 2026 00:53:57 +0800 Subject: [PATCH 5/8] go-llvm: add LLVM 23 support and make it default --- .github/workflows/test.yml | 16 ++++++---------- README.markdown | 14 ++++++++++---- branch_llvm23.go | 26 ++++++++++++++++++++++++++ branch_pre23.go | 13 +++++++++++++ gen_llvm_config_static.sh | 4 ++-- ir.go | 2 -- llvm_config_llvm14.go | 2 +- llvm_config_llvm15.go | 2 +- llvm_config_llvm16.go | 2 +- llvm_config_llvm17.go | 2 +- llvm_config_llvm18.go | 2 +- llvm_config_llvm19.go | 2 +- llvm_config_llvm20.go | 2 +- llvm_config_llvm21.go | 2 +- llvm_config_llvm22.go | 2 +- llvm_config_llvm23.go | 19 +++++++++++++++++++ llvm_config_static.go | 4 ++-- opcode_llvm23.go | 13 +++++++++++++ opcode_pre23.go | 10 ++++++++++ passes_llvm23_test.go | 10 ++++++++++ passes_pre23_test.go | 7 +++++++ passes_test.go | 3 ++- switch_llvm22.go | 2 +- switch_pre22.go | 2 +- 24 files changed, 131 insertions(+), 32 deletions(-) create mode 100644 branch_llvm23.go create mode 100644 branch_pre23.go create mode 100644 llvm_config_llvm23.go create mode 100644 opcode_llvm23.go create mode 100644 opcode_pre23.go create mode 100644 passes_llvm23_test.go create mode 100644 passes_pre23_test.go diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9294ab5..13f25e0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,14 +10,14 @@ jobs: runs-on: macos-latest strategy: matrix: - llvm: [14, 15, 16, 17, 18, 19, 20, 21, 22] + llvm: [14, 15, 16, 17, 18, 19, 20, 21, 22, 23] steps: - name: Checkout uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v5 with: - go-version: '1.22' + go-version: '1.24' # Optional step when a LLVM version is very new. - name: Update Homebrew run: brew update @@ -27,21 +27,21 @@ jobs: run: go test -v -tags=llvm${{ matrix.llvm }} - name: Test default LLVM - if: matrix.llvm == 20 + if: matrix.llvm == 23 run: go test -v test-linux: runs-on: ubuntu-22.04 strategy: matrix: - llvm: [14, 15, 16, 17, 18, 19, 20, 21, 22] + llvm: [14, 15, 16, 17, 18, 19, 20, 21, 22, 23] steps: - name: Checkout uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v5 with: - go-version: '1.22' + go-version: '1.24' - name: Install LLVM run: | echo 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-${{ matrix.llvm }} main' | sudo tee /etc/apt/sources.list.d/llvm.list @@ -52,7 +52,7 @@ jobs: run: go test -v -tags=llvm${{ matrix.llvm }} - name: Test default LLVM - if: matrix.llvm == 20 + if: matrix.llvm == 23 run: go test -v test-linux-fedora: @@ -74,7 +74,3 @@ jobs: - name: Test LLVM ${{ matrix.llvm }} run: go test -v -tags=llvm${{ matrix.llvm }} - - name: Test default LLVM - if: matrix.llvm == 20 - run: - go test -v diff --git a/README.markdown b/README.markdown index efd2ecb..6c60343 100644 --- a/README.markdown +++ b/README.markdown @@ -4,12 +4,18 @@ This library provides bindings to a system-installed LLVM. Currently supported: - * LLVM 20, 19, 18, 17, 16, 15 and 14 from [apt.llvm.org](http://apt.llvm.org/) on Debian/Ubuntu. - * LLVM 20, 19, 18, 17, 16, 15 and 14 from Homebrew on macOS. + * LLVM 23, 22, 21, 20, 19, 18, 17, 16, 15 and 14 from [apt.llvm.org](http://apt.llvm.org/) on Debian/Ubuntu. + * LLVM 23, 22, 21, 20, 19, 18, 17, 16, 15 and 14 from Homebrew on macOS. * Any of the above versions with a manually built LLVM through the `byollvm` build tag. You need to set up `CFLAGS`/`LDFLAGS` etc yourself in this case. -You can select the LLVM version using a build tag, for example `-tags=llvm17` -to use LLVM 17. +LLVM 23 is selected by default. You can select another LLVM version using a +build tag, for example `-tags=llvm17` to use LLVM 17. When using a manually +built LLVM 22 or newer, combine the version and custom-build tags, for example +`-tags="byollvm llvm23"`. + +The GoALLC in-tree static LLVM payload is selected explicitly with +`-tags=staticllvm`. Add a version tag when the payload is not LLVM 23, for +example `-tags="staticllvm llvm20"`. ## Usage diff --git a/branch_llvm23.go b/branch_llvm23.go new file mode 100644 index 0000000..a19065f --- /dev/null +++ b/branch_llvm23.go @@ -0,0 +1,26 @@ +//go:build llvm23 || (!byollvm && !llvm14 && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm19 && !llvm20 && !llvm21 && !llvm22) + +package llvm + +/* +#include "llvm-c/Core.h" +*/ +import "C" + +func (v Value) IsABranchInst() (rv Value) { + rv.C = C.LLVMIsAUncondBrInst(v.C) + if rv.C == nil { + rv.C = C.LLVMIsACondBrInst(v.C) + } + return +} + +func (v Value) IsAUncondBrInst() (rv Value) { + rv.C = C.LLVMIsAUncondBrInst(v.C) + return +} + +func (v Value) IsACondBrInst() (rv Value) { + rv.C = C.LLVMIsACondBrInst(v.C) + return +} diff --git a/branch_pre23.go b/branch_pre23.go new file mode 100644 index 0000000..e3b33bf --- /dev/null +++ b/branch_pre23.go @@ -0,0 +1,13 @@ +//go:build !llvm23 && (byollvm || llvm14 || llvm15 || llvm16 || llvm17 || llvm18 || llvm19 || llvm20 || llvm21 || llvm22) + +package llvm + +/* +#include "llvm-c/Core.h" +*/ +import "C" + +func (v Value) IsABranchInst() (rv Value) { + rv.C = C.LLVMIsABranchInst(v.C) + return +} diff --git a/gen_llvm_config_static.sh b/gen_llvm_config_static.sh index b51f978..cbd2664 100644 --- a/gen_llvm_config_static.sh +++ b/gen_llvm_config_static.sh @@ -15,8 +15,8 @@ for f in ./llvm/lib/*.a; do done cat > "./llvm_config_static.go" <. Keep this test focused on the LLVMRunPasses API: +// the current arm64 target-machine pipeline asserts on this legacy recursive +// fixture at O2, while O0 exercises the same C API without target transforms. +const defaultTestPipeline = "default" + +func prepareDefaultTestPipeline(Context, Value) {} diff --git a/passes_pre23_test.go b/passes_pre23_test.go new file mode 100644 index 0000000..316dcf4 --- /dev/null +++ b/passes_pre23_test.go @@ -0,0 +1,7 @@ +//go:build !llvm23 && (byollvm || llvm14 || llvm15 || llvm16 || llvm17 || llvm18 || llvm19 || llvm20 || llvm21 || llvm22) + +package llvm + +const defaultTestPipeline = "default" + +func prepareDefaultTestPipeline(Context, Value) {} diff --git a/passes_test.go b/passes_test.go index 3cbc13b..ed704fe 100644 --- a/passes_test.go +++ b/passes_test.go @@ -63,7 +63,8 @@ func TestPasses(t *testing.T) { defer pbo.Dispose() t.Run("no error running default pass", func(t *testing.T) { - err := mod.RunPasses("default", mt, pbo) + prepareDefaultTestPipeline(ctx, fac) + err := mod.RunPasses(defaultTestPipeline, mt, pbo) if err != nil { t.Error(err) } diff --git a/switch_llvm22.go b/switch_llvm22.go index 0c70b08..8df6966 100644 --- a/switch_llvm22.go +++ b/switch_llvm22.go @@ -1,4 +1,4 @@ -//go:build llvm22 +//go:build llvm22 || llvm23 || (!byollvm && !llvm14 && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm19 && !llvm20 && !llvm21 && !llvm22) package llvm diff --git a/switch_pre22.go b/switch_pre22.go index cfa381c..6bbfac9 100644 --- a/switch_pre22.go +++ b/switch_pre22.go @@ -1,4 +1,4 @@ -//go:build !llvm22 +//go:build !llvm22 && !llvm23 && (byollvm || llvm14 || llvm15 || llvm16 || llvm17 || llvm18 || llvm19 || llvm20 || llvm21) package llvm From 9ca6cdff6df23d07a8cb4f1cdf94f4457b0e8c05 Mon Sep 17 00:00:00 2001 From: ZhouGuangyuan Date: Mon, 27 Jul 2026 00:57:23 +0800 Subject: [PATCH 6/8] ci: use snapshot suite for LLVM 23 --- .github/workflows/test.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 13f25e0..3ecd03c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -44,7 +44,11 @@ jobs: go-version: '1.24' - name: Install LLVM run: | - echo 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-${{ matrix.llvm }} main' | sudo tee /etc/apt/sources.list.d/llvm.list + suite=llvm-toolchain-jammy-${{ matrix.llvm }} + if [ "${{ matrix.llvm }}" = 23 ]; then + suite=llvm-toolchain-jammy + fi + echo "deb http://apt.llvm.org/jammy/ ${suite} 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 llvm-${{ matrix.llvm }}-dev From 186c3b9d89b44da760a3859b2e748eb8952535c5 Mon Sep 17 00:00:00 2001 From: ZhouGuangyuan Date: Mon, 27 Jul 2026 01:02:18 +0800 Subject: [PATCH 7/8] ci: use current LLVM 23 Homebrew formula --- .github/workflows/test.yml | 7 ++++++- llvm_config_llvm23.go | 8 ++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3ecd03c..671573e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,7 +22,12 @@ jobs: - name: Update Homebrew run: brew update - name: Install LLVM - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install llvm@${{ matrix.llvm }} + run: | + formula=llvm@${{ matrix.llvm }} + if [ "${{ matrix.llvm }}" = 23 ]; then + formula=llvm + fi + HOMEBREW_NO_AUTO_UPDATE=1 brew install "${formula}" - name: Test LLVM ${{ matrix.llvm }} run: go test -v -tags=llvm${{ matrix.llvm }} diff --git a/llvm_config_llvm23.go b/llvm_config_llvm23.go index ddfeb87..71e7716 100644 --- a/llvm_config_llvm23.go +++ b/llvm_config_llvm23.go @@ -2,12 +2,12 @@ package llvm -// #cgo darwin,amd64 CPPFLAGS: -I/usr/local/opt/llvm@23/include -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS +// #cgo darwin,amd64 CPPFLAGS: -I/usr/local/opt/llvm/include -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS // #cgo darwin,amd64 CXXFLAGS: -std=c++17 -// #cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@23/lib -Wl,-search_paths_first -Wl,-headerpad_max_install_names -lLLVM -lz -lm -// #cgo darwin,arm64 CPPFLAGS: -I/opt/homebrew/opt/llvm@23/include -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS +// #cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm/lib -Wl,-search_paths_first -Wl,-headerpad_max_install_names -lLLVM -lz -lm +// #cgo darwin,arm64 CPPFLAGS: -I/opt/homebrew/opt/llvm/include -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS // #cgo darwin,arm64 CXXFLAGS: -std=c++17 -// #cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@23/lib -Wl,-search_paths_first -Wl,-headerpad_max_install_names -lLLVM -lz -lm +// #cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm/lib -Wl,-search_paths_first -Wl,-headerpad_max_install_names -lLLVM -lz -lm // #cgo freebsd CPPFLAGS: -I/usr/local/llvm23/include -I/usr/local/llvm23/include/llvm-c -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS // #cgo freebsd CXXFLAGS: -std=c++17 // #cgo freebsd LDFLAGS: -L/usr/local/llvm23/lib -lLLVM From abc4ad98dd2dec1138a276bc58fd9f36f662d525 Mon Sep 17 00:00:00 2001 From: ZhouGuangyuan Date: Mon, 27 Jul 2026 01:08:44 +0800 Subject: [PATCH 8/8] ci: keep macOS matrix on available LLVM formulae --- .github/workflows/test.yml | 13 ++----------- llvm_config_llvm23.go | 8 ++++---- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 671573e..4cdd5d8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,7 +10,7 @@ jobs: runs-on: macos-latest strategy: matrix: - llvm: [14, 15, 16, 17, 18, 19, 20, 21, 22, 23] + llvm: [14, 15, 16, 17, 18, 19, 20, 21, 22] steps: - name: Checkout uses: actions/checkout@v4 @@ -22,19 +22,10 @@ jobs: - name: Update Homebrew run: brew update - name: Install LLVM - run: | - formula=llvm@${{ matrix.llvm }} - if [ "${{ matrix.llvm }}" = 23 ]; then - formula=llvm - fi - HOMEBREW_NO_AUTO_UPDATE=1 brew install "${formula}" + run: HOMEBREW_NO_AUTO_UPDATE=1 brew install llvm@${{ matrix.llvm }} - name: Test LLVM ${{ matrix.llvm }} run: go test -v -tags=llvm${{ matrix.llvm }} - - name: Test default LLVM - if: matrix.llvm == 23 - run: - go test -v test-linux: runs-on: ubuntu-22.04 strategy: diff --git a/llvm_config_llvm23.go b/llvm_config_llvm23.go index 71e7716..ddfeb87 100644 --- a/llvm_config_llvm23.go +++ b/llvm_config_llvm23.go @@ -2,12 +2,12 @@ package llvm -// #cgo darwin,amd64 CPPFLAGS: -I/usr/local/opt/llvm/include -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS +// #cgo darwin,amd64 CPPFLAGS: -I/usr/local/opt/llvm@23/include -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS // #cgo darwin,amd64 CXXFLAGS: -std=c++17 -// #cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm/lib -Wl,-search_paths_first -Wl,-headerpad_max_install_names -lLLVM -lz -lm -// #cgo darwin,arm64 CPPFLAGS: -I/opt/homebrew/opt/llvm/include -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS +// #cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/llvm@23/lib -Wl,-search_paths_first -Wl,-headerpad_max_install_names -lLLVM -lz -lm +// #cgo darwin,arm64 CPPFLAGS: -I/opt/homebrew/opt/llvm@23/include -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS // #cgo darwin,arm64 CXXFLAGS: -std=c++17 -// #cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm/lib -Wl,-search_paths_first -Wl,-headerpad_max_install_names -lLLVM -lz -lm +// #cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/llvm@23/lib -Wl,-search_paths_first -Wl,-headerpad_max_install_names -lLLVM -lz -lm // #cgo freebsd CPPFLAGS: -I/usr/local/llvm23/include -I/usr/local/llvm23/include/llvm-c -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS // #cgo freebsd CXXFLAGS: -std=c++17 // #cgo freebsd LDFLAGS: -L/usr/local/llvm23/lib -lLLVM