Exemplo n.º 1
0
def replace_shortcut_ops(instructions):
    """Replace opcodes with a corresponding shortcut form."""
    optimizations = []
    # Replace division by 2.
    # OP_2 OP_DIV -> OP_2DIV
    optimizations.append(([types.Two(), types.Div()], lambda values: [types.Div2()]))
    # Replace subtraction by 1.
    # OP_1 OP_SUB -> OP_1SUB
    optimizations.append(([types.One(), types.Sub()], lambda values: [types.Sub1()]))
    # Replace 1 * -1 with -1.
    # OP_1 OP_NEGATE -> OP_1NEGATE
    optimizations.append(([types.One(), types.Negate()], lambda values: [types.NegativeOne()]))
    # Replace addition by 1.
    # OP_1 OP_ADD -> OP_1ADD
    for permutation in permutations([types.Push(), types.One()]) + permutations([types.SmallIntOpCode(), types.One()]):
        idx = 0 if not isinstance(permutation[0], types.One) else 1
        optimizations.append((permutation + [types.Add()], lambda values, idx=idx: [values[idx], types.Add1()]))
    optimizations.append(([types.Assumption(), types.One(), types.Add()], lambda values: [values[0], types.Add1()]))
    # Replace multiplication by 2.
    # OP_2 OP_MUL -> OP_2MUL
    for permutation in permutations([types.Push(), types.Two()]) + permutations([types.SmallIntOpCode(), types.Two()]):
        idx = 0 if not isinstance(permutation[0], types.Two) else 1
        optimizations.append((permutation + [types.Mul()], lambda values, idx=idx: [values[idx], types.Mul2()]))
    optimizations.append(([types.Assumption(), types.Two(), types.Mul()], lambda values: [values[0], types.Mul2()]))


    for template, callback in optimizations:
        instructions.replace_template(template, callback, strict=False)
Exemplo n.º 2
0
    def test_shortcut_ops(self):
        for script in [
            [types.Five(), types.One(), types.Add()],
            [types.One(), types.Five(), types.Add()],
        ]:
            self._do_test('OP_5 OP_1ADD', script)

        self._do_test('OP_1ADD', [types.Assumption('testItem'), types.One(), types.Add()])

        script = [types.Five(), types.One(), types.Sub()]
        self._do_test('OP_5 OP_1SUB', script)

        for script in [
            [types.Five(), types.Two(), types.Mul()],
            [types.Two(), types.Five(), types.Mul()],
        ]:
            self._do_test('OP_5 OP_2MUL', script)

        self._do_test('OP_2MUL', [types.Assumption('testItem'), types.Two(), types.Mul()])

        script = [types.Five(), types.Two(), types.Div()]
        self._do_test('OP_5 OP_2DIV', script)

        script = [types.Five(), types.One(), types.Negate()]
        self._do_test('OP_5 OP_1NEGATE', script)
Exemplo n.º 3
0
    def test_assume_to_pick_or_roll(self):
        script = [types.Five(), types.Assumption('testItem'), types.Add()]
        self._do_test('OP_5 OP_ADD', script)

        self._reset_table(['testItem'])
        script = [types.Five(), types.Five(), types.Assumption('testItem'), types.Add()]
        self._do_test('OP_5 OP_5 OP_2 OP_ROLL OP_ADD', script)

        self._reset_table(['testItem'])
        script = [types.Five(), types.Five(), types.Assumption('testItem'), types.Add(), types.Assumption('testItem')]
        self._do_test('OP_5 OP_5 OP_2 OP_PICK OP_ADD OP_2 OP_ROLL', script)
Exemplo n.º 4
0
    def find_duplicate_assumptions(self):
        """Find operations which use the same assumption more than once."""
        base = [types.Assumption(), types.Assumption()]
        templates = []
        # Collect opcodes that use the last two stack items as arguments.
        for opcode in types.get_opcodes().values():
            if not issubclass(opcode, types.OpCode) or not opcode.args:
                continue
            if all(arg_idx in opcode.args for arg_idx in [1, 2]):
                templates.append(base + [opcode()])

        for i in range(len(self.instructions)):
            for template in templates:
                if not self.instructions.matches_template(
                        template, i, strict=False):
                    continue
                ops = self.instructions[i:i + len(template)]
                # Continue if the assumptions aren't of the same value.
                if ops[0].var_name != ops[1].var_name:
                    continue
                # Append the negative index of the operation to duplicated_assumptions[assumption_name].
                self.duplicated_assumptions[ops[0].var_name].append(
                    -1 * (len(self.instructions) - ops[-1].idx))
Exemplo n.º 5
0
    def inline(self, instructions, peephole_optimizer):
        """Perform inlining of variables in instructions.

        Inlining is performed by iterating through each instruction and
        calling visitor methods. If no result is returned, the next
        instruction is visited.

        If there is a result, the instruction is replaced with that result,
        and the iteration begins again.

        Inlining ends when all instructions have been iterated over without
        any result.
        """
        if not isinstance(instructions, LInstructions):
            raise TypeError('A LInstructions instance is required')
        self.instructions = instructions
        self.stack.clear(clear_assumptions=True)

        stack_names = self.symbol_table.lookup('_stack_names')
        if stack_names:
            self.stack.add_stack_assumptions(
                [types.Assumption(var_name) for var_name in stack_names.value])

        # Find operations that use the same assumed stack item more than once.
        self.contextualizer.contextualize(instructions)
        self.contextualizer.find_duplicate_assumptions()

        # Loop until no inlining can be done.
        while 1:
            self.stack.clear(clear_assumptions=False)
            peephole_optimizer.optimize(instructions)
            self.contextualizer.contextualize(instructions)
            inlined = False
            for i, node in enumerate(instructions):
                result = self.visit(node)
                if result is None:
                    continue
                if not isinstance(result, list):
                    result = [result]

                instructions.replace_slice(i, i + 1, result)
                inlined = True
                break

            if not inlined:
                break
Exemplo n.º 6
0
    def visit_Symbol(self, node):
        self.require_symbol_table('process symbol')

        symbol = self.symbol_table.lookup(node.name)
        if not symbol:
            raise IRError('Symbol "%s" was not declared.' % node.name)

        value = symbol.value
        type_ = symbol.type_
        # Add an assumption for the stack item.
        if type_ == SymbolType.StackItem:
            # Fail if there are assumptions after a conditional and the conditional branches do not result in the
            # same number of stack items.
            if self.after_uneven_conditional:
                raise IRError("Conditional branches must result in the same number of stack values, or assumptions afterward are not supported.")
            return types.Assumption(symbol.name)
        # Push the bytes of the byte array.
        elif type_ in [SymbolType.ByteArray, SymbolType.Integer]:
            return self.visit(value)
        # If the type is an expression, then StructuralOptimizer could not simplify it.
        # Evaluate the expression as if it were encountered in the structural IR.
        elif type_ == SymbolType.Expr:
            return self.visit(value)
Exemplo n.º 7
0
 def test_implicit_assume(self):
     script = [types.Assumption('testItem'), types.Five(), types.Add()]
     self._do_test('OP_5 OP_ADD', script)