Пример #1
0
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])
Пример #2
0
def p_namespace(p):
    """ asm : NAMESPACE ID
    """
    global NAMESPACE

    NAMESPACE = normalize_namespace(p[2])
    __DEBUG__('Setting namespace to ' + (NAMESPACE.rstrip(DOT) or DOT), level=1)
Пример #3
0
    def exit_proc(self, lineno):
        """ 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()
Пример #4
0
def p_line_label_asm(p):
    """ line : LABEL asms NEWLINE
    """
    p[0] = p[2]
    __DEBUG__("Declaring '%s%s' (value %04Xh) in %i" %
              (NAMESPACE, p[1], MEMORY.org, p.lineno(1)))
    MEMORY.declare_label(p[1], p.lineno(1))
Пример #5
0
def p_program_line(p):
    """ program : program line
    """
    if p[2] is not None:
        __DEBUG__('%04Xh [%04Xh] ASM: %s' %
                  (MEMORY.org, MEMORY.org - MEMORY.ORG, p[2].asm))
        MEMORY.add_instruction(p[2])
Пример #6
0
    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)
Пример #7
0
 def enter_proc(self, lineno):
     """ 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))
Пример #8
0
def p_line_label(p):
    """ line : LABEL NEWLINE
    """
    p[0] = None  # Nothing to append
    __DEBUG__("Declaring '%s%s' (value %04Xh) in %i" %
              (NAMESPACE, p[1], MEMORY.org, p.lineno(1)))

    MEMORY.declare_label(p[1], p.lineno(1))
Пример #9
0
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)
Пример #10
0
    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)
Пример #11
0
    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)
Пример #12
0
    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)
Пример #13
0
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))
Пример #14
0
 def visit_BLOCK(self, node):
     __DEBUG__('BLOCK', 2)
     for child in node.children:
         yield child
Пример #15
0
 def emit(*args):
     """ Convert the given args to a Quad (3 address code) instruction
     """
     quad = Quad(*args)
     __DEBUG__('EMIT ' + str(quad))
     MEMORY.append(quad)
Пример #16
0
def main(args=None, emitter=None):
    """ Entry point when executed from command line.
    You can use zxb.py as a module with import, and this
    function won't be executed.
    """
    api.config.init()
    zxbpp.init()
    zxbparser.init()
    arch.zx48k.backend.init()
    arch.zx48k.Translator.reset()
    asmparse.init()

    # ------------------------------------------------------------
    # Command line parsing
    # ------------------------------------------------------------
    parser = argparse.ArgumentParser()
    parser.add_argument('PROGRAM', type=str, help='BASIC program file')
    parser.add_argument(
        '-d',
        '--debug',
        dest='debug',
        default=OPTIONS.Debug.value,
        action='count',
        help=
        'Enable verbosity/debugging output. Additional -d increase verbosity/debug level'
    )
    parser.add_argument('-O',
                        '--optimize',
                        type=int,
                        default=OPTIONS.optimization.value,
                        help='Sets optimization level. '
                        '0 = None (default level is {0})'.format(
                            OPTIONS.optimization.value))
    parser.add_argument(
        '-o',
        '--output',
        type=str,
        dest='output_file',
        default=None,
        help='Sets output file. Default is input filename with .bin extension')
    parser.add_argument('-T',
                        '--tzx',
                        action='store_true',
                        help="Sets output format to tzx (default is .bin)")
    parser.add_argument('-t',
                        '--tap',
                        action='store_true',
                        help="Sets output format to tap (default is .bin)")
    parser.add_argument(
        '-B',
        '--BASIC',
        action='store_true',
        dest='basic',
        help=
        "Creates a BASIC loader which loads the rest of the CODE. Requires -T ot -t"
    )
    parser.add_argument('-a',
                        '--autorun',
                        action='store_true',
                        help="Sets the program to be run once loaded")
    parser.add_argument('-A',
                        '--asm',
                        action='store_true',
                        help="Sets output format to asm")
    parser.add_argument('-S',
                        '--org',
                        type=str,
                        default=str(OPTIONS.org.value),
                        help="Start of machine code. By default %i" %
                        OPTIONS.org.value)
    parser.add_argument(
        '-e',
        '--errmsg',
        type=str,
        dest='stderr',
        default=OPTIONS.StdErrFileName.value,
        help='Error messages file (standard error console by default)')
    parser.add_argument(
        '--array-base',
        type=int,
        default=OPTIONS.array_base.value,
        help='Default lower index for arrays ({0} by default)'.format(
            OPTIONS.array_base.value))
    parser.add_argument(
        '--string-base',
        type=int,
        default=OPTIONS.string_base.value,
        help='Default lower index for strings ({0} by default)'.format(
            OPTIONS.array_base.value))
    parser.add_argument(
        '-Z',
        '--sinclair',
        action='store_true',
        help=
        'Enable by default some more original ZX Spectrum Sinclair BASIC features: ATTR, SCREEN$, '
        'POINT')
    parser.add_argument(
        '-H',
        '--heap-size',
        type=int,
        default=OPTIONS.heap_size.value,
        help='Sets heap size in bytes (default {0} bytes)'.format(
            OPTIONS.heap_size.value))
    parser.add_argument('--debug-memory',
                        action='store_true',
                        help='Enables out-of-memory debug')
    parser.add_argument('--debug-array',
                        action='store_true',
                        help='Enables array boundary checking')
    parser.add_argument('--strict-bool',
                        action='store_true',
                        help='Enforce boolean values to be 0 or 1')
    parser.add_argument('--enable-break',
                        action='store_true',
                        help='Enables program execution BREAK detection')
    parser.add_argument('-E',
                        '--emit-backend',
                        action='store_true',
                        help='Emits backend code instead of ASM or binary')
    parser.add_argument(
        '--explicit',
        action='store_true',
        help='Requires all variables and functions to be declared before used')
    parser.add_argument(
        '-D',
        '--define',
        type=str,
        dest='defines',
        action='append',
        help='Defines de given macro. Eg. -D MYDEBUG or -D NAME=Value')
    parser.add_argument('-M',
                        '--mmap',
                        type=str,
                        dest='memory_map',
                        default=None,
                        help='Generate label memory map')
    parser.add_argument(
        '-i',
        '--ignore-case',
        action='store_true',
        help='Ignore case. Makes variable names are case insensitive')
    parser.add_argument(
        '-I',
        '--include-path',
        type=str,
        default='',
        help=
        'Add colon separated list of directories to add to include path. e.g. -I dir1:dir2'
    )
    parser.add_argument(
        '--strict',
        action='store_true',
        help='Enables strict mode. Force explicit type declaration')
    parser.add_argument(
        '--headerless',
        action='store_true',
        help='Header-less mode: omit asm prologue and epilogue')
    parser.add_argument('--version',
                        action='version',
                        version='%(prog)s {0}'.format(VERSION))
    parser.add_argument(
        '--parse-only',
        action='store_true',
        help='Only parses to check for syntax and semantic errors')
    parser.add_argument(
        '--append-binary',
        default=[],
        action='append',
        help='Appends binary to tape file (only works with -t or -T)')
    parser.add_argument(
        '--append-headless-binary',
        default=[],
        action='append',
        help='Appends binary to tape file (only works with -t or -T)')
    parser.add_argument('-N',
                        '--zxnext',
                        action='store_true',
                        help='Enables ZX Next asm extended opcodes')

    options = parser.parse_args(args=args)

    # ------------------------------------------------------------
    # Setting of internal parameters according to command line
    # ------------------------------------------------------------

    OPTIONS.Debug.value = options.debug
    OPTIONS.optimization.value = options.optimize
    OPTIONS.outputFileName.value = options.output_file
    OPTIONS.StdErrFileName.value = options.stderr
    OPTIONS.array_base.value = options.array_base
    OPTIONS.string_base.value = options.string_base
    OPTIONS.Sinclair.value = options.sinclair
    OPTIONS.heap_size.value = options.heap_size
    OPTIONS.memoryCheck.value = options.debug_memory
    OPTIONS.strictBool.value = options.strict_bool or OPTIONS.Sinclair.value
    OPTIONS.arrayCheck.value = options.debug_array
    OPTIONS.emitBackend.value = options.emit_backend
    OPTIONS.enableBreak.value = options.enable_break
    OPTIONS.explicit.value = options.explicit
    OPTIONS.memory_map.value = options.memory_map
    OPTIONS.strict.value = options.strict
    OPTIONS.headerless.value = options.headerless
    OPTIONS.zxnext.value = options.zxnext

    OPTIONS.org.value = api.utils.parse_int(options.org)
    if OPTIONS.org.value 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.value[name] = val
            zxbpp.ID_TABLE.define(name, value=val, lineno=0)

    if OPTIONS.Sinclair.value:
        OPTIONS.array_base.value = 1
        OPTIONS.string_base.value = 1
        OPTIONS.strictBool.value = True
        OPTIONS.case_insensitive.value = True

    if options.ignore_case:
        OPTIONS.case_insensitive.value = True

    debug.ENABLED = OPTIONS.Debug.value

    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.value = options.basic
    OPTIONS.autorun.value = options.autorun

    if options.tzx:
        OPTIONS.output_file_type.value = 'tzx'
    elif options.tap:
        OPTIONS.output_file_type.value = 'tap'
    elif options.asm:
        OPTIONS.output_file_type.value = 'asm'
    elif options.emit_backend:
        OPTIONS.output_file_type.value = '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.value:
        OPTIONS.__DEFINES.value['__MEMORY_CHECK__'] = ''
        zxbpp.ID_TABLE.define('__MEMORY_CHECK__', lineno=0)

    if OPTIONS.arrayCheck.value:
        OPTIONS.__DEFINES.value['__CHECK_ARRAY_BOUNDARY__'] = ''
        zxbpp.ID_TABLE.define('__CHECK_ARRAY_BOUNDARY__', lineno=0)

    if OPTIONS.enableBreak.value:
        OPTIONS.__DEFINES.value['__ENABLE_BREAK__'] = ''
        zxbpp.ID_TABLE.define('__ENABLE_BREAK__', lineno=0)

    OPTIONS.include_path.value = options.include_path
    OPTIONS.inputFileName.value = zxbparser.FILENAME = \
        os.path.basename(args[0])

    if not OPTIONS.outputFileName.value:
        OPTIONS.outputFileName.value = \
            os.path.splitext(os.path.basename(OPTIONS.inputFileName.value))[0] + os.path.extsep + \
            OPTIONS.output_file_type.value

    if OPTIONS.StdErrFileName.value:
        OPTIONS.stderr.value = open_file(OPTIONS.StdErrFileName.value, '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.value > 1))
    if gl.has_errors:
        debug.__DEBUG__("exiting due to errors.")
        return 1  # Exit with errors

    # Optimizations
    optimizer = api.optimize.OptimizerVisitor()
    optimizer.visit(zxbparser.ast)

    # Emits intermediate code
    translator = arch.zx48k.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.zx48k.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()

    if OPTIONS.emitBackend.value:
        with open_file(OPTIONS.outputFileName.value, '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.zx48k.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.value > 0)
    asm_output = 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
    translator = arch.zx48k.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 += tmp
    asm_output = backend.emit_start() + asm_output
    asm_output += backend.emit_end()

    if options.asm:  # Only output assembler file
        with open_file(OPTIONS.outputFileName.value, '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.value,
            OPTIONS.output_file_type.value,
            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.value:
        if asmparse.MEMORY is not None:
            with open_file(OPTIONS.memory_map.value, 'wt', 'utf-8') as f:
                f.write(asmparse.MEMORY.memory_map)

    return gl.has_errors  # Exit success
Пример #17
0
    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:
                    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
Пример #18
0
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 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
Пример #19
0
    def __call__(self, table):
        __DEBUG__("evaluating id '%s'" % self.name, DEBUG_LEVEL)
        if self.value is None:
            __DEBUG__("undefined (null) value. BUG?", DEBUG_LEVEL)
            return ''

        result = ''
        for token in self.value:
            __DEBUG__("evaluating token '%s'" % str(token), DEBUG_LEVEL)
            if isinstance(token, MacroCall):
                __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)
                        __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.id_, DEBUG_LEVEL)
                    token = token(table)
                tmp = token

            __DEBUG__("token got value '%s'" % tmp, DEBUG_LEVEL)
            result += tmp

        return result
Пример #20
0
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.value <= 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.value > 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
    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.value >= 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)
    ])
Пример #21
0
    def __call__(self, symbolTable=None):
        """ Execute the macro call using LAZY evaluation
        """
        __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
        if ID.hasArgs and self.callargs is None:
            return self.id_  # If no args passed, returned as is

        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)
        if '\n' in tmp:
            tmp += '\n#line %i\n' % self.lineno

        return tmp