def assign_if_null(self, target: AssignmentTargetRegister, get_val: Callable[[], Value], line: int) -> None: """Generate blocks for registers that NULL values.""" error_block, body_block = BasicBlock(), BasicBlock() self.add( Branch(target.register, error_block, body_block, Branch.IS_ERROR)) self.activate_block(error_block) self.add( Assign(target.register, self.coerce(get_val(), target.register.type, line))) self.goto(body_block) self.activate_block(body_block)
def compare_tagged(self, lhs: Value, rhs: Value, op: str, line: int) -> Value: """Compare two tagged integers using given op""" # generate fast binary logic ops on short ints if is_short_int_rprimitive(lhs.type) and is_short_int_rprimitive(rhs.type): return self.binary_int_op(bool_rprimitive, lhs, rhs, int_logical_op_mapping[op][0], line) op_type, c_func_desc, negate_result, swap_op = int_logical_op_mapping[op] result = self.alloc_temp(bool_rprimitive) short_int_block, int_block, out = BasicBlock(), BasicBlock(), BasicBlock() check_lhs = self.check_tagged_short_int(lhs, line) if op in ("==", "!="): check = check_lhs else: # for non-equal logical ops(less than, greater than, etc.), need to check both side check_rhs = self.check_tagged_short_int(rhs, line) check = self.binary_int_op(bool_rprimitive, check_lhs, check_rhs, BinaryIntOp.AND, line) branch = Branch(check, short_int_block, int_block, Branch.BOOL_EXPR) branch.negated = False self.add(branch) self.activate_block(short_int_block) eq = self.binary_int_op(bool_rprimitive, lhs, rhs, op_type, line) self.add(Assign(result, eq, line)) self.goto(out) self.activate_block(int_block) if swap_op: args = [rhs, lhs] else: args = [lhs, rhs] call = self.call_c(c_func_desc, args, line) if negate_result: # TODO: introduce UnaryIntOp? call_result = self.unary_op(call, "not", line) else: call_result = call self.add(Assign(result, call_result, line)) self.goto_and_activate(out) return result
def transform_conditional_expr(builder: IRBuilder, expr: ConditionalExpr) -> Value: if_body, else_body, next_block = BasicBlock(), BasicBlock(), BasicBlock() 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 = Register(expr_type) builder.activate_block(if_body) true_value = builder.accept(expr.if_expr) true_value = builder.coerce(true_value, expr_type, expr.line) builder.add(Assign(target, true_value)) builder.goto(next_block) builder.activate_block(else_body) false_value = builder.accept(expr.else_expr) false_value = builder.coerce(false_value, expr_type, expr.line) builder.add(Assign(target, false_value)) builder.goto(next_block) builder.activate_block(next_block) return target
def try_finally_entry_blocks(builder: IRBuilder, err_handler: BasicBlock, return_entry: BasicBlock, main_entry: BasicBlock, finally_block: BasicBlock, ret_reg: Optional[Register]) -> Value: old_exc = Register(exc_rtuple) # Entry block for non-exceptional flow builder.activate_block(main_entry) if ret_reg: builder.add( Assign( ret_reg, builder.add(LoadErrorValue(builder.ret_types[-1])) ) ) builder.goto(return_entry) builder.activate_block(return_entry) builder.add(Assign(old_exc, builder.add(LoadErrorValue(exc_rtuple)))) builder.goto(finally_block) # Entry block for errors builder.activate_block(err_handler) if ret_reg: builder.add( Assign( ret_reg, builder.add(LoadErrorValue(builder.ret_types[-1])) ) ) builder.add(Assign(old_exc, builder.call_c(error_catch_op, [], -1))) builder.goto(finally_block) return old_exc
def test_invalid_assign(self) -> None: arg_reg = Register(type=int64_rprimitive, name="r1") assign = Assign(dest=arg_reg, src=Integer(value=5, rtype=int32_rprimitive)) ret = Return(value=NONE_VALUE) fn = FuncIR( decl=self.func_decl(name="func_1"), arg_regs=[arg_reg], blocks=[self.basic_block([assign, ret])], ) assert_has_error( fn, FnError(source=assign, desc="Cannot coerce source type int32 to dest type int64"), )
def transform_del_item(builder: IRBuilder, target: AssignmentTarget, line: int) -> None: if isinstance(target, AssignmentTargetIndex): builder.gen_method_call(target.base, '__delitem__', [target.index], result_type=None, line=line) elif isinstance(target, AssignmentTargetAttr): key = builder.load_static_unicode(target.attr) builder.call_c(py_delattr_op, [target.obj, key], line) elif isinstance(target, AssignmentTargetRegister): # Delete a local by assigning an error value to it, which will # prompt the insertion of uninit checks. builder.add( Assign(target.register, builder.add(LoadErrorValue(target.type, undefines=True)))) elif isinstance(target, AssignmentTargetTuple): for subtarget in target.items: transform_del_item(builder, subtarget, line)
def test_register(self) -> None: reg = Register(int_rprimitive) op = Assign(reg, Integer(5)) self.block.ops.append(op) fn = FuncIR( FuncDecl('myfunc', None, 'mod', FuncSignature([self.arg], list_rprimitive)), [self.reg], [self.block]) value_names = generate_names_for_ir(fn.arg_regs, fn.blocks) emitter = Emitter(EmitterContext(NameGenerator([['mod']])), value_names) generate_native_function(fn, emitter, 'prog.py', 'prog') result = emitter.fragments assert_string_arrays_equal([ 'PyObject *CPyDef_myfunc(CPyTagged cpy_r_arg) {\n', ' CPyTagged cpy_r_r0;\n', 'CPyL0: ;\n', ' cpy_r_r0 = 10;\n', '}\n', ], result, msg='Generated code invalid')
def visit_assign(self, op: Assign) -> GenAndKill: return set(op.sources()), {op.dest}
def test_assign_int(self) -> None: self.assert_emit(Assign(self.m, self.n), "cpy_r_m = cpy_r_n;")
def test_integer(self) -> None: self.assert_emit(Assign(self.n, Integer(5)), "cpy_r_n = 10;") self.assert_emit(Assign(self.i32, Integer(5, c_int_rprimitive)), "cpy_r_i32 = 5;")
def test_long_signed(self) -> None: a = Register(int64_rprimitive, 'a') self.assert_emit(Assign(a, Integer(-(1 << 31) + 1, int64_rprimitive)), """cpy_r_a = -2147483647;""") self.assert_emit(Assign(a, Integer(-(1 << 31), int64_rprimitive)), """cpy_r_a = -2147483648LL;""")
def test_long_unsigned(self) -> None: a = Register(int64_rprimitive, 'a') self.assert_emit(Assign(a, Integer(1 << 31, int64_rprimitive)), """cpy_r_a = 2147483648U;""") self.assert_emit(Assign(a, Integer((1 << 31) - 1, int64_rprimitive)), """cpy_r_a = 2147483647;""")
def gen_return(self, builder: 'IRBuilder', value: Value, line: int) -> None: if self.ret_reg is None: self.ret_reg = Register(builder.ret_types[-1]) builder.add(Assign(self.ret_reg, value)) builder.add(Goto(self.target))
def split_blocks_at_uninits( blocks: List[BasicBlock], pre_must_defined: 'AnalysisDict[Value]') -> List[BasicBlock]: new_blocks = [] # type: List[BasicBlock] init_registers = [] init_registers_set = set() # First split blocks on ops that may raise. for block in blocks: ops = block.ops block.ops = [] cur_block = block new_blocks.append(cur_block) for i, op in enumerate(ops): defined = pre_must_defined[block, i] for src in op.unique_sources(): # If a register operand is not guaranteed to be # initialized is an operand to something other than a # check that it is defined, insert a check. # Note that for register operand in a LoadAddress op, # we should be able to use it without initialization # as we may need to use its address to update itself if (isinstance(src, Register) and src not in defined and not (isinstance(op, Branch) and op.op == Branch.IS_ERROR) and not isinstance(op, LoadAddress)): new_block, error_block = BasicBlock(), BasicBlock() new_block.error_handler = error_block.error_handler = cur_block.error_handler new_blocks += [error_block, new_block] if src not in init_registers_set: init_registers.append(src) init_registers_set.add(src) cur_block.ops.append( Branch(src, true_label=error_block, false_label=new_block, op=Branch.IS_ERROR, line=op.line)) raise_std = RaiseStandardError( RaiseStandardError.UNBOUND_LOCAL_ERROR, 'local variable "{}" referenced before assignment'. format(src.name), op.line) error_block.ops.append(raise_std) error_block.ops.append(Unreachable()) cur_block = new_block cur_block.ops.append(op) if init_registers: new_ops = [] # type: List[Op] for reg in init_registers: err = LoadErrorValue(reg.type, undefines=True) new_ops.append(err) new_ops.append(Assign(reg, err)) new_blocks[0].ops[0:0] = new_ops return new_blocks