def visit_AnnAssign(self, node: ast.AnnAssign): target = self.visit(node.target) value = self.visit(node.value) pos = extract_positional_info(node) annotation = self.visit(node.annotation) if isinstance(annotation, ir.NameRef): # Check if type is recognized by name type_ = self.symbols.type_by_name.get(annotation.name) if type_ is None: msg = f"Ignoring unrecognized annotation: {annotation}, line: {pos.line_begin}" warnings.warn(msg) else: ir_type = self.symbols.get_ir_type(type_) if isinstance(target, ir.NameRef): sym = self.symbols.lookup(target) existing_type = sym.type_ # This is an error, since it's an actual conflict. if existing_type != ir_type: msg = f"IR type from type hint conflicts with existing " \ f"(possibly inferred) type {existing_type}, line: {pos.line_begin}" raise CompilerError(msg) if node.value is not None: # CPython will turn the syntax "var: annotation" into an AnnAssign node # with node.value = None. If executed, this won't bind or update the value of var. assign = ir.Assign(target, value, pos) self.body.append(assign)
def visit_AugAssign(self, node: ast.AugAssign): target = self.visit(node.target) operand = self.visit(node.value) op = binary_in_place_ops.get(type(node.op)) pos = extract_positional_info(node) assign = ir.Assign(target, ir.BinOp(target, operand, op), pos) self.body.append(assign)
def p_AssignExpr(p): """AssignExpr : CondExpr | LValueExpr '=' AssignExpr""" # CondExpr if len(p) == 2: p[0] = p[1] # LValueExpr '=' AssignExpr elif len(p) == 4: p[0] = IR.Assign(p[1], p[3])
def visit_Assign(self, node: ast.Assign): # first convert locally to internal IR value = self.visit(node.value) pos = extract_positional_info(node) for target in node.targets: # break cascaded assignments into multiple assignments target = self.visit(target) for subtarget, subvalue in unpack_assignment(target, value, pos): subassign = ir.Assign(subtarget, subvalue, pos) self.body.append(subassign)
def make_single_index_loop(header: ir.ForLoop, symbols): """ Make loop interval of the form (start, stop, step). This tries to find a safe method of calculation. This assumes (with runtime verification if necessary) that 'stop - start' will not overflow. References: LIVINSKII et. al, Random Testing for C and C++ Compilers with YARPGen Dietz et. al, Understanding Integer Overflow in C/C++ Bachmann et. al, Chains of Recurrences - a method to expedite the evaluation of closed-form functions https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html https://developercommunity.visualstudio.com/t/please-implement-integer-overflow-detection/409051 https://numpy.org/doc/stable/user/building.html """ by_iterable = {} intervals = set() interval_from_iterable = IntervalBuilder() for _, iterable in unpack_iterated(header.target, header.iterable): interval = interval_from_iterable(iterable) by_iterable[iterable] = interval intervals.add(interval) # loop_interval = _find_shared_interval(intervals) loop_start, loop_stop, loop_step = _find_shared_interval(intervals) loop_expr = ir.AffineSeq(loop_start, loop_stop, loop_step) # Todo: this needs a default setting to avoid excessive casts loop_counter = symbols.make_unique_name_like("i", type_=tr.Int32) body = [] pos = header.pos simplify_expr = arithmetic_folding() for target, iterable in unpack_iterated(header.target, header.iterable): (start, _, step) = by_iterable[iterable] assert step == loop_step assert (start == loop_start) or (loop_start == ir.Zero) if step == loop_step: if start == loop_start: index = loop_counter else: assert loop_start == ir.Zero index = ir.BinOp(loop_counter, start, "+") else: # loop counter must be normalized assert loop_start == ir.Zero assert loop_step == ir.One index = ir.BinOp(step, loop_counter, "*") if start != ir.Zero: index = ir.BinOp(start, index, "+") value = index if isinstance(iterable, ir.AffineSeq) else ir.Subscript(iterable, index) assign = ir.Assign(target, value, pos) body.append(assign) # Todo: this doesn't hoist initial setup body.extend(header.body) repl = ir.ForLoop(loop_counter, loop_expr, body, pos) return repl
def copy_out(self, append_to, pos): assert self.labeler is not None for target, value in self.labeler.source_assigns: assign = ir.Assign(target, value, pos) append_to.append(assign)
def stfld(self, _, field): v = self.pop() obj = self.pop() self.stmt(ir.Assign(ir.Field(obj, field[2].name), v))
def local(self, num, value=None): if value is None: return ir.Local(num) self.stmt(ir.Assign(ir.Local(num), value))