diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8946fd3..4cdd5d8 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] + llvm: [14, 15, 16, 17, 18, 19, 20, 21, 22] 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 @@ -26,25 +26,25 @@ jobs: - 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 test-linux: 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, 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 + 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 @@ -52,6 +52,25 @@ jobs: run: go test -v -tags=llvm${{ matrix.llvm }} - name: Test default LLVM - if: matrix.llvm == 21 + if: matrix.llvm == 23 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 }} 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/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/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/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" < */ import "C" @@ -132,7 +133,6 @@ func llvmMetadataRefs(mds []Metadata) (*C.LLVMMetadataRef, C.unsigned) { const ( Ret Opcode = C.LLVMRet - Br Opcode = C.LLVMBr Switch Opcode = C.LLVMSwitch IndirectBr Opcode = C.LLVMIndirectBr Invoke Opcode = C.LLVMInvoke @@ -357,7 +357,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 +368,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 +607,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) { @@ -757,7 +747,6 @@ func (v Value) IsAPHINode() (rv Value) { rv.C = C.LLVMIsAPHINode(v.C func (v Value) IsASelectInst() (rv Value) { rv.C = C.LLVMIsASelectInst(v.C); return } func (v Value) IsAShuffleVectorInst() (rv Value) { rv.C = C.LLVMIsAShuffleVectorInst(v.C); return } func (v Value) IsAStoreInst() (rv Value) { rv.C = C.LLVMIsAStoreInst(v.C); return } -func (v Value) IsABranchInst() (rv Value) { rv.C = C.LLVMIsABranchInst(v.C); return } func (v Value) IsAInvokeInst() (rv Value) { rv.C = C.LLVMIsAInvokeInst(v.C); return } func (v Value) IsAReturnInst() (rv Value) { rv.C = C.LLVMIsAReturnInst(v.C); return } func (v Value) IsASwitchInst() (rv Value) { rv.C = C.LLVMIsASwitchInst(v.C); return } @@ -792,6 +781,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 } @@ -870,11 +871,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) @@ -882,9 +879,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) @@ -1191,16 +1186,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/llvm_config_llvm14.go b/llvm_config_llvm14.go index 9d38a5b..154b4f5 100644 --- a/llvm_config_llvm14.go +++ b/llvm_config_llvm14.go @@ -1,4 +1,4 @@ -//go:build !byollvm && llvm14 +//go:build !byollvm && !staticllvm && llvm14 package llvm diff --git a/llvm_config_llvm15.go b/llvm_config_llvm15.go index 34aebc0..bf4c451 100644 --- a/llvm_config_llvm15.go +++ b/llvm_config_llvm15.go @@ -1,4 +1,4 @@ -//go:build !byollvm && llvm15 +//go:build !byollvm && !staticllvm && llvm15 package llvm diff --git a/llvm_config_llvm16.go b/llvm_config_llvm16.go index 936ca0f..6d742ed 100644 --- a/llvm_config_llvm16.go +++ b/llvm_config_llvm16.go @@ -1,4 +1,4 @@ -//go:build !byollvm && llvm16 +//go:build !byollvm && !staticllvm && llvm16 package llvm diff --git a/llvm_config_llvm17.go b/llvm_config_llvm17.go index 6a00b39..58a377c 100644 --- a/llvm_config_llvm17.go +++ b/llvm_config_llvm17.go @@ -1,4 +1,4 @@ -//go:build !byollvm && llvm17 +//go:build !byollvm && !staticllvm && llvm17 package llvm diff --git a/llvm_config_llvm18.go b/llvm_config_llvm18.go index c41fea0..7a706bf 100644 --- a/llvm_config_llvm18.go +++ b/llvm_config_llvm18.go @@ -1,4 +1,4 @@ -//go:build !byollvm && llvm18 +//go:build !byollvm && !staticllvm && llvm18 package llvm diff --git a/llvm_config_llvm19.go b/llvm_config_llvm19.go index e540f57..bbe44d8 100644 --- a/llvm_config_llvm19.go +++ b/llvm_config_llvm19.go @@ -1,4 +1,4 @@ -//go:build !byollvm && llvm19 +//go:build !byollvm && !staticllvm && llvm19 package llvm @@ -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..224418c 100644 --- a/llvm_config_llvm20.go +++ b/llvm_config_llvm20.go @@ -1,4 +1,4 @@ -//go:build !byollvm && llvm20 +//go:build !byollvm && !staticllvm && llvm20 package llvm @@ -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 diff --git a/llvm_config_llvm21.go b/llvm_config_llvm21.go index 263c2fc..de236b4 100644 --- a/llvm_config_llvm21.go +++ b/llvm_config_llvm21.go @@ -1,4 +1,4 @@ -//go:build !byollvm && llvm21 +//go:build !byollvm && !staticllvm && llvm21 package llvm diff --git a/llvm_config_llvm22.go b/llvm_config_llvm22.go new file mode 100644 index 0000000..e468b61 --- /dev/null +++ b/llvm_config_llvm22.go @@ -0,0 +1,19 @@ +//go:build !byollvm && !staticllvm && 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/llvm_config_llvm23.go b/llvm_config_llvm23.go new file mode 100644 index 0000000..ddfeb87 --- /dev/null +++ b/llvm_config_llvm23.go @@ -0,0 +1,19 @@ +//go:build !byollvm && !staticllvm && !llvm14 && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm19 && !llvm20 && !llvm21 && !llvm22 + +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 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,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 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 +// #cgo linux CPPFLAGS: -I/usr/include/llvm-23 -I/usr/include/llvm-c-23 -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-23/lib -lLLVM-23 +import "C" + +type run_build_sh int diff --git a/llvm_config_static.go b/llvm_config_static.go index ef7a766..24e172d 100644 --- a/llvm_config_static.go +++ b/llvm_config_static.go @@ -1,6 +1,6 @@ -//go:build !byollvm && !llvm14 && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm19 && !llvm20 && !llvm21 +//go:build !byollvm && staticllvm -// Code generated by gen_cgo_ldflags.sh; DO NOT EDIT. +// Code generated by gen_llvm_config_static.sh; DO NOT EDIT. package llvm diff --git a/opcode_llvm23.go b/opcode_llvm23.go new file mode 100644 index 0000000..f126e80 --- /dev/null +++ b/opcode_llvm23.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" + +const ( + UncondBr Opcode = C.LLVMUncondBr + CondBr Opcode = C.LLVMCondBr +) diff --git a/opcode_pre23.go b/opcode_pre23.go new file mode 100644 index 0000000..9a32080 --- /dev/null +++ b/opcode_pre23.go @@ -0,0 +1,10 @@ +//go:build !llvm23 && (byollvm || llvm14 || llvm15 || llvm16 || llvm17 || llvm18 || llvm19 || llvm20 || llvm21 || llvm22) + +package llvm + +/* +#include "llvm-c/Core.h" +*/ +import "C" + +const Br Opcode = C.LLVMBr diff --git a/passes_llvm23_test.go b/passes_llvm23_test.go new file mode 100644 index 0000000..c348317 --- /dev/null +++ b/passes_llvm23_test.go @@ -0,0 +1,10 @@ +//go:build llvm23 || (!byollvm && !llvm14 && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm19 && !llvm20 && !llvm21 && !llvm22) + +package llvm + +// LLVM 23 removed default. 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 new file mode 100644 index 0000000..8df6966 --- /dev/null +++ b/switch_llvm22.go @@ -0,0 +1,21 @@ +//go:build llvm22 || llvm23 || (!byollvm && !llvm14 && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm19 && !llvm20 && !llvm21 && !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..6bbfac9 --- /dev/null +++ b/switch_pre22.go @@ -0,0 +1,15 @@ +//go:build !llvm22 && !llvm23 && (byollvm || llvm14 || llvm15 || llvm16 || llvm17 || llvm18 || llvm19 || llvm20 || llvm21) + +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) + } + } +} 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.