def setUp(self) -> None: self.env = Environment() self.n = self.env.add_local(Var('n'), IntRType()) self.m = self.env.add_local(Var('m'), IntRType()) self.k = self.env.add_local(Var('k'), IntRType()) self.l = self.env.add_local(Var('l'), ListRType()) self.ll = self.env.add_local(Var('ll'), ListRType()) self.o = self.env.add_local(Var('o'), ObjectRType()) self.o2 = self.env.add_local(Var('o2'), ObjectRType()) self.d = self.env.add_local(Var('d'), DictRType()) self.b = self.env.add_local(Var('b'), BoolRType()) self.context = EmitterContext() self.emitter = Emitter(self.context, self.env) self.declarations = Emitter(self.context, self.env) self.visitor = FunctionEmitterVisitor(self.emitter, self.declarations)
def binary_op(self, ltype: RType, lreg: Register, rtype: RType, rreg: Register, expr_op: str, target: Optional[Register] = None) -> Register: if ltype.name == 'int' and rtype.name == 'int': # Primitive int operation if target is None: target = self.alloc_target(IntRType()) op = self.int_binary_ops[expr_op] elif (ltype.name == 'list' or rtype.name == 'list') and expr_op == '*': if rtype.name == 'list': ltype, rtype = rtype, ltype lreg, rreg = rreg, lreg if rtype.name != 'int': assert False, 'Unsupported binary operation' # TODO: Operator overloading if target is None: target = self.alloc_target(ListRType()) op = PrimitiveOp.LIST_REPEAT elif isinstance(rtype, DictRType): if expr_op == 'in': if target is None: target = self.alloc_target(BoolRType()) lreg = self.box(lreg, ltype) op = PrimitiveOp.DICT_CONTAINS else: assert False, 'Unsupported binary operation' else: assert False, 'Unsupported binary operation' self.add(PrimitiveOp(target, op, lreg, rreg)) return target
def type_to_rtype(self, typ: Type) -> RType: if isinstance(typ, Instance): if typ.type.fullname() == 'builtins.int': return IntRType() elif typ.type.fullname() == 'builtins.bool': return BoolRType() elif typ.type.fullname() == 'builtins.list': return ListRType() elif typ.type.fullname() == 'builtins.dict': return DictRType() elif typ.type.fullname() == 'builtins.tuple': return SequenceTupleRType() elif typ.type in self.type_to_ir: return UserRType(self.type_to_ir[typ.type]) elif isinstance(typ, TupleType): return TupleRType([self.type_to_rtype(t) for t in typ.items]) elif isinstance(typ, CallableType): return ObjectRType() elif isinstance(typ, NoneTyp): return NoneRType() elif isinstance(typ, UnionType): assert len(typ.items) == 2 and any( isinstance(it, NoneTyp) for it in typ.items) if isinstance(typ.items[0], NoneTyp): value_type = typ.items[1] else: value_type = typ.items[0] return OptionalRType(self.type_to_rtype(value_type)) assert False, '%s unsupported' % type(typ)
def test_unbox(self) -> None: self.assert_emit( Unbox(self.n, self.m, IntRType()), """if (PyLong_Check(cpy_r_m)) cpy_r_n = CPyTagged_FromObject(cpy_r_m); else abort(); """)
def visit_unary_expr(self, expr: UnaryExpr) -> Register: if expr.op != '-': assert False, 'Unsupported unary operation' etype = self.node_type(expr.expr) reg = self.accept(expr.expr) if etype.name != 'int': assert False, 'Unsupported unary operation' target = self.alloc_target(IntRType()) zero = self.accept(IntExpr(0)) self.add(PrimitiveOp(target, PrimitiveOp.INT_SUB, zero, reg)) return target
def test_simple(self) -> None: self.block.ops.append(Return(self.reg)) fn = FuncIR('myfunc', [self.arg], IntRType(), [self.block], self.env) emitter = Emitter(EmitterContext()) generate_native_function(fn, emitter) result = emitter.fragments assert_string_arrays_equal([ 'static CPyTagged CPyDef_myfunc(CPyTagged cpy_r_arg) {\n', 'CPyL0: ;\n', ' return cpy_r_arg;\n', '}\n', ], result, msg='Generated code invalid')
def visit_call_expr(self, expr: CallExpr) -> Register: if isinstance(expr.callee, MemberExpr): is_module_call = self.is_module_member_expr(expr.callee) if expr.callee.expr in self.types and not is_module_call: target = self.translate_special_method_call(expr.callee, expr) if target: return target # Either its a module call or translating to a special method call failed, so we have # to fallback to a PyCall function = self.accept(expr.callee) return self.py_call(function, expr.args, self.node_type(expr)) assert isinstance(expr.callee, NameExpr) fn = expr.callee.name # TODO: fullname if fn == 'len' and len(expr.args) == 1 and expr.arg_kinds == [ARG_POS]: target = self.alloc_target(IntRType()) arg = self.accept(expr.args[0]) expr_rtype = self.node_type(expr.args[0]) if expr_rtype.name == 'list': self.add(PrimitiveOp(target, PrimitiveOp.LIST_LEN, arg)) elif expr_rtype.name == 'sequence_tuple': self.add( PrimitiveOp(target, PrimitiveOp.HOMOGENOUS_TUPLE_LEN, arg)) elif isinstance(expr_rtype, TupleRType): self.add(LoadInt(target, len(expr_rtype.types))) else: assert False, "unsupported use of len" # Handle conversion to sequence tuple elif fn == 'tuple' and len( expr.args) == 1 and expr.arg_kinds == [ARG_POS]: target = self.alloc_target(SequenceTupleRType()) arg = self.accept(expr.args[0]) self.add( PrimitiveOp(target, PrimitiveOp.LIST_TO_HOMOGENOUS_TUPLE, arg)) else: target_type = self.node_type(expr) if not (self.is_native_name_expr(expr.callee)): function = self.accept(expr.callee) return self.py_call(function, expr.args, target_type) target = self.alloc_target(target_type) args = [self.accept(arg) for arg in expr.args] self.add(Call(target, fn, args)) return target
def test_register(self) -> None: self.temp = self.env.add_temp(IntRType()) self.block.ops.append(LoadInt(self.temp, 5)) fn = FuncIR('myfunc', [self.arg], ListRType(), [self.block], self.env) emitter = Emitter(EmitterContext()) generate_native_function(fn, emitter) result = emitter.fragments assert_string_arrays_equal([ 'static 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_int_expr(self, expr: IntExpr) -> Register: reg = self.alloc_target(IntRType()) self.add(LoadInt(reg, expr.value)) return reg
def visit_for_stmt(self, s: ForStmt) -> Register: if (isinstance(s.expr, CallExpr) and isinstance(s.expr.callee, RefExpr) and s.expr.callee.fullname == 'builtins.range'): self.push_loop_stack() # Special case for x in range(...) # TODO: Check argument counts and kinds; check the lvalue end = s.expr.args[0] end_reg = self.accept(end) # Initialize loop index to 0. index_reg = self.assign(s.index, IntExpr(0), IntRType(), IntRType(), declare_new=True) goto = Goto(INVALID_LABEL) self.add(goto) # Add loop condition check. top = self.new_block() goto.label = top.label branch = Branch(index_reg, end_reg, INVALID_LABEL, INVALID_LABEL, Branch.INT_LT) self.add(branch) branches = [branch] body = self.new_block() self.set_branches(branches, True, body) s.body.accept(self) end_goto = Goto(INVALID_LABEL) self.add(end_goto) end_block = self.new_block() end_goto.label = end_block.label # Increment index register. one_reg = self.alloc_temp(IntRType()) self.add(LoadInt(one_reg, 1)) self.add( PrimitiveOp(index_reg, PrimitiveOp.INT_ADD, index_reg, one_reg)) # Go back to loop condition check. self.add(Goto(top.label)) next = self.new_block() self.set_branches(branches, False, next) self.pop_loop_stack(end_block, next) return INVALID_REGISTER if self.node_type(s.expr).name == 'list': self.push_loop_stack() expr_reg = self.accept(s.expr) index_reg = self.alloc_temp(IntRType()) self.add(LoadInt(index_reg, 0)) one_reg = self.alloc_temp(IntRType()) self.add(LoadInt(one_reg, 1)) assert isinstance(s.index, NameExpr) assert isinstance(s.index.node, Var) lvalue_reg = self.environment.add_local(s.index.node, self.node_type(s.index)) condition_block = self.goto_new_block() # For compatibility with python semantics we recalculate the length # at every iteration. len_reg = self.alloc_temp(IntRType()) self.add(PrimitiveOp(len_reg, PrimitiveOp.LIST_LEN, expr_reg)) branch = Branch(index_reg, len_reg, INVALID_LABEL, INVALID_LABEL, Branch.INT_LT) self.add(branch) branches = [branch] body_block = self.new_block() self.set_branches(branches, True, body_block) target_list_type = self.types[s.expr] assert isinstance(target_list_type, Instance) target_type = self.type_to_rtype(target_list_type.args[0]) value_box = self.alloc_temp(ObjectRType()) self.add( PrimitiveOp(value_box, PrimitiveOp.LIST_GET, expr_reg, index_reg)) self.unbox_or_cast(value_box, target_type, target=lvalue_reg) s.body.accept(self) end_block = self.goto_new_block() self.add( PrimitiveOp(index_reg, PrimitiveOp.INT_ADD, index_reg, one_reg)) self.add(Goto(condition_block.label)) next_block = self.new_block() self.set_branches(branches, False, next_block) self.pop_loop_stack(end_block, next_block) return INVALID_REGISTER assert False, 'for not supported'
def test_dec_ref(self) -> None: self.assert_emit(DecRef(self.m, IntRType()), "CPyTagged_DecRef(cpy_r_m);")
def setUp(self) -> None: self.var = Var('arg') self.arg = RuntimeArg('arg', IntRType()) self.env = Environment() self.reg = self.env.add_local(self.var, IntRType()) self.block = BasicBlock(Label(0))
def test_set_attr(self) -> None: ir = ClassIR('A', [('x', BoolRType()), ('y', IntRType())]) rtype = UserRType(ir) self.assert_emit( SetAttr(self.n, 'y', self.m, rtype), """CPY_SET_ATTR(cpy_r_n, 3, cpy_r_m, AObject, CPyTagged);""")
def test_get_attr(self) -> None: ir = ClassIR('A', [('x', BoolRType()), ('y', IntRType())]) rtype = UserRType(ir) self.assert_emit( GetAttr(self.n, self.m, 'y', rtype), """cpy_r_n = CPY_GET_ATTR(cpy_r_m, 2, AObject, CPyTagged);""")
def test_box(self) -> None: self.assert_emit(Box(self.o, self.n, IntRType()), """cpy_r_o = CPyTagged_StealAsObject(cpy_r_n);""")
def test_dec_ref_tuple_nested(self) -> None: tuple_type = TupleRType( [TupleRType([IntRType(), BoolRType()]), BoolRType()]) self.assert_emit(DecRef(self.m, tuple_type), 'CPyTagged_DecRef(cpy_r_m.f0.f0);')
def setUp(self) -> None: self.env = Environment() self.n = self.env.add_local(Var('n'), IntRType()) self.context = EmitterContext() self.emitter = Emitter(self.context, self.env)
def test_inc_ref(self) -> None: self.assert_emit(IncRef(self.m, IntRType()), "CPyTagged_IncRef(cpy_r_m);")