qbepy 2026.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qbepy-2026.2.1/MANIFEST.in +27 -0
- qbepy-2026.2.1/PKG-INFO +359 -0
- qbepy-2026.2.1/README.md +341 -0
- qbepy-2026.2.1/build_ffi.py +96 -0
- qbepy-2026.2.1/csrc/qbepy_wrapper.c +411 -0
- qbepy-2026.2.1/csrc/qbepy_wrapper.h +80 -0
- qbepy-2026.2.1/pyproject.toml +53 -0
- qbepy-2026.2.1/setup.cfg +4 -0
- qbepy-2026.2.1/setup.py +7 -0
- qbepy-2026.2.1/src/qbepy/__init__.py +85 -0
- qbepy-2026.2.1/src/qbepy/_ffi.py +66 -0
- qbepy-2026.2.1/src/qbepy/compiler.py +164 -0
- qbepy-2026.2.1/src/qbepy/errors.py +20 -0
- qbepy-2026.2.1/src/qbepy/ir/__init__.py +73 -0
- qbepy-2026.2.1/src/qbepy/ir/builder.py +259 -0
- qbepy-2026.2.1/src/qbepy/ir/control.py +60 -0
- qbepy-2026.2.1/src/qbepy/ir/instructions.py +238 -0
- qbepy-2026.2.1/src/qbepy/ir/types.py +84 -0
- qbepy-2026.2.1/src/qbepy/ir/values.py +76 -0
- qbepy-2026.2.1/src/qbepy.egg-info/PKG-INFO +359 -0
- qbepy-2026.2.1/src/qbepy.egg-info/SOURCES.txt +83 -0
- qbepy-2026.2.1/src/qbepy.egg-info/dependency_links.txt +1 -0
- qbepy-2026.2.1/src/qbepy.egg-info/requires.txt +1 -0
- qbepy-2026.2.1/src/qbepy.egg-info/top_level.txt +1 -0
- qbepy-2026.2.1/tests/a_unit/__init__.py +0 -0
- qbepy-2026.2.1/tests/a_unit/test_builder.py +218 -0
- qbepy-2026.2.1/tests/a_unit/test_control.py +84 -0
- qbepy-2026.2.1/tests/a_unit/test_instructions.py +207 -0
- qbepy-2026.2.1/tests/a_unit/test_types.py +75 -0
- qbepy-2026.2.1/tests/a_unit/test_values.py +94 -0
- qbepy-2026.2.1/tests/b_integration/__init__.py +0 -0
- qbepy-2026.2.1/tests/b_integration/test_compile_module.py +85 -0
- qbepy-2026.2.1/tests/b_integration/test_compiler.py +209 -0
- qbepy-2026.2.1/tests/c_e2e/__init__.py +0 -0
- qbepy-2026.2.1/tests/c_e2e/test_targets.py +202 -0
- qbepy-2026.2.1/tests/conftest.py +44 -0
- qbepy-2026.2.1/vendor/qbe/LICENSE +19 -0
- qbepy-2026.2.1/vendor/qbe/README +18 -0
- qbepy-2026.2.1/vendor/qbe/abi.c +25 -0
- qbepy-2026.2.1/vendor/qbe/alias.c +222 -0
- qbepy-2026.2.1/vendor/qbe/all.h +629 -0
- qbepy-2026.2.1/vendor/qbe/amd64/all.h +70 -0
- qbepy-2026.2.1/vendor/qbe/amd64/emit.c +726 -0
- qbepy-2026.2.1/vendor/qbe/amd64/isel.c +942 -0
- qbepy-2026.2.1/vendor/qbe/amd64/sysv.c +721 -0
- qbepy-2026.2.1/vendor/qbe/amd64/targ.c +47 -0
- qbepy-2026.2.1/vendor/qbe/arm64/abi.c +852 -0
- qbepy-2026.2.1/vendor/qbe/arm64/all.h +38 -0
- qbepy-2026.2.1/vendor/qbe/arm64/emit.c +679 -0
- qbepy-2026.2.1/vendor/qbe/arm64/isel.c +316 -0
- qbepy-2026.2.1/vendor/qbe/arm64/targ.c +69 -0
- qbepy-2026.2.1/vendor/qbe/cfg.c +567 -0
- qbepy-2026.2.1/vendor/qbe/config.h +1 -0
- qbepy-2026.2.1/vendor/qbe/copy.c +408 -0
- qbepy-2026.2.1/vendor/qbe/emit.c +263 -0
- qbepy-2026.2.1/vendor/qbe/fold.c +246 -0
- qbepy-2026.2.1/vendor/qbe/gcm.c +460 -0
- qbepy-2026.2.1/vendor/qbe/gvn.c +508 -0
- qbepy-2026.2.1/vendor/qbe/ifopt.c +121 -0
- qbepy-2026.2.1/vendor/qbe/live.c +144 -0
- qbepy-2026.2.1/vendor/qbe/load.c +493 -0
- qbepy-2026.2.1/vendor/qbe/main.c +210 -0
- qbepy-2026.2.1/vendor/qbe/mem.c +488 -0
- qbepy-2026.2.1/vendor/qbe/minic/test/collatz.c +33 -0
- qbepy-2026.2.1/vendor/qbe/minic/test/euler9.c +26 -0
- qbepy-2026.2.1/vendor/qbe/minic/test/knight.c +60 -0
- qbepy-2026.2.1/vendor/qbe/minic/test/mandel.c +88 -0
- qbepy-2026.2.1/vendor/qbe/minic/test/prime.c +28 -0
- qbepy-2026.2.1/vendor/qbe/minic/test/queen.c +70 -0
- qbepy-2026.2.1/vendor/qbe/minic/yacc.c +1378 -0
- qbepy-2026.2.1/vendor/qbe/ops.h +228 -0
- qbepy-2026.2.1/vendor/qbe/parse.c +1442 -0
- qbepy-2026.2.1/vendor/qbe/rega.c +696 -0
- qbepy-2026.2.1/vendor/qbe/rv64/abi.c +653 -0
- qbepy-2026.2.1/vendor/qbe/rv64/all.h +52 -0
- qbepy-2026.2.1/vendor/qbe/rv64/emit.c +569 -0
- qbepy-2026.2.1/vendor/qbe/rv64/isel.c +255 -0
- qbepy-2026.2.1/vendor/qbe/rv64/targ.c +57 -0
- qbepy-2026.2.1/vendor/qbe/simpl.c +124 -0
- qbepy-2026.2.1/vendor/qbe/spill.c +531 -0
- qbepy-2026.2.1/vendor/qbe/ssa.c +433 -0
- qbepy-2026.2.1/vendor/qbe/tools/lexh.c +94 -0
- qbepy-2026.2.1/vendor/qbe/tools/log2.c +64 -0
- qbepy-2026.2.1/vendor/qbe/tools/pmov.c +262 -0
- qbepy-2026.2.1/vendor/qbe/util.c +795 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Include build script
|
|
2
|
+
include build_ffi.py
|
|
3
|
+
include setup.py
|
|
4
|
+
|
|
5
|
+
# Include C source files
|
|
6
|
+
include csrc/*.c
|
|
7
|
+
include csrc/*.h
|
|
8
|
+
|
|
9
|
+
# Include vendored QBE source
|
|
10
|
+
recursive-include vendor/qbe *.c
|
|
11
|
+
recursive-include vendor/qbe *.h
|
|
12
|
+
include vendor/qbe/LICENSE
|
|
13
|
+
include vendor/qbe/README
|
|
14
|
+
include vendor/qbe/ops.h
|
|
15
|
+
include vendor/qbe/config.h
|
|
16
|
+
|
|
17
|
+
# Include tests
|
|
18
|
+
recursive-include tests *.py
|
|
19
|
+
include tests/conftest.py
|
|
20
|
+
|
|
21
|
+
# Exclude unnecessary files
|
|
22
|
+
global-exclude *.pyc
|
|
23
|
+
global-exclude *.pyo
|
|
24
|
+
global-exclude __pycache__
|
|
25
|
+
global-exclude *.so
|
|
26
|
+
global-exclude *.o
|
|
27
|
+
global-exclude .git*
|
qbepy-2026.2.1/PKG-INFO
ADDED
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: qbepy
|
|
3
|
+
Version: 2026.2.1
|
|
4
|
+
Summary: Python bindings for QBE (Quite Bare Engine) compiler backend
|
|
5
|
+
Author-email: Stefane Fermigier <sf@abilian.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://git.sr.ht/~sfermigier/qbepy
|
|
8
|
+
Project-URL: Repository, https://git.sr.ht/~sfermigier/qbepy
|
|
9
|
+
Keywords: compiler,qbe,code-generation,assembly
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Topic :: Software Development :: Compilers
|
|
15
|
+
Requires-Python: >=3.12
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
Requires-Dist: cffi>=1.16.0
|
|
18
|
+
|
|
19
|
+
# qbepy
|
|
20
|
+
|
|
21
|
+
Python bindings for [QBE](https://c9x.me/compile/) (Quite Bare Engine), a minimalist compiler backend.
|
|
22
|
+
|
|
23
|
+
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.
|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
|
|
27
|
+
- **Direct FFI bindings** - No subprocess overhead; QBE is compiled as a Python extension
|
|
28
|
+
- **Multiple targets** - Supports amd64 (System V and Apple ABIs), ARM64, and RISC-V 64
|
|
29
|
+
- **Pythonic IR builder** - Construct QBE IL programmatically with a clean API
|
|
30
|
+
- **Error handling** - QBE errors are raised as Python exceptions
|
|
31
|
+
- **Vendored QBE** - QBE source is included and built automatically during installation
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install qbepy
|
|
37
|
+
# or
|
|
38
|
+
uv add qbepy
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Or from source:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
git clone https://github.com/user/qbepy.git
|
|
45
|
+
cd qbepy
|
|
46
|
+
uv sync
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Quick Start
|
|
50
|
+
|
|
51
|
+
### Compiling Raw IL
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
import qbepy
|
|
55
|
+
|
|
56
|
+
# QBE IL for a simple add function
|
|
57
|
+
il = """
|
|
58
|
+
export function w $add(w %a, w %b) {
|
|
59
|
+
@start
|
|
60
|
+
%r =w add %a, %b
|
|
61
|
+
ret %r
|
|
62
|
+
}
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
# Compile to assembly
|
|
66
|
+
asm = qbepy.compile_il(il)
|
|
67
|
+
print(asm)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Output (ARM64 Apple):
|
|
71
|
+
```asm
|
|
72
|
+
.text
|
|
73
|
+
.balign 4
|
|
74
|
+
.globl _add
|
|
75
|
+
_add:
|
|
76
|
+
hint #34
|
|
77
|
+
stp x29, x30, [sp, -16]!
|
|
78
|
+
mov x29, sp
|
|
79
|
+
add w0, w0, w1
|
|
80
|
+
ldp x29, x30, [sp], 16
|
|
81
|
+
ret
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Using the IR Builder
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
import qbepy
|
|
88
|
+
from qbepy import Module, Function, DataDef, W, L
|
|
89
|
+
from qbepy.ir import BinaryOp, Call, Return, Temporary, Global, IntConst
|
|
90
|
+
|
|
91
|
+
# Create a module
|
|
92
|
+
mod = Module()
|
|
93
|
+
|
|
94
|
+
# Add a string constant
|
|
95
|
+
mod.add_data(
|
|
96
|
+
DataDef("greeting")
|
|
97
|
+
.add_string("Hello, World!")
|
|
98
|
+
.add_bytes(0) # null terminator
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# Create main function
|
|
102
|
+
func = Function("main", W, export=True)
|
|
103
|
+
block = func.add_block("start")
|
|
104
|
+
|
|
105
|
+
# Call puts($greeting)
|
|
106
|
+
r = func.new_temp("r")
|
|
107
|
+
block.instructions.append(
|
|
108
|
+
Call(Global("puts"), [(L, Global("greeting"))], r, W)
|
|
109
|
+
)
|
|
110
|
+
block.terminator = Return(IntConst(0))
|
|
111
|
+
|
|
112
|
+
mod.add_function(func)
|
|
113
|
+
|
|
114
|
+
# Compile to assembly
|
|
115
|
+
asm = qbepy.compile_module(mod)
|
|
116
|
+
print(asm)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Specifying a Target
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
# Compile for x86-64 System V ABI
|
|
123
|
+
asm = qbepy.compile_il(il, target="amd64_sysv")
|
|
124
|
+
|
|
125
|
+
# Or use the Compiler class
|
|
126
|
+
compiler = qbepy.Compiler(target="arm64")
|
|
127
|
+
asm = compiler.compile(il)
|
|
128
|
+
|
|
129
|
+
# Available targets
|
|
130
|
+
print(qbepy.Compiler.get_available_targets())
|
|
131
|
+
# ['amd64_sysv', 'amd64_apple', 'arm64', 'arm64_apple', 'rv64']
|
|
132
|
+
|
|
133
|
+
# Get default target for current platform
|
|
134
|
+
print(qbepy.Compiler.get_default_target())
|
|
135
|
+
# 'arm64_apple' (on Apple Silicon)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## API Reference
|
|
139
|
+
|
|
140
|
+
### Compiler
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
from qbepy import Compiler, compile_il, compile_module
|
|
144
|
+
|
|
145
|
+
# Create a compiler instance
|
|
146
|
+
compiler = Compiler(target=None) # None = platform default
|
|
147
|
+
|
|
148
|
+
# Compile IL string to assembly
|
|
149
|
+
asm = compiler.compile(il_string)
|
|
150
|
+
|
|
151
|
+
# Compile a Module object
|
|
152
|
+
asm = compiler.compile_module(module)
|
|
153
|
+
|
|
154
|
+
# Change target
|
|
155
|
+
compiler.set_target("amd64_sysv")
|
|
156
|
+
|
|
157
|
+
# Convenience functions
|
|
158
|
+
asm = compile_il(il_string, target=None)
|
|
159
|
+
asm = compile_module(module, target=None)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Types
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
from qbepy import W, L, S, D, BaseType, ExtType, AggregateType
|
|
166
|
+
|
|
167
|
+
# Base types
|
|
168
|
+
W # word (32-bit integer)
|
|
169
|
+
L # long (64-bit integer)
|
|
170
|
+
S # single (32-bit float)
|
|
171
|
+
D # double (64-bit float)
|
|
172
|
+
|
|
173
|
+
# Extended types (for memory operations)
|
|
174
|
+
ExtType.BYTE, ExtType.HALF, ExtType.WORD, ExtType.LONG
|
|
175
|
+
ExtType.SINGLE, ExtType.DOUBLE
|
|
176
|
+
|
|
177
|
+
# Aggregate types (structs)
|
|
178
|
+
point = AggregateType("point", [
|
|
179
|
+
(ExtType.WORD, 1), # x: 1 word
|
|
180
|
+
(ExtType.WORD, 1), # y: 1 word
|
|
181
|
+
])
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Values
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
from qbepy.ir import Temporary, Global, Label, IntConst, FloatConst
|
|
188
|
+
|
|
189
|
+
Temporary("x") # %x - SSA temporary
|
|
190
|
+
Global("main") # $main - global symbol
|
|
191
|
+
Label("loop") # @loop - block label
|
|
192
|
+
IntConst(42) # 42 - integer constant
|
|
193
|
+
FloatConst(3.14) # d_3.14 - double constant
|
|
194
|
+
FloatConst(3.14, is_single=True) # s_3.14 - float constant
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Instructions
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
from qbepy.ir import (
|
|
201
|
+
BinaryOp, # add, sub, mul, div, rem, or, xor, and, sar, shr, shl
|
|
202
|
+
UnaryOp, # neg, copy
|
|
203
|
+
Copy, # copy value
|
|
204
|
+
Load, # load from memory
|
|
205
|
+
Store, # store to memory
|
|
206
|
+
Alloc, # stack allocation
|
|
207
|
+
Call, # function call
|
|
208
|
+
Comparison, # ceqw, cnew, csltw, etc.
|
|
209
|
+
Conversion, # extsw, truncd, stosi, cast, etc.
|
|
210
|
+
Phi, # SSA phi node
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
# Examples
|
|
214
|
+
BinaryOp("add", Temporary("r"), W, Temporary("a"), Temporary("b"))
|
|
215
|
+
Load(Temporary("v"), W, Temporary("ptr"))
|
|
216
|
+
Store("storew", Temporary("v"), Temporary("ptr"))
|
|
217
|
+
Call(Global("printf"), [(L, Global("fmt"))], Temporary("r"), W)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Control Flow
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
from qbepy.ir import Jump, Branch, Return, Halt
|
|
224
|
+
|
|
225
|
+
Jump(Label("next")) # jmp @next
|
|
226
|
+
Branch(Temporary("c"), Label("t"), Label("f")) # jnz %c, @t, @f
|
|
227
|
+
Return(Temporary("r")) # ret %r
|
|
228
|
+
Return(IntConst(0)) # ret 0
|
|
229
|
+
Return() # ret (void)
|
|
230
|
+
Halt() # hlt (unreachable)
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Building Modules
|
|
234
|
+
|
|
235
|
+
```python
|
|
236
|
+
from qbepy import Module, Function, Block, DataDef
|
|
237
|
+
|
|
238
|
+
# Module - container for types, data, and functions
|
|
239
|
+
mod = Module()
|
|
240
|
+
mod.add_type(aggregate_type)
|
|
241
|
+
mod.add_data(data_def)
|
|
242
|
+
mod.add_function(function)
|
|
243
|
+
|
|
244
|
+
# Function
|
|
245
|
+
func = Function("name", return_type, params=[(W, "a"), (L, "b")], export=True)
|
|
246
|
+
block = func.add_block("start")
|
|
247
|
+
temp = func.new_temp("x") # creates unique temporary
|
|
248
|
+
|
|
249
|
+
# Block
|
|
250
|
+
block.instructions.append(instruction)
|
|
251
|
+
block.terminator = Return(value)
|
|
252
|
+
|
|
253
|
+
# DataDef
|
|
254
|
+
data = (DataDef("name", export=True, align=8)
|
|
255
|
+
.add_string("hello")
|
|
256
|
+
.add_bytes(0)
|
|
257
|
+
.add_words(1, 2, 3)
|
|
258
|
+
.add_longs(0x1234567890)
|
|
259
|
+
.add_zero(16))
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## Supported Targets
|
|
263
|
+
|
|
264
|
+
| Target | Description |
|
|
265
|
+
|--------|-------------|
|
|
266
|
+
| `amd64_sysv` | x86-64 with System V ABI (Linux, BSD) |
|
|
267
|
+
| `amd64_apple` | x86-64 with Apple ABI (macOS Intel) |
|
|
268
|
+
| `arm64` | ARM64 with standard ABI (Linux) |
|
|
269
|
+
| `arm64_apple` | ARM64 with Apple ABI (macOS Apple Silicon) |
|
|
270
|
+
| `rv64` | RISC-V 64-bit |
|
|
271
|
+
|
|
272
|
+
## QBE IL Reference
|
|
273
|
+
|
|
274
|
+
QBE uses a simple SSA-based intermediate language. For the complete specification, see the [QBE IL documentation](https://c9x.me/compile/doc/il.html).
|
|
275
|
+
|
|
276
|
+
### Basic IL Structure
|
|
277
|
+
|
|
278
|
+
```
|
|
279
|
+
# Type definitions
|
|
280
|
+
type :point = { w, w }
|
|
281
|
+
|
|
282
|
+
# Data definitions
|
|
283
|
+
data $message = { b "Hello", b 0 }
|
|
284
|
+
|
|
285
|
+
# Function definitions
|
|
286
|
+
export function w $main() {
|
|
287
|
+
@start
|
|
288
|
+
%x =w copy 42
|
|
289
|
+
ret %x
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### IL Basics
|
|
294
|
+
|
|
295
|
+
- Temporaries: `%name` (SSA values)
|
|
296
|
+
- Globals: `$name` (functions and data)
|
|
297
|
+
- Labels: `@name` (basic blocks)
|
|
298
|
+
- Types: `w` (word), `l` (long), `s` (single), `d` (double)
|
|
299
|
+
|
|
300
|
+
## Error Handling
|
|
301
|
+
|
|
302
|
+
```python
|
|
303
|
+
from qbepy import CompilationError, compile_il
|
|
304
|
+
|
|
305
|
+
try:
|
|
306
|
+
asm = compile_il("invalid IL code")
|
|
307
|
+
except CompilationError as e:
|
|
308
|
+
print(f"Compilation failed: {e}")
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Project Structure
|
|
312
|
+
|
|
313
|
+
```
|
|
314
|
+
qbepy/
|
|
315
|
+
├── src/qbepy/
|
|
316
|
+
│ ├── __init__.py # Public API exports
|
|
317
|
+
│ ├── _ffi.py # Low-level CFFI bindings
|
|
318
|
+
│ ├── compiler.py # Compiler class
|
|
319
|
+
│ ├── errors.py # Exception types
|
|
320
|
+
│ └── ir/ # IR builder module
|
|
321
|
+
│ ├── types.py # Type definitions
|
|
322
|
+
│ ├── values.py # Value types
|
|
323
|
+
│ ├── instructions.py # Instructions
|
|
324
|
+
│ ├── control.py # Control flow
|
|
325
|
+
│ └── builder.py # Module, Function, Block, DataDef
|
|
326
|
+
├── csrc/
|
|
327
|
+
│ ├── qbepy_wrapper.c # C wrapper with error handling
|
|
328
|
+
│ └── qbepy_wrapper.h
|
|
329
|
+
├── vendor/qbe/ # Vendored QBE source
|
|
330
|
+
├── build_ffi.py # CFFI build script
|
|
331
|
+
└── tests/ # Test suite
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
## How It Works
|
|
335
|
+
|
|
336
|
+
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:
|
|
337
|
+
|
|
338
|
+
1. Redirecting QBE's `err()` function to a custom handler using preprocessor macros
|
|
339
|
+
2. Using `setjmp`/`longjmp` to catch errors and return control to Python
|
|
340
|
+
3. Converting errors to Python exceptions
|
|
341
|
+
|
|
342
|
+
This allows QBE to be used as a library rather than a standalone compiler.
|
|
343
|
+
|
|
344
|
+
## License
|
|
345
|
+
|
|
346
|
+
qbepy is released under the MIT License.
|
|
347
|
+
|
|
348
|
+
QBE is developed by Quentin Carbonneaux and is also MIT licensed. See [vendor/qbe/LICENSE](vendor/qbe/LICENSE).
|
|
349
|
+
|
|
350
|
+
## Credits
|
|
351
|
+
|
|
352
|
+
- [QBE](https://c9x.me/compile/) - The compiler backend by Quentin Carbonneaux
|
|
353
|
+
- [CFFI](https://cffi.readthedocs.io/) - C Foreign Function Interface for Python
|
|
354
|
+
|
|
355
|
+
## See Also
|
|
356
|
+
|
|
357
|
+
- [QBE Documentation](https://c9x.me/compile/doc/il.html) - Complete IL specification
|
|
358
|
+
- [cproc](https://sr.ht/~mcf/cproc/) - A C11 compiler using QBE as backend
|
|
359
|
+
- [Hare](https://harelang.org/) - A systems programming language using QBE
|
qbepy-2026.2.1/README.md
ADDED
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
# qbepy
|
|
2
|
+
|
|
3
|
+
Python bindings for [QBE](https://c9x.me/compile/) (Quite Bare Engine), a minimalist compiler backend.
|
|
4
|
+
|
|
5
|
+
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.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Direct FFI bindings** - No subprocess overhead; QBE is compiled as a Python extension
|
|
10
|
+
- **Multiple targets** - Supports amd64 (System V and Apple ABIs), ARM64, and RISC-V 64
|
|
11
|
+
- **Pythonic IR builder** - Construct QBE IL programmatically with a clean API
|
|
12
|
+
- **Error handling** - QBE errors are raised as Python exceptions
|
|
13
|
+
- **Vendored QBE** - QBE source is included and built automatically during installation
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install qbepy
|
|
19
|
+
# or
|
|
20
|
+
uv add qbepy
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Or from source:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
git clone https://github.com/user/qbepy.git
|
|
27
|
+
cd qbepy
|
|
28
|
+
uv sync
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
### Compiling Raw IL
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
import qbepy
|
|
37
|
+
|
|
38
|
+
# QBE IL for a simple add function
|
|
39
|
+
il = """
|
|
40
|
+
export function w $add(w %a, w %b) {
|
|
41
|
+
@start
|
|
42
|
+
%r =w add %a, %b
|
|
43
|
+
ret %r
|
|
44
|
+
}
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
# Compile to assembly
|
|
48
|
+
asm = qbepy.compile_il(il)
|
|
49
|
+
print(asm)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Output (ARM64 Apple):
|
|
53
|
+
```asm
|
|
54
|
+
.text
|
|
55
|
+
.balign 4
|
|
56
|
+
.globl _add
|
|
57
|
+
_add:
|
|
58
|
+
hint #34
|
|
59
|
+
stp x29, x30, [sp, -16]!
|
|
60
|
+
mov x29, sp
|
|
61
|
+
add w0, w0, w1
|
|
62
|
+
ldp x29, x30, [sp], 16
|
|
63
|
+
ret
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Using the IR Builder
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
import qbepy
|
|
70
|
+
from qbepy import Module, Function, DataDef, W, L
|
|
71
|
+
from qbepy.ir import BinaryOp, Call, Return, Temporary, Global, IntConst
|
|
72
|
+
|
|
73
|
+
# Create a module
|
|
74
|
+
mod = Module()
|
|
75
|
+
|
|
76
|
+
# Add a string constant
|
|
77
|
+
mod.add_data(
|
|
78
|
+
DataDef("greeting")
|
|
79
|
+
.add_string("Hello, World!")
|
|
80
|
+
.add_bytes(0) # null terminator
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Create main function
|
|
84
|
+
func = Function("main", W, export=True)
|
|
85
|
+
block = func.add_block("start")
|
|
86
|
+
|
|
87
|
+
# Call puts($greeting)
|
|
88
|
+
r = func.new_temp("r")
|
|
89
|
+
block.instructions.append(
|
|
90
|
+
Call(Global("puts"), [(L, Global("greeting"))], r, W)
|
|
91
|
+
)
|
|
92
|
+
block.terminator = Return(IntConst(0))
|
|
93
|
+
|
|
94
|
+
mod.add_function(func)
|
|
95
|
+
|
|
96
|
+
# Compile to assembly
|
|
97
|
+
asm = qbepy.compile_module(mod)
|
|
98
|
+
print(asm)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Specifying a Target
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
# Compile for x86-64 System V ABI
|
|
105
|
+
asm = qbepy.compile_il(il, target="amd64_sysv")
|
|
106
|
+
|
|
107
|
+
# Or use the Compiler class
|
|
108
|
+
compiler = qbepy.Compiler(target="arm64")
|
|
109
|
+
asm = compiler.compile(il)
|
|
110
|
+
|
|
111
|
+
# Available targets
|
|
112
|
+
print(qbepy.Compiler.get_available_targets())
|
|
113
|
+
# ['amd64_sysv', 'amd64_apple', 'arm64', 'arm64_apple', 'rv64']
|
|
114
|
+
|
|
115
|
+
# Get default target for current platform
|
|
116
|
+
print(qbepy.Compiler.get_default_target())
|
|
117
|
+
# 'arm64_apple' (on Apple Silicon)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## API Reference
|
|
121
|
+
|
|
122
|
+
### Compiler
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
from qbepy import Compiler, compile_il, compile_module
|
|
126
|
+
|
|
127
|
+
# Create a compiler instance
|
|
128
|
+
compiler = Compiler(target=None) # None = platform default
|
|
129
|
+
|
|
130
|
+
# Compile IL string to assembly
|
|
131
|
+
asm = compiler.compile(il_string)
|
|
132
|
+
|
|
133
|
+
# Compile a Module object
|
|
134
|
+
asm = compiler.compile_module(module)
|
|
135
|
+
|
|
136
|
+
# Change target
|
|
137
|
+
compiler.set_target("amd64_sysv")
|
|
138
|
+
|
|
139
|
+
# Convenience functions
|
|
140
|
+
asm = compile_il(il_string, target=None)
|
|
141
|
+
asm = compile_module(module, target=None)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Types
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
from qbepy import W, L, S, D, BaseType, ExtType, AggregateType
|
|
148
|
+
|
|
149
|
+
# Base types
|
|
150
|
+
W # word (32-bit integer)
|
|
151
|
+
L # long (64-bit integer)
|
|
152
|
+
S # single (32-bit float)
|
|
153
|
+
D # double (64-bit float)
|
|
154
|
+
|
|
155
|
+
# Extended types (for memory operations)
|
|
156
|
+
ExtType.BYTE, ExtType.HALF, ExtType.WORD, ExtType.LONG
|
|
157
|
+
ExtType.SINGLE, ExtType.DOUBLE
|
|
158
|
+
|
|
159
|
+
# Aggregate types (structs)
|
|
160
|
+
point = AggregateType("point", [
|
|
161
|
+
(ExtType.WORD, 1), # x: 1 word
|
|
162
|
+
(ExtType.WORD, 1), # y: 1 word
|
|
163
|
+
])
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Values
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
from qbepy.ir import Temporary, Global, Label, IntConst, FloatConst
|
|
170
|
+
|
|
171
|
+
Temporary("x") # %x - SSA temporary
|
|
172
|
+
Global("main") # $main - global symbol
|
|
173
|
+
Label("loop") # @loop - block label
|
|
174
|
+
IntConst(42) # 42 - integer constant
|
|
175
|
+
FloatConst(3.14) # d_3.14 - double constant
|
|
176
|
+
FloatConst(3.14, is_single=True) # s_3.14 - float constant
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Instructions
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
from qbepy.ir import (
|
|
183
|
+
BinaryOp, # add, sub, mul, div, rem, or, xor, and, sar, shr, shl
|
|
184
|
+
UnaryOp, # neg, copy
|
|
185
|
+
Copy, # copy value
|
|
186
|
+
Load, # load from memory
|
|
187
|
+
Store, # store to memory
|
|
188
|
+
Alloc, # stack allocation
|
|
189
|
+
Call, # function call
|
|
190
|
+
Comparison, # ceqw, cnew, csltw, etc.
|
|
191
|
+
Conversion, # extsw, truncd, stosi, cast, etc.
|
|
192
|
+
Phi, # SSA phi node
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# Examples
|
|
196
|
+
BinaryOp("add", Temporary("r"), W, Temporary("a"), Temporary("b"))
|
|
197
|
+
Load(Temporary("v"), W, Temporary("ptr"))
|
|
198
|
+
Store("storew", Temporary("v"), Temporary("ptr"))
|
|
199
|
+
Call(Global("printf"), [(L, Global("fmt"))], Temporary("r"), W)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Control Flow
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
from qbepy.ir import Jump, Branch, Return, Halt
|
|
206
|
+
|
|
207
|
+
Jump(Label("next")) # jmp @next
|
|
208
|
+
Branch(Temporary("c"), Label("t"), Label("f")) # jnz %c, @t, @f
|
|
209
|
+
Return(Temporary("r")) # ret %r
|
|
210
|
+
Return(IntConst(0)) # ret 0
|
|
211
|
+
Return() # ret (void)
|
|
212
|
+
Halt() # hlt (unreachable)
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Building Modules
|
|
216
|
+
|
|
217
|
+
```python
|
|
218
|
+
from qbepy import Module, Function, Block, DataDef
|
|
219
|
+
|
|
220
|
+
# Module - container for types, data, and functions
|
|
221
|
+
mod = Module()
|
|
222
|
+
mod.add_type(aggregate_type)
|
|
223
|
+
mod.add_data(data_def)
|
|
224
|
+
mod.add_function(function)
|
|
225
|
+
|
|
226
|
+
# Function
|
|
227
|
+
func = Function("name", return_type, params=[(W, "a"), (L, "b")], export=True)
|
|
228
|
+
block = func.add_block("start")
|
|
229
|
+
temp = func.new_temp("x") # creates unique temporary
|
|
230
|
+
|
|
231
|
+
# Block
|
|
232
|
+
block.instructions.append(instruction)
|
|
233
|
+
block.terminator = Return(value)
|
|
234
|
+
|
|
235
|
+
# DataDef
|
|
236
|
+
data = (DataDef("name", export=True, align=8)
|
|
237
|
+
.add_string("hello")
|
|
238
|
+
.add_bytes(0)
|
|
239
|
+
.add_words(1, 2, 3)
|
|
240
|
+
.add_longs(0x1234567890)
|
|
241
|
+
.add_zero(16))
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Supported Targets
|
|
245
|
+
|
|
246
|
+
| Target | Description |
|
|
247
|
+
|--------|-------------|
|
|
248
|
+
| `amd64_sysv` | x86-64 with System V ABI (Linux, BSD) |
|
|
249
|
+
| `amd64_apple` | x86-64 with Apple ABI (macOS Intel) |
|
|
250
|
+
| `arm64` | ARM64 with standard ABI (Linux) |
|
|
251
|
+
| `arm64_apple` | ARM64 with Apple ABI (macOS Apple Silicon) |
|
|
252
|
+
| `rv64` | RISC-V 64-bit |
|
|
253
|
+
|
|
254
|
+
## QBE IL Reference
|
|
255
|
+
|
|
256
|
+
QBE uses a simple SSA-based intermediate language. For the complete specification, see the [QBE IL documentation](https://c9x.me/compile/doc/il.html).
|
|
257
|
+
|
|
258
|
+
### Basic IL Structure
|
|
259
|
+
|
|
260
|
+
```
|
|
261
|
+
# Type definitions
|
|
262
|
+
type :point = { w, w }
|
|
263
|
+
|
|
264
|
+
# Data definitions
|
|
265
|
+
data $message = { b "Hello", b 0 }
|
|
266
|
+
|
|
267
|
+
# Function definitions
|
|
268
|
+
export function w $main() {
|
|
269
|
+
@start
|
|
270
|
+
%x =w copy 42
|
|
271
|
+
ret %x
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### IL Basics
|
|
276
|
+
|
|
277
|
+
- Temporaries: `%name` (SSA values)
|
|
278
|
+
- Globals: `$name` (functions and data)
|
|
279
|
+
- Labels: `@name` (basic blocks)
|
|
280
|
+
- Types: `w` (word), `l` (long), `s` (single), `d` (double)
|
|
281
|
+
|
|
282
|
+
## Error Handling
|
|
283
|
+
|
|
284
|
+
```python
|
|
285
|
+
from qbepy import CompilationError, compile_il
|
|
286
|
+
|
|
287
|
+
try:
|
|
288
|
+
asm = compile_il("invalid IL code")
|
|
289
|
+
except CompilationError as e:
|
|
290
|
+
print(f"Compilation failed: {e}")
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## Project Structure
|
|
294
|
+
|
|
295
|
+
```
|
|
296
|
+
qbepy/
|
|
297
|
+
├── src/qbepy/
|
|
298
|
+
│ ├── __init__.py # Public API exports
|
|
299
|
+
│ ├── _ffi.py # Low-level CFFI bindings
|
|
300
|
+
│ ├── compiler.py # Compiler class
|
|
301
|
+
│ ├── errors.py # Exception types
|
|
302
|
+
│ └── ir/ # IR builder module
|
|
303
|
+
│ ├── types.py # Type definitions
|
|
304
|
+
│ ├── values.py # Value types
|
|
305
|
+
│ ├── instructions.py # Instructions
|
|
306
|
+
│ ├── control.py # Control flow
|
|
307
|
+
│ └── builder.py # Module, Function, Block, DataDef
|
|
308
|
+
├── csrc/
|
|
309
|
+
│ ├── qbepy_wrapper.c # C wrapper with error handling
|
|
310
|
+
│ └── qbepy_wrapper.h
|
|
311
|
+
├── vendor/qbe/ # Vendored QBE source
|
|
312
|
+
├── build_ffi.py # CFFI build script
|
|
313
|
+
└── tests/ # Test suite
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
## How It Works
|
|
317
|
+
|
|
318
|
+
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:
|
|
319
|
+
|
|
320
|
+
1. Redirecting QBE's `err()` function to a custom handler using preprocessor macros
|
|
321
|
+
2. Using `setjmp`/`longjmp` to catch errors and return control to Python
|
|
322
|
+
3. Converting errors to Python exceptions
|
|
323
|
+
|
|
324
|
+
This allows QBE to be used as a library rather than a standalone compiler.
|
|
325
|
+
|
|
326
|
+
## License
|
|
327
|
+
|
|
328
|
+
qbepy is released under the MIT License.
|
|
329
|
+
|
|
330
|
+
QBE is developed by Quentin Carbonneaux and is also MIT licensed. See [vendor/qbe/LICENSE](vendor/qbe/LICENSE).
|
|
331
|
+
|
|
332
|
+
## Credits
|
|
333
|
+
|
|
334
|
+
- [QBE](https://c9x.me/compile/) - The compiler backend by Quentin Carbonneaux
|
|
335
|
+
- [CFFI](https://cffi.readthedocs.io/) - C Foreign Function Interface for Python
|
|
336
|
+
|
|
337
|
+
## See Also
|
|
338
|
+
|
|
339
|
+
- [QBE Documentation](https://c9x.me/compile/doc/il.html) - Complete IL specification
|
|
340
|
+
- [cproc](https://sr.ht/~mcf/cproc/) - A C11 compiler using QBE as backend
|
|
341
|
+
- [Hare](https://harelang.org/) - A systems programming language using QBE
|