Python bindings for QBE (Quite Bare Engine) compiler backend
Project description
qbepy
Python bindings for QBE (Quite Bare Engine), a minimalist compiler backend.
QBE is a small, fast compiler backend that takes an SSA-based intermediate language (IL) and produces native machine code for multiple architectures. qbepy provides Python bindings via CFFI, allowing you to compile QBE IL directly from Python without spawning subprocesses.
Features
- Direct FFI bindings - No subprocess overhead; QBE is compiled as a Python extension
- Multiple targets - Supports amd64 (System V and Apple ABIs), ARM64, and RISC-V 64
- Pythonic IR builder - Construct QBE IL programmatically with a clean API
- Error handling - QBE errors are raised as Python exceptions
- Vendored QBE - QBE source is included and built automatically during installation
Installation
pip install qbepy
# or
uv add qbepy
Or from source:
git clone https://github.com/user/qbepy.git
cd qbepy
uv sync
Quick Start
Compiling Raw IL
import qbepy
# QBE IL for a simple add function
il = """
export function w $add(w %a, w %b) {
@start
%r =w add %a, %b
ret %r
}
"""
# Compile to assembly
asm = qbepy.compile_il(il)
print(asm)
Output (ARM64 Apple):
.text
.balign 4
.globl _add
_add:
hint #34
stp x29, x30, [sp, -16]!
mov x29, sp
add w0, w0, w1
ldp x29, x30, [sp], 16
ret
Using the IR Builder
import qbepy
from qbepy import Module, Function, DataDef, W, L
from qbepy.ir import BinaryOp, Call, Return, Temporary, Global, IntConst
# Create a module
mod = Module()
# Add a string constant
mod.add_data(
DataDef("greeting")
.add_string("Hello, World!")
.add_bytes(0) # null terminator
)
# Create main function
func = Function("main", W, export=True)
block = func.add_block("start")
# Call puts($greeting)
r = func.new_temp("r")
block.instructions.append(
Call(Global("puts"), [(L, Global("greeting"))], r, W)
)
block.terminator = Return(IntConst(0))
mod.add_function(func)
# Compile to assembly
asm = qbepy.compile_module(mod)
print(asm)
Specifying a Target
# Compile for x86-64 System V ABI
asm = qbepy.compile_il(il, target="amd64_sysv")
# Or use the Compiler class
compiler = qbepy.Compiler(target="arm64")
asm = compiler.compile(il)
# Available targets
print(qbepy.Compiler.get_available_targets())
# ['amd64_sysv', 'amd64_apple', 'arm64', 'arm64_apple', 'rv64']
# Get default target for current platform
print(qbepy.Compiler.get_default_target())
# 'arm64_apple' (on Apple Silicon)
API Reference
Compiler
from qbepy import Compiler, compile_il, compile_module
# Create a compiler instance
compiler = Compiler(target=None) # None = platform default
# Compile IL string to assembly
asm = compiler.compile(il_string)
# Compile a Module object
asm = compiler.compile_module(module)
# Change target
compiler.set_target("amd64_sysv")
# Convenience functions
asm = compile_il(il_string, target=None)
asm = compile_module(module, target=None)
Types
from qbepy import W, L, S, D, BaseType, ExtType, AggregateType
# Base types
W # word (32-bit integer)
L # long (64-bit integer)
S # single (32-bit float)
D # double (64-bit float)
# Extended types (for memory operations)
ExtType.BYTE, ExtType.HALF, ExtType.WORD, ExtType.LONG
ExtType.SINGLE, ExtType.DOUBLE
# Aggregate types (structs)
point = AggregateType("point", [
(ExtType.WORD, 1), # x: 1 word
(ExtType.WORD, 1), # y: 1 word
])
Values
from qbepy.ir import Temporary, Global, Label, IntConst, FloatConst
Temporary("x") # %x - SSA temporary
Global("main") # $main - global symbol
Label("loop") # @loop - block label
IntConst(42) # 42 - integer constant
FloatConst(3.14) # d_3.14 - double constant
FloatConst(3.14, is_single=True) # s_3.14 - float constant
Instructions
from qbepy.ir import (
BinaryOp, # add, sub, mul, div, rem, or, xor, and, sar, shr, shl
UnaryOp, # neg, copy
Copy, # copy value
Load, # load from memory
Store, # store to memory
Alloc, # stack allocation
Call, # function call
Comparison, # ceqw, cnew, csltw, etc.
Conversion, # extsw, truncd, stosi, cast, etc.
Phi, # SSA phi node
)
# Examples
BinaryOp("add", Temporary("r"), W, Temporary("a"), Temporary("b"))
Load(Temporary("v"), W, Temporary("ptr"))
Store("storew", Temporary("v"), Temporary("ptr"))
Call(Global("printf"), [(L, Global("fmt"))], Temporary("r"), W)
Control Flow
from qbepy.ir import Jump, Branch, Return, Halt
Jump(Label("next")) # jmp @next
Branch(Temporary("c"), Label("t"), Label("f")) # jnz %c, @t, @f
Return(Temporary("r")) # ret %r
Return(IntConst(0)) # ret 0
Return() # ret (void)
Halt() # hlt (unreachable)
Building Modules
from qbepy import Module, Function, Block, DataDef
# Module - container for types, data, and functions
mod = Module()
mod.add_type(aggregate_type)
mod.add_data(data_def)
mod.add_function(function)
# Function
func = Function("name", return_type, params=[(W, "a"), (L, "b")], export=True)
block = func.add_block("start")
temp = func.new_temp("x") # creates unique temporary
# Block
block.instructions.append(instruction)
block.terminator = Return(value)
# DataDef
data = (DataDef("name", export=True, align=8)
.add_string("hello")
.add_bytes(0)
.add_words(1, 2, 3)
.add_longs(0x1234567890)
.add_zero(16))
Supported Targets
| Target | Description |
|---|---|
amd64_sysv |
x86-64 with System V ABI (Linux, BSD) |
amd64_apple |
x86-64 with Apple ABI (macOS Intel) |
arm64 |
ARM64 with standard ABI (Linux) |
arm64_apple |
ARM64 with Apple ABI (macOS Apple Silicon) |
rv64 |
RISC-V 64-bit |
QBE IL Reference
QBE uses a simple SSA-based intermediate language. For the complete specification, see the QBE IL documentation.
Basic IL Structure
# Type definitions
type :point = { w, w }
# Data definitions
data $message = { b "Hello", b 0 }
# Function definitions
export function w $main() {
@start
%x =w copy 42
ret %x
}
IL Basics
- Temporaries:
%name(SSA values) - Globals:
$name(functions and data) - Labels:
@name(basic blocks) - Types:
w(word),l(long),s(single),d(double)
Error Handling
from qbepy import CompilationError, compile_il
try:
asm = compile_il("invalid IL code")
except CompilationError as e:
print(f"Compilation failed: {e}")
Project Structure
qbepy/
├── src/qbepy/
│ ├── __init__.py # Public API exports
│ ├── _ffi.py # Low-level CFFI bindings
│ ├── compiler.py # Compiler class
│ ├── errors.py # Exception types
│ └── ir/ # IR builder module
│ ├── types.py # Type definitions
│ ├── values.py # Value types
│ ├── instructions.py # Instructions
│ ├── control.py # Control flow
│ └── builder.py # Module, Function, Block, DataDef
├── csrc/
│ ├── qbepy_wrapper.c # C wrapper with error handling
│ └── qbepy_wrapper.h
├── vendor/qbe/ # Vendored QBE source
├── build_ffi.py # CFFI build script
└── tests/ # Test suite
How It Works
qbepy vendors the QBE compiler source and builds it as a Python extension using CFFI. The main challenge is that QBE's error handling uses exit(), which would terminate the Python process. qbepy solves this by:
- Redirecting QBE's
err()function to a custom handler using preprocessor macros - Using
setjmp/longjmpto catch errors and return control to Python - Converting errors to Python exceptions
This allows QBE to be used as a library rather than a standalone compiler.
License
qbepy is released under the MIT License.
QBE is developed by Quentin Carbonneaux and is also MIT licensed. See vendor/qbe/LICENSE.
Credits
See Also
- QBE Documentation - Complete IL specification
- cproc - A C11 compiler using QBE as backend
- Hare - A systems programming language using QBE
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file qbepy-2026.2.1.tar.gz.
File metadata
- Download URL: qbepy-2026.2.1.tar.gz
- Upload date:
- Size: 130.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3b75604935c6670c5b6027e05cde80e9d72dfe78af4e646a047a1655ad309ec5
|
|
| MD5 |
f60326fa5ce938f28718db4aa5c47683
|
|
| BLAKE2b-256 |
ea9f09cb6384f1957ad3d79e27c524832574e9a5da7890d27456629e5d9574be
|
File details
Details for the file qbepy-2026.2.1-cp312-cp312-macosx_14_0_arm64.whl.
File metadata
- Download URL: qbepy-2026.2.1-cp312-cp312-macosx_14_0_arm64.whl
- Upload date:
- Size: 116.5 kB
- Tags: CPython 3.12, macOS 14.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8203dda423bb003443b53cae4dff210863880d98795d1eef2399ffe1f420f8d7
|
|
| MD5 |
1a1547d9035657fd8e0d776323be7671
|
|
| BLAKE2b-256 |
b38a761e91240d3106a6c9c7f9ce9bf7e510f75eca4034bd9e631be6863aeecd
|