Skip to content

Commit f0c78c1

Browse files
authored
[mypyc] Generate names lazily when pretty-printing IR or generating C (#9767)
Instead of generating names for ops when building IR, we now generate names immediately before pretty-printing or generating C. This simplifies `Environment` and reduces the amount of mutable state we need to maintain. This a step towards removing the `mypyc.ir.ops.Environment` class. Also move code related to pretty-printing to `mypyc.ir.pprint`. Work on mypyc/mypyc#781.
1 parent 0658911 commit f0c78c1

15 files changed

Lines changed: 488 additions & 424 deletions

mypyc/build.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
from mypyc.options import CompilerOptions
3939
from mypyc.errors import Errors
4040
from mypyc.common import RUNTIME_C_FILES, shared_lib_name
41-
from mypyc.ir.module_ir import format_modules
41+
from mypyc.ir.pprint import format_modules
4242

4343
from mypyc.codegen import emitmodule
4444

mypyc/codegen/emit.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,13 @@ def __init__(self,
8888
class Emitter:
8989
"""Helper for C code generation."""
9090

91-
def __init__(self, context: EmitterContext, env: Optional[Environment] = None) -> None:
91+
def __init__(self,
92+
context: EmitterContext,
93+
env: Optional[Environment] = None,
94+
value_names: Optional[Dict[Value, str]] = None) -> None:
9295
self.context = context
9396
self.names = context.names
97+
self.value_names = value_names or {}
9498
self.env = env or Environment()
9599
self.fragments = [] # type: List[str]
96100
self._indent = 0
@@ -108,7 +112,7 @@ def label(self, label: BasicBlock) -> str:
108112
return 'CPyL%s' % label.label
109113

110114
def reg(self, reg: Value) -> str:
111-
return REG_PREFIX + reg.name
115+
return REG_PREFIX + self.value_names[reg]
112116

113117
def attr(self, name: str) -> str:
114118
return ATTR_PREFIX + name

mypyc/codegen/emitfunc.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from mypyc.ir.func_ir import FuncIR, FuncDecl, FUNC_STATICMETHOD, FUNC_CLASSMETHOD
2222
from mypyc.ir.class_ir import ClassIR
2323
from mypyc.ir.const_int import find_constant_integer_registers
24+
from mypyc.ir.pprint import generate_names_for_env
2425

2526
# Whether to insert debug asserts for all error handling, to quickly
2627
# catch errors propagating without exceptions set.
@@ -54,7 +55,8 @@ def generate_native_function(fn: FuncIR,
5455
else:
5556
const_int_regs = {}
5657
declarations = Emitter(emitter.context, fn.env)
57-
body = Emitter(emitter.context, fn.env)
58+
names = generate_names_for_env(fn.env)
59+
body = Emitter(emitter.context, fn.env, names)
5860
visitor = FunctionEmitterVisitor(body, declarations, source_path, module_name, const_int_regs)
5961

6062
declarations.emit_line('{} {{'.format(native_function_header(fn.decl, emitter)))
@@ -69,11 +71,11 @@ def generate_native_function(fn: FuncIR,
6971
init = ''
7072
if r in fn.env.vars_needing_init:
7173
init = ' = {}'.format(declarations.c_error_value(r.type))
72-
if r.name not in const_int_regs:
74+
if r not in const_int_regs:
7375
declarations.emit_line('{ctype}{prefix}{name}{init};'.format(ctype=ctype,
74-
prefix=REG_PREFIX,
75-
name=r.name,
76-
init=init))
76+
prefix=REG_PREFIX,
77+
name=names[r],
78+
init=init))
7779

7880
# Before we emit the blocks, give them all labels
7981
for i, block in enumerate(fn.blocks):
@@ -96,7 +98,7 @@ def __init__(self,
9698
declarations: Emitter,
9799
source_path: str,
98100
module_name: str,
99-
const_int_regs: Dict[str, int]) -> None:
101+
const_int_regs: Dict[LoadInt, int]) -> None:
100102
self.emitter = emitter
101103
self.names = emitter.names
102104
self.declarations = declarations
@@ -172,7 +174,7 @@ def visit_assign(self, op: Assign) -> None:
172174
self.emit_line('%s = %s;' % (dest, src))
173175

174176
def visit_load_int(self, op: LoadInt) -> None:
175-
if op.name in self.const_int_regs:
177+
if op in self.const_int_regs:
176178
return
177179
dest = self.reg(op)
178180
self.emit_line('%s = %d;' % (dest, op.value))
@@ -403,8 +405,8 @@ def visit_raise_standard_error(self, op: RaiseStandardError) -> None:
403405
'PyErr_SetString(PyExc_{}, "{}");'.format(op.class_name, message))
404406
elif isinstance(op.value, Value):
405407
self.emitter.emit_line(
406-
'PyErr_SetObject(PyExc_{}, {}{});'.format(op.class_name, REG_PREFIX,
407-
op.value.name))
408+
'PyErr_SetObject(PyExc_{}, {});'.format(op.class_name,
409+
self.emitter.reg(op.value)))
408410
else:
409411
assert False, 'op value type must be either str or Value'
410412
else:
@@ -494,8 +496,8 @@ def label(self, label: BasicBlock) -> str:
494496
return self.emitter.label(label)
495497

496498
def reg(self, reg: Value) -> str:
497-
if reg.name in self.const_int_regs:
498-
val = self.const_int_regs[reg.name]
499+
if isinstance(reg, LoadInt) and reg in self.const_int_regs:
500+
val = self.const_int_regs[reg]
499501
if val == 0 and is_pointer_rprimitive(reg.type):
500502
return "NULL"
501503
return str(val)

mypyc/ir/const_int.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,11 @@
33
from mypyc.ir.ops import BasicBlock, LoadInt
44

55

6-
def find_constant_integer_registers(blocks: List[BasicBlock]) -> Dict[str, int]:
7-
"""Find all registers with constant integer values.
8-
9-
Returns a mapping from register names to int values
10-
"""
11-
const_int_regs = {} # type: Dict[str, int]
6+
def find_constant_integer_registers(blocks: List[BasicBlock]) -> Dict[LoadInt, int]:
7+
"""Find all registers with constant integer values."""
8+
const_int_regs = {} # type: Dict[LoadInt, int]
129
for block in blocks:
1310
for op in block.ops:
1411
if isinstance(op, LoadInt):
15-
const_int_regs[op.name] = op.value
12+
const_int_regs[op] = op.value
1613
return const_int_regs

mypyc/ir/func_ir.py

Lines changed: 7 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
"""Intermediate representation of functions."""
2-
import re
32

4-
from typing import List, Optional, Sequence, Dict
3+
from typing import List, Optional, Sequence
54
from typing_extensions import Final
65

76
from mypy.nodes import FuncDef, Block, ARG_POS, ARG_OPT, ARG_NAMED_OPT
87

98
from mypyc.common import JsonDict
10-
from mypyc.ir.ops import (
11-
DeserMaps, Goto, Branch, Return, Unreachable, BasicBlock, Environment
12-
)
9+
from mypyc.ir.ops import DeserMaps, BasicBlock, Environment
1310
from mypyc.ir.rtypes import RType, deserialize_type
14-
from mypyc.ir.const_int import find_constant_integer_registers
1511
from mypyc.namegen import NameGenerator
1612

1713

@@ -195,8 +191,11 @@ def fullname(self) -> str:
195191
def cname(self, names: NameGenerator) -> str:
196192
return self.decl.cname(names)
197193

198-
def __str__(self) -> str:
199-
return '\n'.join(format_func(self))
194+
def __repr__(self) -> str:
195+
if self.class_name:
196+
return '<FuncIR {}.{}>'.format(self.class_name, self.name)
197+
else:
198+
return '<FuncIR {}>'.format(self.name)
200199

201200
def serialize(self) -> JsonDict:
202201
# We don't include blocks or env in the serialized version
@@ -218,58 +217,3 @@ def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> 'FuncIR':
218217

219218

220219
INVALID_FUNC_DEF = FuncDef('<INVALID_FUNC_DEF>', [], Block([])) # type: Final
221-
222-
223-
def format_blocks(blocks: List[BasicBlock],
224-
env: Environment,
225-
const_regs: Dict[str, int]) -> List[str]:
226-
"""Format a list of IR basic blocks into a human-readable form."""
227-
# First label all of the blocks
228-
for i, block in enumerate(blocks):
229-
block.label = i
230-
231-
handler_map = {} # type: Dict[BasicBlock, List[BasicBlock]]
232-
for b in blocks:
233-
if b.error_handler:
234-
handler_map.setdefault(b.error_handler, []).append(b)
235-
236-
lines = []
237-
for i, block in enumerate(blocks):
238-
handler_msg = ''
239-
if block in handler_map:
240-
labels = sorted(env.format('%l', b.label) for b in handler_map[block])
241-
handler_msg = ' (handler for {})'.format(', '.join(labels))
242-
243-
lines.append(env.format('%l:%s', block.label, handler_msg))
244-
ops = block.ops
245-
if (isinstance(ops[-1], Goto) and i + 1 < len(blocks)
246-
and ops[-1].label == blocks[i + 1]):
247-
# Hide the last goto if it just goes to the next basic block.
248-
ops = ops[:-1]
249-
# load int registers start with 'i'
250-
regex = re.compile(r'\bi[0-9]+\b')
251-
for op in ops:
252-
if op.name not in const_regs:
253-
line = ' ' + op.to_str(env)
254-
line = regex.sub(lambda i: str(const_regs[i.group()]) if i.group() in const_regs
255-
else i.group(), line)
256-
lines.append(line)
257-
258-
if not isinstance(block.ops[-1], (Goto, Branch, Return, Unreachable)):
259-
# Each basic block needs to exit somewhere.
260-
lines.append(' [MISSING BLOCK EXIT OPCODE]')
261-
return lines
262-
263-
264-
def format_func(fn: FuncIR) -> List[str]:
265-
lines = []
266-
cls_prefix = fn.class_name + '.' if fn.class_name else ''
267-
lines.append('def {}{}({}):'.format(cls_prefix, fn.name,
268-
', '.join(arg.name for arg in fn.args)))
269-
# compute constants
270-
const_regs = find_constant_integer_registers(fn.blocks)
271-
for line in fn.env.to_lines(const_regs):
272-
lines.append(' ' + line)
273-
code = format_blocks(fn.blocks, fn.env, const_regs)
274-
lines.extend(code)
275-
return lines

mypyc/ir/module_ir.py

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from mypyc.common import JsonDict
66
from mypyc.ir.ops import DeserMaps
77
from mypyc.ir.rtypes import RType, deserialize_type
8-
from mypyc.ir.func_ir import FuncIR, FuncDecl, format_func
8+
from mypyc.ir.func_ir import FuncIR, FuncDecl
99
from mypyc.ir.class_ir import ClassIR
1010

1111

@@ -82,12 +82,3 @@ def deserialize_modules(data: Dict[str, JsonDict], ctx: DeserMaps) -> Dict[str,
8282
# ModulesIRs should also always be an *OrderedDict*, but if we
8383
# declared it that way we would need to put it in quotes everywhere...
8484
ModuleIRs = Dict[str, ModuleIR]
85-
86-
87-
def format_modules(modules: ModuleIRs) -> List[str]:
88-
ops = []
89-
for module in modules.values():
90-
for fn in module.functions:
91-
ops.extend(format_func(fn))
92-
ops.append('')
93-
return ops

0 commit comments

Comments
 (0)