Source code for ducky.cc.passes.ast_codegen

from pycparser import c_ast

from six import iteritems, iterkeys

from . import ASTVisitor
from .. import *  # noqa
from ..types import *  # noqa

[docs]class CodegenVisitor(ASTVisitor): priority = 1000 def __init__(self, *args, **kwargs): super(CodegenVisitor, self).__init__(*args, **kwargs) self.types = { 'void': VoidType(self), 'int': IntType(self), 'unsigned int': UnsignedIntType(self), 'char': CharType(self), 'unsigned char': UnsignedCharType(self) } self.types['unsigned'] = self.types['unsigned int'] for desc in list(iterkeys(self.types)): self.types[desc + ' *'] = PointerType(self.types[desc], self) self.functions = [] self.global_scope = Scope(self) self.FN = None self.BLOCK = None self.SCOPE = self.global_scope self.EMIT = None self.GET_REG = None self.REGS = None self.blocks = [] self.prolog_blocks = [] self.epilog_blocks = [] self.label_index = 0 self.literal_label_index = 0 self.string_literals = {} self.break_stack = [] self.continue_stack = [] self.make_current(self.block(name = self.get_new_label(name = 'global')))
[docs] def block(self, stage = None, *args, **kwargs): block = Block(*args, **kwargs) if stage == 'prolog': self.prolog_blocks.append(block) elif stage == 'epilog': self.epilog_blocks.append(block) else: self.blocks.append(block) return block
[docs] def make_current(self, block): self.DEBUG(self.log_prefix + 'new current block: %s', block) self.BLOCK = block self.EMIT = block.emit
[docs] def reset_scope(self): while self.SCOPE != self.global_scope: self.pop_scope()
[docs] def push_scope(self): self.SCOPE = Scope(self, parent = self.SCOPE) self.DEBUG(self.log_prefix + 'scope: pushing new scope: scope=%s, parent=%s', self.SCOPE, self.SCOPE.parent) return self.SCOPE
[docs] def pop_scope(self): self.DEBUG(self.log_prefix + 'scope: popping scope: scope=%s, parent=%s', self.SCOPE, self.SCOPE.parent) self.SCOPE = self.SCOPE.parent
[docs] def get_new_label(self, name = None): self.label_index += 1 return '.L%i' % self.label_index if name is None else '.L%i_%s' % (self.label_index, name)
[docs] def get_new_literal_label(self): self.literal_label_index += 1 return '.LC%i' % self.literal_label_index
[docs] def get_new_local_storage(self, size): assert self.FN is not None if size <= 4: self.FN.fp_offset -= 4 else: self.WARN('Unhandled get_new_local_storage branch: size=%s', size) return StackSlotStorage(None, self.FN.fp_offset)
[docs] def emit_string_literals(self): B = self.block(stage = 'prolog', comment = 'string literals block') B.emit(Directive('.include "ducky.asm"')) B.emit(Directive('.section .rodata')) for label, s in iteritems(self.string_literals): B.emit(Directive('.type %s, string' % label)) B.emit(Directive('.string %s' % s))
[docs] def emit_trampoline(self): pass
[docs] def emit_prolog(self): B = self.block(stage = 'prolog', comment = 'global pre-prolog') B.emit(Directive('.data')) B.emit(Directive('.type __global_stack, space')) B.emit(Directive('.space 256')) B.emit(Directive('.text')) B = self.block(stage = 'prolog', name = '_start', comment = 'global prolog') B.emit(LA('sp', '&__global_stack')) B.emit(ADD('sp', 256)) B.emit(MOV('fp', 'sp')) B.emit(CALL('&main')) B.emit(HLT('r0')) B.emit(Directive('.global _start')) self.emit_string_literals()
[docs] def emit_epilog(self): self.emit_trampoline()
[docs] def materialize(self): # Emit translation unit prolog and epilog self.emit_prolog() self.emit_epilog() # Now materialize block tree code = [] for block in self.prolog_blocks: block.materialize(code) code.append('') for block in self.blocks: block.materialize(code) code.append('') for fn in self.functions: code += fn.materialize() code.append('') for block in self.epilog_blocks: block.materialize(code) code.append('') return '\n'.join(code)
[docs] def visit(self, node, **kwargs): method = 'visit_' + node.__class__.__name__ visitor = getattr(self, method, self.generic_visit) if visitor == self.generic_visit: self.WARN(self.log_prefix + 'Unhandled node class: %s', node.__class__.__name__) return visitor(node, **kwargs)
[docs] def generic_visit(self, node, **kwargs): old_parent = self.node_parent self.node_parent = node ret = [] for c_name, c in node.children(): ret.append(self.visit(c, **kwargs)) self.node_parent = old_parent return ret
[docs] def visit_constant_value(self, node): self.DEBUG(self.log_prefix + 'visit_constant_value: %s', dump_node(node)) if node.type == 'string': label = self.get_new_literal_label() self.string_literals[label] = node.value return RValueExpression(value = StringConstantValue('&%s' % label), type = CType.get_from_desc(self, 'char *')) if node.type == 'int': return RValueExpression(value = ConstantValue(node.value), type = CType.get_from_desc(self, 'int')) if node.type == 'char': special_chars = [r'\0'] c = node.value[1:-1] if c in special_chars: return RValueExpression(value = ConstantValue(str(special_chars.index(c))), type = CType.get_from_desc(self, 'char')) return RValueExpression(value = ConstantValue(str(ord(c))), type = CType.get_from_desc(self, 'char')) raise CompilerError(node.coord, 'Unhandled branch: Constant, type=%s' % node.type)
[docs] def visit_expr(self, node, preferred = None, keep = None): self.DEBUG(self.log_prefix + 'visit_expr: node=%s, preferred=%s, keep=%s', dump_node(node), preferred, keep) self.DOWN() E = self.visit(node, preferred = preferred, keep = keep) self.DEBUG(self.log_prefix + 'visit_expr: expr=%s', E) self.UP() return E
[docs] def process_cond(self, node, iftrue_label = None, iffalse_label = None): CE = self.visit(node) self.DEBUG(self.log_prefix + 'cond=%s', CE) if CE.value is None: # check flags according to our cond if isinstance(node, c_ast.BinaryOp): if iftrue_label is not None: if node.op == '==': self.EMIT(BE('&' + iftrue_label)) elif node.op == '!=': self.EMIT(BNE('&' + iftrue_label)) elif node.op == '<=': self.EMIT(BLE('&' + iftrue_label)) elif node.op == '>=': self.EMIT(BGE('&' + iftrue_label)) else: self.WARN(self.log_prefix + 'Condition handling not implemented: BinaryOp, op=%s', node.op) if iffalse_label is not None: if node.op == '==': self.EMIT(BNE('&' + iffalse_label)) elif node.op == '!=': self.EMIT(BE('&' + iffalse_label)) elif node.op == '<=': self.EMIT(BG('&' + iffalse_label)) elif node.op == '>=': self.EMIT(BL('&' + iffalse_label)) else: self.WARN(self.log_prefix + 'Condition handling not implemented: BinaryOp, op=%s', node.op) else: self.WARN(self.log_prefix + 'Condition handling not implemented: cond=%s', dump_node(node))
def _perform_implicit_conversion(self, loc, E, T): self.DEBUG(self.log_prefix + '_perform_implicit_conversion: E=%s, T=%s', E, T) self.DOWN() assert E.is_rvalue() # Any integer type rvalue can be converted to any other integer type, with # possible loss of precision. if isinstance(E.type, IntType): if isinstance(T, UnsignedIntType): value = E.value if isinstance(value, ConstantValue): if int(value.value) < 0: value = 2 ^ 16 + int(value.value) else: value = value.value self.UP() return RValueExpression(value = ConstantValue(str(value)), type = T), E elif isinstance(value, RegisterValue): self.UP() return RValueExpression(value = value, type = T), E else: self.WARN(self.log_prefix + 'Unhandled conversion branch') if isinstance(E.type, UnsignedIntType): if isinstance(T, IntType): value = E.value if isinstance(value, ConstantValue): if int(value.value) >= 32768: self.WARN(self.log_prefix + 'Implicit "%s" -> "%s" conversion - value "%s" cannot fit into destination', E.type, T, value.value) self.UP() return RValueExpression(value = ConstantValue(str(value.value)), type = T), E elif isinstance(value, RegisterValue): self.UP() return RValueExpression(value = value, type = T), E else: self.WARN(self.log_prefix + 'Unhandled conversion branch') if isinstance(E.type, PointerType): if isinstance(T, PointerType): # void * can be casted to any pointer, and any pointer can be casted to void * if isinstance(E.type.ptr_to_type, VoidType) or isinstance(T.ptr_to_type, VoidType): self.UP() return RValueExpression(value = E.value, type = T), E raise IncompatibleTypesError(loc, T, E.type) # # Real visitors #
[docs] def visit_ArrayRef(self, node, preferred = None, keep = None): comment = dump_node(node) self.INFO(self.log_prefix + comment) self.DOWN() self.EMIT(Comment(comment)) AE = self.visit_expr(node.name) self.DEBUG(self.log_prefix + 'array=%s', AE) SE = self.visit_expr(node.subscript) self.DEBUG(self.log_prefix + 'subscript=%s', SE) if not SE.is_rvalue(): SE, old_SE = SE.to_rvalue(self) self.DEBUG(self.log_prefix + 'subscript=%s', SE) reg = self.GET_REG(keep = [array_reg, subscript_reg]) self.EMIT(MOV(reg.name, subscript)) self.EMIT(MUL(reg.name, len(array_symbol.type.ptr_to_type))) self.EMIT(ADD(reg.name, array)) self.PUT_REG(array_reg) self.PUT_REG(subscript_reg) self.EMIT(Comment('/ ' + comment)) self.UP() return reg.name, reg, array_symbol
[docs] def visit_Assignment(self, node, preferred = None, keep = None): comment = dump_node(node) self.INFO(self.log_prefix + comment) self.DOWN() self.EMIT(Comment(comment)) LE = self.visit_expr(node.lvalue) RE = self.visit_expr(node.rvalue) self.DEBUG(self.log_prefix + 'left=%s', LE) self.DEBUG(self.log_prefix + 'right=%s', RE) if not LE.is_mlvalue(): raise CompilerError(node.coord, 'Unable to assign to a read-only variable') if RE.is_lvalue(): # Implicit lvalue->rvalue conversion must take a place here RE, old_RE = RE.to_rvalue(self) self.DEBUG(self.log_prefix + 'right=%s', RE) self.DEBUG(self.log_prefix + 'value "%s" to storage "%s", %i bytes', RE.type, LE.type, len(LE.type.ptr_to_type)) if LE.type.ptr_to_type != RE.type: RE, old_RE = self._perform_implicit_conversion(node.coord, RE, LE.type.ptr_to_type) self.DEBUG(self.log_prefix + 'right=%s', RE) if LE.value.can_register_backed(): if not LE.value.is_register_backed(): self.EMIT(Comment('LE can be backed by register, no register acquired yet')) if isinstance(RE.value, RegisterValue) and RE.value.register.storage is None: LE.value.storage.acquire_register(RE.value.register) LE.value.backing_register().dirty = True else: R = self.GET_REG() LE.value.storage.acquire_register(R) self.EMIT(MOV(LE.value.backing_register().name, RE.value.name)) else: self.EMIT(MOV(LE.value.backing_register().name, RE.value.name)) LE.value.backing_register().dirty = True else: if len(LE.type.ptr_to_type) == 1: self.EMIT(STB(LE.value.name, RE.value.name)) elif len(LE.type.ptr_to_type) == 2: self.EMIT(STW(LE.value.name, RE.value.name)) else: self.WARN(self.log_prefix + 'Unhandled assignment size branch') if isinstance(LE.value, RegisterValue): LE.value.register.put() self.EMIT(Comment('/ ' + comment)) self.UP() return RE
[docs] def visit_BinaryOp(self, node, preferred = None, keep = None): comment = dump_node(node) self.INFO(self.log_prefix + '%s, preferred=%s, keep=%s', dump_node(node), preferred, keep) self.DOWN() self.EMIT(Comment(dump_node(node))) self.EMIT(Comment('left: ' + dump_node(node.left))) self.EMIT(Comment('right: ' + dump_node(node.right))) LE = self.visit_expr(node.left) RE = self.visit_expr(node.right) self.DEBUG(self.log_prefix + 'left=%s', LE) self.DEBUG(self.log_prefix + 'right=%s', RE) if not LE.is_rvalue(): LE, old_LE = LE.to_rvalue(self) self.DEBUG(self.log_prefix + 'left=%s', LE) if not RE.is_rvalue(): RE, old_RE = RE.to_rvalue(self) self.DEBUG(self.log_prefix + 'right=%s', RE) keep = keep or [] def __binop_arith(inst): E = RValueExpression(value = RegisterValue(self.GET_REG(preferred = preferred, keep = keep + [LE.value, RE.value])), type = LE.type) if isinstance(LE.value, RegisterValue): self.EMIT(MOV(E.value.name, LE.value.name)) elif isinstance(LE.value, StringConstantValue): self.EMIT(LA(E.value.name, LE.value.name)) elif isinstance(LE.value, ConstantValue): self.EMIT(LI(E.value.name, LE.value.name)) else: self.WARN(self.log_prefix + 'Unhandled binop arith branch') self.EMIT(inst(E.value.name, RE.value.name)) return E if LE.type != RE.type: RE, old_RE = self._perform_implicit_conversion(node.coord, RE, LE.type) self.DEBUG(self.log_prefix + 'right=%s', RE) R = None if node.op == '+': R = __binop_arith(ADD) elif node.op == '-': R = __binop_arith(SUB) elif node.op == '*': ret, ret_reg = __binop_arith(MUL) elif node.op == '&': R = __binop_arith(AND) elif node.op == '||': R = __binop_arith(OR) elif node.op in ('!=', '>=', '<=', '==', '>', '<'): R = RValueExpression(type = IntType(self)) self.EMIT(CMP(LE.value.name, RE.value.name)) else: self.WARN(self.log_prefix + 'Unhandled binary op: op=%s', node.op) if isinstance(LE.value, RegisterValue): LE.value.register.put() if isinstance(RE.value, RegisterValue): RE.value.register.put() self.EMIT(Comment('/ ' + comment)) self.UP() return R
[docs] def visit_Cast(self, node, **kwargs): comment = dump_node(node) self.INFO(self.log_prefix + comment) self.DOWN() self.EMIT(Comment(comment)) E = self.visit_expr(node.expr, **kwargs) self.DEBUG(self.log_prefix + 'expr=%s', E) if E.is_lvalue(): E, old_E = E.to_rvalue(self) self.DEBUG(self.log_prefix + 'expr=%s', E) self.DEBUG(self.log_prefix + 'typename: %s', dump_node(node.to_type)) T = CType.get_from_decl(self, node.to_type.type) self.DEBUG(self.log_prefix + 'type: %s', T) R = RValueExpression(value = RegisterValue(self.GET_REG(**kwargs)), type = T) self.EMIT(Comment('casting %s to %s' % (E, T))) self.EMIT(MOV(R.value.name, E.value.name)) self.EMIT(Comment('/ ' + comment)) self.UP() return R
[docs] def visit_Compound(self, node, create_scope = True): self.INFO(self.log_prefix + dump_node(node)) self.DOWN() self.EMIT(Comment(dump_node(node))) if create_scope: self.push_scope() ret = self.generic_visit(node) self.DEBUG(self.log_prefix + 'compound output: %s', ret) for r in ret: if r is None or not isinstance(r.value, RegisterValue): continue r.value.register.put() self.pop_scope() self.UP()
[docs] def visit_Constant(self, node, preferred = None, keep = None): comment = dump_node(node) self.DEBUG(self.log_prefix + '%s: preferred=%s, keep=%s', comment, preferred, keep) self.DOWN() self.EMIT(Comment(comment)) E = self.visit_constant_value(node) self.EMIT(Comment('/ ' + comment)) self.UP() return E
[docs] def visit_Decl(self, node): comment = dump_node(node) self.INFO(self.log_prefix + comment) self.DOWN() self.EMIT(Comment(comment)) if node.name is None: # declaring a type, structure probably... we'll see T = CType.create_from_decl(self, node) self.types[repr(T)] = T # self.EMIT(Comment('/ ' + comment)) self.UP() return T symbol = self.SCOPE.add(node.coord, Symbol(self, node.name, CType.create_from_decl(self, node.type), extern = ('extern' in node.storage), const = ('const' in node.quals))) RE_class = MLValueExpression if 'const' in node.quals else LValueExpression RE = RE_class(type = PointerType(symbol.type, self)) # get storage for new symbol if self.FN is None: storage = MemorySlotStorage(symbol, node.name) RE.value = MemorySlotValue(storage) else: storage = self.get_new_local_storage(len(RE.type.ptr_to_type)) RE.value = StackSlotValue(storage) storage.symbol = symbol symbol.storage = storage self.DEBUG(self.log_prefix + 'storage: size=%i, slot=%s', len(RE.type.ptr_to_type), storage) self.EMIT(Comment('storage: %s => size=%i, slot=%s' % (node.name, len(RE.type.ptr_to_type), RE.value.name))) self.DEBUG(self.log_prefix + 'RE=%s', RE) if node.init is not None: self.DEBUG(self.log_prefix + 'has init, process it') self.DOWN() self.EMIT(Comment('init of %s' % comment)) IE = self.visit(node.init) self.DEBUG(self.log_prefix + 'init=%s', IE) if isinstance(IE.value, RegisterValue): self.WARN(self.log_prefix + 'Unhandled init branch') else: R = self.GET_REG() R.dirty = True symbol.storage.acquire_register(R) self.EMIT(LI(R.name, IE.value.name)) # self.EMIT(STW(RE.value.name, R.name)) self.EMIT(Comment('/ init of %s' % comment)) self.UP() self.DEBUG(self.log_prefix + 'declared symbol: %s', symbol) self.EMIT(Comment('declared symbol: %s' % symbol)) # self.EMIT(Comment('/ ' + comment)) self.UP() return RE
[docs] def visit_ExprList(self, node): self.INFO(self.log_prefix + dump_node(node)) self.DOWN() for expr in node.exprs: RE = self.visit_expr(expr) self.UP() return RE
[docs] def visit_FileAST(self, node): self.generic_visit(node)
[docs] def visit_For(self, node): comment = dump_node(node) self.INFO(self.log_prefix + comment) self.DOWN() self.EMIT(Comment(comment)) init_block = self.FN.block(name = self.get_new_label(name = 'for_init')) head_block = self.FN.block(name = self.get_new_label(name = 'for_head')) body_block = self.FN.block(name = self.get_new_label(name = 'for_body')) past_block = self.FN.block(name = self.get_new_label(name = 'for_past')) self.break_stack.append(past_block) self.continue_stack.append(head_block) self.BLOCK.connect(init_block) init_block.connect(head_block) head_block.connect(body_block) head_block.connect(past_block) body_block.connect(head_block) body_block.connect(past_block) self.make_current(init_block) IE = self.visit(node.init) self.DEBUG(self.log_prefix + ' init=%s', IE) self.make_current(head_block) self.process_cond(node.cond, iftrue_label = body_block.names[0], iffalse_label = past_block.names[0]) self.make_current(body_block) self.visit(node.stmt) self.visit(node.next) self.EMIT(J('&' + head_block.names[0])) self.make_current(past_block) self.break_stack.pop() self.continue_stack.pop() self.UP()
[docs] def visit_FuncCall(self, node, preferred = None, keep = None): comment = dump_node(node) self.INFO(self.log_prefix + comment) self.DOWN() self.EMIT(Comment(comment)) self.INFO(self.log_prefix + 'Name: %s', dump_node(node.name)) self.INFO(self.log_prefix + 'Args: %s', dump_node(node.args)) if isinstance(node.name, c_ast.ID) and node.name.name == 'asm' and node.args is not None and isinstance(node.args.exprs[0], c_ast.Constant) and node.args.exprs[0].type == 'string': for line in node.args.exprs[0].value[1:-1].split('\n'): self.EMIT(InlineAsm(line)) self.UP() return None FE = self.visit(node.name) if FE.value is None: raise SymbolUndefined(node.coord, dump_node(node.name)) FE_symbol = FE.value.storage.symbol self.DEBUG(self.log_prefix + 'fn=%s', FE) if node.args is not None: args = [] restore_regs = [] for i, arg in enumerate(node.args.exprs): arg_comment = 'arg #%i: %s' % (i, dump_node(arg)) self.DEBUG(self.log_prefix + arg_comment) self.DOWN() self.EMIT(Comment(arg_comment)) R = self.FN.registers.all_regs[i + 1] self.EMIT(Comment('free register')) if R.storage is None: restore_regs.append((R, None)) self.EMIT(PUSH(R.name)) else: restore_regs.append((R, R.storage)) R.free() self.DEBUG(self.log_prefix + 'place into %s', R) A = self.visit(arg, preferred = R.index) self.DEBUG(self.log_prefix + 'type=%s', FE_symbol.type.args[i]) self.DEBUG(self.log_prefix + 'expr=%s', A) if not A.is_rvalue(): A, old_A = A.to_rvalue(self, preferred = R) self.DEBUG(self.log_prefix + 'expr=%s', A) if isinstance(A.value, RegisterValue) and A.value.register.index != R.index: self.EMIT(MOV(R.name, A.value.name)) elif isinstance(A.value, StringConstantValue): self.EMIT(LA(R.name, A.value.name)) elif isinstance(A.value, ConstantValue): self.EMIT(LI(R.name, A.value.name)) else: self.WARN('Unahndled func call arg load branch') if FE.value.storage.symbol.type.args[i] != A.type: A, old_A = self._perform_implicit_conversion(node.coord, A, FE.value.storage.symbol.type.args[i]) self.DEBUG(self.log_prefix + 'expr=%s', A) args.append((R, arg)) self.UP() self.DEBUG(self.log_prefix + 'args: %s', args) ret_reg = FE_symbol is not None and len(FE_symbol.type.rtype) != 0 and self.node_parent is not None and not isinstance(self.node_parent, c_ast.Compound) self.DEBUG(self.log_prefix + 'return type: %s', FE_symbol.type.rtype) if ret_reg: RE = Expression(value = RegisterValue(self.GET_REG(preferred = 0)), type = FE_symbol.type.rtype) else: RE = None self.DEBUG(self.log_prefix + 'FN retval: %s', RE) if isinstance(FE.value, MemorySlotValue): self.EMIT(CALL(FE.value.name)) else: self.WARN(self.log_prefix + 'Unhandled call branch') if RE is not None: R = self.GET_REG() self.EMIT(MOV(R.name, RE.value.name)) self.PUT_REG(RE.value.register) RE.value = RegisterValue(R) for R, storage in reversed(restore_regs): self.DEBUG(self.log_prefix + 'restore value of %s', R) self.EMIT(Comment('restore content of %s' % R)) if storage is None: self.EMIT(POP(R.name)) else: storage.unspill_register(self, R) self.INFO(self.log_prefix + 'parent=%s', dump_node(self.node_parent)) self.EMIT(Comment('/ ' + comment)) self.UP() return RE
[docs] def visit_FuncDef(self, node): comment = dump_node(node) self.INFO(self.log_prefix + comment) self.DOWN() self.EMIT(Comment(comment)) decl = node.decl self.INFO(self.log_prefix + 'Decl: %s', dump_node(decl)) self.INFO(self.log_prefix + 'Type: %s', dump_node(decl.type)) if decl.type.args is not None: self.INFO(self.log_prefix + 'Args: %s', dump_node(decl.type.args)) self.INFO(self.log_prefix + 'Type type: %s', dump_node(decl.type.type)) self.INFO(self.log_prefix + 'Return type: %s', dump_node(decl.type.type.type)) T = CType.create_from_decl(self, decl.type) self.reset_scope() self.push_scope() self.FN = FN = Function(self, decl, T) self.functions.append(FN) self.GET_REG = FN.registers.get self.REGS = FN.registers symbol = self.global_scope.get(decl.name) if symbol is None: symbol = self.global_scope.add(node.coord, Symbol(self, decl.name, FN.type)) symbol.storage = MemorySlotStorage(symbol, decl.name) symbol.storage.symbol = symbol symbol.defined = True self.make_current(FN.args_block()) if decl.type.args is not None: for i, arg in enumerate(decl.type.args.params): self.visit(arg) symbol = self.SCOPE.get(arg.name) symbol.defined = True self.DEBUG(self.log_prefix + 'arg symbol: %s', symbol) R = self.GET_REG(preferred = i + 1) R.dirty = True symbol.storage.acquire_register(R) # self.EMIT(STW(symbol.storage.name(), 'r%i' % (i + 1))) self.DEBUG(self.log_prefix + 'arg #%i: %s', i, symbol) self.EMIT(Comment('arg #%i: %s' % (i, symbol))) del FN.registers.callee_saved_regs[i + 1] if len(FN.type.rtype) != 0: del FN.registers.callee_saved_regs[0] self.make_current(FN.body_block()) self.visit_Compound(node.body, create_scope = False) FN.finish() self.UP()
[docs] def visit_ID(self, node, preferred = None, keep = None): comment = dump_node(node) self.INFO(self.log_prefix + '%s: preferred=%s, keep=%s', comment, preferred, keep) self.DOWN() self.EMIT(Comment(comment)) symbol = self.SCOPE.get(node.name) self.DEBUG(self.log_prefix + 'symbol=%s', symbol) self.EMIT(Comment('/ ' + comment)) RE_class = LValueExpression if symbol.const is True else MLValueExpression value_class = StackSlotValue if isinstance(symbol.storage, StackSlotStorage) else MemorySlotValue RE = RE_class(value = value_class(symbol.storage), type = PointerType(symbol.type, self)) self.UP() return RE
[docs] def visit_If(self, node): self.INFO(self.log_prefix + dump_node(node)) self.DOWN() cond_label = self.get_new_label(name = 'if_cond') iftrue_label = self.get_new_label(name = 'if_iftrue') iffalse_label = self.get_new_label(name = 'if_iffalse') past_label = self.get_new_label(name = 'if_past') self.EMIT(Comment(dump_node(node))) self.EMIT(Comment(' iftrue=%s, iffalse=%s, past=%s' % (iftrue_label, iffalse_label, past_label))) cond_block = self.FN.block(name = cond_label) iftrue_block = self.FN.block(name = iftrue_label) iffalse_block = self.FN.block(name = iffalse_label) self.BLOCK.connect(cond_block) cond_block.connect(iftrue_block) cond_block.connect(iffalse_block) self.make_current(cond_block) self.process_cond(node.cond, iftrue_label = iftrue_block.names[0], iffalse_label = iffalse_block.names[0]) self.make_current(iftrue_block) self.visit(node.iftrue) self.make_current(iffalse_block) if node.iffalse is not None: self.visit(node.iffalse) past_block = self.FN.block(name = past_label) iftrue_block.connect(past_block) iffalse_block.connect(past_block) iftrue_block.emit(J('&' + past_label)) iffalse_block.emit(J('&' + past_label)) self.BLOCK.connect(past_block) self.EMIT(Comment('connecting %s to %s' % (self.BLOCK, past_block))) self.make_current(past_block) self.UP() return None
[docs] def visit_Return(self, node): comment = dump_node(node) self.INFO(self.log_prefix + comment) self.DOWN() self.EMIT(Comment(comment)) E = self.visit_expr(node.expr) self.DEBUG(self.log_prefix + 'expr=%s', E) if not E.is_rvalue(): E, old_E = E.to_rvalue(self, preferred = 0) def __free_r0(): R0 = self.FN.registers.all_regs[0] self.EMIT(Comment('free r0')) R0.free() return R0 if isinstance(E.value, RegisterValue): if E.value.register.index != 0: R = __free_r0() self.EMIT(MOV(R.name, E.value.name)) else: pass elif isinstance(E.value, StringConstantValue): R = __free_r0() self.EMIT(LA(R.name, E.value.name)) elif isinstance(E.value, ConstantValue): R = __free_r0() self.EMIT(LI(R.name, E.value.name)) else: self.WARN(self.log_prefix + 'Unhandled return branch') self.EMIT(J('&' + self.FN.epilog_block().names[0])) self.BLOCK.connect(self.FN.epilog_block()) self.EMIT(Comment('/ ' + comment)) self.UP() return None
[docs] def visit_StructRef(self, node, **kwargs): comment = dump_node(node) self.INFO(self.log_prefix + comment) self.DOWN() self.INFO(self.log_prefix + 'name=%s, field=%s', dump_node(node.name), dump_node(node.field)) self.EMIT(Comment(comment)) NE = self.visit_expr(node.name) self.DEBUG(self.log_prefix + 'name=%s', NE) RE_class = NE.__class__ self.DEBUG(self.log_prefix + 'RE class=%s', RE_class.__name__) if node.type == '->': if not isinstance(NE.type.ptr_to_type, PointerType): raise NotAPointerError(node.coord, NE.type) RE_class = MLValueExpression old_NE = NE NE = LValueExpression(value = RegisterMemorySlotValue(self.GET_REG()), type = NE.type.ptr_to_type) self.EMIT(LW(NE.value.name, old_NE.value.name)) if isinstance(NE.type.ptr_to_type, PointerType): raise IsAPointerError(node.coord, NE.type) field_type = NE.type.ptr_to_type.field_type(node.field.name) field_offset = NE.type.ptr_to_type.field_offset(node.field.name) self.DEBUG(self.log_prefix + 'field_type=%s, field_offset=%s', field_type, field_offset) RE = None if isinstance(NE.value, StackSlotValue): RE = RE_class(value = StackSlotValue(StackSlotStorage(None, NE.value.storage.offset + field_offset)), type = PointerType(field_type, self)) elif isinstance(NE.value, RegisterMemorySlotValue): RE = RE_class(value = NE.value, type = PointerType(field_type, self)) self.EMIT(ADD(RE.value.name, field_offset)) elif isinstance(NE.value, MemorySlotValue): RE = RE_class(value = RegisterValue(self.GET_REG()), type = PointerType(field_type, self)) NE.value.storage.addrof(RE.value.register, self.EMIT) self.EMIT(ADD(RE.value.name, field_offset)) else: self.WARN(self.log_prefix + 'Unhandled struct ref for name') self.EMIT(Comment('/ ' + comment)) self.UP() return RE
[docs] def visit_TypeDecl(self, node, **kwargs): self.INFO(self.log_prefix + dump_node(node)) self.DOWN() t = CType.create_from_decl(self, node) self.types[node.declname] = t self.UP() return RValueExpression(type = t)
[docs] def visit_Typedef(self, node): self.INFO(self.log_prefix + dump_node(node)) self.DOWN() ts = self.generic_visit(node) T = ts[0] self.types[node.name] = T.type self.DEBUG(self.log_prefix + 'typedef new type: %s', T) self.UP() return RValueExpression(type = T)
[docs] def visit_Typename(self, node, **kwargs): self.INFO(self.log_prefix + dump_node(node)) self.DOWN() T = CType.get_from_decl(self, node.type) self.UP() return RValueExpression(type = T)
[docs] def visit_UnaryOp(self, node, preferred = None, keep = None): comment = dump_node(node) self.INFO(self.log_prefix + '%s, preferred=%s, keep=%s', comment, preferred, keep) self.DOWN() self.EMIT(Comment(comment)) keep = keep or [] E = self.visit_expr(node.expr) self.DEBUG(self.log_prefix + 'expr=%s', E) if node.op == 'p++': assert E.is_mlvalue() is True E, old_E = E.to_rvalue(self) self.DEBUG(self.log_prefix + 'expr=%s', E) RE = RValueExpression(value = self.GET_REG(), type = E.type) self.EMIT(MOV(RE.value.name, E.value.name)) self.EMIT(INC(E.value.name)) if old_E.value.is_register_backed(): old_E.value.backing_register().dirty = True else: if len(E.type) == 1: self.EMIT(STB(old_E.value.name, E.value.name)) elif len(E.type) == 2: self.EMIT(STB(old_E.value.name, E.value.name)) else: self.WARN(self.log_prefix + 'Unhandled save branch') self.DEBUG(self.log_prefix + 'postfix inc: expr=%s, rexpr=%s', E, RE) self.EMIT(Comment('/ ' + comment)) self.UP() return RE if node.op == '*': assert isinstance(E.type, PointerType) is True RE = E.__class__(value = RegisterValue(self.GET_REG(preferred = preferred, keep = keep + [E.value])), type = E.type.ptr_to_type) if len(RE.type) == 1: self.EMIT(LB(RE.value.name, E.value.name)) elif len(RE.type) == 2: self.EMIT(LW(RE.value.name, E.value.name)) else: self.WARN(self.log_prefix + 'Unhandled deref branch') self.EMIT(Comment('/ ' + comment)) self.UP() return RE if node.op == '&': assert E.is_lvalue() is True if E.value.is_register_backed(): E.value.storage.spill_register(self) if isinstance(E.value, StackSlotValue): RE = RValueExpression(value = RegisterValue(self.GET_REG()), type = E.type) E.value.storage.addrof(RE.value, self.EMIT) elif isinstance(E.value, RegisterMemorySlotValue): RE = RValueExpression(value = E.value, type = E.type) else: self.WARN(self.log_prefix + 'Unhandled addrof branch') self.EMIT(Comment('/ ' + comment)) self.UP() return RE if node.op == '~': if E.is_lvalue(): RE, old_E = E.to_rvalue(self) else: RE = RValueExpression(value = RegisterValue(self.GET_REG()), type = E.type) if isinstance(E.value, StringConstantValue): self.EMIT(LA(RE.value.name, E.value.name)) elif isinstance(E.value, ConstantValue): self.EMIT(LI(RE.value.name, E.value.name)) self.EMIT(NOT(RE.value.name)) self.EMIT(Comment('/ ' + comment)) self.UP() return RE if node.op == 'sizeof': RE = RValueExpression(value = RegisterValue(self.GET_REG(preferred = preferred, keep = keep)), type = UnsignedIntType(self)) self.EMIT(LI(RE.value.name, len(E.type))) self.UP() return RE self.WARN('Unhandled unary op: op=%s', node.op) self.UP() return None, None, None
[docs] def visit_While(self, node): comment = dump_node(node) self.INFO(self.log_prefix + dump_node(node)) self.DOWN() self.EMIT(Comment(comment)) head_block = self.FN.block(name = self.get_new_label(name = 'while_head')) body_block = self.FN.block(name = self.get_new_label(name = 'while_body')) past_block = self.FN.block(name = self.get_new_label(name = 'while_past')) self.break_stack.append(past_block.names[0]) self.continue_stack.append(head_block.names[0]) self.BLOCK.connect(head_block) head_block.connect(body_block) head_block.connect(past_block) body_block.connect(head_block) body_block.connect(past_block) self.DEBUG(self.log_prefix + 'head_block: %s', head_block) self.make_current(head_block) self.process_cond(node.cond, iffalse_label = past_block.names[0]) self.make_current(body_block) self.visit(node.stmt) self.EMIT(J('&' + head_block.names[0])) self.make_current(past_block) self.break_stack.pop() self.continue_stack.pop() self.EMIT(Comment('/ ' + comment)) self.UP()
from . import AST_PASSES AST_PASSES['ast-codegen'] = CodegenVisitor