def update_next_block(self): """ If the last instruction of this block is a JP, JR or RET (with no conditions) then goes_to set contains just a single block """ last = self.mem[-1] if last.inst not in { 'djnz', 'jp', 'jr', 'call', 'ret', 'reti', 'retn', 'rst' }: return if last.inst in {'reti', 'retn'}: if self.next is not None: self.next.delete_comes_from(self) return if self.next is not None and last.condition_flag is None: # jp NNN, call NNN, rst, jr NNNN, ret self.next.delete_comes_from(self) if last.inst == 'ret': return if last.opers[0] not in LABELS.keys(): __DEBUG__( "INFO: %s is not defined. No optimization is done." % last.opers[0], 2) LABELS[last.opers[0]] = LabelInfo( last.opers[0], 0, DummyBasicBlock(ALL_REGS, ALL_REGS)) n_block = LABELS[last.opers[0]].basic_block self.add_goes_to(n_block)
def p_namespace(p): """ asm : NAMESPACE ID """ global NAMESPACE NAMESPACE = normalize_namespace(p[2]) __DEBUG__('Setting namespace to ' + (NAMESPACE or DOT), level=1)
def p_def_label(p): """ line : ID EQU expr NEWLINE | ID EQU pexpr NEWLINE """ p[0] = None __DEBUG__("Declaring '%s%s' in %i" % (NAMESPACE, p[1], p.lineno(1))) MEMORY.declare_label(p[1], p.lineno(1), p[3])
def declare_label(self, label: str, lineno: int, value: int = None, local: bool = False, namespace: Optional[str] = None): """ Sets a label with the given value or with the current address (org) if no value is passed. Exits with error if label already set, otherwise return the label object """ ex_label, namespace = Memory.id_name(label, namespace) is_address = value is None if value is None: value = self.org if is_address: __DEBUG__( f"Declaring '{ex_label}' (value {'%04Xh' % value}) in {lineno}" ) else: __DEBUG__(f"Declaring '{ex_label}' in {lineno}") if ex_label in self.local_labels[-1].keys(): self.local_labels[-1][ex_label].define(value, lineno) self.local_labels[-1][ex_label].is_address = is_address else: self.local_labels[-1][ex_label] = Label(ex_label, lineno, value, local, namespace, is_address) self.set_memory_slot() return self.local_labels[-1][ex_label]
def exit_proc(self, lineno: int): """ Exits current procedure. Local labels are transferred to global scope unless they have been marked as local ones. Raises an error if no current local context (stack underflow) """ __DEBUG__('Exiting current scope from lineno %i' % lineno) if len(self.local_labels) <= 1: error(lineno, 'ENDP in global scope (with no PROC)') return for label in self.local_labels[-1].values(): if label.local: if not label.defined: error(lineno, "Undefined LOCAL label '%s'" % label.name) return continue name = label.name _lineno = label.lineno value = label.value if name not in self.global_labels.keys(): self.global_labels[name] = label else: self.global_labels[name].define(value, _lineno) self.local_labels.pop() # Removes current context self.scopes.pop()
def enter_proc(self, lineno: int): """ Enters (pushes) a new context """ self.local_labels.append({}) # Add a new context self.scopes.append(lineno) __DEBUG__('Entering scope level %i at line %i' % (len(self.scopes), lineno))
def p_LOCAL(p): """ asm : LOCAL id_list """ p[0] = None for label, line in p[2]: __DEBUG__("Setting label '%s' as local at line %i" % (label, line)) MEMORY.set_label(label, line, local=True)
def _visit(self, node): self.norm_attr() if isinstance(node, Symbol): __DEBUG__('Visiting {}'.format(node.token), 1) if node.token in self.ATTR_TMP: return self.visit_ATTR_TMP(node) return TranslatorInstVisitor._visit(self, node)
def _visit(self, node): if node.obj is None: return None __DEBUG__("Optimizer: Visiting node {}".format(str(node.obj)), 1) methname = 'visit_' + node.obj.token meth = getattr(self, methname, None) if meth is None: meth = self.generic_visit return meth(node.obj)
def p_push_namespace(p): """ asm : PUSH NAMESPACE | PUSH NAMESPACE ID """ global NAMESPACE NAMESPACE_STACK.append(NAMESPACE) NAMESPACE = normalize_namespace(p[3] if len(p) == 4 else NAMESPACE) if NAMESPACE != NAMESPACE_STACK[-1]: __DEBUG__('Setting namespace to ' + (NAMESPACE or DOT), level=1)
def add_instruction(self, instr): """ This will insert an asm instruction at the current memory position in a t-uple as (mnemonic, params). It will also insert the opcodes at the memory_bytes """ if gl.has_errors: return __DEBUG__('%04Xh [%04Xh] ASM: %s' % (self.org, self.org - self.ORG, instr.asm)) self.set_memory_slot() self.orgs[self.org] += (instr, ) for byte in instr.bytes(): self.__set_byte(byte, instr.lineno)
def main(args=None, emitter=None): """ Entry point when executed from command line. You can use zxbc.py as a module with import, and this function won't be executed. """ # region [Initialization] config.init() zxbpp.init() zxbparser.init() arch.target.backend.init() arch.target.Translator.reset() asmparse.init() # endregion parser = args_parser.parser() options = parser.parse_args(args=args) # ------------------------------------------------------------ # Setting of internal parameters according to command line # ------------------------------------------------------------ OPTIONS.Debug = options.debug OPTIONS.optimization = options.optimize OPTIONS.outputFileName = options.output_file OPTIONS.StdErrFileName = options.stderr OPTIONS.array_base = options.array_base OPTIONS.string_base = options.string_base OPTIONS.Sinclair = options.sinclair OPTIONS.heap_size = options.heap_size OPTIONS.memoryCheck = options.debug_memory OPTIONS.strictBool = options.strict_bool or OPTIONS.Sinclair OPTIONS.arrayCheck = options.debug_array OPTIONS.emitBackend = options.emit_backend OPTIONS.enableBreak = options.enable_break OPTIONS.explicit = options.explicit OPTIONS.memory_map = options.memory_map OPTIONS.strict = options.strict OPTIONS.headerless = options.headerless OPTIONS.zxnext = options.zxnext OPTIONS.expect_warnings = gl.EXPECTED_WARNINGS = options.expect_warnings OPTIONS.hide_warning_codes = options.hide_warning_codes if options.arch not in arch.AVAILABLE_ARCHITECTURES: parser.error(f"Invalid architecture '{options.arch}'") return 2 OPTIONS.architecture = options.arch arch.set_target_arch(options.arch) backend = arch.target.backend # region [Enable/Disable Warnings] enabled_warnings = set(options.enable_warning or []) disabled_warnings = set(options.disable_warning or []) duplicated_options = [f"W{x}" for x in enabled_warnings.intersection(disabled_warnings)] if duplicated_options: parser.error(f"Warning(s) {', '.join(duplicated_options)} cannot be enabled " f"and disabled simultaneously") return 2 for warn_code in enabled_warnings: errmsg.enable_warning(warn_code) for warn_code in disabled_warnings: errmsg.disable_warning(warn_code) # endregion OPTIONS.org = src.api.utils.parse_int(options.org) if OPTIONS.org is None: parser.error("Invalid --org option '{}'".format(options.org)) if options.defines: for i in options.defines: macro = list(i.split('=', 1)) name = macro[0] val = ''.join(macro[1:]) OPTIONS.__DEFINES[name] = val zxbpp.ID_TABLE.define(name, value=val, lineno=0) if OPTIONS.Sinclair: OPTIONS.array_base = 1 OPTIONS.string_base = 1 OPTIONS.strictBool = True OPTIONS.case_insensitive = True if options.ignore_case: OPTIONS.case_insensitive = True debug.ENABLED = OPTIONS.Debug if int(options.tzx) + int(options.tap) + int(options.asm) + int(options.emit_backend) + \ int(options.parse_only) > 1: parser.error("Options --tap, --tzx, --emit-backend, --parse-only and --asm are mutually exclusive") return 3 if options.basic and not options.tzx and not options.tap: parser.error('Option --BASIC and --autorun requires --tzx or tap format') return 4 if options.append_binary and not options.tzx and not options.tap: parser.error('Option --append-binary needs either --tap or --tzx') return 5 if options.asm and options.memory_map: parser.error('Option --asm and --mmap cannot be used together') return 6 OPTIONS.use_loader = options.basic OPTIONS.autorun = options.autorun if options.tzx: OPTIONS.output_file_type = 'tzx' elif options.tap: OPTIONS.output_file_type = 'tap' elif options.asm: OPTIONS.output_file_type = 'asm' elif options.emit_backend: OPTIONS.output_file_type = 'ic' args = [options.PROGRAM] if not os.path.exists(options.PROGRAM): parser.error("No such file or directory: '%s'" % args[0]) return 2 if OPTIONS.memoryCheck: OPTIONS.__DEFINES['__MEMORY_CHECK__'] = '' zxbpp.ID_TABLE.define('__MEMORY_CHECK__', lineno=0) if OPTIONS.arrayCheck: OPTIONS.__DEFINES['__CHECK_ARRAY_BOUNDARY__'] = '' zxbpp.ID_TABLE.define('__CHECK_ARRAY_BOUNDARY__', lineno=0) if OPTIONS.enableBreak: OPTIONS.__DEFINES['__ENABLE_BREAK__'] = '' zxbpp.ID_TABLE.define('__ENABLE_BREAK__', lineno=0) OPTIONS.include_path = options.include_path OPTIONS.inputFileName = zxbparser.FILENAME = \ os.path.basename(args[0]) if not OPTIONS.outputFileName: OPTIONS.outputFileName = \ os.path.splitext(os.path.basename(OPTIONS.inputFileName))[0] + os.path.extsep + \ OPTIONS.output_file_type if OPTIONS.StdErrFileName: OPTIONS.stderr = open_file(OPTIONS.StdErrFileName, 'wt', 'utf-8') zxbpp.setMode('basic') zxbpp.main(args) if gl.has_errors: debug.__DEBUG__("exiting due to errors.") return 1 # Exit with errors input_ = zxbpp.OUTPUT zxbparser.parser.parse(input_, lexer=zxblex.lexer, tracking=True, debug=(OPTIONS.Debug > 1)) if gl.has_errors: debug.__DEBUG__("exiting due to errors.") return 1 # Exit with errors # Unreachable code removal unreachable_code_visitor = src.api.optimize.UnreachableCodeVisitor() unreachable_code_visitor.visit(zxbparser.ast) # Function calls graph func_call_visitor = src.api.optimize.FunctionGraphVisitor() func_call_visitor.visit(zxbparser.ast) # Optimizations optimizer = src.api.optimize.OptimizerVisitor() optimizer.visit(zxbparser.ast) # Emits intermediate code translator = arch.target.Translator() translator.visit(zxbparser.ast) if gl.DATA_IS_USED: gl.FUNCTIONS.extend(gl.DATA_FUNCTIONS) # This will fill MEMORY with pending functions func_visitor = arch.target.FunctionTranslator(gl.FUNCTIONS) func_visitor.start() # Emits data lines translator.emit_data_blocks() # Emits default constant strings translator.emit_strings() # Emits jump tables translator.emit_jump_tables() # Signals end of user code translator.ic_inline(';; --- end of user code ---') if OPTIONS.emitBackend: with open_file(OPTIONS.outputFileName, 'wt', 'utf-8') as output_file: for quad in translator.dumpMemory(backend.MEMORY): output_file.write(str(quad) + '\n') backend.MEMORY[:] = [] # Empties memory # This will fill MEMORY with global declared variables translator = arch.target.VarTranslator() translator.visit(zxbparser.data_ast) for quad in translator.dumpMemory(backend.MEMORY): output_file.write(str(quad) + '\n') return 0 # Exit success # Join all lines into a single string and ensures an INTRO at end of file asm_output = backend.emit(backend.MEMORY, optimize=OPTIONS.optimization > 0) asm_output = arch.target.optimizer.optimize(asm_output) + '\n' # invoke the -O3 asm_output = asm_output.split('\n') for i in range(len(asm_output)): tmp = backend.ASMS.get(asm_output[i], None) if tmp is not None: asm_output[i] = '\n'.join(tmp) asm_output = '\n'.join(asm_output) # Now filter them against the preprocessor again zxbpp.setMode('asm') zxbpp.OUTPUT = '' zxbpp.filter_(asm_output, args[0]) # Now output the result asm_output = zxbpp.OUTPUT.split('\n') get_inits(asm_output) # Find out remaining inits backend.MEMORY[:] = [] # This will fill MEMORY with global declared variables var_checker = src.api.optimize.VariableVisitor() var_checker.visit(zxbparser.data_ast) translator = arch.target.VarTranslator() translator.visit(zxbparser.data_ast) if gl.has_errors: debug.__DEBUG__("exiting due to errors.") return 1 # Exit with errors tmp = [x for x in backend.emit(backend.MEMORY, optimize=False) if x.strip()[0] != '#'] asm_output = backend.emit_start() + tmp \ + ['%s:' % backend.DATA_END_LABEL, '%s:' % backend.MAIN_LABEL] \ + asm_output + backend.emit_end() if options.asm: # Only output assembler file with open_file(OPTIONS.outputFileName, 'wt', 'utf-8') as output_file: output(asm_output, output_file) elif not options.parse_only: fout = StringIO() output(asm_output, fout) asmparse.assemble(fout.getvalue()) fout.close() asmparse.generate_binary(OPTIONS.outputFileName, OPTIONS.output_file_type, binary_files=options.append_binary, headless_binary_files=options.append_headless_binary, emitter=emitter) if gl.has_errors: return 5 # Error in assembly if OPTIONS.memory_map: if asmparse.MEMORY is not None: with open_file(OPTIONS.memory_map, 'wt', 'utf-8') as f: f.write(asmparse.MEMORY.memory_map) return gl.has_errors # Exit success
def optimize(initial_memory): """ This will remove useless instructions """ global BLOCKS global PROC_COUNTER del MEMORY[:] PROC_COUNTER = 0 cleanupmem(initial_memory) if OPTIONS.optimization <= 2: # if -O2 or lower, do nothing and return return '\n'.join(x for x in initial_memory if not RE_PRAGMA.match(x)) basicblock.BasicBlock.clean_asm_args = OPTIONS.optimization > 3 bb = basicblock.BasicBlock(initial_memory) cleanup_local_labels(bb) initialize_memory(bb) BLOCKS = basic_blocks = basicblock.get_basic_blocks( bb) # 1st partition the Basic Blocks for b in basic_blocks: __DEBUG__('--- BASIC BLOCK: {} ---'.format(b.id), 1) __DEBUG__('Code:\n' + '\n'.join(' {}'.format(x) for x in b.code), 1) __DEBUG__('Requires: {}'.format(b.requires()), 1) __DEBUG__('Destroys: {}'.format(b.destroys()), 1) __DEBUG__('Label goes: {}'.format(b.label_goes), 1) __DEBUG__('Comes from: {}'.format([x.id for x in b.comes_from]), 1) __DEBUG__('Goes to: {}'.format([x.id for x in b.goes_to]), 1) __DEBUG__('Next: {}'.format(b.next.id if b.next is not None else None), 1) __DEBUG__('Size: {} Time: {}'.format(b.sizeof, b.max_tstates), 1) __DEBUG__('--- END ---', 1) LABELS['*START*'].basic_block.add_goes_to(basic_blocks[0]) LABELS['*START*'].basic_block.next = basic_blocks[0] basic_blocks[0].prev = LABELS['*START*'].basic_block if END_PROGRAM_LABEL in LABELS: LABELS[END_PROGRAM_LABEL].basic_block.add_goes_to( LABELS['*__END_PROGRAM*'].basic_block) # In O3 we simplify the graph by reducing jumps over jumps for label in JUMP_LABELS: block = LABELS[label].basic_block if isinstance(block, DummyBasicBlock): continue # The instruction that starts this block must be one of jr / jp first = block.get_next_exec_instruction() if first is None or first.inst not in ('jp', 'jr'): continue for blk in list(LABELS[label].used_by): if not first.condition_flag or blk[ -1].condition_flag == first.condition_flag: new_label = first.opers[0] blk[-1].asm = blk[-1].code.replace(label, new_label) block.delete_comes_from(blk) LABELS[label].used_by.remove(blk) LABELS[new_label].used_by.add(blk) blk.add_goes_to(LABELS[new_label].basic_block) for x in basic_blocks: x.compute_cpu_state() filtered_patterns_list = [ p for p in engine.PATTERNS if OPTIONS.optimization >= p.level >= 3 ] for x in basic_blocks: x.optimize(filtered_patterns_list) for x in basic_blocks: if x.comes_from == [] and len( [y for y in JUMP_LABELS if x is LABELS[y].basic_block]): x.ignored = True return '\n'.join([ y for y in flatten_list([x.code for x in basic_blocks if not x.ignored]) if not RE_PRAGMA.match(y) ])
def emit(*args): """ Convert the given args to a Quad (3 address code) instruction """ quad = Quad(*args) __DEBUG__('EMIT ' + str(quad)) MEMORY.append(quad)
def main(args=None, emitter=None): """ Entry point when executed from command line. zxbc can be used as python module. If so, bear in mind this function won't be executed unless explicitly called. """ # region [Initialization] config.init() zxbpp.init() zxbparser.init() arch.target.backend.init() arch.target.Translator.reset() asmparse.init() # endregion options = parse_options(args) arch.set_target_arch(OPTIONS.architecture) backend = arch.target.backend args = [ options.PROGRAM ] # Strip out other options, because they're already set in the OPTIONS container input_filename = options.PROGRAM zxbpp.setMode('basic') zxbpp.main(args) if gl.has_errors: debug.__DEBUG__("exiting due to errors.") return 1 # Exit with errors input_ = zxbpp.OUTPUT zxbparser.parser.parse(input_, lexer=zxblex.lexer, tracking=True, debug=(OPTIONS.debug_level > 1)) if gl.has_errors: debug.__DEBUG__("exiting due to errors.") return 1 # Exit with errors # Unreachable code removal unreachable_code_visitor = src.api.optimize.UnreachableCodeVisitor() unreachable_code_visitor.visit(zxbparser.ast) # Function calls graph func_call_visitor = src.api.optimize.FunctionGraphVisitor() func_call_visitor.visit(zxbparser.ast) # Optimizations optimizer = src.api.optimize.OptimizerVisitor() optimizer.visit(zxbparser.ast) # Emits intermediate code translator = arch.target.Translator() translator.visit(zxbparser.ast) if gl.DATA_IS_USED: gl.FUNCTIONS.extend(gl.DATA_FUNCTIONS) # This will fill MEMORY with pending functions func_visitor = arch.target.FunctionTranslator(gl.FUNCTIONS) func_visitor.start() # Emits data lines translator.emit_data_blocks() # Emits default constant strings translator.emit_strings() # Emits jump tables translator.emit_jump_tables() # Signals end of user code translator.ic_inline(';; --- end of user code ---') if OPTIONS.emit_backend: with open_file(OPTIONS.output_filename, 'wt', 'utf-8') as output_file: for quad in translator.dumpMemory(backend.MEMORY): output_file.write(str(quad) + '\n') backend.MEMORY[:] = [] # Empties memory # This will fill MEMORY with global declared variables translator = arch.target.VarTranslator() translator.visit(zxbparser.data_ast) for quad in translator.dumpMemory(backend.MEMORY): output_file.write(str(quad) + '\n') return 0 # Exit success # Join all lines into a single string and ensures an INTRO at end of file asm_output = backend.emit(backend.MEMORY, optimize=OPTIONS.optimization_level > 0) asm_output = arch.target.optimizer.optimize( asm_output) + '\n' # invoke the -O3 asm_output = asm_output.split('\n') for i in range(len(asm_output)): tmp = backend.ASMS.get(asm_output[i], None) if tmp is not None: asm_output[i] = '\n'.join(tmp) asm_output = '\n'.join(asm_output) # Now filter them against the preprocessor again zxbpp.setMode('asm') zxbpp.OUTPUT = '' zxbpp.filter_(asm_output, filename=input_filename) # Now output the result asm_output = zxbpp.OUTPUT.split('\n') get_inits(asm_output) # Find out remaining inits backend.MEMORY[:] = [] # This will fill MEMORY with global declared variables var_checker = src.api.optimize.VariableVisitor() var_checker.visit(zxbparser.data_ast) translator = arch.target.VarTranslator() translator.visit(zxbparser.data_ast) if gl.has_errors: debug.__DEBUG__("exiting due to errors.") return 1 # Exit with errors tmp = [ x for x in backend.emit(backend.MEMORY, optimize=False) if x.strip()[0] != '#' ] asm_output = backend.emit_start() + tmp \ + ['%s:' % backend.DATA_END_LABEL, '%s:' % backend.MAIN_LABEL] \ + asm_output + backend.emit_end() if OPTIONS.output_file_type == FileType.ASM: # Only output assembler file with open_file(OPTIONS.output_filename, 'wt', 'utf-8') as output_file: output(asm_output, output_file) elif not options.parse_only: fout = StringIO() output(asm_output, fout) asmparse.assemble(fout.getvalue()) fout.close() asmparse.generate_binary( OPTIONS.output_filename, OPTIONS.output_file_type, binary_files=options.append_binary, headless_binary_files=options.append_headless_binary, emitter=emitter) if gl.has_errors: return 5 # Error in assembly if OPTIONS.memory_map: if asmparse.MEMORY is not None: with open_file(OPTIONS.memory_map, 'wt', 'utf-8') as f: f.write(asmparse.MEMORY.memory_map) if not gl.has_errors and options.save_config: src.api.config.save_config_into_file( options.save_config, src.api.config.ConfigSections.ZXBC) return gl.has_errors # Exit success
def get_basic_blocks(block): """ If a block is not partitionable, returns a list with the same block. Otherwise, returns a list with the resulting blocks, recursively. """ result = [] EDP = END_PROGRAM_LABEL + ':' new_block = block while new_block: block = new_block new_block = None for i, mem in enumerate(block): if i and mem.code == EDP: # END_PROGRAM label always starts a basic block block, new_block = block_partition(block, i - 1) LABELS[END_PROGRAM_LABEL].basic_block = new_block break if mem.is_ender: block, new_block = block_partition(block, i) if not mem.condition_flag: block.delete_goes_to(new_block) for l in mem.opers: if l in LABELS: JUMP_LABELS.add(l) block.label_goes.append(l) break if mem.is_label and mem.code[:-1] not in LABELS: raise OptimizerError( "Missing label '{}' in labels list".format(mem.code[:-1])) if mem.code in src.arch.zx48k.backend.ASMS: # An inline ASM block block, new_block = block_partition(block, max(0, i - 1)) break result.append(block) for label in JUMP_LABELS: blk = LABELS[label].basic_block if isinstance(blk, DummyBasicBlock): continue must_partition = False # This label must point to the beginning of blk, just before the code # Otherwise we must partition it (must_partition = True) for i, cell in enumerate(blk): if cell.inst == label: break # already starts with this label if cell.is_label: continue # It's another label if cell.is_ender: raise OptimizerInvalidBasicBlockError(blk) must_partition = True else: __DEBUG__("Label {} not found in BasicBlock {}".format( label, blk.id)) continue if must_partition: j = result.index(blk) block_, new_block_ = block_partition(blk, i - 1) LABELS[label].basic_block = new_block_ result.pop(j) result.insert(j, block_) result.insert(j + 1, new_block_) for b in result: b.update_goes_and_comes() return result
def update_goes_and_comes(self): """ Once the block is a Basic one, check the last instruction and updates goes_to and comes_from set of the receivers. Note: jp, jr and ret are already done in update_next_block() """ if not len(self): return last = self.mem[-1] inst = last.inst oper = last.opers cond = last.condition_flag if not last.is_ender: return if cond is None: self.delete_goes_to(self.next) if last.inst in {'ret', 'reti', 'retn'} and cond is None: return # subroutine returns are updated from CALLer blocks if oper and oper[0]: if oper[0] not in LABELS: __DEBUG__( "INFO: %s is not defined. No optimization is done." % oper[0], 1) LABELS[oper[0]] = LabelInfo( oper[0], 0, DummyBasicBlock(ALL_REGS, ALL_REGS)) LABELS[oper[0]].used_by.add(self) self.add_goes_to(LABELS[oper[0]].basic_block) if inst in {'djnz', 'jp', 'jr'}: return assert inst in ('call', 'rst') if self.next is None: raise OptimizerError("Unexpected NULL next block") final_blk = self.next # The block all the final returns should go to stack = [LABELS[oper[0]].basic_block] bbset = IdentitySet() while stack: bb = stack.pop(0) while True: if bb is None: bb = DummyBasicBlock(ALL_REGS, ALL_REGS) if bb in bbset: break bbset.add(bb) if isinstance(bb, DummyBasicBlock): bb.add_goes_to(final_blk) break if bb: bb1 = bb[-1] if bb1.inst in {'ret', 'reti', 'retn'}: bb.add_goes_to(final_blk) if bb1.condition_flag is None: # 'ret' break elif bb1.inst in ( 'jp', 'jr' ) and bb1.condition_flag is not None: # jp/jr nc/nz/.. LABEL if bb1.opers[ 0] in LABELS: # some labels does not exist (e.g. immediate numeric addresses) stack.append(LABELS[bb1.opers[0]].basic_block) else: raise OptimizerError( "Unknown block label '{}'".format( bb1.opers[0])) bb = bb.next # next contiguous block
def __call__(self, table, macro: MacroCall) -> str: __DEBUG__("evaluating id '%s'" % self.name, DEBUG_LEVEL) if self.value is None: __DEBUG__("undefined (null) value. BUG?", DEBUG_LEVEL) return '' if self.evaluating: result = self.name if self.hasArgs: result += '(' result += ', '.join( table[arg.name] (table, self) if isinstance(arg, ID) else str(arg) for arg in self.args) result += ')' return result self.evaluating = True result = '' for token in self.value: __DEBUG__("evaluating token '%s'" % str(token), DEBUG_LEVEL) if isinstance(token, MacroCall): if isinstance(token.id_, MacroCall): token.id_ = token.id_(table) __DEBUG__( "token '%s'(%s) is a MacroCall" % (token.id_, str(token)), DEBUG_LEVEL) if table.defined(token.id_): tmp = table[token.id_] __DEBUG__( "'%s' is defined in the symbol table as '%s'" % (token.id_, tmp.name), DEBUG_LEVEL) if isinstance(tmp, ID) and not tmp.hasArgs: __DEBUG__("'%s' is an ID" % tmp.name, DEBUG_LEVEL) token = copy.deepcopy(token) token.id_ = tmp(table, macro) __DEBUG__("'%s' is the new id" % token.id_, DEBUG_LEVEL) __DEBUG__("executing MacroCall '%s'" % token.id_, DEBUG_LEVEL) tmp = token(table) else: if isinstance(token, ID): __DEBUG__("token '%s' is an ID" % token.name, DEBUG_LEVEL) token = token(table, macro) tmp = token __DEBUG__("token got value '%s'" % tmp, DEBUG_LEVEL) result += tmp self.evaluating = False return result
def p_asm_label(p): """ asm : ID """ __DEBUG__("Declaring '%s%s' (value %04Xh) in %i" % (NAMESPACE, p[1], MEMORY.org, p.lineno(1))) MEMORY.declare_label(p[1], p.lineno(1))
def visit_BLOCK(self, node): __DEBUG__('BLOCK', 2) for child in node.children: yield child
def __call__(self, symbolTable: 'prepro.DefinesTable' = None) -> str: """ Execute the macro call using LAZY evaluation """ if isinstance(self.id_, MacroCall): self.id_ = self.id_() __DEBUG__("evaluating '%s'" % self.id_, DEBUG_LEVEL) if symbolTable is None: symbolTable = self.table # The macro is not defined => returned as is if not self.is_defined(symbolTable): __DEBUG__("macro '%s' not defined" % self.id_, DEBUG_LEVEL) tmp = self.id_ if self.callargs is not None: tmp += str(self.callargs) __DEBUG__("evaluation result: %s" % tmp, DEBUG_LEVEL) return tmp # The macro is defined __DEBUG__("macro '%s' defined" % self.id_, DEBUG_LEVEL) table = copy.deepcopy(symbolTable) id_ = table[self.id_] # Get the defined macro assert isinstance(id_, prepro.ID) if id_.hasArgs and self.callargs is None: return self.id_ # If no args passed, returned as is args = [] if self.callargs: # has args. Evaluate them removing spaces __DEBUG__("'%s' has args defined" % self.id_, DEBUG_LEVEL) __DEBUG__( "evaluating %i arg(s) for '%s'" % (len(self.callargs), self.id_), DEBUG_LEVEL) args = [x(table).strip() for x in self.callargs] __DEBUG__( "macro call: %s%s" % (self.id_, '(' + ', '.join(args) + ')'), DEBUG_LEVEL) if not id_.hasArgs: # The macro doesn't need args __DEBUG__("'%s' has no args defined" % self.id_, DEBUG_LEVEL) tmp = id_(table) # If no args passed, returned as is if self.callargs is not None: tmp += '(' + ', '.join(args) + ')' __DEBUG__("evaluation result: %s" % tmp, DEBUG_LEVEL) return tmp # Now ensure both args and callargs have the same length if len(self.callargs) != len(id_.args): raise PreprocError( 'Macro "%s" expected %i params, got %i' % (str(self.id_), len(id_.args), len(self.callargs)), self.lineno) # Carry out unification __DEBUG__('carrying out args unification', DEBUG_LEVEL) for i in range(len(self.callargs)): __DEBUG__("arg '%s' = '%s'" % (id_.args[i].name, args[i]), DEBUG_LEVEL) table.set(id_.args[i].name, self.lineno, args[i]) tmp = id_(table) return tmp