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/ir/control.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""QBE control flow instructions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from .values import Label, Value
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class Terminator:
|
|
14
|
+
"""Base class for block terminators."""
|
|
15
|
+
|
|
16
|
+
def emit(self) -> str:
|
|
17
|
+
"""Emit this terminator as QBE IL."""
|
|
18
|
+
raise NotImplementedError
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class Jump(Terminator):
|
|
23
|
+
"""Unconditional jump."""
|
|
24
|
+
|
|
25
|
+
target: Label
|
|
26
|
+
|
|
27
|
+
def emit(self) -> str:
|
|
28
|
+
return f"jmp {self.target}"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class Branch(Terminator):
|
|
33
|
+
"""Conditional branch (jump if non-zero)."""
|
|
34
|
+
|
|
35
|
+
condition: Value
|
|
36
|
+
if_true: Label
|
|
37
|
+
if_false: Label
|
|
38
|
+
|
|
39
|
+
def emit(self) -> str:
|
|
40
|
+
return f"jnz {self.condition}, {self.if_true}, {self.if_false}"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class Return(Terminator):
|
|
45
|
+
"""Function return."""
|
|
46
|
+
|
|
47
|
+
value: Value | None = None
|
|
48
|
+
|
|
49
|
+
def emit(self) -> str:
|
|
50
|
+
if self.value is not None:
|
|
51
|
+
return f"ret {self.value}"
|
|
52
|
+
return "ret"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass
|
|
56
|
+
class Halt(Terminator):
|
|
57
|
+
"""Program halt (unreachable code marker)."""
|
|
58
|
+
|
|
59
|
+
def emit(self) -> str:
|
|
60
|
+
return "hlt"
|
qbepy/ir/instructions.py
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"""QBE instruction definitions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
from .types import BaseType
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from .values import Label, Temporary, Value
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class Instruction:
|
|
16
|
+
"""Base class for all instructions."""
|
|
17
|
+
|
|
18
|
+
def emit(self) -> str:
|
|
19
|
+
"""Emit this instruction as QBE IL."""
|
|
20
|
+
raise NotImplementedError
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class BinaryOp(Instruction):
|
|
25
|
+
"""Binary arithmetic/logic operation.
|
|
26
|
+
|
|
27
|
+
Operations: add, sub, mul, div, rem, udiv, urem,
|
|
28
|
+
and, or, xor, sar, shr, shl
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
op: str
|
|
32
|
+
result: Temporary
|
|
33
|
+
result_type: BaseType
|
|
34
|
+
left: Value
|
|
35
|
+
right: Value
|
|
36
|
+
|
|
37
|
+
def emit(self) -> str:
|
|
38
|
+
return f"{self.result} ={self.result_type.value} {self.op} {self.left}, {self.right}"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class UnaryOp(Instruction):
|
|
43
|
+
"""Unary operation (neg)."""
|
|
44
|
+
|
|
45
|
+
op: str
|
|
46
|
+
result: Temporary
|
|
47
|
+
result_type: BaseType
|
|
48
|
+
operand: Value
|
|
49
|
+
|
|
50
|
+
def emit(self) -> str:
|
|
51
|
+
return f"{self.result} ={self.result_type.value} {self.op} {self.operand}"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@dataclass
|
|
55
|
+
class Copy(Instruction):
|
|
56
|
+
"""Copy operation."""
|
|
57
|
+
|
|
58
|
+
result: Temporary
|
|
59
|
+
result_type: BaseType
|
|
60
|
+
value: Value
|
|
61
|
+
|
|
62
|
+
def emit(self) -> str:
|
|
63
|
+
return f"{self.result} ={self.result_type.value} copy {self.value}"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass
|
|
67
|
+
class Load(Instruction):
|
|
68
|
+
"""Memory load operation.
|
|
69
|
+
|
|
70
|
+
Load types: load, loadsb, loadub, loadsh, loaduh, loadsw, loaduw
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
result: Temporary
|
|
74
|
+
result_type: BaseType
|
|
75
|
+
address: Value
|
|
76
|
+
load_type: str = "load"
|
|
77
|
+
|
|
78
|
+
def emit(self) -> str:
|
|
79
|
+
return (
|
|
80
|
+
f"{self.result} ={self.result_type.value} {self.load_type} {self.address}"
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@dataclass
|
|
85
|
+
class Store(Instruction):
|
|
86
|
+
"""Memory store operation.
|
|
87
|
+
|
|
88
|
+
Store types: storeb, storeh, storew, storel, stores, stored
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
store_type: str
|
|
92
|
+
value: Value
|
|
93
|
+
address: Value
|
|
94
|
+
|
|
95
|
+
def emit(self) -> str:
|
|
96
|
+
return f"{self.store_type} {self.value}, {self.address}"
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@dataclass
|
|
100
|
+
class Blit(Instruction):
|
|
101
|
+
"""Memory copy (blit) operation."""
|
|
102
|
+
|
|
103
|
+
src: Value
|
|
104
|
+
dst: Value
|
|
105
|
+
size: int
|
|
106
|
+
|
|
107
|
+
def emit(self) -> str:
|
|
108
|
+
return f"blit {self.src}, {self.dst}, {self.size}"
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@dataclass
|
|
112
|
+
class Alloc(Instruction):
|
|
113
|
+
"""Stack allocation.
|
|
114
|
+
|
|
115
|
+
Alignment can be 4, 8, or 16 bytes.
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
result: Temporary
|
|
119
|
+
size: Value
|
|
120
|
+
align: int = 8
|
|
121
|
+
|
|
122
|
+
def emit(self) -> str:
|
|
123
|
+
alloc_op = f"alloc{self.align}"
|
|
124
|
+
return f"{self.result} =l {alloc_op} {self.size}"
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@dataclass
|
|
128
|
+
class Comparison(Instruction):
|
|
129
|
+
"""Comparison operation.
|
|
130
|
+
|
|
131
|
+
Integer comparisons: ceqw, cnew, csltw, csgtw, cslew, csgew,
|
|
132
|
+
cultw, cugtw, culew, cugew
|
|
133
|
+
(and 'l' variants for 64-bit)
|
|
134
|
+
Float comparisons: ceqs, cges, cgts, cles, clts, cnes, cos, cuos
|
|
135
|
+
(and 'd' variants for double)
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
op: str
|
|
139
|
+
result: Temporary
|
|
140
|
+
result_type: BaseType
|
|
141
|
+
left: Value
|
|
142
|
+
right: Value
|
|
143
|
+
|
|
144
|
+
def emit(self) -> str:
|
|
145
|
+
return f"{self.result} ={self.result_type.value} {self.op} {self.left}, {self.right}"
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@dataclass
|
|
149
|
+
class Conversion(Instruction):
|
|
150
|
+
"""Type conversion operation.
|
|
151
|
+
|
|
152
|
+
Operations: extsb, extub, extsh, extuh, extsw, extuw,
|
|
153
|
+
exts, truncd, stosi, stoui, dtosi, dtoui,
|
|
154
|
+
swtof, uwtof, sltof, ultof, cast
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
op: str
|
|
158
|
+
result: Temporary
|
|
159
|
+
result_type: BaseType
|
|
160
|
+
operand: Value
|
|
161
|
+
|
|
162
|
+
def emit(self) -> str:
|
|
163
|
+
return f"{self.result} ={self.result_type.value} {self.op} {self.operand}"
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@dataclass
|
|
167
|
+
class Call(Instruction):
|
|
168
|
+
"""Function call.
|
|
169
|
+
|
|
170
|
+
Args are tuples of (type, value).
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
target: Value
|
|
174
|
+
args: list[tuple[BaseType | str, Value]]
|
|
175
|
+
result: Temporary | None = None
|
|
176
|
+
result_type: BaseType | str | None = None
|
|
177
|
+
is_variadic: bool = False
|
|
178
|
+
|
|
179
|
+
def emit(self) -> str:
|
|
180
|
+
# Format arguments
|
|
181
|
+
arg_strs = []
|
|
182
|
+
for arg_type, arg_val in self.args:
|
|
183
|
+
if isinstance(arg_type, BaseType):
|
|
184
|
+
type_str = arg_type.value
|
|
185
|
+
else:
|
|
186
|
+
type_str = f":{arg_type}"
|
|
187
|
+
arg_strs.append(f"{type_str} {arg_val}")
|
|
188
|
+
|
|
189
|
+
if self.is_variadic:
|
|
190
|
+
arg_strs.append("...")
|
|
191
|
+
|
|
192
|
+
args_str = ", ".join(arg_strs)
|
|
193
|
+
|
|
194
|
+
if self.result:
|
|
195
|
+
if isinstance(self.result_type, BaseType):
|
|
196
|
+
type_str = self.result_type.value
|
|
197
|
+
else:
|
|
198
|
+
type_str = f":{self.result_type}"
|
|
199
|
+
return f"{self.result} ={type_str} call {self.target}({args_str})"
|
|
200
|
+
return f"call {self.target}({args_str})"
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
@dataclass
|
|
204
|
+
class Phi(Instruction):
|
|
205
|
+
"""Phi instruction for SSA form.
|
|
206
|
+
|
|
207
|
+
Incoming is a list of (Label, Value) pairs.
|
|
208
|
+
"""
|
|
209
|
+
|
|
210
|
+
result: Temporary
|
|
211
|
+
result_type: BaseType
|
|
212
|
+
incoming: list[tuple[Label, Value]]
|
|
213
|
+
|
|
214
|
+
def emit(self) -> str:
|
|
215
|
+
pairs = ", ".join(f"{label} {value}" for label, value in self.incoming)
|
|
216
|
+
return f"{self.result} ={self.result_type.value} phi {pairs}"
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
@dataclass
|
|
220
|
+
class VaStart(Instruction):
|
|
221
|
+
"""Initialize variadic argument list."""
|
|
222
|
+
|
|
223
|
+
va_list: Value
|
|
224
|
+
|
|
225
|
+
def emit(self) -> str:
|
|
226
|
+
return f"vastart {self.va_list}"
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
@dataclass
|
|
230
|
+
class VaArg(Instruction):
|
|
231
|
+
"""Fetch next variadic argument."""
|
|
232
|
+
|
|
233
|
+
result: Temporary
|
|
234
|
+
result_type: BaseType
|
|
235
|
+
va_list: Value
|
|
236
|
+
|
|
237
|
+
def emit(self) -> str:
|
|
238
|
+
return f"{self.result} ={self.result_type.value} vaarg {self.va_list}"
|
qbepy/ir/types.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""QBE type definitions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from enum import Enum
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BaseType(Enum):
|
|
10
|
+
"""Base types for temporaries and function returns."""
|
|
11
|
+
|
|
12
|
+
WORD = "w" # 32-bit integer
|
|
13
|
+
LONG = "l" # 64-bit integer
|
|
14
|
+
SINGLE = "s" # 32-bit float
|
|
15
|
+
DOUBLE = "d" # 64-bit float
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ExtType(Enum):
|
|
19
|
+
"""Extended types for data and aggregate fields."""
|
|
20
|
+
|
|
21
|
+
BYTE = "b" # 8-bit
|
|
22
|
+
HALF = "h" # 16-bit halfword
|
|
23
|
+
WORD = "w" # 32-bit word
|
|
24
|
+
LONG = "l" # 64-bit long
|
|
25
|
+
SINGLE = "s" # 32-bit float
|
|
26
|
+
DOUBLE = "d" # 64-bit float
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class SubWordType(Enum):
|
|
30
|
+
"""Sub-word types for ABI compatibility."""
|
|
31
|
+
|
|
32
|
+
SIGNED_BYTE = "sb"
|
|
33
|
+
UNSIGNED_BYTE = "ub"
|
|
34
|
+
SIGNED_HALF = "sh"
|
|
35
|
+
UNSIGNED_HALF = "uh"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# Convenience aliases
|
|
39
|
+
W = BaseType.WORD
|
|
40
|
+
L = BaseType.LONG
|
|
41
|
+
S = BaseType.SINGLE
|
|
42
|
+
D = BaseType.DOUBLE
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class AggregateType:
|
|
47
|
+
"""User-defined aggregate type (struct/union).
|
|
48
|
+
|
|
49
|
+
Example:
|
|
50
|
+
# type :point = { w, w }
|
|
51
|
+
point = AggregateType("point", [(ExtType.WORD, 1), (ExtType.WORD, 1)])
|
|
52
|
+
|
|
53
|
+
# type :vec = align 16 { d 4 }
|
|
54
|
+
vec = AggregateType("vec", [(ExtType.DOUBLE, 4)], align=16)
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
name: str
|
|
58
|
+
fields: list[tuple[ExtType | str, int]] = field(default_factory=list)
|
|
59
|
+
align: int | None = None
|
|
60
|
+
is_opaque: bool = False
|
|
61
|
+
opaque_size: int | None = None
|
|
62
|
+
|
|
63
|
+
def emit(self) -> str:
|
|
64
|
+
"""Emit this type definition as QBE IL."""
|
|
65
|
+
align_str = f"align {self.align} " if self.align else ""
|
|
66
|
+
|
|
67
|
+
if self.is_opaque:
|
|
68
|
+
return f"type :{self.name} = {align_str}{{ {self.opaque_size} }}"
|
|
69
|
+
|
|
70
|
+
parts = []
|
|
71
|
+
for field_type, count in self.fields:
|
|
72
|
+
if isinstance(field_type, ExtType):
|
|
73
|
+
type_str = field_type.value
|
|
74
|
+
else:
|
|
75
|
+
# Nested aggregate type reference
|
|
76
|
+
type_str = f":{field_type}"
|
|
77
|
+
|
|
78
|
+
if count > 1:
|
|
79
|
+
parts.append(f"{type_str} {count}")
|
|
80
|
+
else:
|
|
81
|
+
parts.append(type_str)
|
|
82
|
+
|
|
83
|
+
fields_str = ", ".join(parts)
|
|
84
|
+
return f"type :{self.name} = {align_str}{{ {fields_str} }}"
|
qbepy/ir/values.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""QBE value types."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True)
|
|
9
|
+
class Temporary:
|
|
10
|
+
"""Function-scope temporary variable (%name)."""
|
|
11
|
+
|
|
12
|
+
name: str
|
|
13
|
+
|
|
14
|
+
def __str__(self) -> str:
|
|
15
|
+
return f"%{self.name}"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True)
|
|
19
|
+
class Global:
|
|
20
|
+
"""Global symbol ($name)."""
|
|
21
|
+
|
|
22
|
+
name: str
|
|
23
|
+
|
|
24
|
+
def __str__(self) -> str:
|
|
25
|
+
return f"${self.name}"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True)
|
|
29
|
+
class Label:
|
|
30
|
+
"""Block label (@name)."""
|
|
31
|
+
|
|
32
|
+
name: str
|
|
33
|
+
|
|
34
|
+
def __str__(self) -> str:
|
|
35
|
+
return f"@{self.name}"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass(frozen=True)
|
|
39
|
+
class IntConst:
|
|
40
|
+
"""Integer constant."""
|
|
41
|
+
|
|
42
|
+
value: int
|
|
43
|
+
|
|
44
|
+
def __str__(self) -> str:
|
|
45
|
+
return str(self.value)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass(frozen=True)
|
|
49
|
+
class FloatConst:
|
|
50
|
+
"""Floating-point constant.
|
|
51
|
+
|
|
52
|
+
QBE uses special syntax for floats:
|
|
53
|
+
s_<float> for single precision
|
|
54
|
+
d_<double> for double precision
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
value: float
|
|
58
|
+
is_single: bool = False
|
|
59
|
+
|
|
60
|
+
def __str__(self) -> str:
|
|
61
|
+
prefix = "s_" if self.is_single else "d_"
|
|
62
|
+
return f"{prefix}{self.value}"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass(frozen=True)
|
|
66
|
+
class TypeRef:
|
|
67
|
+
"""Reference to an aggregate type (:typename)."""
|
|
68
|
+
|
|
69
|
+
name: str
|
|
70
|
+
|
|
71
|
+
def __str__(self) -> str:
|
|
72
|
+
return f":{self.name}"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# Type alias for any value that can be used as an operand
|
|
76
|
+
Value = Temporary | Global | IntConst | FloatConst | TypeRef
|