Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 75cd2ab

Browse files
authored
Overhaul the IR (mypyc/mypyc#92)
Overhaul the intermediate representation to be more like LLVM's. (Though not exactly the same, since they do real SSA and this is at most "half-ssa".) Instead of having operations have explicit registers as their operands and destinations, ops instead serve as *intrinsic* temporaries. The old `Register` type, which was used to represent operands to `Op`s and the result of expressions, is replaced with a new `Value` type, which is the root of a class hierarchy. Every `Value` has an associated `RType` (and a new `RVoid` is added for operations that produce no useful output.) and a name. `Op` then becomes a subclass of `Value`, with the value being the result of the op. We then add a new `Register` type as a subclass of `Value`, used for arguments, locals, and temporaries that need multiple assignments (because of loops or branches). The `Assign` op assigns a `Value` source to a `Register` destination. This allows `genops` to be simpler, eliminating almost allocation of temporaries. Making `RType` intrinsic to values allows cleaning up and eliminating a lot of code that messes around with types. One downside is that a lot of computations have an extra assignment now. The C compiler will fix that up, but it still makes the generated code uglier. This also resulted in a lot of test churn.
1 parent d8eb814 commit 75cd2ab

24 files changed

Lines changed: 1465 additions & 1325 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ test_capi
88

99
/env
1010
/env-debug
11+
/env-3.5
1112
/.mypy_cache
1213
/.cache
1314
/.pytest_cache

mypyc/analysis.py

Lines changed: 38 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22

33
from abc import abstractmethod
44

5-
from typing import Dict, Tuple, List, Set, TypeVar, Iterator, Generic, Optional
5+
from typing import Dict, Tuple, List, Set, TypeVar, Iterator, Generic, Optional, Iterable
66

77
from mypyc.ops import (
8+
Value, Register,
89
BasicBlock, OpVisitor, Assign, LoadInt, LoadErrorValue, RegisterOp, Goto,
910
Branch, Return, Call, Environment, Box, Unbox, Cast, Op, Unreachable,
10-
TupleGet, TupleSet, GetAttr, SetAttr, PyCall, LoadStatic, PyGetAttr, Label, Register,
11+
TupleGet, TupleSet, GetAttr, SetAttr, PyCall, LoadStatic, PyGetAttr, Label,
1112
PyMethodCall, PrimitiveOp, MethodCall,
1213
)
1314

@@ -75,7 +76,7 @@ def __init__(self, before: AnalysisDict[T], after: AnalysisDict[T]) -> None:
7576
def __str__(self) -> str:
7677
return 'before: %s\nafter: %s\n' % (self.before, self.after)
7778

78-
GenAndKill = Tuple[Set[Register], Set[Register]]
79+
GenAndKill = Tuple[Set[Value], Set[Value]]
7980

8081

8182
class BaseAnalysisVisitor(OpVisitor[GenAndKill]):
@@ -86,6 +87,10 @@ def visit_goto(self, op: Goto) -> GenAndKill:
8687
def visit_register_op(self, op: RegisterOp) -> GenAndKill:
8788
raise NotImplementedError
8889

90+
@abstractmethod
91+
def visit_assign(self, op: Assign) -> GenAndKill:
92+
raise NotImplementedError
93+
8994
def visit_call(self, op: Call) -> GenAndKill:
9095
return self.visit_register_op(op)
9196

@@ -101,9 +106,6 @@ def visit_py_method_call(self, op: PyMethodCall) -> GenAndKill:
101106
def visit_primitive_op(self, op: PrimitiveOp) -> GenAndKill:
102107
return self.visit_register_op(op)
103108

104-
def visit_assign(self, op: Assign) -> GenAndKill:
105-
return self.visit_register_op(op)
106-
107109
def visit_load_int(self, op: LoadInt) -> GenAndKill:
108110
return self.visit_register_op(op)
109111

@@ -149,15 +151,18 @@ def visit_unreachable(self, op: Unreachable) -> GenAndKill:
149151
return set(), set()
150152

151153
def visit_register_op(self, op: RegisterOp) -> GenAndKill:
152-
if op.dest is not None:
153-
return {op.dest}, set()
154+
if not op.is_void:
155+
return {op}, set()
154156
else:
155157
return set(), set()
156158

159+
def visit_assign(self, op: Assign) -> GenAndKill:
160+
return {op.dest}, set()
161+
157162

158163
def analyze_maybe_defined_regs(blocks: List[BasicBlock],
159164
cfg: CFG,
160-
initial_defined: Set[Register]) -> AnalysisResult[Register]:
165+
initial_defined: Set[Value]) -> AnalysisResult[Value]:
161166
"""Calculate potentially defined registers at each CFG location.
162167
163168
A register is defined if it has a value along some path from the initial location.
@@ -183,17 +188,20 @@ def visit_unreachable(self, op: Unreachable) -> GenAndKill:
183188
return set(), set()
184189

185190
def visit_register_op(self, op: RegisterOp) -> GenAndKill:
186-
if op.dest is not None:
187-
return {op.dest}, set()
191+
if not op.is_void:
192+
return {op}, set()
188193
else:
189194
return set(), set()
190195

196+
def visit_assign(self, op: Assign) -> GenAndKill:
197+
return {op.dest}, set()
198+
191199

192200
def analyze_must_defined_regs(
193201
blocks: List[BasicBlock],
194202
cfg: CFG,
195-
initial_defined: Set[Register],
196-
num_regs: int) -> AnalysisResult[Register]:
203+
initial_defined: Set[Value],
204+
regs: Iterable[Value]) -> AnalysisResult[Value]:
197205
"""Calculate always defined registers at each CFG location.
198206
199207
A register is defined if it has a value along all paths from the initial location.
@@ -204,11 +212,11 @@ def analyze_must_defined_regs(
204212
initial=initial_defined,
205213
backward=False,
206214
kind=MUST_ANALYSIS,
207-
universe=set([Register(r) for r in range(num_regs)]))
215+
universe=set(regs))
208216

209217

210218
class BorrowedArgumentsVisitor(BaseAnalysisVisitor):
211-
def __init__(self, args: Set[Register]) -> None:
219+
def __init__(self, args: Set[Value]) -> None:
212220
self.args = args
213221

214222
def visit_branch(self, op: Branch) -> GenAndKill:
@@ -221,15 +229,17 @@ def visit_unreachable(self, op: Unreachable) -> GenAndKill:
221229
return set(), set()
222230

223231
def visit_register_op(self, op: RegisterOp) -> GenAndKill:
232+
return set(), set()
233+
234+
def visit_assign(self, op: Assign) -> GenAndKill:
224235
if op.dest in self.args:
225236
return set(), {op.dest}
226237
return set(), set()
227238

228-
229239
def analyze_borrowed_arguments(
230240
blocks: List[BasicBlock],
231241
cfg: CFG,
232-
args: Set[Register]) -> AnalysisResult[Register]:
242+
args: Set[Value]) -> AnalysisResult[Value]:
233243
"""Calculate arguments that can use references borrowed from the caller.
234244
235245
When assigning to an argument, it no longer is borrowed.
@@ -254,21 +264,21 @@ def visit_unreachable(self, op: Unreachable) -> GenAndKill:
254264
return set(), set()
255265

256266
def visit_register_op(self, op: RegisterOp) -> GenAndKill:
257-
return set(), {op.dest} if op.dest is not None else set()
267+
return set(), {op} if not op.is_void else set()
258268

269+
def visit_assign(self, op: Assign) -> GenAndKill:
270+
return set(), {op.dest}
259271

260272
def analyze_undefined_regs(blocks: List[BasicBlock],
261273
cfg: CFG,
262274
env: Environment,
263-
initial_defined: Set[Register]) -> AnalysisResult[Register]:
275+
initial_defined: Set[Value]) -> AnalysisResult[Value]:
264276
"""Calculate potentially undefined registers at each CFG location.
265277
266278
A register is undefined if there is some path from initial block
267279
where it has an undefined value.
268280
"""
269-
initial_undefined = {Register(reg)
270-
for reg in range(len(env.names))
271-
if Register(reg) not in initial_defined}
281+
initial_undefined = set(env.regs()) - initial_defined
272282
return run_analysis(blocks=blocks,
273283
cfg=cfg,
274284
gen_and_kill=UndefinedVisitor(),
@@ -289,14 +299,17 @@ def visit_unreachable(self, op: Unreachable) -> GenAndKill:
289299

290300
def visit_register_op(self, op: RegisterOp) -> GenAndKill:
291301
gen = set(op.sources())
292-
if op.dest is not None:
293-
return gen, {op.dest}
302+
if not op.is_void:
303+
return gen, {op}
294304
else:
295305
return gen, set()
296306

307+
def visit_assign(self, op: Assign) -> GenAndKill:
308+
return set(op.sources()), {op.dest}
309+
297310

298311
def analyze_live_regs(blocks: List[BasicBlock],
299-
cfg: CFG) -> AnalysisResult[Register]:
312+
cfg: CFG) -> AnalysisResult[Value]:
300313
"""Calculate live registers at each CFG location.
301314
302315
A register is live at a location if it can be read along some CFG path starting

mypyc/emit.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
from mypyc.common import REG_PREFIX
77
from mypyc.ops import (
8-
Environment, Label, Register, RType, RTuple, RInstance, ROptional,
8+
Environment, Label, Value, Register, RType, RTuple, RInstance, ROptional,
99
RPrimitive, type_struct_name, is_int_rprimitive, is_bool_rprimitive, short_name,
1010
is_list_rprimitive, is_dict_rprimitive, is_tuple_rprimitive, is_none_rprimitive,
1111
object_rprimitive, is_str_rprimitive
@@ -51,9 +51,8 @@ def dedent(self) -> None:
5151
def label(self, label: Label) -> str:
5252
return 'CPyL%d' % label
5353

54-
def reg(self, reg: Register) -> str:
55-
name = self.env.names[reg]
56-
return REG_PREFIX + name
54+
def reg(self, reg: Value) -> str:
55+
return REG_PREFIX + reg.name
5756

5857
def emit_line(self, line: str = '') -> None:
5958
if line.startswith('}'):

0 commit comments

Comments
 (0)