Skip to main content

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:

  1. Redirecting QBE's err() function to a custom handler using preprocessor macros
  2. Using setjmp/longjmp to catch errors and return control to Python
  3. 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

  • QBE - The compiler backend by Quentin Carbonneaux
  • CFFI - C Foreign Function Interface for Python

See Also

  • QBE Documentation - Complete IL specification
  • cproc - A C11 compiler using QBE as backend
  • Hare - A systems programming language using QBE

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

qbepy-2026.2.1.tar.gz (130.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

qbepy-2026.2.1-cp312-cp312-macosx_14_0_arm64.whl (116.5 kB view details)

Uploaded CPython 3.12macOS 14.0+ ARM64

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

Hashes for qbepy-2026.2.1.tar.gz
Algorithm Hash digest
SHA256 3b75604935c6670c5b6027e05cde80e9d72dfe78af4e646a047a1655ad309ec5
MD5 f60326fa5ce938f28718db4aa5c47683
BLAKE2b-256 ea9f09cb6384f1957ad3d79e27c524832574e9a5da7890d27456629e5d9574be

See more details on using hashes here.

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

Hashes for qbepy-2026.2.1-cp312-cp312-macosx_14_0_arm64.whl
Algorithm Hash digest
SHA256 8203dda423bb003443b53cae4dff210863880d98795d1eef2399ffe1f420f8d7
MD5 1a1547d9035657fd8e0d776323be7671
BLAKE2b-256 b38a761e91240d3106a6c9c7f9ce9bf7e510f75eca4034bd9e631be6863aeecd

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page