From 901fae99708a027a9ea84707a8c01f77c5bcfc34 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 24 Nov 2020 14:39:06 +0000 Subject: [PATCH 01/10] Start refactoring away of indexes and symbol table in Environment Also refactor Register.__init__ and temp register generation. --- mypyc/analysis/dataflow.py | 5 +- mypyc/codegen/emitfunc.py | 12 ++- mypyc/ir/func_ir.py | 66 ++++++++++++- mypyc/ir/ops.py | 62 ++---------- mypyc/ir/pprint.py | 91 +++++++++++------- mypyc/irbuild/builder.py | 64 ++++++++++--- mypyc/irbuild/callable_class.py | 18 ++-- mypyc/irbuild/classdef.py | 23 ++--- mypyc/irbuild/env_class.py | 22 +++-- mypyc/irbuild/expression.py | 16 ++-- mypyc/irbuild/for_helpers.py | 2 +- mypyc/irbuild/function.py | 56 +++++------ mypyc/irbuild/generator.py | 65 ++++++------- mypyc/irbuild/ll_builder.py | 18 ++-- mypyc/irbuild/main.py | 4 +- mypyc/irbuild/nonlocalcontrol.py | 2 +- mypyc/irbuild/specialize.py | 6 +- mypyc/irbuild/statement.py | 4 +- mypyc/irbuild/util.py | 11 --- mypyc/test-data/analysis.test | 9 +- mypyc/test-data/exceptions.test | 27 +++--- mypyc/test-data/irbuild-any.test | 8 +- mypyc/test-data/irbuild-basic.test | 62 ++++++------ mypyc/test-data/irbuild-dict.test | 4 +- mypyc/test-data/irbuild-int.test | 21 +++-- mypyc/test-data/irbuild-optional.test | 16 ++-- mypyc/test-data/irbuild-statements.test | 14 +-- mypyc/test-data/irbuild-try.test | 27 +++--- mypyc/test-data/irbuild-tuple.test | 82 ++++++++-------- mypyc/test-data/refcount.test | 3 +- mypyc/test/test_analysis.py | 10 +- mypyc/test/test_emit.py | 10 +- mypyc/test/test_emitfunc.py | 120 +++++++++++++----------- mypyc/transform/exceptions.py | 2 - mypyc/transform/refcount.py | 66 ++++++++----- mypyc/transform/uninit.py | 10 +- 36 files changed, 563 insertions(+), 475 deletions(-) diff --git a/mypyc/analysis/dataflow.py b/mypyc/analysis/dataflow.py index ddf0bf865d067..07ea70bed7e98 100644 --- a/mypyc/analysis/dataflow.py +++ b/mypyc/analysis/dataflow.py @@ -11,6 +11,7 @@ LoadStatic, InitStatic, MethodCall, RaiseStandardError, CallC, LoadGlobal, Truncate, BinaryIntOp, LoadMem, GetElementPtr, LoadAddress, ComparisonOp, SetMem ) +from mypyc.ir.func_ir import all_values class CFG: @@ -361,8 +362,10 @@ def analyze_undefined_regs(blocks: List[BasicBlock], A register is undefined if there is some path from initial block where it has an undefined value. + + Function arguments are assumed to be always defined. """ - initial_undefined = set(env.regs()) - initial_defined + initial_undefined = set(all_values([], blocks)) - initial_defined return run_analysis(blocks=blocks, cfg=cfg, gen_and_kill=UndefinedVisitor(), diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index fe26c25912f77..2afcf63f8e2c8 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -18,7 +18,7 @@ RType, RTuple, is_tagged, is_int32_rprimitive, is_int64_rprimitive, RStruct, is_pointer_rprimitive ) -from mypyc.ir.func_ir import FuncIR, FuncDecl, FUNC_STATICMETHOD, FUNC_CLASSMETHOD +from mypyc.ir.func_ir import FuncIR, FuncDecl, FUNC_STATICMETHOD, FUNC_CLASSMETHOD, all_values from mypyc.ir.class_ir import ClassIR from mypyc.ir.const_int import find_constant_integer_registers from mypyc.ir.pprint import generate_names_for_env @@ -55,18 +55,20 @@ def generate_native_function(fn: FuncIR, else: const_int_regs = {} declarations = Emitter(emitter.context, fn.env) - names = generate_names_for_env(fn.env) + names = generate_names_for_env(fn.arg_regs, fn.blocks) body = Emitter(emitter.context, fn.env, names) visitor = FunctionEmitterVisitor(body, declarations, source_path, module_name, const_int_regs) declarations.emit_line('{} {{'.format(native_function_header(fn.decl, emitter))) body.indent() - for r, i in fn.env.indexes.items(): + for r in all_values(fn.arg_regs, fn.blocks): if isinstance(r.type, RTuple): emitter.declare_tuple_struct(r.type) - if i < len(fn.args): - continue # skip the arguments + + if r in fn.arg_regs: + continue # Skip the arguments + ctype = emitter.ctype_spaced(r.type) init = '' if r in fn.env.vars_needing_init: diff --git a/mypyc/ir/func_ir.py b/mypyc/ir/func_ir.py index d0da1553aaeb7..cec8ab046924a 100644 --- a/mypyc/ir/func_ir.py +++ b/mypyc/ir/func_ir.py @@ -6,7 +6,7 @@ from mypy.nodes import FuncDef, Block, ARG_POS, ARG_OPT, ARG_NAMED_OPT from mypyc.common import JsonDict -from mypyc.ir.ops import DeserMaps, BasicBlock, Environment +from mypyc.ir.ops import DeserMaps, BasicBlock, Environment, Value, Register, Assign, ControlOp from mypyc.ir.rtypes import RType, deserialize_type from mypyc.namegen import NameGenerator @@ -151,11 +151,13 @@ class FuncIR: def __init__(self, decl: FuncDecl, + arg_regs: List[Register], blocks: List[BasicBlock], env: Environment, line: int = -1, traceback_name: Optional[str] = None) -> None: self.decl = decl + self.arg_regs = arg_regs self.blocks = blocks self.env = env self.line = line @@ -210,6 +212,7 @@ def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> 'FuncIR': return FuncIR( FuncDecl.deserialize(data['decl'], ctx), [], + [], Environment(), data['line'], data['traceback_name'], @@ -217,3 +220,64 @@ def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> 'FuncIR': INVALID_FUNC_DEF = FuncDef('', [], Block([])) # type: Final + + +def all_values(args: List[Register], blocks: List[BasicBlock]) -> List[Value]: + """Return set of all values that are initialized at least once. + + This omits registers that are never assigned to. + """ + values = list(args) # type: List[Value] + seen_registers = set(args) + + for block in blocks: + for op in block.ops: + if not isinstance(op, ControlOp): + if isinstance(op, Assign): + if op.dest not in seen_registers: + values.append(op.dest) + seen_registers.add(op.dest) + elif op.is_void: + continue + else: + values.append(op) + + return values + + +def all_values_full(args: List[Register], blocks: List[BasicBlock]) -> List[Value]: + """Return set of all values that are initialized or accessed.""" + values = list(args) # type: List[Value] + seen_registers = set(args) + + for block in blocks: + for op in block.ops: + for source in op.sources(): + # Look for unitialized registers that are accessed. Ignore + # non-registers since we don't allow ops outside basic blocks. + if isinstance(source, Register) and source not in seen_registers: + values.append(source) + seen_registers.add(source) + if not isinstance(op, ControlOp): + if isinstance(op, Assign): + if op.dest not in seen_registers: + values.append(op.dest) + seen_registers.add(op.dest) + elif op.is_void: + continue + else: + values.append(op) + + return values + + +def all_registers(ir: FuncIR) -> List[Register]: + """Return set of all registers that are initialized at least once.""" + registers = list(ir.arg_regs) + seen = set(registers) + for block in ir.blocks: + for op in block.ops: + if isinstance(op, Assign) and op.dest not in seen: + registers.append(op.dest) + seen.add(op.dest) + return registers diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index c669912a40a3f..23f61f9eee69c 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -12,15 +12,12 @@ from abc import abstractmethod from typing import ( - List, Sequence, Dict, Generic, TypeVar, Optional, NamedTuple, Tuple, Union, Iterable, Set + List, Sequence, Dict, Generic, TypeVar, Optional, NamedTuple, Tuple, Union, Set ) -from mypy.ordered_dict import OrderedDict from typing_extensions import Final, Type, TYPE_CHECKING from mypy_extensions import trait -from mypy.nodes import SymbolNode - from mypyc.ir.rtypes import ( RType, RInstance, RTuple, RVoid, is_bool_rprimitive, is_int_rprimitive, is_short_int_rprimitive, is_none_rprimitive, object_rprimitive, bool_rprimitive, @@ -114,59 +111,11 @@ def __init__(self, items: List[AssignmentTarget], class Environment: - """Maintain the register symbol table and manage temp generation""" + # TODO: Remove this class def __init__(self) -> None: - self.indexes = OrderedDict() # type: Dict[Value, int] - self.symtable = OrderedDict() # type: OrderedDict[SymbolNode, AssignmentTarget] self.vars_needing_init = set() # type: Set[Value] - def regs(self) -> Iterable['Value']: - return self.indexes.keys() - - def add(self, value: 'Value') -> None: - self.indexes[value] = len(self.indexes) - - def add_local(self, symbol: SymbolNode, typ: RType, is_arg: bool = False) -> 'Register': - """Add register that represents a symbol to the symbol table. - - Args: - is_arg: is this a function argument - """ - assert isinstance(symbol, SymbolNode) - reg = Register(typ, symbol.line, is_arg=is_arg, name=symbol.name) - self.symtable[symbol] = AssignmentTargetRegister(reg) - self.add(reg) - return reg - - def add_local_reg(self, symbol: SymbolNode, - typ: RType, is_arg: bool = False) -> AssignmentTargetRegister: - """Like add_local, but return an assignment target instead of value.""" - self.add_local(symbol, typ, is_arg) - target = self.symtable[symbol] - assert isinstance(target, AssignmentTargetRegister) - return target - - def add_target(self, symbol: SymbolNode, target: AssignmentTarget) -> AssignmentTarget: - self.symtable[symbol] = target - return target - - def lookup(self, symbol: SymbolNode) -> AssignmentTarget: - return self.symtable[symbol] - - def add_temp(self, typ: RType) -> 'Register': - """Add register that contains a temporary value with the given type.""" - assert isinstance(typ, RType) - reg = Register(typ) - self.add(reg) - return reg - - def add_op(self, reg: 'RegisterOp') -> None: - """Record the value of an operation.""" - if reg.is_void: - return - self.add(reg) - class BasicBlock: """Basic IR block. @@ -244,9 +193,9 @@ class Register(Value): (but not all) temporary values. """ - def __init__(self, type: RType, line: int = -1, is_arg: bool = False, name: str = '') -> None: - self.name = name + def __init__(self, type: RType, name: str = '', is_arg: bool = False, line: int = -1) -> None: self.type = type + self.name = name self.is_arg = is_arg self.is_borrowed = is_arg self.line = line @@ -255,6 +204,9 @@ def __init__(self, type: RType, line: int = -1, is_arg: bool = False, name: str def is_void(self) -> bool: return False + def __repr__(self) -> str: + return '' % self.name + class Op(Value): """Abstract base class for all operations (as opposed to values).""" diff --git a/mypyc/ir/pprint.py b/mypyc/ir/pprint.py index 7deb48084ced2..cb8ef80029163 100644 --- a/mypyc/ir/pprint.py +++ b/mypyc/ir/pprint.py @@ -10,9 +10,9 @@ Goto, Branch, Return, Unreachable, Assign, LoadInt, LoadErrorValue, GetAttr, SetAttr, LoadStatic, InitStatic, TupleGet, TupleSet, IncRef, DecRef, Call, MethodCall, Cast, Box, Unbox, RaiseStandardError, CallC, Truncate, LoadGlobal, BinaryIntOp, ComparisonOp, LoadMem, SetMem, - GetElementPtr, LoadAddress, Register, Value, OpVisitor, BasicBlock, Environment + GetElementPtr, LoadAddress, Register, Value, OpVisitor, BasicBlock, Environment, ControlOp ) -from mypyc.ir.func_ir import FuncIR +from mypyc.ir.func_ir import FuncIR, all_values_full from mypyc.ir.module_ir import ModuleIRs from mypyc.ir.rtypes import is_bool_rprimitive, is_int_rprimitive, RType from mypyc.ir.const_int import find_constant_integer_registers @@ -132,13 +132,13 @@ def visit_unbox(self, op: Unbox) -> str: def visit_raise_standard_error(self, op: RaiseStandardError) -> str: if op.value is not None: if isinstance(op.value, str): - return 'raise %s(%r)' % (op.class_name, op.value) + return self.format('%r = raise %s(%s)', op, op.class_name, repr(op.value)) elif isinstance(op.value, Value): - return self.format('raise %s(%r)', op.class_name, op.value) + return self.format('%r = raise %s(%r)', op, op.class_name, op.value) else: assert False, 'value type must be either str or Value' else: - return 'raise %s' % op.class_name + return self.format('%r = raise %s', op, op.class_name) def visit_call_c(self, op: CallC) -> str: args_str = ', '.join(self.format('%r', arg) for arg in op.args) @@ -244,12 +244,12 @@ def format(self, fmt: str, *args: Any) -> str: return ''.join(result) -def env_to_lines(env: Environment, - names: Dict[Value, str], - const_regs: Optional[Dict[LoadInt, int]] = None) -> List[str]: +def format_registers(func_ir: FuncIR, + names: Dict[Value, str], + const_regs: Optional[Dict[LoadInt, int]] = None) -> List[str]: result = [] i = 0 - regs = list(env.regs()) + regs = all_values_full(func_ir.arg_regs, func_ir.blocks) if const_regs is None: const_regs = {} regs = [reg for reg in regs if reg not in const_regs] @@ -323,8 +323,8 @@ def format_func(fn: FuncIR) -> List[str]: ', '.join(arg.name for arg in fn.args))) # compute constants const_regs = find_constant_integer_registers(fn.blocks) - names = generate_names_for_env(fn.env) - for line in env_to_lines(fn.env, names, const_regs): + names = generate_names_for_env(fn.arg_regs, fn.blocks) + for line in format_registers(fn, names, const_regs): lines.append(' ' + line) code = format_blocks(fn.blocks, fn.env, names, const_regs) lines.extend(code) @@ -340,39 +340,60 @@ def format_modules(modules: ModuleIRs) -> List[str]: return ops -def generate_names_for_env(env: Environment) -> Dict[Value, str]: +def generate_names_for_env(args: List[Register], blocks: List[BasicBlock]) -> Dict[Value, str]: """Generate unique names for values in an environment. Give names such as 'r5' or 'i0' to temp values in IR which are useful when pretty-printing or generating C. Ensure generated names are unique. """ - names = {} + names = {} # type: Dict[Value, str] used_names = set() temp_index = 0 int_index = 0 - for value in env.indexes: - if isinstance(value, Register) and value.name: - name = value.name - elif isinstance(value, LoadInt): - name = 'i%d' % int_index - int_index += 1 - else: - name = 'r%d' % temp_index - temp_index += 1 - - # Append _2, _3, ... if needed to make the name unique. - if name in used_names: - n = 2 - while True: - candidate = '%s_%d' % (name, n) - if candidate not in used_names: - name = candidate - break - n += 1 - - names[value] = name - used_names.add(name) + for arg in args: + names[arg] = arg.name + used_names.add(arg.name) + + for block in blocks: + for op in block.ops: + values = [] + + for source in op.sources(): + if source not in names: + values.append(source) + + if isinstance(op, Assign): + values.append(op.dest) + elif isinstance(op, ControlOp) or op.is_void: + continue + elif op not in names: + values.append(op) + + for value in values: + if value in names: + continue + if isinstance(value, Register) and value.name: + name = value.name + elif isinstance(value, LoadInt): + name = 'i%d' % int_index + int_index += 1 + else: + name = 'r%d' % temp_index + temp_index += 1 + + # Append _2, _3, ... if needed to make the name unique. + if name in used_names: + n = 2 + while True: + candidate = '%s_%d' % (name, n) + if candidate not in used_names: + name = candidate + break + n += 1 + + names[value] = name + used_names.add(name) return names diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 80549c71ae4e8..b4685afa7e6f1 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -28,7 +28,7 @@ from mypy.visitor import ExpressionVisitor, StatementVisitor from mypy.util import split_target -from mypyc.common import TEMP_ATTR_NAME +from mypyc.common import TEMP_ATTR_NAME, SELF_NAME from mypyc.irbuild.prebuildvisitor import PreBuildVisitor from mypyc.ir.ops import ( BasicBlock, AssignmentTarget, AssignmentTargetRegister, AssignmentTargetIndex, @@ -80,6 +80,8 @@ def __init__(self, options: CompilerOptions) -> None: self.builder = LowLevelIRBuilder(current_module, mapper) self.builders = [self.builder] + self.symtables = [OrderedDict()] # type: List[OrderedDict[SymbolNode, AssignmentTarget]] + self.args = [[]] # type: List[List[Register]] self.current_module = current_module self.mapper = mapper @@ -153,7 +155,7 @@ def accept(self, node: Union[Statement, Expression]) -> Optional[Value]: # messages. Generate a temp of the right type to keep # from causing more downstream trouble. except UnsupportedException: - res = self.alloc_temp(self.node_type(node)) + res = Register(self.node_type(node)) return res else: try: @@ -176,9 +178,6 @@ def activate_block(self, block: BasicBlock) -> None: def goto_and_activate(self, block: BasicBlock) -> None: self.builder.goto_and_activate(block) - def alloc_temp(self, type: RType) -> Register: - return self.builder.alloc_temp(type) - def py_get_attr(self, obj: Value, attr: str, line: int) -> Value: return self.builder.py_get_attr(obj, attr, line) @@ -396,7 +395,7 @@ def get_assignment_target(self, lvalue: Lvalue, assert lvalue.is_special_form symbol = Var(lvalue.name) if lvalue.kind == LDEF: - if symbol not in self.environment.symtable: + if symbol not in self.symtables[-1]: # If the function is a generator function, then first define a new variable # in the current function's environment class. Next, define a target that # refers to the newly defined variable in that environment class. Add the @@ -408,10 +407,10 @@ def get_assignment_target(self, lvalue: Lvalue, reassign=False) # Otherwise define a new local variable. - return self.environment.add_local_reg(symbol, self.node_type(lvalue)) + return self.add_local_reg(symbol, self.node_type(lvalue)) else: # Assign to a previously defined variable. - return self.environment.lookup(symbol) + return self.lookup(symbol) elif lvalue.kind == GDEF: globals_dict = self.load_globals_dict() name = self.load_static_unicode(lvalue.name) @@ -617,7 +616,7 @@ def maybe_spill_assignable(self, value: Value) -> Union[Register, AssignmentTarg return value # Allocate a temporary register for the assignable value. - reg = self.alloc_temp(value.type) + reg = Register(value.type) self.assign(reg, value, -1) return reg @@ -871,6 +870,8 @@ def enter(self, fn_info: Union[FuncInfo, str] = '') -> None: fn_info = FuncInfo(name=fn_info) self.builder = LowLevelIRBuilder(self.current_module, self.mapper) self.builders.append(self.builder) + self.symtables.append(OrderedDict()) + self.args.append([]) self.fn_info = fn_info self.fn_infos.append(self.fn_info) self.ret_types.append(none_rprimitive) @@ -880,14 +881,49 @@ def enter(self, fn_info: Union[FuncInfo, str] = '') -> None: self.nonlocal_control.append(BaseNonlocalControl()) self.activate_block(BasicBlock()) - def leave(self) -> Tuple[List[BasicBlock], Environment, RType, FuncInfo]: + def leave(self) -> Tuple[List[Register], List[BasicBlock], Environment, RType, FuncInfo]: builder = self.builders.pop() + self.symtables.pop() + args = self.args.pop() ret_type = self.ret_types.pop() fn_info = self.fn_infos.pop() self.nonlocal_control.pop() self.builder = self.builders[-1] self.fn_info = self.fn_infos[-1] - return builder.blocks, builder.environment, ret_type, fn_info + return args, builder.blocks, builder.environment, ret_type, fn_info + + def lookup(self, symbol: SymbolNode) -> AssignmentTarget: + return self.symtables[-1][symbol] + + def add_local(self, symbol: SymbolNode, typ: RType, is_arg: bool = False) -> 'Register': + """Add register that represents a symbol to the symbol table. + + Args: + is_arg: is this a function argument + """ + assert isinstance(symbol, SymbolNode) + reg = Register(typ, symbol.name, is_arg=is_arg, line=symbol.line) + self.symtables[-1][symbol] = AssignmentTargetRegister(reg) + if is_arg: + self.args[-1].append(reg) + return reg + + def add_local_reg(self, + symbol: SymbolNode, + typ: RType, + is_arg: bool = False) -> AssignmentTargetRegister: + """Like add_local, but return an assignment target instead of value.""" + self.add_local(symbol, typ, is_arg) + target = self.symtables[-1][symbol] + assert isinstance(target, AssignmentTargetRegister) + return target + + def add_self_to_env(self, cls: ClassIR) -> AssignmentTargetRegister: + return self.add_local_reg(Var(SELF_NAME), RInstance(cls), is_arg=True) + + def add_target(self, symbol: SymbolNode, target: AssignmentTarget) -> AssignmentTarget: + self.symtables[-1][symbol] = target + return target def type_to_rtype(self, typ: Optional[Type]) -> RType: return self.mapper.type_to_rtype(typ) @@ -914,12 +950,12 @@ def add_var_to_env_class(self, if reassign: # Read the local definition of the variable, and set the corresponding attribute of # the environment class' variable to be that value. - reg = self.read(self.environment.lookup(var), self.fn_info.fitem.line) + reg = self.read(self.lookup(var), self.fn_info.fitem.line) self.add(SetAttr(base.curr_env_reg, var.name, reg, self.fn_info.fitem.line)) # Override the local definition of the variable to instead point at the variable in # the environment class. - return self.environment.add_target(var, attr_target) + return self.add_target(var, attr_target) def is_builtin_ref_expr(self, expr: RefExpr) -> bool: assert expr.node, "RefExpr not resolved" @@ -974,7 +1010,7 @@ def gen_arg_defaults(builder: IRBuilder) -> None: fitem = builder.fn_info.fitem for arg in fitem.arguments: if arg.initializer: - target = builder.environment.lookup(arg.variable) + target = builder.lookup(arg.variable) def get_default() -> Value: assert arg.initializer is not None diff --git a/mypyc/irbuild/callable_class.py b/mypyc/irbuild/callable_class.py index 06973df4894d0..94fa519ff9c61 100644 --- a/mypyc/irbuild/callable_class.py +++ b/mypyc/irbuild/callable_class.py @@ -9,13 +9,12 @@ from mypy.nodes import Var from mypyc.common import SELF_NAME, ENV_ATTR_NAME -from mypyc.ir.ops import BasicBlock, Return, Call, SetAttr, Value, Environment +from mypyc.ir.ops import BasicBlock, Return, Call, SetAttr, Value, Register, Environment from mypyc.ir.rtypes import RInstance, object_rprimitive from mypyc.ir.func_ir import FuncIR, FuncSignature, RuntimeArg, FuncDecl from mypyc.ir.class_ir import ClassIR from mypyc.irbuild.builder import IRBuilder from mypyc.irbuild.context import FuncInfo, ImplicitClass -from mypyc.irbuild.util import add_self_to_env from mypyc.primitives.misc_ops import method_new_op @@ -79,11 +78,12 @@ class for the nested function. # Add a 'self' variable to the environment of the callable class, # and store that variable in a register to be accessed later. - self_target = add_self_to_env(builder.environment, callable_class_ir) + self_target = builder.add_self_to_env(callable_class_ir) builder.fn_info.callable_class.self_reg = builder.read(self_target, builder.fn_info.fitem.line) def add_call_to_callable_class(builder: IRBuilder, + args: List[Register], blocks: List[BasicBlock], sig: FuncSignature, env: Environment, @@ -98,7 +98,7 @@ def add_call_to_callable_class(builder: IRBuilder, # Since we create a method, we also add a 'self' parameter. sig = FuncSignature((RuntimeArg(SELF_NAME, object_rprimitive),) + sig.args, sig.ret_type) call_fn_decl = FuncDecl('__call__', fn_info.callable_class.ir.name, builder.module_name, sig) - call_fn_ir = FuncIR(call_fn_decl, blocks, env, + call_fn_ir = FuncIR(call_fn_decl, args, blocks, env, fn_info.fitem.line, traceback_name=fn_info.fitem.name) fn_info.callable_class.ir.methods['__call__'] = call_fn_ir return call_fn_ir @@ -110,10 +110,10 @@ def add_get_to_callable_class(builder: IRBuilder, fn_info: FuncInfo) -> None: builder.enter(fn_info) vself = builder.read( - builder.environment.add_local_reg(Var(SELF_NAME), object_rprimitive, True) + builder.add_local_reg(Var(SELF_NAME), object_rprimitive, True) ) - instance = builder.environment.add_local_reg(Var('instance'), object_rprimitive, True) - builder.environment.add_local_reg(Var('owner'), object_rprimitive, True) + instance = builder.add_local_reg(Var('instance'), object_rprimitive, True) + builder.add_local_reg(Var('owner'), object_rprimitive, True) # If accessed through the class, just return the callable # object. If accessed through an object, create a new bound @@ -130,14 +130,14 @@ def add_get_to_callable_class(builder: IRBuilder, fn_info: FuncInfo) -> None: builder.activate_block(instance_block) builder.add(Return(builder.call_c(method_new_op, [vself, builder.read(instance)], line))) - blocks, env, _, fn_info = builder.leave() + args, blocks, env, _, fn_info = builder.leave() sig = FuncSignature((RuntimeArg(SELF_NAME, object_rprimitive), RuntimeArg('instance', object_rprimitive), RuntimeArg('owner', object_rprimitive)), object_rprimitive) get_fn_decl = FuncDecl('__get__', fn_info.callable_class.ir.name, builder.module_name, sig) - get_fn_ir = FuncIR(get_fn_decl, blocks, env) + get_fn_ir = FuncIR(get_fn_decl, args, blocks, env) fn_info.callable_class.ir.methods['__get__'] = get_fn_ir builder.functions.append(get_fn_ir) diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index 28605ac5c7468..b8534ed3a40e5 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -7,7 +7,7 @@ ExpressionStmt, TempNode, Decorator, Lvalue, RefExpr, Var, is_class_var ) from mypyc.ir.ops import ( - Value, Call, LoadErrorValue, LoadStatic, InitStatic, TupleSet, SetAttr, Return, + Value, Register, Call, LoadErrorValue, LoadStatic, InitStatic, TupleSet, SetAttr, Return, BasicBlock, Branch, MethodCall, NAMESPACE_TYPE, LoadAddress ) from mypyc.ir.rtypes import ( @@ -24,7 +24,7 @@ from mypyc.primitives.dict_ops import dict_set_item_op, dict_new_op from mypyc.common import SELF_NAME from mypyc.irbuild.util import ( - is_dataclass_decorator, get_func_def, is_dataclass, is_constant, add_self_to_env + is_dataclass_decorator, get_func_def, is_dataclass, is_constant ) from mypyc.irbuild.builder import IRBuilder from mypyc.irbuild.function import transform_method @@ -245,7 +245,7 @@ def setup_non_ext_dict(builder: IRBuilder, [metaclass, builder.load_static_unicode('__prepare__')], cdef.line) - non_ext_dict = builder.alloc_temp(dict_rprimitive) + non_ext_dict = Register(dict_rprimitive) true_block, false_block, exit_block, = BasicBlock(), BasicBlock(), BasicBlock() builder.add_bool_branch(has_prepare, true_block, false_block) @@ -331,7 +331,7 @@ def generate_attr_defaults(builder: IRBuilder, cdef: ClassDef) -> None: builder.ret_types[-1] = bool_rprimitive rt_args = (RuntimeArg(SELF_NAME, RInstance(cls)),) - self_var = builder.read(add_self_to_env(builder.environment, cls), -1) + self_var = builder.read(builder.add_self_to_env(cls), -1) for stmt in default_assignments: lvalue = stmt.lvalues[0] @@ -351,12 +351,12 @@ def generate_attr_defaults(builder: IRBuilder, cdef: ClassDef) -> None: builder.add(Return(builder.true())) - blocks, env, ret_type, _ = builder.leave() + args, blocks, env, ret_type, _ = builder.leave() ir = FuncIR( FuncDecl('__mypyc_defaults_setup', cls.name, builder.module_name, FuncSignature(rt_args, ret_type)), - blocks, env) + args, blocks, env) builder.functions.append(ir) cls.methods[ir.name] = ir @@ -380,12 +380,7 @@ def gen_glue_ne_method(builder: IRBuilder, cls: ClassIR, line: int) -> FuncIR: # The environment operates on Vars, so we make some up fake_vars = [(Var(arg.name), arg.type) for arg in rt_args] args = [ - builder.read( - builder.environment.add_local_reg( - var, type, is_arg=True - ), - line - ) + builder.read(builder.add_local_reg(var, type, is_arg=True), line) for var, type in fake_vars ] # type: List[Value] builder.ret_types[-1] = object_rprimitive @@ -410,11 +405,11 @@ def gen_glue_ne_method(builder: IRBuilder, cls: ClassIR, line: int) -> FuncIR: builder.activate_block(not_implemented_block) builder.add(Return(not_implemented)) - blocks, env, ret_type, _ = builder.leave() + arg_regs, blocks, env, ret_type, _ = builder.leave() return FuncIR( FuncDecl('__ne__', cls.name, builder.module_name, FuncSignature(rt_args, ret_type)), - blocks, env) + arg_regs, blocks, env) def load_non_ext_class(builder: IRBuilder, diff --git a/mypyc/irbuild/env_class.py b/mypyc/irbuild/env_class.py index 87a72b4385e4f..4f39157e8df89 100644 --- a/mypyc/irbuild/env_class.py +++ b/mypyc/irbuild/env_class.py @@ -11,16 +11,16 @@ def g() -> int: # allow accessing 'x' return x + 2 - x + 1 # Modify the attribute + x = x + 1 # Modify the attribute return g() """ -from typing import Optional, Union +from typing import Dict, Optional, Union from mypy.nodes import FuncDef, SymbolNode from mypyc.common import SELF_NAME, ENV_ATTR_NAME -from mypyc.ir.ops import Call, GetAttr, SetAttr, Value, Environment, AssignmentTargetAttr +from mypyc.ir.ops import Call, GetAttr, SetAttr, Value, AssignmentTarget, AssignmentTargetAttr from mypyc.ir.rtypes import RInstance, object_rprimitive from mypyc.ir.class_ir import ClassIR from mypyc.irbuild.builder import IRBuilder @@ -106,7 +106,9 @@ def load_env_registers(builder: IRBuilder) -> None: setup_func_for_recursive_call(builder, fitem, fn_info.callable_class) -def load_outer_env(builder: IRBuilder, base: Value, outer_env: Environment) -> Value: +def load_outer_env(builder: IRBuilder, + base: Value, + outer_env: Dict[SymbolNode, AssignmentTarget]) -> Value: """Load the environment class for a given base into a register. Additionally, iterates through all of the SymbolNode and @@ -121,10 +123,10 @@ def load_outer_env(builder: IRBuilder, base: Value, outer_env: Environment) -> V env = builder.add(GetAttr(base, ENV_ATTR_NAME, builder.fn_info.fitem.line)) assert isinstance(env.type, RInstance), '{} must be of type RInstance'.format(env) - for symbol, target in outer_env.symtable.items(): + for symbol, target in outer_env.items(): env.type.class_ir.attributes[symbol.name] = target.type symbol_target = AssignmentTargetAttr(env, symbol.name) - builder.environment.add_target(symbol, symbol_target) + builder.add_target(symbol, symbol_target) return env @@ -136,7 +138,7 @@ def load_outer_envs(builder: IRBuilder, base: ImplicitClass) -> None: # FuncInfo instance's prev_env_reg field. if index > 1: # outer_env = builder.fn_infos[index].environment - outer_env = builder.builders[index].environment + outer_env = builder.symtables[index] if isinstance(base, GeneratorClass): base.prev_env_reg = load_outer_env(builder, base.curr_env_reg, outer_env) else: @@ -147,7 +149,7 @@ def load_outer_envs(builder: IRBuilder, base: ImplicitClass) -> None: # Load the remaining outer environments into registers. while index > 1: # outer_env = builder.fn_infos[index].environment - outer_env = builder.builders[index].environment + outer_env = builder.symtables[index] env_reg = load_outer_env(builder, env_reg, outer_env) index -= 1 @@ -160,7 +162,7 @@ def add_args_to_env(builder: IRBuilder, if local: for arg in fn_info.fitem.arguments: rtype = builder.type_to_rtype(arg.variable.type) - builder.environment.add_local_reg(arg.variable, rtype, is_arg=True) + builder.add_local_reg(arg.variable, rtype, is_arg=True) else: for arg in fn_info.fitem.arguments: if is_free_variable(builder, arg.variable) or fn_info.is_generator: @@ -192,7 +194,7 @@ def setup_func_for_recursive_call(builder: IRBuilder, fdef: FuncDef, base: Impli # Obtain the instance of the callable class representing the FuncDef, and add it to the # current environment. val = builder.add(GetAttr(prev_env_reg, fdef.name, -1)) - target = builder.environment.add_local_reg(fdef, object_rprimitive) + target = builder.add_local_reg(fdef, object_rprimitive) builder.assign(target, val, -1) diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index 14c11e07090df..1deab4e8b613d 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -18,7 +18,7 @@ from mypyc.common import MAX_SHORT_INT from mypyc.ir.ops import ( - Value, TupleGet, TupleSet, BasicBlock, Assign, LoadAddress + Value, Register, TupleGet, TupleSet, BasicBlock, Assign, LoadAddress ) from mypyc.ir.rtypes import ( RTuple, object_rprimitive, is_none_rprimitive, int_rprimitive, is_int_rprimitive @@ -125,11 +125,12 @@ def transform_super_expr(builder: IRBuilder, o: SuperExpr) -> Value: assert o.info is not None typ = builder.load_native_type_object(o.info.fullname) ir = builder.mapper.type_to_ir[o.info] - iter_env = iter(builder.environment.indexes) - vself = next(iter_env) # grab first argument + iter_env = iter(builder.args[-1]) + # Grab first argument + vself = next(iter_env) # type: Value if builder.fn_info.is_generator: # grab sixth argument (see comment in translate_super_method_call) - self_targ = list(builder.environment.symtable.values())[6] + self_targ = list(builder.symtables[-1].values())[6] vself = builder.read(self_targ, builder.fn_info.fitem.line) elif not ir.is_ext_class: vself = next(iter_env) # second argument is self if non_extension class @@ -287,7 +288,8 @@ def translate_super_method_call(builder: IRBuilder, expr: CallExpr, callee: Supe arg_kinds, arg_names = expr.arg_kinds[:], expr.arg_names[:] if decl.kind != FUNC_STATICMETHOD: - vself = next(iter(builder.environment.indexes)) # grab first argument + # Grab first argument + vself = builder.args[-1][0] # type: Value if decl.kind == FUNC_CLASSMETHOD: vself = builder.call_c(type_op, [vself], expr.line) elif builder.fn_info.is_generator: @@ -296,7 +298,7 @@ def translate_super_method_call(builder: IRBuilder, expr: CallExpr, callee: Supe # of ugly, but we can't search by name since the 'self' parameter # could be named anything, and it doesn't get added to the # environment indexes. - self_targ = list(builder.environment.symtable.values())[6] + self_targ = list(builder.symtables[-1].values())[6] vself = builder.read(self_targ, builder.fn_info.fitem.line) arg_values.insert(0, vself) arg_kinds.insert(0, ARG_POS) @@ -387,7 +389,7 @@ def transform_conditional_expr(builder: IRBuilder, expr: ConditionalExpr) -> Val builder.process_conditional(expr.cond, if_body, else_body) expr_type = builder.node_type(expr) # Having actual Phi nodes would be really nice here! - target = builder.alloc_temp(expr_type) + target = Register(expr_type) builder.activate_block(if_body) true_value = builder.accept(expr.if_expr) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 94c11c4d1356b..3f1c8a971035b 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -609,7 +609,7 @@ def init(self, start_reg: Value, end_reg: Value, step: int) -> None: index_type = short_int_rprimitive else: index_type = int_rprimitive - index_reg = builder.alloc_temp(index_type) + index_reg = Register(index_type) builder.assign(index_reg, start_reg, -1) self.index_reg = builder.maybe_spill_assignable(index_reg) # Initialize loop index to 0. Assert that the index target is assignable. diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py index 401617a329011..a00414d845b4a 100644 --- a/mypyc/irbuild/function.py +++ b/mypyc/irbuild/function.py @@ -19,8 +19,8 @@ from mypy.types import CallableType, get_proper_type from mypyc.ir.ops import ( - BasicBlock, Value, Return, SetAttr, LoadInt, Environment, GetAttr, Branch, AssignmentTarget, - InitStatic, LoadAddress + BasicBlock, Value, Register, Return, SetAttr, LoadInt, Environment, GetAttr, Branch, + AssignmentTarget, InitStatic, LoadAddress ) from mypyc.ir.rtypes import object_rprimitive, RInstance, object_pointer_rprimitive from mypyc.ir.func_ir import ( @@ -32,7 +32,7 @@ from mypyc.primitives.dict_ops import dict_set_item_op from mypyc.common import SELF_NAME, LAMBDA_NAME, decorator_helper_name from mypyc.sametype import is_same_method_signature -from mypyc.irbuild.util import concrete_arg_kind, is_constant, add_self_to_env +from mypyc.irbuild.util import concrete_arg_kind, is_constant from mypyc.irbuild.context import FuncInfo, ImplicitClass from mypyc.irbuild.statement import transform_try_except from mypyc.irbuild.builder import IRBuilder, gen_arg_defaults @@ -225,8 +225,8 @@ def c() -> None: if builder.fn_info.is_generator: # Do a first-pass and generate a function that just returns a generator object. gen_generator_func(builder) - blocks, env, ret_type, fn_info = builder.leave() - func_ir, func_reg = gen_func_ir(builder, blocks, sig, env, fn_info, cdef) + args, blocks, env, ret_type, fn_info = builder.leave() + func_ir, func_reg = gen_func_ir(builder, args, blocks, sig, env, fn_info, cdef) # Re-enter the FuncItem and visit the body of the function this time. builder.enter(fn_info) @@ -283,19 +283,21 @@ def c() -> None: if builder.fn_info.is_generator: populate_switch_for_generator_class(builder) - blocks, env, ret_type, fn_info = builder.leave() + args, blocks, env, ret_type, fn_info = builder.leave() if fn_info.is_generator: - add_methods_to_generator_class(builder, fn_info, sig, env, blocks, fitem.is_coroutine) + add_methods_to_generator_class( + builder, fn_info, sig, env, args, blocks, fitem.is_coroutine) else: - func_ir, func_reg = gen_func_ir(builder, blocks, sig, env, fn_info, cdef) + func_ir, func_reg = gen_func_ir(builder, args, blocks, sig, env, fn_info, cdef) - calculate_arg_defaults(builder, fn_info, env, func_reg) + calculate_arg_defaults(builder, fn_info, func_reg) return (func_ir, func_reg) def gen_func_ir(builder: IRBuilder, + args: List[Register], blocks: List[BasicBlock], sig: FuncSignature, env: Environment, @@ -310,7 +312,7 @@ def gen_func_ir(builder: IRBuilder, """ func_reg = None # type: Optional[Value] if fn_info.is_nested or fn_info.in_non_ext: - func_ir = add_call_to_callable_class(builder, blocks, sig, env, fn_info) + func_ir = add_call_to_callable_class(builder, args, blocks, sig, env, fn_info) add_get_to_callable_class(builder, fn_info) func_reg = instantiate_callable_class(builder, fn_info) else: @@ -321,10 +323,10 @@ def gen_func_ir(builder: IRBuilder, func_decl = FuncDecl(fn_info.name, class_name, builder.module_name, sig, func_decl.kind, func_decl.is_prop_getter, func_decl.is_prop_setter) - func_ir = FuncIR(func_decl, blocks, env, fn_info.fitem.line, + func_ir = FuncIR(func_decl, args, blocks, env, fn_info.fitem.line, traceback_name=fn_info.fitem.name) else: - func_ir = FuncIR(func_decl, blocks, env, + func_ir = FuncIR(func_decl, args, blocks, env, fn_info.fitem.line, traceback_name=fn_info.fitem.name) return (func_ir, func_reg) @@ -430,7 +432,6 @@ def handle_non_ext_method( def calculate_arg_defaults(builder: IRBuilder, fn_info: FuncInfo, - env: Environment, func_reg: Optional[Value]) -> None: """Calculate default argument values and store them. @@ -444,7 +445,7 @@ def calculate_arg_defaults(builder: IRBuilder, if arg.initializer and not is_constant(arg.initializer): value = builder.coerce( builder.accept(arg.initializer), - env.lookup(arg.variable).type, + builder.lookup(arg.variable).type, arg.line ) if not fn_info.is_nested: @@ -486,9 +487,9 @@ def handle_yield_from_and_await(builder: IRBuilder, o: Union[YieldFromExpr, Awai # This is basically an implementation of the code in PEP 380. # TODO: do we want to use the right types here? - result = builder.alloc_temp(object_rprimitive) - to_yield_reg = builder.alloc_temp(object_rprimitive) - received_reg = builder.alloc_temp(object_rprimitive) + result = Register(object_rprimitive) + to_yield_reg = Register(object_rprimitive) + received_reg = Register(object_rprimitive) if isinstance(o, YieldFromExpr): iter_val = builder.call_c(iter_op, [builder.accept(o.expr)], o.line) @@ -523,7 +524,7 @@ def except_body() -> None: # The body of the except is all implemented in a C function to # reduce how much code we need to generate. It returns a value # indicating whether to break or yield (or raise an exception). - val = builder.alloc_temp(object_rprimitive) + val = Register(object_rprimitive) val_address = builder.add(LoadAddress(object_pointer_rprimitive, val)) to_stop = builder.call_c(yield_from_except_op, [builder.read(iter_reg), val_address], o.line) @@ -655,7 +656,7 @@ def f(builder: IRBuilder, x: object) -> int: ... # The environment operates on Vars, so we make some up fake_vars = [(Var(arg.name), arg.type) for arg in rt_args] - args = [builder.read(builder.environment.add_local_reg(var, type, is_arg=True), line) + args = [builder.read(builder.add_local_reg(var, type, is_arg=True), line) for var, type in fake_vars] arg_names = [arg.name for arg in rt_args] arg_kinds = [concrete_arg_kind(arg.kind) for arg in rt_args] @@ -668,13 +669,13 @@ def f(builder: IRBuilder, x: object) -> int: ... retval = builder.coerce(retval, sig.ret_type, line) builder.add(Return(retval)) - blocks, env, ret_type, _ = builder.leave() + arg_regs, blocks, env, ret_type, _ = builder.leave() return FuncIR( FuncDecl(target.name + '__' + base.name + '_glue', cls.name, builder.module_name, FuncSignature(rt_args, ret_type), target.decl.kind), - blocks, env) + arg_regs, blocks, env) def gen_glue_property(builder: IRBuilder, @@ -696,7 +697,8 @@ def gen_glue_property(builder: IRBuilder, builder.enter() rt_arg = RuntimeArg(SELF_NAME, RInstance(cls)) - arg = builder.read(add_self_to_env(builder.environment, cls), line) + self_target = builder.add_self_to_env(cls) + arg = builder.read(self_target, line) builder.ret_types[-1] = sig.ret_type if do_pygetattr: retval = builder.py_get_attr(arg, target.name, line) @@ -705,11 +707,11 @@ def gen_glue_property(builder: IRBuilder, retbox = builder.coerce(retval, sig.ret_type, line) builder.add(Return(retbox)) - blocks, env, return_type, _ = builder.leave() + args, blocks, env, return_type, _ = builder.leave() return FuncIR( FuncDecl(target.name + '__' + base.name + '_glue', cls.name, builder.module_name, FuncSignature([rt_arg], return_type)), - blocks, env) + args, blocks, env) def get_func_target(builder: IRBuilder, fdef: FuncDef) -> AssignmentTarget: @@ -720,9 +722,9 @@ def get_func_target(builder: IRBuilder, fdef: FuncDef) -> AssignmentTarget: """ if fdef.original_def: # Get the target associated with the previously defined FuncDef. - return builder.environment.lookup(fdef.original_def) + return builder.lookup(fdef.original_def) if builder.fn_info.is_generator or builder.fn_info.contains_nested: - return builder.environment.lookup(fdef) + return builder.lookup(fdef) - return builder.environment.add_local_reg(fdef, object_rprimitive) + return builder.add_local_reg(fdef, object_rprimitive) diff --git a/mypyc/irbuild/generator.py b/mypyc/irbuild/generator.py index 8d77c5ed6d962..530c1546dd048 100644 --- a/mypyc/irbuild/generator.py +++ b/mypyc/irbuild/generator.py @@ -15,13 +15,12 @@ from mypyc.common import SELF_NAME, NEXT_LABEL_ATTR_NAME, ENV_ATTR_NAME from mypyc.ir.ops import ( BasicBlock, Call, Return, Goto, LoadInt, SetAttr, Environment, Unreachable, RaiseStandardError, - Value + Value, Register ) from mypyc.ir.rtypes import RInstance, int_rprimitive, object_rprimitive from mypyc.ir.func_ir import FuncIR, FuncDecl, FuncSignature, RuntimeArg from mypyc.ir.class_ir import ClassIR from mypyc.primitives.exc_ops import raise_exception_with_tb_op -from mypyc.irbuild.util import add_self_to_env from mypyc.irbuild.env_class import ( add_args_to_env, load_outer_env, load_env_registers, finalize_env_class ) @@ -123,9 +122,10 @@ def add_methods_to_generator_class(builder: IRBuilder, fn_info: FuncInfo, sig: FuncSignature, env: Environment, + arg_regs: List[Register], blocks: List[BasicBlock], is_coroutine: bool) -> None: - helper_fn_decl = add_helper_to_generator_class(builder, blocks, sig, env, fn_info) + helper_fn_decl = add_helper_to_generator_class(builder, arg_regs, blocks, sig, env, fn_info) add_next_to_generator_class(builder, fn_info, helper_fn_decl, sig) add_send_to_generator_class(builder, fn_info, helper_fn_decl, sig) add_iter_to_generator_class(builder, fn_info) @@ -136,6 +136,7 @@ def add_methods_to_generator_class(builder: IRBuilder, def add_helper_to_generator_class(builder: IRBuilder, + arg_regs: List[Register], blocks: List[BasicBlock], sig: FuncSignature, env: Environment, @@ -149,7 +150,7 @@ def add_helper_to_generator_class(builder: IRBuilder, ), sig.ret_type) helper_fn_decl = FuncDecl('__mypyc_generator_helper__', fn_info.generator_class.ir.name, builder.module_name, sig) - helper_fn_ir = FuncIR(helper_fn_decl, blocks, env, + helper_fn_ir = FuncIR(helper_fn_decl, arg_regs, blocks, env, fn_info.fitem.line, traceback_name=fn_info.fitem.name) fn_info.generator_class.ir.methods['__mypyc_generator_helper__'] = helper_fn_ir builder.functions.append(helper_fn_ir) @@ -159,14 +160,14 @@ def add_helper_to_generator_class(builder: IRBuilder, def add_iter_to_generator_class(builder: IRBuilder, fn_info: FuncInfo) -> None: """Generates the '__iter__' method for a generator class.""" builder.enter(fn_info) - self_target = add_self_to_env(builder.environment, fn_info.generator_class.ir) + self_target = builder.add_self_to_env(fn_info.generator_class.ir) builder.add(Return(builder.read(self_target, fn_info.fitem.line))) - blocks, env, _, fn_info = builder.leave() + args, blocks, env, _, fn_info = builder.leave() # Next, add the actual function as a method of the generator class. sig = FuncSignature((RuntimeArg(SELF_NAME, object_rprimitive),), object_rprimitive) iter_fn_decl = FuncDecl('__iter__', fn_info.generator_class.ir.name, builder.module_name, sig) - iter_fn_ir = FuncIR(iter_fn_decl, blocks, env) + iter_fn_ir = FuncIR(iter_fn_decl, args, blocks, env) fn_info.generator_class.ir.methods['__iter__'] = iter_fn_ir builder.functions.append(iter_fn_ir) @@ -177,18 +178,18 @@ def add_next_to_generator_class(builder: IRBuilder, sig: FuncSignature) -> None: """Generates the '__next__' method for a generator class.""" builder.enter(fn_info) - self_reg = builder.read(add_self_to_env(builder.environment, fn_info.generator_class.ir)) + self_reg = builder.read(builder.add_self_to_env(fn_info.generator_class.ir)) none_reg = builder.none_object() # Call the helper function with error flags set to Py_None, and return that result. result = builder.add(Call(fn_decl, [self_reg, none_reg, none_reg, none_reg, none_reg], fn_info.fitem.line)) builder.add(Return(result)) - blocks, env, _, fn_info = builder.leave() + args, blocks, env, _, fn_info = builder.leave() sig = FuncSignature((RuntimeArg(SELF_NAME, object_rprimitive),), sig.ret_type) next_fn_decl = FuncDecl('__next__', fn_info.generator_class.ir.name, builder.module_name, sig) - next_fn_ir = FuncIR(next_fn_decl, blocks, env) + next_fn_ir = FuncIR(next_fn_decl, args, blocks, env) fn_info.generator_class.ir.methods['__next__'] = next_fn_ir builder.functions.append(next_fn_ir) @@ -200,20 +201,20 @@ def add_send_to_generator_class(builder: IRBuilder, """Generates the 'send' method for a generator class.""" # FIXME: this is basically the same as add_next... builder.enter(fn_info) - self_reg = builder.read(add_self_to_env(builder.environment, fn_info.generator_class.ir)) - arg = builder.environment.add_local_reg(Var('arg'), object_rprimitive, True) + self_reg = builder.read(builder.add_self_to_env(fn_info.generator_class.ir)) + arg = builder.add_local_reg(Var('arg'), object_rprimitive, True) none_reg = builder.none_object() # Call the helper function with error flags set to Py_None, and return that result. result = builder.add(Call(fn_decl, [self_reg, none_reg, none_reg, none_reg, builder.read(arg)], fn_info.fitem.line)) builder.add(Return(result)) - blocks, env, _, fn_info = builder.leave() + args, blocks, env, _, fn_info = builder.leave() sig = FuncSignature((RuntimeArg(SELF_NAME, object_rprimitive), RuntimeArg('arg', object_rprimitive),), sig.ret_type) next_fn_decl = FuncDecl('send', fn_info.generator_class.ir.name, builder.module_name, sig) - next_fn_ir = FuncIR(next_fn_decl, blocks, env) + next_fn_ir = FuncIR(next_fn_decl, args, blocks, env) fn_info.generator_class.ir.methods['send'] = next_fn_ir builder.functions.append(next_fn_ir) @@ -224,12 +225,12 @@ def add_throw_to_generator_class(builder: IRBuilder, sig: FuncSignature) -> None: """Generates the 'throw' method for a generator class.""" builder.enter(fn_info) - self_reg = builder.read(add_self_to_env(builder.environment, fn_info.generator_class.ir)) + self_reg = builder.read(builder.add_self_to_env(fn_info.generator_class.ir)) # Add the type, value, and traceback variables to the environment. - typ = builder.environment.add_local_reg(Var('type'), object_rprimitive, True) - val = builder.environment.add_local_reg(Var('value'), object_rprimitive, True) - tb = builder.environment.add_local_reg(Var('traceback'), object_rprimitive, True) + typ = builder.add_local_reg(Var('type'), object_rprimitive, True) + val = builder.add_local_reg(Var('value'), object_rprimitive, True) + tb = builder.add_local_reg(Var('traceback'), object_rprimitive, True) # Because the value and traceback arguments are optional and hence # can be NULL if not passed in, we have to assign them Py_None if @@ -247,7 +248,7 @@ def add_throw_to_generator_class(builder: IRBuilder, ) ) builder.add(Return(result)) - blocks, env, _, fn_info = builder.leave() + args, blocks, env, _, fn_info = builder.leave() # Create the FuncSignature for the throw function. Note that the # value and traceback fields are optional, and are assigned to if @@ -259,7 +260,7 @@ def add_throw_to_generator_class(builder: IRBuilder, sig.ret_type) throw_fn_decl = FuncDecl('throw', fn_info.generator_class.ir.name, builder.module_name, sig) - throw_fn_ir = FuncIR(throw_fn_decl, blocks, env) + throw_fn_ir = FuncIR(throw_fn_decl, args, blocks, env) fn_info.generator_class.ir.methods['throw'] = throw_fn_ir builder.functions.append(throw_fn_ir) @@ -269,17 +270,17 @@ def add_close_to_generator_class(builder: IRBuilder, fn_info: FuncInfo) -> None: # TODO: Currently this method just triggers a runtime error, # we should fill this out eventually. builder.enter(fn_info) - add_self_to_env(builder.environment, fn_info.generator_class.ir) + builder.add_self_to_env(fn_info.generator_class.ir) builder.add(RaiseStandardError(RaiseStandardError.RUNTIME_ERROR, 'close method on generator classes uimplemented', fn_info.fitem.line)) builder.add(Unreachable()) - blocks, env, _, fn_info = builder.leave() + args, blocks, env, _, fn_info = builder.leave() # Next, add the actual function as a method of the generator class. sig = FuncSignature((RuntimeArg(SELF_NAME, object_rprimitive),), object_rprimitive) close_fn_decl = FuncDecl('close', fn_info.generator_class.ir.name, builder.module_name, sig) - close_fn_ir = FuncIR(close_fn_decl, blocks, env) + close_fn_ir = FuncIR(close_fn_decl, args, blocks, env) fn_info.generator_class.ir.methods['close'] = close_fn_ir builder.functions.append(close_fn_ir) @@ -287,15 +288,15 @@ def add_close_to_generator_class(builder: IRBuilder, fn_info: FuncInfo) -> None: def add_await_to_generator_class(builder: IRBuilder, fn_info: FuncInfo) -> None: """Generates the '__await__' method for a generator class.""" builder.enter(fn_info) - self_target = add_self_to_env(builder.environment, fn_info.generator_class.ir) + self_target = builder.add_self_to_env(fn_info.generator_class.ir) builder.add(Return(builder.read(self_target, fn_info.fitem.line))) - blocks, env, _, fn_info = builder.leave() + args, blocks, env, _, fn_info = builder.leave() # Next, add the actual function as a method of the generator class. sig = FuncSignature((RuntimeArg(SELF_NAME, object_rprimitive),), object_rprimitive) await_fn_decl = FuncDecl('__await__', fn_info.generator_class.ir.name, builder.module_name, sig) - await_fn_ir = FuncIR(await_fn_decl, blocks, env) + await_fn_ir = FuncIR(await_fn_decl, args, blocks, env) fn_info.generator_class.ir.methods['__await__'] = await_fn_ir builder.functions.append(await_fn_ir) @@ -304,20 +305,20 @@ def setup_env_for_generator_class(builder: IRBuilder) -> None: """Populates the environment for a generator class.""" fitem = builder.fn_info.fitem cls = builder.fn_info.generator_class - self_target = add_self_to_env(builder.environment, cls.ir) + self_target = builder.add_self_to_env(cls.ir) # Add the type, value, and traceback variables to the environment. - exc_type = builder.environment.add_local(Var('type'), object_rprimitive, is_arg=True) - exc_val = builder.environment.add_local(Var('value'), object_rprimitive, is_arg=True) - exc_tb = builder.environment.add_local(Var('traceback'), object_rprimitive, is_arg=True) + exc_type = builder.add_local(Var('type'), object_rprimitive, is_arg=True) + exc_val = builder.add_local(Var('value'), object_rprimitive, is_arg=True) + exc_tb = builder.add_local(Var('traceback'), object_rprimitive, is_arg=True) # TODO: Use the right type here instead of object? - exc_arg = builder.environment.add_local(Var('arg'), object_rprimitive, is_arg=True) + exc_arg = builder.add_local(Var('arg'), object_rprimitive, is_arg=True) cls.exc_regs = (exc_type, exc_val, exc_tb) cls.send_arg_reg = exc_arg cls.self_reg = builder.read(self_target, fitem.line) - cls.curr_env_reg = load_outer_env(builder, cls.self_reg, builder.environment) + cls.curr_env_reg = load_outer_env(builder, cls.self_reg, builder.symtables[-1]) # Define a variable representing the label to go to the next time # the '__next__' function of the generator is called, and add it diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index b6328cbef5473..a2c9d4b863c01 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -19,7 +19,7 @@ from mypyc.ir.ops import ( BasicBlock, Environment, Op, LoadInt, Value, Register, Assign, Branch, Goto, Call, Box, Unbox, Cast, GetAttr, - LoadStatic, MethodCall, RegisterOp, CallC, Truncate, + LoadStatic, MethodCall, CallC, Truncate, RaiseStandardError, Unreachable, LoadErrorValue, LoadGlobal, NAMESPACE_TYPE, NAMESPACE_MODULE, NAMESPACE_STATIC, BinaryIntOp, GetElementPtr, LoadMem, ComparisonOp, LoadAddress, TupleGet, SetMem, ERR_NEVER, ERR_FALSE @@ -86,10 +86,7 @@ def __init__( def add(self, op: Op) -> Value: """Add an op.""" assert not self.blocks[-1].terminated, "Can't add to finished block" - self.blocks[-1].ops.append(op) - if isinstance(op, RegisterOp): - self.environment.add_op(op) return op def goto(self, target: BasicBlock) -> None: @@ -116,9 +113,6 @@ def push_error_handler(self, handler: Optional[BasicBlock]) -> None: def pop_error_handler(self) -> Optional[BasicBlock]: return self.error_handlers.pop() - def alloc_temp(self, type: RType) -> Register: - return self.environment.add_temp(type) - # Type conversions def box(self, src: Value) -> Value: @@ -156,7 +150,7 @@ def coerce(self, src: Value, target_type: RType, line: int, force: bool = False) or not is_subtype(src.type, target_type)): return self.unbox_or_cast(src, target_type, line) elif force: - tmp = self.alloc_temp(target_type) + tmp = Register(target_type) self.add(Assign(tmp, src)) return tmp return src @@ -566,7 +560,7 @@ def compare_tagged(self, lhs: Value, rhs: Value, op: str, line: int) -> Value: if is_short_int_rprimitive(lhs.type) and is_short_int_rprimitive(rhs.type): return self.comparison_op(lhs, rhs, int_comparison_op_mapping[op][0], line) op_type, c_func_desc, negate_result, swap_op = int_comparison_op_mapping[op] - result = self.alloc_temp(bool_rprimitive) + result = Register(bool_rprimitive) short_int_block, int_block, out = BasicBlock(), BasicBlock(), BasicBlock() check_lhs = self.check_tagged_short_int(lhs, line) if op in ("==", "!="): @@ -685,7 +679,7 @@ def compare_tuples(self, # type cast to pass mypy check assert isinstance(lhs.type, RTuple) and isinstance(rhs.type, RTuple) equal = True if op == '==' else False - result = self.alloc_temp(bool_rprimitive) + result = Register(bool_rprimitive) # empty tuples if len(lhs.type.types) == 0 and len(rhs.type.types) == 0: self.add(Assign(result, self.true() if equal else self.false(), line)) @@ -823,7 +817,7 @@ def shortcircuit_helper(self, op: str, left: Callable[[], Value], right: Callable[[], Value], line: int) -> Value: # Having actual Phi nodes would be really nice here! - target = self.alloc_temp(expr_type) + target = Register(expr_type) # left_body takes the value of the left side, right_body the right left_body, right_body, next = BasicBlock(), BasicBlock(), BasicBlock() # true_body is taken if the left is true, false_body if it is false. @@ -1040,7 +1034,7 @@ def decompose_union_helper(self, # For everything but RInstance we fall back to C API rest_items.append(item) exit_block = BasicBlock() - result = self.alloc_temp(result_type) + result = Register(result_type) for i, item in enumerate(fast_items): more_types = i < len(fast_items) - 1 or rest_items if more_types: diff --git a/mypyc/irbuild/main.py b/mypyc/irbuild/main.py index 2fd8ea99d1025..27118acdc3509 100644 --- a/mypyc/irbuild/main.py +++ b/mypyc/irbuild/main.py @@ -124,8 +124,8 @@ def transform_mypy_file(builder: IRBuilder, mypyfile: MypyFile) -> None: builder.maybe_add_implicit_return() # Generate special function representing module top level. - blocks, env, ret_type, _ = builder.leave() + args, blocks, env, ret_type, _ = builder.leave() sig = FuncSignature([], none_rprimitive) - func_ir = FuncIR(FuncDecl(TOP_LEVEL_NAME, None, builder.module_name, sig), blocks, env, + func_ir = FuncIR(FuncDecl(TOP_LEVEL_NAME, None, builder.module_name, sig), args, blocks, env, traceback_name="") builder.functions.append(func_ir) diff --git a/mypyc/irbuild/nonlocalcontrol.py b/mypyc/irbuild/nonlocalcontrol.py index f19c376da4bc9..6319091b6b44f 100644 --- a/mypyc/irbuild/nonlocalcontrol.py +++ b/mypyc/irbuild/nonlocalcontrol.py @@ -141,7 +141,7 @@ def gen_continue(self, builder: 'IRBuilder', line: int) -> None: def gen_return(self, builder: 'IRBuilder', value: Value, line: int) -> None: if self.ret_reg is None: - self.ret_reg = builder.alloc_temp(builder.ret_types[-1]) + self.ret_reg = Register(builder.ret_types[-1]) builder.add(Assign(self.ret_reg, value)) builder.add(Goto(self.target)) diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index 42b9a5795968d..2f035c0b6908d 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -18,7 +18,7 @@ from mypy.types import AnyType, TypeOfAny from mypyc.ir.ops import ( - Value, BasicBlock, LoadInt, RaiseStandardError, Unreachable + Value, Register, BasicBlock, LoadInt, RaiseStandardError, Unreachable ) from mypyc.ir.rtypes import ( RType, RTuple, str_rprimitive, list_rprimitive, dict_rprimitive, set_rprimitive, @@ -170,7 +170,7 @@ def any_all_helper(builder: IRBuilder, initial_value: Callable[[], Value], modify: Callable[[Value], Value], new_value: Callable[[], Value]) -> Value: - retval = builder.alloc_temp(bool_rprimitive) + retval = Register(bool_rprimitive) builder.assign(retval, initial_value(), -1) loop_params = list(zip(gen.indices, gen.sequences, gen.condlists)) true_block, false_block, exit_block = BasicBlock(), BasicBlock(), BasicBlock() @@ -216,7 +216,7 @@ def translate_next_call(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> gen = expr.args[0] - retval = builder.alloc_temp(builder.node_type(expr)) + retval = Register(builder.node_type(expr)) default_val = None if len(expr.args) > 1: default_val = builder.accept(expr.args[1]) diff --git a/mypyc/irbuild/statement.py b/mypyc/irbuild/statement.py index b83bc4beafe9e..c08f3e338c649 100644 --- a/mypyc/irbuild/statement.py +++ b/mypyc/irbuild/statement.py @@ -85,7 +85,7 @@ def transform_assignment_stmt(builder: IRBuilder, stmt: AssignmentStmt) -> None: temps = [] for right in stmt.rvalue.items: rvalue_reg = builder.accept(right) - temp = builder.alloc_temp(rvalue_reg.type) + temp = Register(rvalue_reg.type) builder.assign(temp, rvalue_reg, stmt.line) temps.append(temp) for (left, temp) in zip(lvalue.items, temps): @@ -390,7 +390,7 @@ def try_finally_entry_blocks(builder: IRBuilder, main_entry: BasicBlock, finally_block: BasicBlock, ret_reg: Optional[Register]) -> Value: - old_exc = builder.alloc_temp(exc_rtuple) + old_exc = Register(exc_rtuple) # Entry block for non-exceptional flow builder.activate_block(main_entry) diff --git a/mypyc/irbuild/util.py b/mypyc/irbuild/util.py index cc98903d8e309..40c7d20401339 100644 --- a/mypyc/irbuild/util.py +++ b/mypyc/irbuild/util.py @@ -8,11 +8,6 @@ ARG_OPT, GDEF ) -from mypyc.ir.ops import Environment, AssignmentTargetRegister -from mypyc.ir.rtypes import RInstance -from mypyc.ir.class_ir import ClassIR -from mypyc.common import SELF_NAME - def is_trait_decorator(d: Expression) -> bool: return isinstance(d, RefExpr) and d.fullname == 'mypy_extensions.trait' @@ -129,9 +124,3 @@ def is_constant(e: Expression) -> bool: or (isinstance(e, RefExpr) and e.kind == GDEF and (e.fullname in ('builtins.True', 'builtins.False', 'builtins.None') or (isinstance(e.node, Var) and e.node.is_final)))) - - -def add_self_to_env(environment: Environment, cls: ClassIR) -> AssignmentTargetRegister: - return environment.add_local_reg( - Var(SELF_NAME), RInstance(cls), is_arg=True - ) diff --git a/mypyc/test-data/analysis.test b/mypyc/test-data/analysis.test index 781a8b1ac8a89..08ce0effad0a5 100644 --- a/mypyc/test-data/analysis.test +++ b/mypyc/test-data/analysis.test @@ -651,8 +651,8 @@ L11: (4, 2) {r1, r6} {r6} (4, 3) {r6} {} (5, 0) {r1} {r1} -(5, 1) {r1} {i2, r1} -(5, 2) {i2, r1} {r1} +(5, 1) {r1} {i1, r1} +(5, 2) {i1, r1} {r1} (6, 0) {} {} (7, 0) {r1, st} {st} (7, 1) {st} {st} @@ -660,9 +660,8 @@ L11: (8, 1) {} {r7} (8, 2) {r7} {} (9, 0) {} {} -(10, 0) {st} {i1, st} -(10, 1) {i1, st} {r8} +(10, 0) {st} {i2, st} +(10, 1) {i2, st} {r8} (10, 2) {r8} {} (11, 0) {} {r9} (11, 1) {r9} {} - diff --git a/mypyc/test-data/exceptions.test b/mypyc/test-data/exceptions.test index 1612ffa6c7c8d..620aa824c89b8 100644 --- a/mypyc/test-data/exceptions.test +++ b/mypyc/test-data/exceptions.test @@ -352,8 +352,6 @@ def lol(x): r1, st :: object r2 :: tuple[object, object, object] r3 :: str - r4 :: bit - r5 :: object L0: L1: r0 = load_global CPyStatic_unicode_3 :: static ('foo') @@ -390,10 +388,8 @@ def lol(x): r2 :: str r3, b :: object r4 :: tuple[object, object, object] - r5 :: bit - r6 :: object - r7, r8 :: bool - r9 :: object + r5, r6 :: bool + r7, r8 :: object L0: L1: r0 = load_global CPyStatic_unicode_3 :: static ('foo') @@ -415,27 +411,27 @@ L5: L6: if is_error(a) goto L17 else goto L9 L7: - raise UnboundLocalError("local variable 'a' referenced before assignment") - if not r7 goto L14 (error at lol:9) else goto L8 :: bool + r5 = raise UnboundLocalError("local variable 'a' referenced before assignment") + if not r5 goto L14 (error at lol:9) else goto L8 :: bool L8: unreachable L9: if is_error(b) goto L18 else goto L12 L10: - raise UnboundLocalError("local variable 'b' referenced before assignment") - if not r8 goto L14 (error at lol:9) else goto L11 :: bool + r6 = raise UnboundLocalError("local variable 'b' referenced before assignment") + if not r6 goto L14 (error at lol:9) else goto L11 :: bool L11: unreachable L12: - r6 = PyNumber_Add(a, b) + r7 = PyNumber_Add(a, b) xdec_ref a xdec_ref b - if is_error(r6) goto L14 (error at lol:9) else goto L13 + if is_error(r7) goto L14 (error at lol:9) else goto L13 L13: - return r6 + return r7 L14: - r9 = :: object - return r9 + r8 = :: object + return r8 L15: xdec_ref a goto L2 @@ -517,4 +513,3 @@ L13: L14: dec_ref r7 goto L8 - diff --git a/mypyc/test-data/irbuild-any.test b/mypyc/test-data/irbuild-any.test index 33e9cad9ff03f..8c07aeb8fe6d0 100644 --- a/mypyc/test-data/irbuild-any.test +++ b/mypyc/test-data/irbuild-any.test @@ -166,12 +166,12 @@ L3: a = r0 if b goto L4 else goto L5 :: bool L4: - r3 = box(int, n) - r2 = r3 + r2 = box(int, n) + r3 = r2 goto L6 L5: - r2 = a + r3 = a L6: - r4 = unbox(int, r2) + r4 = unbox(int, r3) n = r4 return 1 diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index fc60b99ea29b7..d4be063b14d8a 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -195,25 +195,25 @@ def f(x: object, y: object) -> str: [out] def f(x, y): x, y :: object - r0, r1 :: str - r2 :: int32 - r3 :: bit - r4 :: bool - r5 :: str + r0 :: str + r1 :: int32 + r2 :: bit + r3 :: bool + r4, r5 :: str L0: - r1 = PyObject_Str(x) - r2 = PyObject_IsTrue(r1) - r3 = r2 >= 0 :: signed - r4 = truncate r2: int32 to builtins.bool - if r4 goto L1 else goto L2 :: bool + r0 = PyObject_Str(x) + r1 = PyObject_IsTrue(r0) + r2 = r1 >= 0 :: signed + r3 = truncate r1: int32 to builtins.bool + if r3 goto L1 else goto L2 :: bool L1: - r0 = r1 + r4 = r0 goto L3 L2: r5 = PyObject_Str(y) - r0 = r5 + r4 = r5 L3: - return r0 + return r4 [case testOr] def f(x: int, y: int) -> int: @@ -275,25 +275,25 @@ def f(x: object, y: object) -> str: [out] def f(x, y): x, y :: object - r0, r1 :: str - r2 :: int32 - r3 :: bit - r4 :: bool - r5 :: str + r0 :: str + r1 :: int32 + r2 :: bit + r3 :: bool + r4, r5 :: str L0: - r1 = PyObject_Str(x) - r2 = PyObject_IsTrue(r1) - r3 = r2 >= 0 :: signed - r4 = truncate r2: int32 to builtins.bool - if r4 goto L2 else goto L1 :: bool + r0 = PyObject_Str(x) + r1 = PyObject_IsTrue(r0) + r2 = r1 >= 0 :: signed + r3 = truncate r1: int32 to builtins.bool + if r3 goto L2 else goto L1 :: bool L1: - r0 = r1 + r4 = r0 goto L3 L2: r5 = PyObject_Str(y) - r0 = r5 + r4 = r5 L3: - return r0 + return r4 [case testSimpleNot] def f(x: int, y: int) -> int: @@ -786,7 +786,7 @@ def f(x): x :: object r0 :: str r1 :: object - y, r2 :: int + r2, y :: int r3 :: str r4 :: object r5 :: int @@ -2116,20 +2116,18 @@ def f(l): r3 :: short_int r4 :: bit r5 :: object - x, y, z :: int r6 :: tuple[int, int, int] - r7, r8, r9 :: int + r7, x, r8, y, r9, z :: int r10 :: short_int r11 :: list r12 :: short_int r13 :: ptr - r14 :: native_int + r14 :: int64 r15 :: short_int r16 :: bit r17 :: object - x_2, y_2, z_2 :: int r18 :: tuple[int, int, int] - r19, r20, r21, r22, r23 :: int + r19, x_2, r20, y_2, r21, z_2, r22, r23 :: int r24 :: object r25 :: int32 r26 :: bit diff --git a/mypyc/test-data/irbuild-dict.test b/mypyc/test-data/irbuild-dict.test index 37bbd09d1cef1..694a3babbc57e 100644 --- a/mypyc/test-data/irbuild-dict.test +++ b/mypyc/test-data/irbuild-dict.test @@ -145,7 +145,7 @@ def increment(d): r5 :: int r6 :: bool r7 :: object - k, r8 :: str + r8, k :: str r9, r10, r11 :: object r12 :: int32 r13, r14, r15 :: bit @@ -223,7 +223,7 @@ def print_dict_methods(d1, d2): r5 :: int r6 :: bool r7 :: object - v, r8 :: int + r8, v :: int r9 :: object r10 :: int32 r11 :: bit diff --git a/mypyc/test-data/irbuild-int.test b/mypyc/test-data/irbuild-int.test index bdf15ad52964e..b1b8d191bd059 100644 --- a/mypyc/test-data/irbuild-int.test +++ b/mypyc/test-data/irbuild-int.test @@ -4,23 +4,24 @@ def f(x: int, y: int) -> bool: [out] def f(x, y): x, y :: int - r0 :: bool - r1 :: native_int - r2, r3, r4, r5 :: bit + r0 :: native_int + r1, r2 :: bit + r3 :: bool + r4, r5 :: bit L0: - r1 = x & 1 - r2 = r1 == 0 - if r2 goto L1 else goto L2 :: bool + r0 = x & 1 + r1 = r0 == 0 + if r1 goto L1 else goto L2 :: bool L1: - r3 = x != y - r0 = r3 + r2 = x != y + r3 = r2 goto L3 L2: r4 = CPyTagged_IsEq_(x, y) r5 = r4 ^ 1 - r0 = r5 + r3 = r5 L3: - return r0 + return r3 [case testShortIntComparisons] def f(x: int) -> int: diff --git a/mypyc/test-data/irbuild-optional.test b/mypyc/test-data/irbuild-optional.test index a8368fbd88c0e..dcbb758d75f12 100644 --- a/mypyc/test-data/irbuild-optional.test +++ b/mypyc/test-data/irbuild-optional.test @@ -215,8 +215,8 @@ def f(y: int) -> None: [out] def f(y): y :: int - x :: union[int, None] r0 :: object + x :: union[int, None] r1 :: bit r2, r3 :: object r4, r5 :: bit @@ -527,13 +527,13 @@ class B: [out] def f(o): o :: union[object, object] - r0, r1 :: object - r2 :: str - r3 :: object + r0 :: object + r1 :: str + r2, r3 :: object L0: - r1 = o - r2 = load_global CPyStatic_unicode_6 :: static ('x') - r3 = CPyObject_GetAttr(r1, r2) - r0 = r3 + r0 = o + r1 = load_global CPyStatic_unicode_6 :: static ('x') + r2 = CPyObject_GetAttr(r0, r1) + r3 = r2 L1: return 1 diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index d824bedb206f4..67e30a7458759 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -303,7 +303,7 @@ def f(ls): r3 :: short_int r4 :: bit r5 :: object - x, r6, r7 :: int + r6, x, r7 :: int r8 :: short_int L0: y = 0 @@ -344,7 +344,7 @@ def f(d): r5 :: int r6 :: bool r7 :: object - key, r8 :: int + r8, key :: int r9, r10 :: object r11 :: int r12, r13 :: bit @@ -581,13 +581,13 @@ def multi_assign(t, a, l): t :: tuple[int, tuple[str, object]] a :: __main__.A l :: list - z, r0 :: int + r0 :: int r1 :: bool r2 :: tuple[str, object] r3 :: str r4 :: bit r5 :: object - r6 :: int + r6, z :: int L0: r0 = t[0] a.x = r0; r1 = is_error @@ -874,7 +874,7 @@ def f(a): r4 :: short_int r5 :: bit r6 :: object - x, r7, r8 :: int + r7, x, r8 :: int r9, r10 :: short_int L0: r0 = 0 @@ -951,7 +951,7 @@ def f(a, b): r4 :: short_int r5 :: bit r6, r7 :: object - x, r8 :: int + r8, x :: int r9, y :: bool r10 :: int32 r11 :: bit @@ -1004,7 +1004,7 @@ def g(a, b): r7, r8 :: bit r9, x :: bool r10 :: object - y, r11 :: int + r11, y :: int r12, r13 :: short_int r14 :: bit L0: diff --git a/mypyc/test-data/irbuild-try.test b/mypyc/test-data/irbuild-try.test index 3687b4b931e49..7d6804031474e 100644 --- a/mypyc/test-data/irbuild-try.test +++ b/mypyc/test-data/irbuild-try.test @@ -115,7 +115,7 @@ def g(): r11 :: str r12 :: object r13 :: bit - e, r14 :: object + r14, e :: object r15 :: str r16 :: object r17 :: str @@ -289,28 +289,28 @@ L2: L3: L4: L5: - r6 = :: tuple[object, object, object] - r5 = r6 + r5 = :: tuple[object, object, object] + r6 = r5 goto L7 L6: (handler for L1, L2, L3) r7 = CPy_CatchError() - r5 = r7 + r6 = r7 L7: r8 = load_global CPyStatic_unicode_3 :: static ('finally') r9 = builtins :: module r10 = load_global CPyStatic_unicode_4 :: static ('print') r11 = CPyObject_GetAttr(r9, r10) r12 = PyObject_CallFunctionObjArgs(r11, r8, 0) - if is_error(r5) goto L9 else goto L8 + if is_error(r6) goto L9 else goto L8 L8: CPy_Reraise() unreachable L9: goto L13 L10: (handler for L7, L8) - if is_error(r5) goto L12 else goto L11 + if is_error(r6) goto L12 else goto L11 L11: - CPy_RestoreExcInfo(r5) + CPy_RestoreExcInfo(r6) L12: r13 = CPy_KeepPropagating() unreachable @@ -388,31 +388,30 @@ L7: (handler for L3, L4, L5) L8: L9: L10: - r24 = :: tuple[object, object, object] - r23 = r24 + r23 = :: tuple[object, object, object] + r24 = r23 goto L12 L11: (handler for L1, L6, L7, L8) r25 = CPy_CatchError() - r23 = r25 + r24 = r25 L12: if r7 goto L13 else goto L14 :: bool L13: r26 = load_address _Py_NoneStruct r27 = PyObject_CallFunctionObjArgs(r3, r0, r26, r26, r26, 0) L14: - if is_error(r23) goto L16 else goto L15 + if is_error(r24) goto L16 else goto L15 L15: CPy_Reraise() unreachable L16: goto L20 L17: (handler for L12, L13, L14, L15) - if is_error(r23) goto L19 else goto L18 + if is_error(r24) goto L19 else goto L18 L18: - CPy_RestoreExcInfo(r23) + CPy_RestoreExcInfo(r24) L19: r28 = CPy_KeepPropagating() unreachable L20: return 1 - diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test index e1a8bf69a14eb..2bff1eea25f39 100644 --- a/mypyc/test-data/irbuild-tuple.test +++ b/mypyc/test-data/irbuild-tuple.test @@ -79,8 +79,9 @@ def f() -> int: [out] def f(): r0 :: tuple[int, int] + r1 :: object t :: tuple - r1, r2 :: object + r2 :: object r3 :: int L0: r0 = (2, 4) @@ -135,7 +136,7 @@ def f(xs): r3 :: short_int r4 :: bit r5 :: object - x, r6 :: str + r6, x :: str r7 :: short_int L0: r0 = 0 @@ -191,61 +192,66 @@ def f(i: int) -> bool: [out] def f(i): i :: int - r0, r1, r2 :: bool - r3 :: native_int - r4, r5, r6 :: bit - r7 :: bool - r8 :: native_int - r9, r10, r11 :: bit - r12 :: bool - r13 :: native_int - r14, r15, r16 :: bit + r0 :: native_int + r1, r2 :: bit + r3 :: bool + r4 :: bit + r5 :: bool + r6 :: native_int + r7, r8 :: bit + r9 :: bool + r10 :: bit + r11 :: bool + r12 :: native_int + r13, r14 :: bit + r15 :: bool + r16 :: bit L0: - r3 = i & 1 - r4 = r3 == 0 - if r4 goto L1 else goto L2 :: bool + r0 = i & 1 + r1 = r0 == 0 + if r1 goto L1 else goto L2 :: bool L1: - r5 = i == 2 - r2 = r5 + r2 = i == 2 + r3 = r2 goto L3 L2: - r6 = CPyTagged_IsEq_(i, 2) - r2 = r6 + r4 = CPyTagged_IsEq_(i, 2) + r3 = r4 L3: - if r2 goto L4 else goto L5 :: bool + if r3 goto L4 else goto L5 :: bool L4: - r1 = r2 + r5 = r3 goto L9 L5: - r8 = i & 1 - r9 = r8 == 0 - if r9 goto L6 else goto L7 :: bool + r6 = i & 1 + r7 = r6 == 0 + if r7 goto L6 else goto L7 :: bool L6: - r10 = i == 4 - r7 = r10 + r8 = i == 4 + r9 = r8 goto L8 L7: - r11 = CPyTagged_IsEq_(i, 4) - r7 = r11 + r10 = CPyTagged_IsEq_(i, 4) + r9 = r10 L8: - r1 = r7 + r5 = r9 L9: - if r1 goto L10 else goto L11 :: bool + if r5 goto L10 else goto L11 :: bool L10: - r0 = r1 + r11 = r5 goto L15 L11: - r13 = i & 1 - r14 = r13 == 0 - if r14 goto L12 else goto L13 :: bool + r12 = i & 1 + r13 = r12 == 0 + if r13 goto L12 else goto L13 :: bool L12: - r15 = i == 6 - r12 = r15 + r14 = i == 6 + r15 = r14 goto L14 L13: r16 = CPyTagged_IsEq_(i, 6) - r12 = r16 + r15 = r16 L14: - r0 = r12 + r11 = r15 L15: - return r0 + return r11 diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index a817d9538dfba..c42d4834ae5c0 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -728,7 +728,7 @@ def f(d): r5 :: int r6 :: bool r7 :: object - key, r8 :: int + r8, key :: int r9, r10 :: object r11 :: int r12, r13 :: bit @@ -812,4 +812,3 @@ L0: dec_ref x r3 = r2 << 1 return r3 - diff --git a/mypyc/test/test_analysis.py b/mypyc/test/test_analysis.py index 826b91a32a60f..c998218abbb10 100644 --- a/mypyc/test/test_analysis.py +++ b/mypyc/test/test_analysis.py @@ -10,6 +10,8 @@ from mypyc.analysis import dataflow from mypyc.transform import exceptions from mypyc.ir.pprint import format_func, generate_names_for_env +from mypyc.ir.ops import Value +from mypyc.ir.func_ir import all_values from mypyc.test.testutil import ( ICODE_GEN_BUILTINS, use_custom_builtins, MypycDataSuite, build_ir_for_single_file, assert_test_output, replace_native_int @@ -43,9 +45,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: exceptions.insert_exception_handling(fn) actual.extend(format_func(fn)) cfg = dataflow.get_cfg(fn.blocks) - - args = set(reg for reg, i in fn.env.indexes.items() if i < len(fn.args)) - + args = set(fn.arg_regs) # type: Set[Value] name = testcase.name if name.endswith('_MaybeDefined'): # Forward, maybe @@ -57,14 +57,14 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: # Forward, must analysis_result = dataflow.analyze_must_defined_regs( fn.blocks, cfg, args, - regs=fn.env.regs()) + regs=all_values(fn.arg_regs, fn.blocks)) elif name.endswith('_BorrowedArgument'): # Forward, must analysis_result = dataflow.analyze_borrowed_arguments(fn.blocks, cfg, args) else: assert False, 'No recognized _AnalysisName suffix in test case' - names = generate_names_for_env(fn.env) + names = generate_names_for_env(fn.arg_regs, fn.blocks) for key in sorted(analysis_result.before.keys(), key=lambda x: (x[0].label, x[1])): diff --git a/mypyc/test/test_emit.py b/mypyc/test/test_emit.py index fb44d1ebc7a2c..b7029c195db77 100644 --- a/mypyc/test/test_emit.py +++ b/mypyc/test/test_emit.py @@ -1,18 +1,16 @@ import unittest - -from mypy.nodes import Var +from typing import Dict from mypyc.codegen.emit import Emitter, EmitterContext -from mypyc.ir.ops import BasicBlock, Environment +from mypyc.ir.ops import BasicBlock, Environment, Value, Register from mypyc.ir.rtypes import int_rprimitive -from mypyc.ir.pprint import generate_names_for_env from mypyc.namegen import NameGenerator class TestEmitter(unittest.TestCase): def setUp(self) -> None: self.env = Environment() - self.n = self.env.add_local(Var('n'), int_rprimitive) + self.n = Register(int_rprimitive, 'n') self.context = EmitterContext(NameGenerator([['mod']])) def test_label(self) -> None: @@ -20,7 +18,7 @@ def test_label(self) -> None: assert emitter.label(BasicBlock(4)) == 'CPyL4' def test_reg(self) -> None: - names = generate_names_for_env(self.env) + names = {self.n: 'n'} # type: Dict[Value, str] emitter = Emitter(self.context, self.env, names) assert emitter.reg(self.n) == 'cpy_r_n' diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index 90214b6d0f4d4..beb2daecebd79 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -4,17 +4,15 @@ from mypy.ordered_dict import OrderedDict -from mypy.nodes import Var from mypy.test.helpers import assert_string_arrays_equal from mypyc.ir.ops import ( Environment, BasicBlock, Goto, Return, LoadInt, Assign, IncRef, DecRef, Branch, - Call, Unbox, Box, TupleGet, GetAttr, RegisterOp, - SetAttr, Op, Value, CallC, BinaryIntOp, LoadMem, GetElementPtr, LoadAddress, ComparisonOp, - SetMem + Call, Unbox, Box, TupleGet, GetAttr, SetAttr, Op, Value, CallC, BinaryIntOp, LoadMem, + GetElementPtr, LoadAddress, ComparisonOp, SetMem, Register ) from mypyc.ir.rtypes import ( - RTuple, RInstance, int_rprimitive, bool_rprimitive, list_rprimitive, + RTuple, RInstance, RType, int_rprimitive, bool_rprimitive, list_rprimitive, dict_rprimitive, object_rprimitive, c_int_rprimitive, short_int_rprimitive, int32_rprimitive, int64_rprimitive, RStruct, pointer_rprimitive ) @@ -40,31 +38,38 @@ class TestFunctionEmitterVisitor(unittest.TestCase): def setUp(self) -> None: self.env = Environment() - self.n = self.env.add_local(Var('n'), int_rprimitive) - self.m = self.env.add_local(Var('m'), int_rprimitive) - self.k = self.env.add_local(Var('k'), int_rprimitive) - self.l = self.env.add_local(Var('l'), list_rprimitive) # noqa - self.ll = self.env.add_local(Var('ll'), list_rprimitive) - self.o = self.env.add_local(Var('o'), object_rprimitive) - self.o2 = self.env.add_local(Var('o2'), object_rprimitive) - self.d = self.env.add_local(Var('d'), dict_rprimitive) - self.b = self.env.add_local(Var('b'), bool_rprimitive) - self.s1 = self.env.add_local(Var('s1'), short_int_rprimitive) - self.s2 = self.env.add_local(Var('s2'), short_int_rprimitive) - self.i32 = self.env.add_local(Var('i32'), int32_rprimitive) - self.i32_1 = self.env.add_local(Var('i32_1'), int32_rprimitive) - self.i64 = self.env.add_local(Var('i64'), int64_rprimitive) - self.i64_1 = self.env.add_local(Var('i64_1'), int64_rprimitive) - self.ptr = self.env.add_local(Var('ptr'), pointer_rprimitive) - self.t = self.env.add_local(Var('t'), RTuple([int_rprimitive, bool_rprimitive])) - self.tt = self.env.add_local( - Var('tt'), - RTuple([RTuple([int_rprimitive, bool_rprimitive]), bool_rprimitive])) + + self.registers = [] # type: List[Register] + + def add_local(name: str, rtype: RType) -> Register: + reg = Register(rtype, name) + self.registers.append(reg) + return reg + + self.n = add_local('n', int_rprimitive) + self.m = add_local('m', int_rprimitive) + self.k = add_local('k', int_rprimitive) + self.l = add_local('l', list_rprimitive) # noqa + self.ll = add_local('ll', list_rprimitive) + self.o = add_local('o', object_rprimitive) + self.o2 = add_local('o2', object_rprimitive) + self.d = add_local('d', dict_rprimitive) + self.b = add_local('b', bool_rprimitive) + self.s1 = add_local('s1', short_int_rprimitive) + self.s2 = add_local('s2', short_int_rprimitive) + self.i32 = add_local('i32', int32_rprimitive) + self.i32_1 = add_local('i32_1', int32_rprimitive) + self.i64 = add_local('i64', int64_rprimitive) + self.i64_1 = add_local('i64_1', int64_rprimitive) + self.ptr = add_local('ptr', pointer_rprimitive) + self.t = add_local('t', RTuple([int_rprimitive, bool_rprimitive])) + self.tt = add_local( + 'tt', RTuple([RTuple([int_rprimitive, bool_rprimitive]), bool_rprimitive])) ir = ClassIR('A', 'mod') ir.attributes = OrderedDict([('x', bool_rprimitive), ('y', int_rprimitive)]) compute_vtable(ir) ir.mro = [ir] - self.r = self.env.add_local(Var('r'), RInstance(ir)) + self.r = add_local('r', RInstance(ir)) self.context = EmitterContext(NameGenerator([['mod']])) @@ -80,7 +85,7 @@ def test_load_int(self) -> None: self.assert_emit(LoadInt(5), "cpy_r_i0 = 10;") self.assert_emit(LoadInt(5, -1, c_int_rprimitive), - "cpy_r_i1 = 5;") + "cpy_r_i0 = 5;") def test_tuple_get(self) -> None: self.assert_emit(TupleGet(self.t, 1, 0), 'cpy_r_r0 = cpy_r_t.f1;') @@ -239,53 +244,53 @@ def test_binary_int_op(self) -> None: self.assert_emit(BinaryIntOp(short_int_rprimitive, self.s1, self.s2, BinaryIntOp.ADD, 1), """cpy_r_r0 = cpy_r_s1 + cpy_r_s2;""") self.assert_emit(BinaryIntOp(short_int_rprimitive, self.s1, self.s2, BinaryIntOp.SUB, 1), - """cpy_r_r1 = cpy_r_s1 - cpy_r_s2;""") + """cpy_r_r0 = cpy_r_s1 - cpy_r_s2;""") self.assert_emit(BinaryIntOp(short_int_rprimitive, self.s1, self.s2, BinaryIntOp.MUL, 1), - """cpy_r_r2 = cpy_r_s1 * cpy_r_s2;""") + """cpy_r_r0 = cpy_r_s1 * cpy_r_s2;""") self.assert_emit(BinaryIntOp(short_int_rprimitive, self.s1, self.s2, BinaryIntOp.DIV, 1), - """cpy_r_r3 = cpy_r_s1 / cpy_r_s2;""") + """cpy_r_r0 = cpy_r_s1 / cpy_r_s2;""") self.assert_emit(BinaryIntOp(short_int_rprimitive, self.s1, self.s2, BinaryIntOp.MOD, 1), - """cpy_r_r4 = cpy_r_s1 % cpy_r_s2;""") + """cpy_r_r0 = cpy_r_s1 % cpy_r_s2;""") self.assert_emit(BinaryIntOp(short_int_rprimitive, self.s1, self.s2, BinaryIntOp.AND, 1), - """cpy_r_r5 = cpy_r_s1 & cpy_r_s2;""") + """cpy_r_r0 = cpy_r_s1 & cpy_r_s2;""") self.assert_emit(BinaryIntOp(short_int_rprimitive, self.s1, self.s2, BinaryIntOp.OR, 1), - """cpy_r_r6 = cpy_r_s1 | cpy_r_s2;""") + """cpy_r_r0 = cpy_r_s1 | cpy_r_s2;""") self.assert_emit(BinaryIntOp(short_int_rprimitive, self.s1, self.s2, BinaryIntOp.XOR, 1), - """cpy_r_r7 = cpy_r_s1 ^ cpy_r_s2;""") + """cpy_r_r0 = cpy_r_s1 ^ cpy_r_s2;""") self.assert_emit(BinaryIntOp(short_int_rprimitive, self.s1, self.s2, BinaryIntOp.LEFT_SHIFT, 1), - """cpy_r_r8 = cpy_r_s1 << cpy_r_s2;""") + """cpy_r_r0 = cpy_r_s1 << cpy_r_s2;""") self.assert_emit(BinaryIntOp(short_int_rprimitive, self.s1, self.s2, BinaryIntOp.RIGHT_SHIFT, 1), - """cpy_r_r9 = cpy_r_s1 >> cpy_r_s2;""") + """cpy_r_r0 = cpy_r_s1 >> cpy_r_s2;""") def test_comparison_op(self) -> None: # signed self.assert_emit(ComparisonOp(self.s1, self.s2, ComparisonOp.SLT, 1), """cpy_r_r0 = (Py_ssize_t)cpy_r_s1 < (Py_ssize_t)cpy_r_s2;""") self.assert_emit(ComparisonOp(self.i32, self.i32_1, ComparisonOp.SLT, 1), - """cpy_r_r1 = cpy_r_i32 < cpy_r_i32_1;""") + """cpy_r_r0 = cpy_r_i32 < cpy_r_i32_1;""") self.assert_emit(ComparisonOp(self.i64, self.i64_1, ComparisonOp.SLT, 1), - """cpy_r_r2 = cpy_r_i64 < cpy_r_i64_1;""") + """cpy_r_r0 = cpy_r_i64 < cpy_r_i64_1;""") # unsigned self.assert_emit(ComparisonOp(self.s1, self.s2, ComparisonOp.ULT, 1), - """cpy_r_r3 = cpy_r_s1 < cpy_r_s2;""") + """cpy_r_r0 = cpy_r_s1 < cpy_r_s2;""") self.assert_emit(ComparisonOp(self.i32, self.i32_1, ComparisonOp.ULT, 1), - """cpy_r_r4 = (uint32_t)cpy_r_i32 < (uint32_t)cpy_r_i32_1;""") + """cpy_r_r0 = (uint32_t)cpy_r_i32 < (uint32_t)cpy_r_i32_1;""") self.assert_emit(ComparisonOp(self.i64, self.i64_1, ComparisonOp.ULT, 1), - """cpy_r_r5 = (uint64_t)cpy_r_i64 < (uint64_t)cpy_r_i64_1;""") + """cpy_r_r0 = (uint64_t)cpy_r_i64 < (uint64_t)cpy_r_i64_1;""") # object type self.assert_emit(ComparisonOp(self.o, self.o2, ComparisonOp.EQ, 1), - """cpy_r_r6 = cpy_r_o == cpy_r_o2;""") + """cpy_r_r0 = cpy_r_o == cpy_r_o2;""") self.assert_emit(ComparisonOp(self.o, self.o2, ComparisonOp.NEQ, 1), - """cpy_r_r7 = cpy_r_o != cpy_r_o2;""") + """cpy_r_r0 = cpy_r_o != cpy_r_o2;""") def test_load_mem(self) -> None: self.assert_emit(LoadMem(bool_rprimitive, self.ptr, None), """cpy_r_r0 = *(char *)cpy_r_ptr;""") self.assert_emit(LoadMem(bool_rprimitive, self.ptr, self.s1), - """cpy_r_r1 = *(char *)cpy_r_ptr;""") + """cpy_r_r0 = *(char *)cpy_r_ptr;""") def test_set_mem(self) -> None: self.assert_emit(SetMem(bool_rprimitive, self.ptr, self.b, None), @@ -297,19 +302,18 @@ def test_get_element_ptr(self) -> None: self.assert_emit(GetElementPtr(self.o, r, "b"), """cpy_r_r0 = (CPyPtr)&((Foo *)cpy_r_o)->b;""") self.assert_emit(GetElementPtr(self.o, r, "i32"), - """cpy_r_r1 = (CPyPtr)&((Foo *)cpy_r_o)->i32;""") + """cpy_r_r0 = (CPyPtr)&((Foo *)cpy_r_o)->i32;""") self.assert_emit(GetElementPtr(self.o, r, "i64"), - """cpy_r_r2 = (CPyPtr)&((Foo *)cpy_r_o)->i64;""") + """cpy_r_r0 = (CPyPtr)&((Foo *)cpy_r_o)->i64;""") def test_load_address(self) -> None: self.assert_emit(LoadAddress(object_rprimitive, "PyDict_Type"), """cpy_r_r0 = (PyObject *)&PyDict_Type;""") def assert_emit(self, op: Op, expected: str) -> None: - if isinstance(op, RegisterOp): - self.env.add_op(op) - - value_names = generate_names_for_env(self.env) + block = BasicBlock(0) + block.ops.append(op) + value_names = generate_names_for_env(self.registers, [block]) emitter = Emitter(self.context, self.env, value_names) declarations = Emitter(self.context, self.env, value_names) emitter.fragments = [] @@ -352,17 +356,18 @@ def assert_emit_binary_op(self, class TestGenerateFunction(unittest.TestCase): def setUp(self) -> None: - self.var = Var('arg') self.arg = RuntimeArg('arg', int_rprimitive) self.env = Environment() - self.reg = self.env.add_local(self.var, int_rprimitive) + self.reg = Register(int_rprimitive, 'arg') self.block = BasicBlock(0) def test_simple(self) -> None: self.block.ops.append(Return(self.reg)) fn = FuncIR(FuncDecl('myfunc', None, 'mod', FuncSignature([self.arg], int_rprimitive)), - [self.block], self.env) - value_names = generate_names_for_env(self.env) + [self.reg], + [self.block], + self.env) + value_names = generate_names_for_env(fn.arg_regs, fn.blocks) emitter = Emitter(EmitterContext(NameGenerator([['mod']])), self.env, value_names) generate_native_function(fn, emitter, 'prog.py', 'prog', optimize_int=False) result = emitter.fragments @@ -378,10 +383,11 @@ def test_simple(self) -> None: def test_register(self) -> None: op = LoadInt(5) self.block.ops.append(op) - self.env.add_op(op) fn = FuncIR(FuncDecl('myfunc', None, 'mod', FuncSignature([self.arg], list_rprimitive)), - [self.block], self.env) - value_names = generate_names_for_env(self.env) + [self.reg], + [self.block], + self.env) + value_names = generate_names_for_env(fn.arg_regs, fn.blocks) emitter = Emitter(EmitterContext(NameGenerator([['mod']])), self.env, value_names) generate_native_function(fn, emitter, 'prog.py', 'prog', optimize_int=False) result = emitter.fragments diff --git a/mypyc/transform/exceptions.py b/mypyc/transform/exceptions.py index bd5395dcf4a54..dc0639dd86ff2 100644 --- a/mypyc/transform/exceptions.py +++ b/mypyc/transform/exceptions.py @@ -38,7 +38,6 @@ def add_handler_block(ir: FuncIR) -> BasicBlock: ir.blocks.append(block) op = LoadErrorValue(ir.ret_type) block.ops.append(op) - ir.env.add_op(op) block.ops.append(Return(op)) return block @@ -84,7 +83,6 @@ def split_blocks_at_errors(blocks: List[BasicBlock], # semantics, using a temporary bool with value false tmp = LoadInt(0, rtype=bool_rprimitive) cur_block.ops.append(tmp) - env.add_op(tmp) target = tmp else: assert False, 'unknown error kind %d' % op.error_kind diff --git a/mypyc/transform/refcount.py b/mypyc/transform/refcount.py index 2018cf32f8002..6bc4f0417510c 100644 --- a/mypyc/transform/refcount.py +++ b/mypyc/transform/refcount.py @@ -27,10 +27,9 @@ AnalysisDict ) from mypyc.ir.ops import ( - BasicBlock, Assign, RegisterOp, DecRef, IncRef, Branch, Goto, Environment, - Op, ControlOp, Value, Register + BasicBlock, Assign, RegisterOp, DecRef, IncRef, Branch, Goto, Op, ControlOp, Value, Register ) -from mypyc.ir.func_ir import FuncIR +from mypyc.ir.func_ir import FuncIR, all_values DecIncs = Tuple[Tuple[Tuple[Value, bool], ...], Tuple[Value, ...]] @@ -47,12 +46,14 @@ def insert_ref_count_opcodes(ir: FuncIR) -> None: This is the entry point to this module. """ cfg = get_cfg(ir.blocks) - borrowed = set(reg for reg in ir.env.regs() if reg.is_borrowed) - args = set(reg for reg in ir.env.regs() if ir.env.indexes[reg] < len(ir.args)) - regs = [reg for reg in ir.env.regs() if isinstance(reg, Register)] + values = all_values(ir.arg_regs, ir.blocks) + + borrowed = {value for value in values if value.is_borrowed} + args = set(ir.arg_regs) # type: Set[Value] live = analyze_live_regs(ir.blocks, cfg) borrow = analyze_borrowed_arguments(ir.blocks, cfg, borrowed) - defined = analyze_must_defined_regs(ir.blocks, cfg, args, regs) + defined = analyze_must_defined_regs(ir.blocks, cfg, args, values) + ordering = make_value_ordering(ir) cache = {} # type: BlockCache for block in ir.blocks[:]: if isinstance(block.ops[-1], (Branch, Goto)): @@ -63,8 +64,8 @@ def insert_ref_count_opcodes(ir: FuncIR) -> None: borrow.before, borrow.after, defined.after, - ir.env) - transform_block(block, live.before, live.after, borrow.before, defined.after, ir.env) + ordering) + transform_block(block, live.before, live.after, borrow.before, defined.after) # Find all the xdecs we inserted and note the registers down as # needing to be initialized. @@ -95,8 +96,7 @@ def transform_block(block: BasicBlock, pre_live: 'AnalysisDict[Value]', post_live: 'AnalysisDict[Value]', pre_borrow: 'AnalysisDict[Value]', - post_must_defined: 'AnalysisDict[Value]', - env: Environment) -> None: + post_must_defined: 'AnalysisDict[Value]') -> None: old_ops = block.ops ops = [] # type: List[Op] for i, op in enumerate(old_ops): @@ -144,7 +144,7 @@ def insert_branch_inc_and_decrefs( pre_borrow: 'AnalysisDict[Value]', post_borrow: 'AnalysisDict[Value]', post_must_defined: 'AnalysisDict[Value]', - env: Environment) -> None: + ordering: Dict[Value, int]) -> None: """Insert inc_refs and/or dec_refs after a branch/goto. Add dec_refs for registers that become dead after a branch. @@ -178,21 +178,22 @@ def f(a: int) -> None true_decincs = ( after_branch_decrefs( branch.true, pre_live, source_defined, - source_borrowed, source_live_regs, env, omitted), + source_borrowed, source_live_regs, ordering, omitted), after_branch_increfs( - branch.true, pre_live, pre_borrow, source_borrowed, env)) + branch.true, pre_live, pre_borrow, source_borrowed, ordering)) branch.true = add_block(true_decincs, cache, blocks, branch.true) false_decincs = ( after_branch_decrefs( - branch.false, pre_live, source_defined, source_borrowed, source_live_regs, env), + branch.false, pre_live, source_defined, source_borrowed, source_live_regs, + ordering), after_branch_increfs( - branch.false, pre_live, pre_borrow, source_borrowed, env)) + branch.false, pre_live, pre_borrow, source_borrowed, ordering)) branch.false = add_block(false_decincs, cache, blocks, branch.false) elif isinstance(block.ops[-1], Goto): goto = block.ops[-1] new_decincs = ((), after_branch_increfs( - goto.label, pre_live, pre_borrow, source_borrowed, env)) + goto.label, pre_live, pre_borrow, source_borrowed, ordering)) goto.label = add_block(new_decincs, cache, blocks, goto.label) @@ -201,13 +202,13 @@ def after_branch_decrefs(label: BasicBlock, source_defined: Set[Value], source_borrowed: Set[Value], source_live_regs: Set[Value], - env: Environment, + ordering: Dict[Value, int], omitted: Iterable[Value] = ()) -> Tuple[Tuple[Value, bool], ...]: target_pre_live = pre_live[label, 0] decref = source_live_regs - target_pre_live - source_borrowed if decref: return tuple((reg, is_maybe_undefined(source_defined, reg)) - for reg in sorted(decref, key=lambda r: env.indexes[r]) + for reg in sorted(decref, key=lambda r: ordering[r]) if reg.type.is_refcounted and reg not in omitted) return () @@ -216,13 +217,13 @@ def after_branch_increfs(label: BasicBlock, pre_live: 'AnalysisDict[Value]', pre_borrow: 'AnalysisDict[Value]', source_borrowed: Set[Value], - env: Environment) -> Tuple[Value, ...]: + ordering: Dict[Value, int]) -> Tuple[Value, ...]: target_pre_live = pre_live[label, 0] target_borrowed = pre_borrow[label, 0] incref = (source_borrowed - target_borrowed) & target_pre_live if incref: return tuple(reg - for reg in sorted(incref, key=lambda r: env.indexes[r]) + for reg in sorted(incref, key=lambda r: ordering[r]) if reg.type.is_refcounted) return () @@ -244,3 +245,26 @@ def add_block(decincs: DecIncs, cache: BlockCache, block.ops.append(Goto(label)) cache[label, decincs] = block return block + + +def make_value_ordering(ir: FuncIR) -> Dict[Value, int]: + """Create a ordering of values that allows them to be sorted.""" + # TODO: Never initialized values?? + result = {} # type: Dict[Value, int] + n = 0 + + for arg in ir.arg_regs: + result[arg] = n + n += 1 + + for block in ir.blocks: + for op in block.ops: + if isinstance(op, Assign): + if op.dest not in result: + result[op.dest] = n + n += 1 + elif op not in result: + result[op] = n + n += 1 + + return result diff --git a/mypyc/transform/uninit.py b/mypyc/transform/uninit.py index cb741495f4b5b..3e5eb55309080 100644 --- a/mypyc/transform/uninit.py +++ b/mypyc/transform/uninit.py @@ -12,7 +12,7 @@ BasicBlock, Branch, Value, RaiseStandardError, Unreachable, Environment, Register, LoadAddress ) -from mypyc.ir.func_ir import FuncIR +from mypyc.ir.func_ir import FuncIR, all_values def insert_uninit_checks(ir: FuncIR) -> None: @@ -21,8 +21,11 @@ def insert_uninit_checks(ir: FuncIR) -> None: cleanup_cfg(ir.blocks) cfg = get_cfg(ir.blocks) - args = set(reg for reg in ir.env.regs() if ir.env.indexes[reg] < len(ir.args)) - must_defined = analyze_must_defined_regs(ir.blocks, cfg, args, ir.env.regs()) + must_defined = analyze_must_defined_regs( + ir.blocks, + cfg, + set(ir.arg_regs), + all_values(ir.arg_regs, ir.blocks)) ir.blocks = split_blocks_at_uninits(ir.env, ir.blocks, must_defined.before) @@ -67,7 +70,6 @@ def split_blocks_at_uninits(env: Environment, RaiseStandardError.UNBOUND_LOCAL_ERROR, "local variable '{}' referenced before assignment".format(src.name), op.line) - env.add_op(raise_std) error_block.ops.append(raise_std) error_block.ops.append(Unreachable()) cur_block = new_block From 4254ddf0c303064efeecad7064e37171448a92a4 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 27 Nov 2020 11:31:26 +0000 Subject: [PATCH 02/10] Add tests for IR pretty printing --- mypyc/test/test_pprint.py | 40 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 mypyc/test/test_pprint.py diff --git a/mypyc/test/test_pprint.py b/mypyc/test/test_pprint.py new file mode 100644 index 0000000000000..77ae68d697c90 --- /dev/null +++ b/mypyc/test/test_pprint.py @@ -0,0 +1,40 @@ +import unittest +from typing import List + +from mypyc.ir.ops import BasicBlock, Register, Op, LoadInt, BinaryIntOp, Unreachable, Assign +from mypyc.ir.rtypes import int_rprimitive +from mypyc.ir.pprint import generate_names_for_env + + +def register(name: str) -> Register: + return Register(int_rprimitive, 'foo', is_arg=True) + + +def make_block(ops: List[Op]) -> BasicBlock: + block = BasicBlock() + block.ops.extend(ops) + return block + + +class TestGenerateNames(unittest.TestCase): + def test_empty(self) -> None: + assert generate_names_for_env([], []) == {} + + def test_arg(self) -> None: + reg = register('foo') + assert generate_names_for_env([reg], []) == {reg: 'foo'} + + def test_int_op(self) -> None: + op1 = LoadInt(2) + op2 = LoadInt(4) + op3 = BinaryIntOp(int_rprimitive, op1, op2, BinaryIntOp.ADD) + block = make_block([op1, op2, op3, Unreachable()]) + assert generate_names_for_env([], [block]) == {op1: 'i0', op2: 'i1', op3: 'r0'} + + def test_assign(self) -> None: + reg = register('foo') + op1 = LoadInt(2) + op2 = Assign(reg, op1) + op3 = Assign(reg, op1) + block = make_block([op1, op2, op3]) + assert generate_names_for_env([reg], [block]) == {op1: 'i0', reg: 'foo'} From c2c942d35e038ec9718f64e99ff35ef09060e5e4 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 27 Nov 2020 11:35:40 +0000 Subject: [PATCH 03/10] Fix self check --- mypyc/test/test_analysis.py | 1 + mypyc/test/test_emitfunc.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mypyc/test/test_analysis.py b/mypyc/test/test_analysis.py index c998218abbb10..dc48cf5d2407a 100644 --- a/mypyc/test/test_analysis.py +++ b/mypyc/test/test_analysis.py @@ -1,6 +1,7 @@ """Test runner for data-flow analysis test cases.""" import os.path +from typing import Set from mypy.test.data import DataDrivenTestCase from mypy.test.config import test_temp_dir diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index beb2daecebd79..28b7e6eeae7f6 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -1,6 +1,6 @@ import unittest -from typing import Dict +from typing import Dict, List from mypy.ordered_dict import OrderedDict From 770c9ab23046166a1a351088c48090018ed5aa6a Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 27 Nov 2020 12:05:08 +0000 Subject: [PATCH 04/10] Update test cases --- mypyc/test-data/irbuild-basic.test | 265 ++++++++++++------------ mypyc/test-data/irbuild-optional.test | 136 ++++++------ mypyc/test-data/irbuild-statements.test | 97 +++++---- 3 files changed, 252 insertions(+), 246 deletions(-) diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index d4be063b14d8a..adf75fc7470e7 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -1293,23 +1293,24 @@ L3: unreachable def num(x): x :: int - r0 :: bool - r1 :: native_int - r2, r3, r4, r5 :: bit + r0 :: native_int + r1, r2 :: bit + r3 :: bool + r4, r5 :: bit L0: - r1 = x & 1 - r2 = r1 == 0 - if r2 goto L1 else goto L2 :: bool + r0 = x & 1 + r1 = r0 == 0 + if r1 goto L1 else goto L2 :: bool L1: - r3 = x != 0 - r0 = r3 + r2 = x != 0 + r3 = r2 goto L3 L2: r4 = CPyTagged_IsEq_(x, 0) r5 = r4 ^ 1 - r0 = r5 + r3 = r5 L3: - if r0 goto L4 else goto L5 :: bool + if r3 goto L4 else goto L5 :: bool L4: return 2 L5: @@ -1363,28 +1364,29 @@ def opt_int(x): r0 :: object r1 :: bit r2 :: int - r3 :: bool - r4 :: native_int - r5, r6, r7, r8 :: bit + r3 :: native_int + r4, r5 :: bit + r6 :: bool + r7, r8 :: bit L0: r0 = load_address _Py_NoneStruct r1 = x != r0 if r1 goto L1 else goto L6 :: bool L1: r2 = unbox(int, x) - r4 = r2 & 1 - r5 = r4 == 0 - if r5 goto L2 else goto L3 :: bool + r3 = r2 & 1 + r4 = r3 == 0 + if r4 goto L2 else goto L3 :: bool L2: - r6 = r2 != 0 - r3 = r6 + r5 = r2 != 0 + r6 = r5 goto L4 L3: r7 = CPyTagged_IsEq_(r2, 0) r8 = r7 ^ 1 - r3 = r8 + r6 = r8 L4: - if r3 goto L5 else goto L6 :: bool + if r6 goto L5 else goto L6 :: bool L5: return 2 L6: @@ -1926,13 +1928,15 @@ def f(): r12 :: short_int r13 :: bit r14 :: object - x, r15 :: int - r16 :: bool - r17 :: native_int - r18, r19, r20, r21 :: bit - r22 :: bool - r23 :: native_int - r24, r25, r26, r27 :: bit + r15, x :: int + r16 :: native_int + r17, r18 :: bit + r19 :: bool + r20, r21 :: bit + r22 :: native_int + r23, r24 :: bit + r25 :: bool + r26, r27 :: bit r28 :: int r29 :: object r30 :: int32 @@ -1947,9 +1951,9 @@ L0: r5 = get_element_ptr r1 ob_item :: PyListObject r6 = load_mem r5, r1 :: ptr* set_mem r6, r2, r1 :: builtins.object* - r7 = r6 + WORD_SIZE*1 + r7 = r6 + 8 set_mem r7, r3, r1 :: builtins.object* - r8 = r6 + WORD_SIZE*2 + r8 = r6 + 16 set_mem r8, r4, r1 :: builtins.object* r9 = 0 L1: @@ -1962,35 +1966,35 @@ L2: r14 = CPyList_GetItemUnsafe(r1, r9) r15 = unbox(int, r14) x = r15 - r17 = x & 1 - r18 = r17 == 0 - if r18 goto L3 else goto L4 :: bool + r16 = x & 1 + r17 = r16 == 0 + if r17 goto L3 else goto L4 :: bool L3: - r19 = x != 4 - r16 = r19 + r18 = x != 4 + r19 = r18 goto L5 L4: r20 = CPyTagged_IsEq_(x, 4) r21 = r20 ^ 1 - r16 = r21 + r19 = r21 L5: - if r16 goto L7 else goto L6 :: bool + if r19 goto L7 else goto L6 :: bool L6: goto L13 L7: - r23 = x & 1 - r24 = r23 == 0 - if r24 goto L8 else goto L9 :: bool + r22 = x & 1 + r23 = r22 == 0 + if r23 goto L8 else goto L9 :: bool L8: - r25 = x != 6 - r22 = r25 + r24 = x != 6 + r25 = r24 goto L10 L9: r26 = CPyTagged_IsEq_(x, 6) r27 = r26 ^ 1 - r22 = r27 + r25 = r27 L10: - if r22 goto L12 else goto L11 :: bool + if r25 goto L12 else goto L11 :: bool L11: goto L13 L12: @@ -2021,13 +2025,15 @@ def f(): r12 :: short_int r13 :: bit r14 :: object - x, r15 :: int - r16 :: bool - r17 :: native_int - r18, r19, r20, r21 :: bit - r22 :: bool - r23 :: native_int - r24, r25, r26, r27 :: bit + r15, x :: int + r16 :: native_int + r17, r18 :: bit + r19 :: bool + r20, r21 :: bit + r22 :: native_int + r23, r24 :: bit + r25 :: bool + r26, r27 :: bit r28 :: int r29, r30 :: object r31 :: int32 @@ -2042,9 +2048,9 @@ L0: r5 = get_element_ptr r1 ob_item :: PyListObject r6 = load_mem r5, r1 :: ptr* set_mem r6, r2, r1 :: builtins.object* - r7 = r6 + WORD_SIZE*1 + r7 = r6 + 8 set_mem r7, r3, r1 :: builtins.object* - r8 = r6 + WORD_SIZE*2 + r8 = r6 + 16 set_mem r8, r4, r1 :: builtins.object* r9 = 0 L1: @@ -2057,35 +2063,35 @@ L2: r14 = CPyList_GetItemUnsafe(r1, r9) r15 = unbox(int, r14) x = r15 - r17 = x & 1 - r18 = r17 == 0 - if r18 goto L3 else goto L4 :: bool + r16 = x & 1 + r17 = r16 == 0 + if r17 goto L3 else goto L4 :: bool L3: - r19 = x != 4 - r16 = r19 + r18 = x != 4 + r19 = r18 goto L5 L4: r20 = CPyTagged_IsEq_(x, 4) r21 = r20 ^ 1 - r16 = r21 + r19 = r21 L5: - if r16 goto L7 else goto L6 :: bool + if r19 goto L7 else goto L6 :: bool L6: goto L13 L7: - r23 = x & 1 - r24 = r23 == 0 - if r24 goto L8 else goto L9 :: bool + r22 = x & 1 + r23 = r22 == 0 + if r23 goto L8 else goto L9 :: bool L8: - r25 = x != 6 - r22 = r25 + r24 = x != 6 + r25 = r24 goto L10 L9: r26 = CPyTagged_IsEq_(x, 6) r27 = r26 ^ 1 - r22 = r27 + r25 = r27 L10: - if r22 goto L12 else goto L11 :: bool + if r25 goto L12 else goto L11 :: bool L11: goto L13 L12: @@ -2122,7 +2128,7 @@ def f(l): r11 :: list r12 :: short_int r13 :: ptr - r14 :: int64 + r14 :: native_int r15 :: short_int r16 :: bit r17 :: object @@ -2203,18 +2209,18 @@ L0: r0 = self.is_add if r0 goto L1 else goto L2 :: bool L1: - r2 = self.left - r3 = self.right - r4 = CPyTagged_Add(r2, r3) - r1 = r4 + r1 = self.left + r2 = self.right + r3 = CPyTagged_Add(r1, r2) + r4 = r3 goto L3 L2: r5 = self.left r6 = self.right r7 = CPyTagged_Subtract(r5, r6) - r1 = r7 + r4 = r7 L3: - return r1 + return r4 def PropertyHolder.__init__(self, left, right, is_add): self :: __main__.PropertyHolder left, right :: int @@ -2702,57 +2708,60 @@ L0: return x def f(x, y, z): x, y, z, r0, r1 :: int - r2, r3 :: bool + r2 :: native_int + r3 :: bit r4 :: native_int - r5 :: bit - r6 :: native_int - r7, r8, r9, r10 :: bit + r5, r6, r7 :: bit + r8 :: bool + r9 :: bit + r10 :: bool r11 :: int - r12 :: bool - r13 :: native_int - r14 :: bit - r15 :: native_int - r16, r17, r18, r19 :: bit + r12 :: native_int + r13 :: bit + r14 :: native_int + r15, r16, r17 :: bit + r18 :: bool + r19 :: bit L0: r0 = g(x) r1 = g(y) - r4 = r0 & 1 + r2 = r0 & 1 + r3 = r2 == 0 + r4 = r1 & 1 r5 = r4 == 0 - r6 = r1 & 1 - r7 = r6 == 0 - r8 = r5 & r7 - if r8 goto L1 else goto L2 :: bool + r6 = r3 & r5 + if r6 goto L1 else goto L2 :: bool L1: - r9 = r0 < r1 :: signed - r3 = r9 + r7 = r0 < r1 :: signed + r8 = r7 goto L3 L2: - r10 = CPyTagged_IsLt_(r0, r1) - r3 = r10 + r9 = CPyTagged_IsLt_(r0, r1) + r8 = r9 L3: - if r3 goto L5 else goto L4 :: bool + if r8 goto L5 else goto L4 :: bool L4: - r2 = r3 + r10 = r8 goto L9 L5: r11 = g(z) - r13 = r1 & 1 - r14 = r13 == 0 - r15 = r11 & 1 - r16 = r15 == 0 - r17 = r14 & r16 - if r17 goto L6 else goto L7 :: bool + r12 = r1 & 1 + r13 = r12 == 0 + r14 = r11 & 1 + r15 = r14 == 0 + r16 = r13 & r15 + if r16 goto L6 else goto L7 :: bool L6: - r18 = r1 > r11 :: signed - r12 = r18 + r17 = r1 > r11 :: signed + r18 = r17 goto L8 L7: r19 = CPyTagged_IsLt_(r11, r1) - r12 = r19 + r18 = r19 L8: - r2 = r12 + r10 = r18 L9: - return r2 + return r10 [case testEq] class A: @@ -3191,9 +3200,10 @@ def call_any(l): r0 :: bool r1, r2 :: object r3, i :: int - r4 :: bool - r5 :: native_int - r6, r7, r8, r9 :: bit + r4 :: native_int + r5, r6 :: bit + r7 :: bool + r8, r9 :: bit L0: r0 = 0 r1 = PyObject_GetIter(l) @@ -3203,18 +3213,18 @@ L1: L2: r3 = unbox(int, r2) i = r3 - r5 = i & 1 - r6 = r5 == 0 - if r6 goto L3 else goto L4 :: bool + r4 = i & 1 + r5 = r4 == 0 + if r5 goto L3 else goto L4 :: bool L3: - r7 = i == 0 - r4 = r7 + r6 = i == 0 + r7 = r6 goto L5 L4: r8 = CPyTagged_IsEq_(i, 0) - r4 = r8 + r7 = r8 L5: - if r4 goto L6 else goto L7 :: bool + if r7 goto L6 else goto L7 :: bool L6: r0 = 1 goto L11 @@ -3231,9 +3241,10 @@ def call_all(l): r0 :: bool r1, r2 :: object r3, i :: int - r4 :: bool - r5 :: native_int - r6, r7, r8 :: bit + r4 :: native_int + r5, r6 :: bit + r7 :: bool + r8 :: bit r9 :: bool r10 :: bit L0: @@ -3245,18 +3256,18 @@ L1: L2: r3 = unbox(int, r2) i = r3 - r5 = i & 1 - r6 = r5 == 0 - if r6 goto L3 else goto L4 :: bool + r4 = i & 1 + r5 = r4 == 0 + if r5 goto L3 else goto L4 :: bool L3: - r7 = i == 0 - r4 = r7 + r6 = i == 0 + r7 = r6 goto L5 L4: r8 = CPyTagged_IsEq_(i, 0) - r4 = r8 + r7 = r8 L5: - r9 = r4 ^ 1 + r9 = r7 ^ 1 if r9 goto L6 else goto L7 :: bool L6: r0 = 0 @@ -3410,7 +3421,7 @@ L0: r0 = __main__.x :: static if is_error(r0) goto L1 else goto L2 L1: - raise NameError('value for final name "x" was not set') + r1 = raise NameError('value for final name "x" was not set') unreachable L2: r2 = CPyList_GetItemShort(r0, 0) @@ -3433,7 +3444,7 @@ L0: r0 = __main__.x :: static if is_error(r0) goto L1 else goto L2 L1: - raise NameError('value for final name "x" was not set') + r1 = raise NameError('value for final name "x" was not set') unreachable L2: r2 = r0[0] @@ -3455,7 +3466,7 @@ L0: r0 = __main__.x :: static if is_error(r0) goto L1 else goto L2 L1: - raise NameError('value for final name "x" was not set') + r1 = raise NameError('value for final name "x" was not set') unreachable L2: r2 = CPyTagged_Subtract(r0, 2) diff --git a/mypyc/test-data/irbuild-optional.test b/mypyc/test-data/irbuild-optional.test index dcbb758d75f12..20a9ce3392b80 100644 --- a/mypyc/test-data/irbuild-optional.test +++ b/mypyc/test-data/irbuild-optional.test @@ -307,33 +307,33 @@ def set(o: Union[A, B], s: str) -> None: [out] def get(o): o :: union[__main__.A, __main__.B] - r0, r1 :: object - r2 :: ptr - r3 :: object - r4 :: bit - r5 :: __main__.A - r6 :: int - r7 :: object + r0 :: object + r1 :: ptr + r2 :: object + r3 :: bit + r4 :: __main__.A + r5 :: int + r6, r7 :: object r8 :: __main__.B r9, z :: object L0: - r1 = __main__.A :: type - r2 = get_element_ptr o ob_type :: PyObject - r3 = load_mem r2, o :: builtins.object* - r4 = r3 == r1 - if r4 goto L1 else goto L2 :: bool + r0 = __main__.A :: type + r1 = get_element_ptr o ob_type :: PyObject + r2 = load_mem r1, o :: builtins.object* + r3 = r2 == r0 + if r3 goto L1 else goto L2 :: bool L1: - r5 = cast(__main__.A, o) - r6 = r5.a - r7 = box(int, r6) - r0 = r7 + r4 = cast(__main__.A, o) + r5 = r4.a + r6 = box(int, r5) + r7 = r6 goto L3 L2: r8 = cast(__main__.B, o) r9 = r8.a - r0 = r9 + r7 = r9 L3: - z = r0 + z = r7 return 1 def set(o, s): o :: union[__main__.A, __main__.B] @@ -378,13 +378,13 @@ L0: return 0 def g(o): o :: union[__main__.A, __main__.B, __main__.C] - r0, r1 :: object - r2 :: ptr - r3 :: object - r4 :: bit - r5 :: __main__.A - r6 :: int - r7, r8 :: object + r0 :: object + r1 :: ptr + r2 :: object + r3 :: bit + r4 :: __main__.A + r5 :: int + r6, r7, r8 :: object r9 :: ptr r10 :: object r11 :: bit @@ -395,16 +395,16 @@ def g(o): r17 :: int r18, z :: object L0: - r1 = __main__.A :: type - r2 = get_element_ptr o ob_type :: PyObject - r3 = load_mem r2, o :: builtins.object* - r4 = r3 == r1 - if r4 goto L1 else goto L2 :: bool + r0 = __main__.A :: type + r1 = get_element_ptr o ob_type :: PyObject + r2 = load_mem r1, o :: builtins.object* + r3 = r2 == r0 + if r3 goto L1 else goto L2 :: bool L1: - r5 = cast(__main__.A, o) - r6 = r5.f(2) - r7 = box(int, r6) - r0 = r7 + r4 = cast(__main__.A, o) + r5 = r4.f(2) + r6 = box(int, r5) + r7 = r6 goto L5 L2: r8 = __main__.B :: type @@ -416,16 +416,16 @@ L3: r12 = cast(__main__.B, o) r13 = box(short_int, 2) r14 = r12.f(r13) - r0 = r14 + r7 = r14 goto L5 L4: r15 = cast(__main__.C, o) r16 = box(short_int, 2) r17 = r15.f(r16) r18 = box(int, r17) - r0 = r18 + r7 = r18 L5: - z = r0 + z = r7 return 1 [case testUnionWithNonNativeItem] @@ -448,66 +448,64 @@ class B: [out] def f(o): o :: union[__main__.A, object] - r0 :: int - r1 :: object - r2 :: ptr - r3 :: object - r4 :: bit - r5 :: __main__.A - r6 :: int + r0 :: object + r1 :: ptr + r2 :: object + r3 :: bit + r4 :: __main__.A + r5, r6 :: int r7 :: object r8 :: str r9 :: object r10 :: int L0: - r1 = __main__.A :: type - r2 = get_element_ptr o ob_type :: PyObject - r3 = load_mem r2, o :: builtins.object* - r4 = r3 == r1 - if r4 goto L1 else goto L2 :: bool + r0 = __main__.A :: type + r1 = get_element_ptr o ob_type :: PyObject + r2 = load_mem r1, o :: builtins.object* + r3 = r2 == r0 + if r3 goto L1 else goto L2 :: bool L1: - r5 = cast(__main__.A, o) - r6 = r5.x - r0 = r6 + r4 = cast(__main__.A, o) + r5 = r4.x + r6 = r5 goto L3 L2: r7 = o r8 = load_global CPyStatic_unicode_7 :: static ('x') r9 = CPyObject_GetAttr(r7, r8) r10 = unbox(int, r9) - r0 = r10 + r6 = r10 L3: return 1 def g(o): o :: union[object, __main__.A] - r0 :: int - r1 :: object - r2 :: ptr - r3 :: object - r4 :: bit - r5 :: __main__.A - r6 :: int + r0 :: object + r1 :: ptr + r2 :: object + r3 :: bit + r4 :: __main__.A + r5, r6 :: int r7 :: object r8 :: str r9 :: object r10 :: int L0: - r1 = __main__.A :: type - r2 = get_element_ptr o ob_type :: PyObject - r3 = load_mem r2, o :: builtins.object* - r4 = r3 == r1 - if r4 goto L1 else goto L2 :: bool + r0 = __main__.A :: type + r1 = get_element_ptr o ob_type :: PyObject + r2 = load_mem r1, o :: builtins.object* + r3 = r2 == r0 + if r3 goto L1 else goto L2 :: bool L1: - r5 = cast(__main__.A, o) - r6 = r5.x - r0 = r6 + r4 = cast(__main__.A, o) + r5 = r4.x + r6 = r5 goto L3 L2: r7 = o r8 = load_global CPyStatic_unicode_7 :: static ('x') r9 = CPyObject_GetAttr(r7, r8) r10 = unbox(int, r9) - r0 = r10 + r6 = r10 L3: return 1 diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index 67e30a7458759..5be16070907eb 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -36,31 +36,32 @@ def f(a: int) -> None: [out] def f(a): a, r0, i :: int - r1 :: bool - r2 :: native_int - r3 :: bit - r4 :: native_int - r5, r6, r7, r8 :: bit + r1 :: native_int + r2 :: bit + r3 :: native_int + r4, r5, r6 :: bit + r7 :: bool + r8 :: bit r9 :: int L0: r0 = 0 i = r0 L1: - r2 = r0 & 1 - r3 = r2 == 0 - r4 = a & 1 - r5 = r4 == 0 - r6 = r3 & r5 - if r6 goto L2 else goto L3 :: bool + r1 = r0 & 1 + r2 = r1 == 0 + r3 = a & 1 + r4 = r3 == 0 + r5 = r2 & r4 + if r5 goto L2 else goto L3 :: bool L2: - r7 = r0 < a :: signed - r1 = r7 + r6 = r0 < a :: signed + r7 = r6 goto L4 L3: r8 = CPyTagged_IsLt_(r0, a) - r1 = r8 + r7 = r8 L4: - if r1 goto L5 else goto L7 :: bool + if r7 goto L5 else goto L7 :: bool L5: L6: r9 = CPyTagged_Add(r0, 2) @@ -396,12 +397,13 @@ def sum_over_even_values(d): r5 :: int r6 :: bool r7 :: object - key, r8 :: int + r8, key :: int r9, r10 :: object r11, r12 :: int - r13 :: bool - r14 :: native_int - r15, r16, r17, r18 :: bit + r13 :: native_int + r14, r15 :: bit + r16 :: bool + r17, r18 :: bit r19, r20 :: object r21, r22 :: int r23, r24 :: bit @@ -425,19 +427,19 @@ L2: r10 = CPyDict_GetItem(d, r9) r11 = unbox(int, r10) r12 = CPyTagged_Remainder(r11, 4) - r14 = r12 & 1 - r15 = r14 == 0 - if r15 goto L3 else goto L4 :: bool + r13 = r12 & 1 + r14 = r13 == 0 + if r14 goto L3 else goto L4 :: bool L3: - r16 = r12 != 0 - r13 = r16 + r15 = r12 != 0 + r16 = r15 goto L5 L4: r17 = CPyTagged_IsEq_(r12, 0) r18 = r17 ^ 1 - r13 = r18 + r16 = r18 L5: - if r13 goto L6 else goto L7 :: bool + if r16 goto L6 else goto L7 :: bool L6: goto L8 L7: @@ -465,10 +467,8 @@ def from_any(a: Any) -> None: [out] def from_tuple(t): t :: tuple[int, str] - x :: int - y :: str - r0 :: int - r1 :: str + r0, x :: int + r1, y :: str L0: r0 = t[0] x = r0 @@ -476,32 +476,32 @@ L0: y = r1 return 1 def from_any(a): - a, x, y, r0, r1 :: object + a, r0, r1 :: object r2 :: bool - r3 :: object + x, r3 :: object r4 :: bool - r5 :: object + y, r5 :: object r6 :: bool L0: r0 = PyObject_GetIter(a) r1 = PyIter_Next(r0) if is_error(r1) goto L1 else goto L2 L1: - raise ValueError('not enough values to unpack') + r2 = raise ValueError('not enough values to unpack') unreachable L2: x = r1 r3 = PyIter_Next(r0) if is_error(r3) goto L3 else goto L4 L3: - raise ValueError('not enough values to unpack') + r4 = raise ValueError('not enough values to unpack') unreachable L4: y = r3 r5 = PyIter_Next(r0) if is_error(r5) goto L6 else goto L5 L5: - raise ValueError('too many values to unpack') + r6 = raise ValueError('too many values to unpack') unreachable L6: return 1 @@ -520,10 +520,9 @@ def from_any(a: Any) -> None: [out] def from_tuple(t): t :: tuple[int, object] - x :: object - y, r0 :: int - r1, r2 :: object - r3 :: int + r0 :: int + r1, x, r2 :: object + r3, y :: int L0: r0 = t[0] r1 = box(int, r0) @@ -533,21 +532,19 @@ L0: y = r3 return 1 def from_any(a): - a :: object - x :: int - y, r0, r1 :: object + a, r0, r1 :: object r2 :: bool - r3 :: int + r3, x :: int r4 :: object r5 :: bool - r6 :: object + y, r6 :: object r7 :: bool L0: r0 = PyObject_GetIter(a) r1 = PyIter_Next(r0) if is_error(r1) goto L1 else goto L2 L1: - raise ValueError('not enough values to unpack') + r2 = raise ValueError('not enough values to unpack') unreachable L2: r3 = unbox(int, r1) @@ -555,14 +552,14 @@ L2: r4 = PyIter_Next(r0) if is_error(r4) goto L3 else goto L4 L3: - raise ValueError('not enough values to unpack') + r5 = raise ValueError('not enough values to unpack') unreachable L4: y = r4 r6 = PyIter_Next(r0) if is_error(r6) goto L6 else goto L5 L5: - raise ValueError('too many values to unpack') + r7 = raise ValueError('too many values to unpack') unreachable L6: return 1 @@ -618,7 +615,7 @@ def no_msg(x): L0: if x goto L2 else goto L1 :: bool L1: - raise AssertionError + r0 = raise AssertionError unreachable L2: return 2 @@ -633,7 +630,7 @@ L0: r2 = truncate r0: int32 to builtins.bool if r2 goto L2 else goto L1 :: bool L1: - raise AssertionError('message') + r3 = raise AssertionError('message') unreachable L2: return 4 From 729e11f392db3d27c7f8b17eb15b99bf56c4995c Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 27 Nov 2020 13:06:33 +0000 Subject: [PATCH 05/10] Fix argument defaults --- mypyc/irbuild/function.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py index a00414d845b4a..90d924b3f01f0 100644 --- a/mypyc/irbuild/function.py +++ b/mypyc/irbuild/function.py @@ -10,11 +10,11 @@ instance of the callable class. """ -from typing import Optional, List, Tuple, Union +from typing import Optional, List, Tuple, Union, Dict from mypy.nodes import ( ClassDef, FuncDef, OverloadedFuncDef, Decorator, Var, YieldFromExpr, AwaitExpr, YieldExpr, - FuncItem, LambdaExpr + FuncItem, LambdaExpr, SymbolNode ) from mypy.types import CallableType, get_proper_type @@ -210,7 +210,7 @@ def c() -> None: class_name = cdef.name builder.enter(FuncInfo(fitem, name, class_name, gen_func_ns(builder), - is_nested, contains_nested, is_decorated, in_non_ext)) + is_nested, contains_nested, is_decorated, in_non_ext)) # Functions that contain nested functions need an environment class to store variables that # are free in their nested functions. Generator functions need an environment class to @@ -283,6 +283,10 @@ def c() -> None: if builder.fn_info.is_generator: populate_switch_for_generator_class(builder) + # Hang on to the local symbol table for a while, since we use it + # to calculate argument defaults below. + symtable = builder.symtables[-1] + args, blocks, env, ret_type, fn_info = builder.leave() if fn_info.is_generator: @@ -291,7 +295,9 @@ def c() -> None: else: func_ir, func_reg = gen_func_ir(builder, args, blocks, sig, env, fn_info, cdef) - calculate_arg_defaults(builder, fn_info, func_reg) + # Evaluate argument defaults in the surrounding scope, since we + # calculate them *once* when the function definition is evaluated. + calculate_arg_defaults(builder, fn_info, func_reg, symtable) return (func_ir, func_reg) @@ -432,7 +438,8 @@ def handle_non_ext_method( def calculate_arg_defaults(builder: IRBuilder, fn_info: FuncInfo, - func_reg: Optional[Value]) -> None: + func_reg: Optional[Value], + symtable: Dict[SymbolNode, AssignmentTarget]) -> None: """Calculate default argument values and store them. They are stored in statics for top level functions and in @@ -445,7 +452,7 @@ def calculate_arg_defaults(builder: IRBuilder, if arg.initializer and not is_constant(arg.initializer): value = builder.coerce( builder.accept(arg.initializer), - builder.lookup(arg.variable).type, + symtable[arg.variable].type, arg.line ) if not fn_info.is_nested: From 37f3f6fbc89e98802aa84a33434a6cbc8220a271 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 27 Nov 2020 13:30:47 +0000 Subject: [PATCH 06/10] Better repr for Register --- mypyc/ir/ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index 23f61f9eee69c..a482bafd52280 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -205,7 +205,7 @@ def is_void(self) -> bool: return False def __repr__(self) -> str: - return '' % self.name + return '' % (self.name, hex(id(self))) class Op(Value): From 9309427f1e53a436500fa51cb84193310ecb88f2 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 28 Nov 2020 11:08:41 +0000 Subject: [PATCH 07/10] Fix issues with registers initialized after taking their address --- mypyc/ir/func_ir.py | 25 +++++++++++++++++++++---- mypyc/transform/refcount.py | 14 ++++++++++++-- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/mypyc/ir/func_ir.py b/mypyc/ir/func_ir.py index cec8ab046924a..c54a4cfcb7fd0 100644 --- a/mypyc/ir/func_ir.py +++ b/mypyc/ir/func_ir.py @@ -6,7 +6,9 @@ from mypy.nodes import FuncDef, Block, ARG_POS, ARG_OPT, ARG_NAMED_OPT from mypyc.common import JsonDict -from mypyc.ir.ops import DeserMaps, BasicBlock, Environment, Value, Register, Assign, ControlOp +from mypyc.ir.ops import ( + DeserMaps, BasicBlock, Environment, Value, Register, Assign, ControlOp, LoadAddress +) from mypyc.ir.rtypes import RType, deserialize_type from mypyc.namegen import NameGenerator @@ -223,9 +225,9 @@ def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> 'FuncIR': def all_values(args: List[Register], blocks: List[BasicBlock]) -> List[Value]: - """Return set of all values that are initialized at least once. + """Return the set of all values that may be initialized in the blocks. - This omits registers that are never assigned to. + This omits registers that are only read. """ values = list(args) # type: List[Value] seen_registers = set(args) @@ -240,6 +242,12 @@ def all_values(args: List[Register], blocks: List[BasicBlock]) -> List[Value]: elif op.is_void: continue else: + # If we take the address of a register, it might get initialized. + if (isinstance(op, LoadAddress) + and isinstance(op.src, Register) + and op.src not in seen_registers): + values.append(op.src) + seen_registers.add(op.src) values.append(op) return values @@ -272,7 +280,10 @@ def all_values_full(args: List[Register], blocks: List[BasicBlock]) -> List[Valu def all_registers(ir: FuncIR) -> List[Register]: - """Return set of all registers that are initialized at least once.""" + """Return set of all registers that may be initialized within function. + + This omits registers that are only read. + """ registers = list(ir.arg_regs) seen = set(registers) for block in ir.blocks: @@ -280,4 +291,10 @@ def all_registers(ir: FuncIR) -> List[Register]: if isinstance(op, Assign) and op.dest not in seen: registers.append(op.dest) seen.add(op.dest) + elif (isinstance(op, LoadAddress) + and isinstance(op.src, Register) + and op.src not in seen): + # Taking the address of a register allows initialization. + registers.append(op.src) + seen.add(op.src) return registers diff --git a/mypyc/transform/refcount.py b/mypyc/transform/refcount.py index 6bc4f0417510c..f732ec8ab33b5 100644 --- a/mypyc/transform/refcount.py +++ b/mypyc/transform/refcount.py @@ -27,7 +27,8 @@ AnalysisDict ) from mypyc.ir.ops import ( - BasicBlock, Assign, RegisterOp, DecRef, IncRef, Branch, Goto, Op, ControlOp, Value, Register + BasicBlock, Assign, RegisterOp, DecRef, IncRef, Branch, Goto, Op, ControlOp, Value, Register, + LoadAddress ) from mypyc.ir.func_ir import FuncIR, all_values @@ -248,7 +249,10 @@ def add_block(decincs: DecIncs, cache: BlockCache, def make_value_ordering(ir: FuncIR) -> Dict[Value, int]: - """Create a ordering of values that allows them to be sorted.""" + """Create a ordering of values that allows them to be sorted. + + This omits registers that are only ever read. + """ # TODO: Never initialized values?? result = {} # type: Dict[Value, int] n = 0 @@ -259,6 +263,12 @@ def make_value_ordering(ir: FuncIR) -> Dict[Value, int]: for block in ir.blocks: for op in block.ops: + if (isinstance(op, LoadAddress) + and isinstance(op.src, Register) + and op.src not in result): + # Taking the address of a register allows initialization. + result[op.src] = n + n += 1 if isinstance(op, Assign): if op.dest not in result: result[op.dest] = n From 4231da5659b2c6cd334e25db43450800c344a168 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 28 Nov 2020 11:09:27 +0000 Subject: [PATCH 08/10] Fix unused function --- mypyc/ir/func_ir.py | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/mypyc/ir/func_ir.py b/mypyc/ir/func_ir.py index c54a4cfcb7fd0..af09ed4e3b726 100644 --- a/mypyc/ir/func_ir.py +++ b/mypyc/ir/func_ir.py @@ -277,24 +277,3 @@ def all_values_full(args: List[Register], blocks: List[BasicBlock]) -> List[Valu values.append(op) return values - - -def all_registers(ir: FuncIR) -> List[Register]: - """Return set of all registers that may be initialized within function. - - This omits registers that are only read. - """ - registers = list(ir.arg_regs) - seen = set(registers) - for block in ir.blocks: - for op in block.ops: - if isinstance(op, Assign) and op.dest not in seen: - registers.append(op.dest) - seen.add(op.dest) - elif (isinstance(op, LoadAddress) - and isinstance(op.src, Register) - and op.src not in seen): - # Taking the address of a register allows initialization. - registers.append(op.src) - seen.add(op.src) - return registers From 7c71f035162ca2b4566d20560dbdc30b9abb53dd Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sun, 29 Nov 2020 17:11:08 +0000 Subject: [PATCH 09/10] Update more tests --- mypyc/test-data/exceptions.test | 84 ++++++++++++++++----------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/mypyc/test-data/exceptions.test b/mypyc/test-data/exceptions.test index 620aa824c89b8..5674f6f905499 100644 --- a/mypyc/test-data/exceptions.test +++ b/mypyc/test-data/exceptions.test @@ -241,17 +241,15 @@ def a(): r1 :: str r2, r3 :: object r4, r5 :: str - r6 :: tuple[object, object, object] - r7 :: str - r8 :: tuple[object, object, object] - r9 :: str - r10 :: tuple[object, object, object] - r11 :: str - r12 :: object - r13 :: str - r14, r15 :: object - r16 :: bit - r17 :: str + r6, r7 :: tuple[object, object, object] + r8 :: str + r9 :: tuple[object, object, object] + r10 :: str + r11 :: object + r12 :: str + r13, r14 :: object + r15 :: bit + r16 :: str L0: L1: r0 = builtins :: module @@ -267,26 +265,26 @@ L3: inc_ref r4 r5 = r4 L4: - r8 = :: tuple[object, object, object] - r6 = r8 + r6 = :: tuple[object, object, object] + r7 = r6 goto L6 L5: - r9 = :: str - r5 = r9 - r10 = CPy_CatchError() - r6 = r10 + r8 = :: str + r5 = r8 + r9 = CPy_CatchError() + r7 = r9 L6: - r11 = load_global CPyStatic_unicode_3 :: static ('goodbye!') - r12 = builtins :: module - r13 = load_global CPyStatic_unicode_1 :: static ('print') - r14 = CPyObject_GetAttr(r12, r13) - if is_error(r14) goto L13 (error at a:6) else goto L7 + r10 = load_global CPyStatic_unicode_3 :: static ('goodbye!') + r11 = builtins :: module + r12 = load_global CPyStatic_unicode_1 :: static ('print') + r13 = CPyObject_GetAttr(r11, r12) + if is_error(r13) goto L13 (error at a:6) else goto L7 L7: - r15 = PyObject_CallFunctionObjArgs(r14, r11, 0) - dec_ref r14 - if is_error(r15) goto L13 (error at a:6) else goto L21 + r14 = PyObject_CallFunctionObjArgs(r13, r10, 0) + dec_ref r13 + if is_error(r14) goto L13 (error at a:6) else goto L21 L8: - if is_error(r6) goto L11 else goto L9 + if is_error(r7) goto L11 else goto L9 L9: CPy_Reraise() if not 0 goto L13 else goto L22 :: bool @@ -299,29 +297,29 @@ L12: L13: if is_error(r5) goto L14 else goto L23 L14: - if is_error(r6) goto L16 else goto L15 + if is_error(r7) goto L16 else goto L15 L15: - CPy_RestoreExcInfo(r6) - dec_ref r6 + CPy_RestoreExcInfo(r7) + dec_ref r7 L16: - r16 = CPy_KeepPropagating() - if not r16 goto L19 else goto L17 :: bool + r15 = CPy_KeepPropagating() + if not r15 goto L19 else goto L17 :: bool L17: unreachable L18: unreachable L19: - r17 = :: str - return r17 + r16 = :: str + return r16 L20: dec_ref r3 goto L3 L21: - dec_ref r15 + dec_ref r14 goto L8 L22: dec_ref r5 - dec_ref r6 + dec_ref r7 goto L10 L23: dec_ref r5 @@ -460,8 +458,9 @@ def f(b): r2, r3 :: bit r4 :: object r5 :: str - r6, r7 :: object - r8 :: bool + r6 :: object + r7 :: bool + r8 :: object r9 :: None L0: r0 = load_global CPyStatic_unicode_1 :: static ('a') @@ -484,15 +483,15 @@ L3: L4: if is_error(v) goto L13 else goto L7 L5: - raise UnboundLocalError("local variable 'v' referenced before assignment") - if not r8 goto L9 (error at f:7) else goto L6 :: bool + r7 = raise UnboundLocalError("local variable 'v' referenced before assignment") + if not r7 goto L9 (error at f:7) else goto L6 :: bool L6: unreachable L7: - r7 = PyObject_CallFunctionObjArgs(r6, v, 0) + r8 = PyObject_CallFunctionObjArgs(r6, v, 0) dec_ref r6 xdec_ref v - if is_error(r7) goto L9 (error at f:7) else goto L14 + if is_error(r8) goto L9 (error at f:7) else goto L14 L8: return 1 L9: @@ -511,5 +510,6 @@ L13: dec_ref r6 goto L5 L14: - dec_ref r7 + dec_ref r8 goto L8 + From cfa2325088d1af992b685335eb2e68824eacd192 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 11 Dec 2020 18:34:05 +0000 Subject: [PATCH 10/10] Attempt to fix 32-bit build --- mypyc/test-data/irbuild-basic.test | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index a52d20c6b6fae..7c52ad583bbb8 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -1951,9 +1951,9 @@ L0: r5 = get_element_ptr r1 ob_item :: PyListObject r6 = load_mem r5, r1 :: ptr* set_mem r6, r2, r1 :: builtins.object* - r7 = r6 + 8 + r7 = r6 + WORD_SIZE*1 set_mem r7, r3, r1 :: builtins.object* - r8 = r6 + 16 + r8 = r6 + WORD_SIZE*2 set_mem r8, r4, r1 :: builtins.object* r9 = 0 L1: @@ -2048,9 +2048,9 @@ L0: r5 = get_element_ptr r1 ob_item :: PyListObject r6 = load_mem r5, r1 :: ptr* set_mem r6, r2, r1 :: builtins.object* - r7 = r6 + 8 + r7 = r6 + WORD_SIZE*1 set_mem r7, r3, r1 :: builtins.object* - r8 = r6 + 16 + r8 = r6 + WORD_SIZE*2 set_mem r8, r4, r1 :: builtins.object* r9 = 0 L1: