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
39 changes: 29 additions & 10 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -26,32 +26,51 @@ 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
- name: Test LLVM ${{ matrix.llvm }}
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 }}
14 changes: 10 additions & 4 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
26 changes: 26 additions & 0 deletions branch_llvm23.go
Original file line number Diff line number Diff line change
@@ -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
}
13 changes: 13 additions & 0 deletions branch_pre23.go
Original file line number Diff line number Diff line change
@@ -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
}
47 changes: 47 additions & 0 deletions captures_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
24 changes: 24 additions & 0 deletions deprecated.c
Original file line number Diff line number Diff line change
@@ -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
30 changes: 30 additions & 0 deletions deprecated.h
Original file line number Diff line number Diff line change
@@ -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
4 changes: 2 additions & 2 deletions gen_llvm_config_static.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ for f in ./llvm/lib/*.a; do
done

cat > "./llvm_config_static.go" <<EOF
//go:build !byollvm && !llvm14 && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm19 && !llvm20 && !llvm21
// Code generated by gen_cgo_ldflags.sh; DO NOT EDIT.
//go:build !byollvm && staticllvm
// Code generated by gen_llvm_config_static.sh; DO NOT EDIT.

package llvm
/*
Expand Down
51 changes: 20 additions & 31 deletions ir.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package llvm
#include "llvm-c/Core.h"
#include "llvm-c/Comdat.h"
#include "IRBindings.h"
#include "deprecated.h"
#include <stdlib.h>
*/
import "C"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand All @@ -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)
}

//-------------------------------------------------------------------------
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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 }
Expand Down Expand Up @@ -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 }
Expand Down Expand Up @@ -870,21 +871,15 @@ 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)
v.C = C.LLVMConstArray(t.C, ptr, nvals)
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)
Expand Down Expand Up @@ -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) }
Expand Down
2 changes: 1 addition & 1 deletion llvm_config_llvm14.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build !byollvm && llvm14
//go:build !byollvm && !staticllvm && llvm14

package llvm

Expand Down
2 changes: 1 addition & 1 deletion llvm_config_llvm15.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build !byollvm && llvm15
//go:build !byollvm && !staticllvm && llvm15

package llvm

Expand Down
2 changes: 1 addition & 1 deletion llvm_config_llvm16.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build !byollvm && llvm16
//go:build !byollvm && !staticllvm && llvm16

package llvm

Expand Down
Loading
Loading