qbepy 2026.2.1__cp312-cp312-macosx_14_0_arm64.whl
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/__init__.py +85 -0
- qbepy/_ffi.py +66 -0
- qbepy/_qbe_ffi.abi3.so +0 -0
- qbepy/compiler.py +164 -0
- qbepy/errors.py +20 -0
- qbepy/ir/__init__.py +73 -0
- qbepy/ir/builder.py +259 -0
- qbepy/ir/control.py +60 -0
- qbepy/ir/instructions.py +238 -0
- qbepy/ir/types.py +84 -0
- qbepy/ir/values.py +76 -0
- qbepy-2026.2.1.dist-info/METADATA +359 -0
- qbepy-2026.2.1.dist-info/RECORD +15 -0
- qbepy-2026.2.1.dist-info/WHEEL +5 -0
- qbepy-2026.2.1.dist-info/top_level.txt +1 -0
qbepy/__init__.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""QBE Python Bindings - Compile QBE IL to assembly.
|
|
2
|
+
|
|
3
|
+
This package provides Python bindings for QBE (Quite Bare Engine), a minimalist
|
|
4
|
+
compiler backend. It includes:
|
|
5
|
+
|
|
6
|
+
1. A Compiler class that uses FFI to compile IL to assembly
|
|
7
|
+
2. An IR builder module for constructing QBE IL programmatically
|
|
8
|
+
|
|
9
|
+
Example usage:
|
|
10
|
+
|
|
11
|
+
# Raw IL compilation
|
|
12
|
+
import qbepy
|
|
13
|
+
|
|
14
|
+
il = '''
|
|
15
|
+
data $str = { b "hello", b 0 }
|
|
16
|
+
export function w $main() {
|
|
17
|
+
@start
|
|
18
|
+
%r =w call $puts(l $str)
|
|
19
|
+
ret 0
|
|
20
|
+
}
|
|
21
|
+
'''
|
|
22
|
+
asm = qbepy.compile_il(il)
|
|
23
|
+
|
|
24
|
+
# Using the IR builder
|
|
25
|
+
from qbepy import Module, Function, DataDef, W, L
|
|
26
|
+
from qbepy.ir import Call, Return, Global, Temporary, IntConst
|
|
27
|
+
|
|
28
|
+
mod = Module()
|
|
29
|
+
mod.add_data(DataDef("str").add_string("hello").add_bytes(0))
|
|
30
|
+
|
|
31
|
+
func = Function("main", W, export=True)
|
|
32
|
+
block = func.add_block("start")
|
|
33
|
+
block.instructions.append(
|
|
34
|
+
Call(Global("puts"), [(L, Global("str"))], Temporary("r"), W)
|
|
35
|
+
)
|
|
36
|
+
block.terminator = Return(IntConst(0))
|
|
37
|
+
mod.add_function(func)
|
|
38
|
+
|
|
39
|
+
asm = qbepy.compile_module(mod)
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
from __future__ import annotations
|
|
43
|
+
|
|
44
|
+
from .compiler import Compiler, compile_il, compile_module
|
|
45
|
+
from .errors import CompilationError, QBEPyError
|
|
46
|
+
from .ir import (
|
|
47
|
+
AggregateType,
|
|
48
|
+
BaseType,
|
|
49
|
+
Block,
|
|
50
|
+
D,
|
|
51
|
+
DataDef,
|
|
52
|
+
ExtType,
|
|
53
|
+
Function,
|
|
54
|
+
L,
|
|
55
|
+
Module,
|
|
56
|
+
S,
|
|
57
|
+
SubWordType,
|
|
58
|
+
W,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
__all__ = [
|
|
62
|
+
"AggregateType",
|
|
63
|
+
# Types
|
|
64
|
+
"BaseType",
|
|
65
|
+
"Block",
|
|
66
|
+
"CompilationError",
|
|
67
|
+
# Compiler
|
|
68
|
+
"Compiler",
|
|
69
|
+
"D",
|
|
70
|
+
"DataDef",
|
|
71
|
+
"ExtType",
|
|
72
|
+
"Function",
|
|
73
|
+
"L",
|
|
74
|
+
# IR building
|
|
75
|
+
"Module",
|
|
76
|
+
"QBEPyError",
|
|
77
|
+
"S",
|
|
78
|
+
"SubWordType",
|
|
79
|
+
"W",
|
|
80
|
+
# Convenience functions
|
|
81
|
+
"compile_il",
|
|
82
|
+
"compile_module",
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
__version__ = "0.1.0"
|
qbepy/_ffi.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""Low-level FFI bindings to QBE."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from ._qbe_ffi import ffi, lib
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class QBEError(Exception):
|
|
9
|
+
"""Error from QBE compilation."""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def compile_il(source: str, target: str | None = None) -> str:
|
|
13
|
+
"""
|
|
14
|
+
Compile QBE IL to assembly.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
source: QBE IL source code
|
|
18
|
+
target: Target architecture (or None for default)
|
|
19
|
+
Valid targets: amd64_sysv, amd64_apple, arm64, arm64_apple, rv64
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
Assembly code as string
|
|
23
|
+
|
|
24
|
+
Raises:
|
|
25
|
+
QBEError: If compilation fails
|
|
26
|
+
"""
|
|
27
|
+
source_bytes = source.encode("utf-8")
|
|
28
|
+
target_bytes = target.encode("utf-8") if target else ffi.NULL
|
|
29
|
+
|
|
30
|
+
output_ptr = ffi.new("char**")
|
|
31
|
+
output_len = ffi.new("size_t*")
|
|
32
|
+
|
|
33
|
+
result = lib.qbepy_compile(
|
|
34
|
+
source_bytes, len(source_bytes), target_bytes, output_ptr, output_len
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
if result != lib.QBEPY_OK:
|
|
38
|
+
error_msg = ffi.string(lib.qbepy_get_error()).decode("utf-8")
|
|
39
|
+
raise QBEError(error_msg)
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
if output_ptr[0] == ffi.NULL:
|
|
43
|
+
msg = "No output generated"
|
|
44
|
+
raise QBEError(msg)
|
|
45
|
+
output = ffi.string(output_ptr[0], output_len[0]).decode("utf-8")
|
|
46
|
+
finally:
|
|
47
|
+
if output_ptr[0] != ffi.NULL:
|
|
48
|
+
lib.qbepy_free(output_ptr[0])
|
|
49
|
+
|
|
50
|
+
return output
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def get_targets() -> list[str]:
|
|
54
|
+
"""Get list of available target architectures."""
|
|
55
|
+
targets_str = ffi.string(lib.qbepy_get_targets()).decode("utf-8")
|
|
56
|
+
return targets_str.split(",")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def get_default_target() -> str:
|
|
60
|
+
"""Get default target for current platform."""
|
|
61
|
+
return ffi.string(lib.qbepy_get_default_target()).decode("utf-8")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def version() -> str:
|
|
65
|
+
"""Get QBE version string."""
|
|
66
|
+
return ffi.string(lib.qbepy_version()).decode("utf-8")
|
qbepy/_qbe_ffi.abi3.so
ADDED
|
Binary file
|
qbepy/compiler.py
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""High-level QBE compiler interface."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Literal
|
|
6
|
+
|
|
7
|
+
from ._ffi import QBEError, compile_il as _compile_il, get_default_target, get_targets
|
|
8
|
+
from .errors import CompilationError
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from .ir.builder import Module
|
|
12
|
+
|
|
13
|
+
Target = Literal["amd64_sysv", "amd64_apple", "arm64", "arm64_apple", "rv64"]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Compiler:
|
|
17
|
+
"""QBE compiler interface.
|
|
18
|
+
|
|
19
|
+
Provides methods to compile QBE IL to native assembly code.
|
|
20
|
+
|
|
21
|
+
Example:
|
|
22
|
+
>>> compiler = Compiler()
|
|
23
|
+
>>> il = '''
|
|
24
|
+
... export function w $add(w %a, w %b) {
|
|
25
|
+
... @start
|
|
26
|
+
... %r =w add %a, %b
|
|
27
|
+
... ret %r
|
|
28
|
+
... }
|
|
29
|
+
... '''
|
|
30
|
+
>>> asm = compiler.compile(il)
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
TARGETS: list[str] = ["amd64_sysv", "amd64_apple", "arm64", "arm64_apple", "rv64"]
|
|
34
|
+
|
|
35
|
+
def __init__(self, target: Target | None = None):
|
|
36
|
+
"""Initialize compiler.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
target: Target architecture. If None, uses platform default.
|
|
40
|
+
Valid targets: amd64_sysv, amd64_apple, arm64, arm64_apple, rv64
|
|
41
|
+
|
|
42
|
+
Raises:
|
|
43
|
+
ValueError: If target is not a valid target name.
|
|
44
|
+
"""
|
|
45
|
+
if target and target not in self.TARGETS:
|
|
46
|
+
msg = f"Unknown target: {target}. Valid targets: {self.TARGETS}"
|
|
47
|
+
raise ValueError(msg)
|
|
48
|
+
self.target = target
|
|
49
|
+
|
|
50
|
+
def compile(self, il: str) -> str:
|
|
51
|
+
"""Compile QBE IL to assembly.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
il: QBE IL source code
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
Generated assembly code as a string
|
|
58
|
+
|
|
59
|
+
Raises:
|
|
60
|
+
CompilationError: If compilation fails
|
|
61
|
+
"""
|
|
62
|
+
try:
|
|
63
|
+
return _compile_il(il, self.target)
|
|
64
|
+
except QBEError as e:
|
|
65
|
+
raise CompilationError(str(e), source=il) from e
|
|
66
|
+
|
|
67
|
+
def compile_module(self, module: Module) -> str:
|
|
68
|
+
"""Compile a Module to assembly.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
module: IR Module object
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
Generated assembly code as a string
|
|
75
|
+
|
|
76
|
+
Raises:
|
|
77
|
+
CompilationError: If compilation fails
|
|
78
|
+
"""
|
|
79
|
+
il = module.emit()
|
|
80
|
+
return self.compile(il)
|
|
81
|
+
|
|
82
|
+
def set_target(self, target: Target) -> None:
|
|
83
|
+
"""Set the compilation target.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
target: Target architecture name
|
|
87
|
+
|
|
88
|
+
Raises:
|
|
89
|
+
ValueError: If the target is not recognized
|
|
90
|
+
"""
|
|
91
|
+
if target not in self.TARGETS:
|
|
92
|
+
msg = (
|
|
93
|
+
f"Unknown target: {target}. "
|
|
94
|
+
f"Available targets: {', '.join(self.TARGETS)}"
|
|
95
|
+
)
|
|
96
|
+
raise ValueError(msg)
|
|
97
|
+
self.target = target
|
|
98
|
+
|
|
99
|
+
@staticmethod
|
|
100
|
+
def get_default_target() -> str:
|
|
101
|
+
"""Get QBE's default target for the current platform.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
The default target name
|
|
105
|
+
"""
|
|
106
|
+
return get_default_target()
|
|
107
|
+
|
|
108
|
+
@staticmethod
|
|
109
|
+
def get_available_targets() -> list[str]:
|
|
110
|
+
"""Get list of available compilation targets.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
List of target names
|
|
114
|
+
"""
|
|
115
|
+
return get_targets()
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def compile_il(il: str, target: Target | None = None) -> str:
|
|
119
|
+
"""Compile QBE IL text to assembly.
|
|
120
|
+
|
|
121
|
+
Convenience function that creates a Compiler and compiles the IL.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
il: QBE intermediate language source code
|
|
125
|
+
target: Target architecture (e.g., 'amd64_sysv', 'arm64').
|
|
126
|
+
If None, uses QBE's default for the current platform.
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
Generated assembly code as a string
|
|
130
|
+
|
|
131
|
+
Raises:
|
|
132
|
+
CompilationError: If compilation fails
|
|
133
|
+
|
|
134
|
+
Example:
|
|
135
|
+
>>> il = '''
|
|
136
|
+
... export function w $main() {
|
|
137
|
+
... @start
|
|
138
|
+
... ret 0
|
|
139
|
+
... }
|
|
140
|
+
... '''
|
|
141
|
+
>>> asm = compile_il(il)
|
|
142
|
+
"""
|
|
143
|
+
compiler = Compiler(target)
|
|
144
|
+
return compiler.compile(il)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def compile_module(module: Module, target: Target | None = None) -> str:
|
|
148
|
+
"""Compile a Module to assembly.
|
|
149
|
+
|
|
150
|
+
Convenience function that creates a Compiler and compiles the module.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
module: A Module containing type definitions, data, and functions
|
|
154
|
+
target: Target architecture (e.g., 'amd64_sysv', 'arm64').
|
|
155
|
+
If None, uses QBE's default for the current platform.
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
Generated assembly code as a string
|
|
159
|
+
|
|
160
|
+
Raises:
|
|
161
|
+
CompilationError: If compilation fails
|
|
162
|
+
"""
|
|
163
|
+
compiler = Compiler(target)
|
|
164
|
+
return compiler.compile_module(module)
|
qbepy/errors.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Exception types for qbepy."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class QBEPyError(Exception):
|
|
7
|
+
"""Base exception for qbepy errors."""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CompilationError(QBEPyError):
|
|
11
|
+
"""Error during QBE compilation.
|
|
12
|
+
|
|
13
|
+
Attributes:
|
|
14
|
+
message: Error message from QBE
|
|
15
|
+
source: The IL source that caused the error (if available)
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, message: str, source: str | None = None):
|
|
19
|
+
self.source = source
|
|
20
|
+
super().__init__(message)
|
qbepy/ir/__init__.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""QBE Intermediate Representation builder.
|
|
2
|
+
|
|
3
|
+
This module provides a Pythonic API for constructing QBE IL programs.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from .builder import Block, DataDef, Function, Module
|
|
9
|
+
from .control import Branch, Halt, Jump, Return, Terminator
|
|
10
|
+
from .instructions import (
|
|
11
|
+
Alloc,
|
|
12
|
+
BinaryOp,
|
|
13
|
+
Blit,
|
|
14
|
+
Call,
|
|
15
|
+
Comparison,
|
|
16
|
+
Conversion,
|
|
17
|
+
Copy,
|
|
18
|
+
Instruction,
|
|
19
|
+
Load,
|
|
20
|
+
Phi,
|
|
21
|
+
Store,
|
|
22
|
+
UnaryOp,
|
|
23
|
+
VaArg,
|
|
24
|
+
VaStart,
|
|
25
|
+
)
|
|
26
|
+
from .types import AggregateType, BaseType, D, ExtType, L, S, SubWordType, W
|
|
27
|
+
from .values import FloatConst, Global, IntConst, Label, Temporary, TypeRef, Value
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"AggregateType",
|
|
31
|
+
"Alloc",
|
|
32
|
+
# Types
|
|
33
|
+
"BaseType",
|
|
34
|
+
"BinaryOp",
|
|
35
|
+
"Blit",
|
|
36
|
+
# Builders
|
|
37
|
+
"Block",
|
|
38
|
+
"Branch",
|
|
39
|
+
"Call",
|
|
40
|
+
"Comparison",
|
|
41
|
+
"Conversion",
|
|
42
|
+
"Copy",
|
|
43
|
+
"D",
|
|
44
|
+
"DataDef",
|
|
45
|
+
"ExtType",
|
|
46
|
+
"FloatConst",
|
|
47
|
+
"Function",
|
|
48
|
+
"Global",
|
|
49
|
+
"Halt",
|
|
50
|
+
# Instructions
|
|
51
|
+
"Instruction",
|
|
52
|
+
"IntConst",
|
|
53
|
+
"Jump",
|
|
54
|
+
"L",
|
|
55
|
+
"Label",
|
|
56
|
+
"Load",
|
|
57
|
+
"Module",
|
|
58
|
+
"Phi",
|
|
59
|
+
"Return",
|
|
60
|
+
"S",
|
|
61
|
+
"Store",
|
|
62
|
+
"SubWordType",
|
|
63
|
+
# Values
|
|
64
|
+
"Temporary",
|
|
65
|
+
# Control flow
|
|
66
|
+
"Terminator",
|
|
67
|
+
"TypeRef",
|
|
68
|
+
"UnaryOp",
|
|
69
|
+
"VaArg",
|
|
70
|
+
"VaStart",
|
|
71
|
+
"Value",
|
|
72
|
+
"W",
|
|
73
|
+
]
|
qbepy/ir/builder.py
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
"""QBE IR builder classes."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
from .types import AggregateType, BaseType
|
|
9
|
+
from .values import Global, Label, Temporary
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from .control import Terminator
|
|
13
|
+
from .instructions import Instruction, Phi
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class Block:
|
|
18
|
+
"""A basic block in a function.
|
|
19
|
+
|
|
20
|
+
Each block has:
|
|
21
|
+
- A label name
|
|
22
|
+
- Optional phi instructions (for SSA)
|
|
23
|
+
- Regular instructions
|
|
24
|
+
- A terminator (jump, branch, return, or halt)
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
name: str
|
|
28
|
+
phis: list[Phi] = field(default_factory=list)
|
|
29
|
+
instructions: list[Instruction] = field(default_factory=list)
|
|
30
|
+
terminator: Terminator | None = None
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def label(self) -> Label:
|
|
34
|
+
"""Get this block's label."""
|
|
35
|
+
return Label(self.name)
|
|
36
|
+
|
|
37
|
+
def emit(self) -> str:
|
|
38
|
+
"""Emit this block as QBE IL."""
|
|
39
|
+
lines = [f"@{self.name}"]
|
|
40
|
+
|
|
41
|
+
for phi in self.phis:
|
|
42
|
+
lines.append(f"\t{phi.emit()}")
|
|
43
|
+
|
|
44
|
+
for ins in self.instructions:
|
|
45
|
+
lines.append(f"\t{ins.emit()}")
|
|
46
|
+
|
|
47
|
+
if self.terminator:
|
|
48
|
+
lines.append(f"\t{self.terminator.emit()}")
|
|
49
|
+
|
|
50
|
+
return "\n".join(lines)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass
|
|
54
|
+
class Function:
|
|
55
|
+
"""A function definition.
|
|
56
|
+
|
|
57
|
+
Example:
|
|
58
|
+
# export function w $add(w %a, w %b) { ... }
|
|
59
|
+
func = Function("add", BaseType.WORD,
|
|
60
|
+
params=[(BaseType.WORD, "a"), (BaseType.WORD, "b")],
|
|
61
|
+
export=True)
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
name: str
|
|
65
|
+
return_type: BaseType | str | None = None
|
|
66
|
+
params: list[tuple[BaseType | str, str]] = field(default_factory=list)
|
|
67
|
+
export: bool = False
|
|
68
|
+
vararg: bool = False
|
|
69
|
+
blocks: list[Block] = field(default_factory=list)
|
|
70
|
+
_temp_counter: int = field(default=0, repr=False)
|
|
71
|
+
|
|
72
|
+
def new_temp(self, prefix: str = "t") -> Temporary:
|
|
73
|
+
"""Create a new unique temporary variable."""
|
|
74
|
+
self._temp_counter += 1
|
|
75
|
+
return Temporary(f"{prefix}.{self._temp_counter}")
|
|
76
|
+
|
|
77
|
+
def add_block(self, name: str) -> Block:
|
|
78
|
+
"""Add a new basic block to this function."""
|
|
79
|
+
block = Block(name)
|
|
80
|
+
self.blocks.append(block)
|
|
81
|
+
return block
|
|
82
|
+
|
|
83
|
+
def emit(self) -> str:
|
|
84
|
+
"""Emit this function as QBE IL."""
|
|
85
|
+
lines = []
|
|
86
|
+
|
|
87
|
+
if self.export:
|
|
88
|
+
lines.append("export")
|
|
89
|
+
|
|
90
|
+
# Return type
|
|
91
|
+
if self.return_type:
|
|
92
|
+
if isinstance(self.return_type, BaseType):
|
|
93
|
+
ret_str = self.return_type.value
|
|
94
|
+
else:
|
|
95
|
+
ret_str = f":{self.return_type}"
|
|
96
|
+
else:
|
|
97
|
+
ret_str = ""
|
|
98
|
+
|
|
99
|
+
# Parameters
|
|
100
|
+
param_strs = []
|
|
101
|
+
for param_type, param_name in self.params:
|
|
102
|
+
if isinstance(param_type, BaseType):
|
|
103
|
+
type_str = param_type.value
|
|
104
|
+
else:
|
|
105
|
+
type_str = f":{param_type}"
|
|
106
|
+
param_strs.append(f"{type_str} %{param_name}")
|
|
107
|
+
|
|
108
|
+
if self.vararg:
|
|
109
|
+
param_strs.append("...")
|
|
110
|
+
|
|
111
|
+
params_str = ", ".join(param_strs)
|
|
112
|
+
|
|
113
|
+
lines.append(f"function {ret_str} ${self.name}({params_str}) {{")
|
|
114
|
+
|
|
115
|
+
for block in self.blocks:
|
|
116
|
+
lines.append(block.emit())
|
|
117
|
+
|
|
118
|
+
lines.append("}")
|
|
119
|
+
|
|
120
|
+
return "\n".join(lines)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@dataclass
|
|
124
|
+
class DataDef:
|
|
125
|
+
"""A data definition.
|
|
126
|
+
|
|
127
|
+
Example:
|
|
128
|
+
# data $str = { b "hello world", b 0 }
|
|
129
|
+
data = DataDef("str")
|
|
130
|
+
data.add_string("hello world")
|
|
131
|
+
data.add_bytes(0)
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
|
+
name: str
|
|
135
|
+
export: bool = False
|
|
136
|
+
align: int | None = None
|
|
137
|
+
section: str | None = None
|
|
138
|
+
items: list = field(default_factory=list)
|
|
139
|
+
|
|
140
|
+
def add_bytes(self, *values: int) -> DataDef:
|
|
141
|
+
"""Add byte values."""
|
|
142
|
+
self.items.append(("b", list(values)))
|
|
143
|
+
return self
|
|
144
|
+
|
|
145
|
+
def add_halfs(self, *values: int) -> DataDef:
|
|
146
|
+
"""Add half-word (16-bit) values."""
|
|
147
|
+
self.items.append(("h", list(values)))
|
|
148
|
+
return self
|
|
149
|
+
|
|
150
|
+
def add_words(self, *values: int) -> DataDef:
|
|
151
|
+
"""Add word (32-bit) values."""
|
|
152
|
+
self.items.append(("w", list(values)))
|
|
153
|
+
return self
|
|
154
|
+
|
|
155
|
+
def add_longs(self, *values: int | Global) -> DataDef:
|
|
156
|
+
"""Add long (64-bit) values or global references."""
|
|
157
|
+
self.items.append(("l", list(values)))
|
|
158
|
+
return self
|
|
159
|
+
|
|
160
|
+
def add_singles(self, *values: float) -> DataDef:
|
|
161
|
+
"""Add single-precision float values."""
|
|
162
|
+
formatted = [f"s_{v}" for v in values]
|
|
163
|
+
self.items.append(("s", formatted))
|
|
164
|
+
return self
|
|
165
|
+
|
|
166
|
+
def add_doubles(self, *values: float) -> DataDef:
|
|
167
|
+
"""Add double-precision float values."""
|
|
168
|
+
formatted = [f"d_{v}" for v in values]
|
|
169
|
+
self.items.append(("d", formatted))
|
|
170
|
+
return self
|
|
171
|
+
|
|
172
|
+
def add_string(self, s: str) -> DataDef:
|
|
173
|
+
"""Add a string (as bytes). Does NOT add null terminator."""
|
|
174
|
+
# Escape the string for QBE
|
|
175
|
+
escaped = s.replace("\\", "\\\\").replace('"', '\\"')
|
|
176
|
+
self.items.append(("b", [f'"{escaped}"']))
|
|
177
|
+
return self
|
|
178
|
+
|
|
179
|
+
def add_zero(self, count: int) -> DataDef:
|
|
180
|
+
"""Add zero-initialized padding."""
|
|
181
|
+
self.items.append(("z", count))
|
|
182
|
+
return self
|
|
183
|
+
|
|
184
|
+
def add_ref(self, name: str, offset: int = 0) -> DataDef:
|
|
185
|
+
"""Add a reference to another global symbol."""
|
|
186
|
+
if offset:
|
|
187
|
+
self.items.append(("l", [f"${name} + {offset}"]))
|
|
188
|
+
else:
|
|
189
|
+
self.items.append(("l", [f"${name}"]))
|
|
190
|
+
return self
|
|
191
|
+
|
|
192
|
+
def emit(self) -> str:
|
|
193
|
+
"""Emit this data definition as QBE IL."""
|
|
194
|
+
lines = []
|
|
195
|
+
|
|
196
|
+
if self.export:
|
|
197
|
+
lines.append("export")
|
|
198
|
+
|
|
199
|
+
if self.section:
|
|
200
|
+
lines.append(f'section "{self.section}"')
|
|
201
|
+
|
|
202
|
+
align_str = f"align {self.align} " if self.align else ""
|
|
203
|
+
lines.append(f"data ${self.name} = {align_str}{{")
|
|
204
|
+
|
|
205
|
+
for item in self.items:
|
|
206
|
+
if item[0] == "z":
|
|
207
|
+
lines.append(f"\tz {item[1]}")
|
|
208
|
+
else:
|
|
209
|
+
type_char, values = item
|
|
210
|
+
vals_str = " ".join(str(v) for v in values)
|
|
211
|
+
# Handle empty values (shouldn't happen but be safe)
|
|
212
|
+
if vals_str:
|
|
213
|
+
lines.append(f"\t{type_char} {vals_str},")
|
|
214
|
+
|
|
215
|
+
lines.append("}")
|
|
216
|
+
|
|
217
|
+
return "\n".join(lines)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
@dataclass
|
|
221
|
+
class Module:
|
|
222
|
+
"""A complete QBE IL module.
|
|
223
|
+
|
|
224
|
+
Contains type definitions, data definitions, and functions.
|
|
225
|
+
"""
|
|
226
|
+
|
|
227
|
+
types: list[AggregateType] = field(default_factory=list)
|
|
228
|
+
data: list[DataDef] = field(default_factory=list)
|
|
229
|
+
functions: list[Function] = field(default_factory=list)
|
|
230
|
+
|
|
231
|
+
def add_type(self, type_def: AggregateType) -> Module:
|
|
232
|
+
"""Add a type definition."""
|
|
233
|
+
self.types.append(type_def)
|
|
234
|
+
return self
|
|
235
|
+
|
|
236
|
+
def add_data(self, data_def: DataDef) -> Module:
|
|
237
|
+
"""Add a data definition."""
|
|
238
|
+
self.data.append(data_def)
|
|
239
|
+
return self
|
|
240
|
+
|
|
241
|
+
def add_function(self, func: Function) -> Module:
|
|
242
|
+
"""Add a function definition."""
|
|
243
|
+
self.functions.append(func)
|
|
244
|
+
return self
|
|
245
|
+
|
|
246
|
+
def emit(self) -> str:
|
|
247
|
+
"""Emit this module as QBE IL."""
|
|
248
|
+
parts = []
|
|
249
|
+
|
|
250
|
+
for t in self.types:
|
|
251
|
+
parts.append(t.emit())
|
|
252
|
+
|
|
253
|
+
for d in self.data:
|
|
254
|
+
parts.append(d.emit())
|
|
255
|
+
|
|
256
|
+
for f in self.functions:
|
|
257
|
+
parts.append(f.emit())
|
|
258
|
+
|
|
259
|
+
return "\n\n".join(parts)
|