예제 #1
0
def optimize(node: LLLnode) -> LLLnode:
    argz = [optimize(arg) for arg in node.args]
    if node.value in arith and int_at(argz, 0) and int_at(argz, 1):
        left, right = get_int_at(argz, 0), get_int_at(argz, 1)
        calcer, symb = arith[node.value]
        new_value = calcer(left, right)
        if argz[0].annotation and argz[1].annotation:
            annotation = argz[0].annotation + symb + argz[1].annotation
        elif argz[0].annotation or argz[1].annotation:
            annotation = (argz[0].annotation or str(left)) + symb + (
                argz[1].annotation or str(right))
        else:
            annotation = ''
        return LLLnode(
            new_value,
            [],
            node.typ,
            None,
            node.pos,
            annotation,
            add_gas_estimate=node.add_gas_estimate,
            valency=node.valency,
        )
    elif _is_constant_add(node, argz):
        calcer, symb = arith[node.value]
        if argz[0].annotation and argz[1].args[0].annotation:
            annotation = argz[0].annotation + symb + argz[1].args[0].annotation
        elif argz[0].annotation or argz[1].args[0].annotation:
            annotation = (argz[0].annotation or str(argz[0].value)) + symb + (
                argz[1].args[0].annotation or str(argz[1].args[0].value))
        else:
            annotation = ''
        return LLLnode(
            "add",
            [
                LLLnode(argz[0].value + argz[1].args[0].value,
                        annotation=annotation),
                argz[1].args[1],
            ],
            node.typ,
            None,
            node.annotation,
            add_gas_estimate=node.add_gas_estimate,
            valency=node.valency,
        )
    elif node.value == "add" and get_int_at(argz, 0) == 0:
        return LLLnode(
            argz[1].value,
            argz[1].args,
            node.typ,
            node.location,
            node.pos,
            argz[1].annotation,
            add_gas_estimate=node.add_gas_estimate,
            valency=node.valency,
        )
    elif node.value == "add" and get_int_at(argz, 1) == 0:
        return LLLnode(
            argz[0].value,
            argz[0].args,
            node.typ,
            node.location,
            node.pos,
            argz[0].annotation,
            add_gas_estimate=node.add_gas_estimate,
            valency=node.valency,
        )
    elif node.value == "clamp" and int_at(argz, 0) and int_at(
            argz, 1) and int_at(argz, 2):
        if get_int_at(argz, 0, True) > get_int_at(argz, 1, True):
            raise Exception("Clamp always fails")
        elif get_int_at(argz, 1, True) > get_int_at(argz, 2, True):
            raise Exception("Clamp always fails")
        else:
            return argz[1]
    elif node.value == "clamp" and int_at(argz, 0) and int_at(argz, 1):
        if get_int_at(argz, 0, True) > get_int_at(argz, 1, True):
            raise Exception("Clamp always fails")
        else:
            return LLLnode(
                "clample",
                [argz[1], argz[2]],
                node.typ,
                node.location,
                node.pos,
                node.annotation,
                add_gas_estimate=node.add_gas_estimate,
                valency=node.valency,
            )
    elif node.value == "clamp_nonzero" and int_at(argz, 0):
        if get_int_at(argz, 0) != 0:
            return LLLnode(
                argz[0].value,
                [],
                node.typ,
                node.location,
                node.pos,
                node.annotation,
                add_gas_estimate=node.add_gas_estimate,
                valency=node.valency,
            )
        else:
            raise Exception("Clamp always fails")
    # [eq, x, 0] is the same as [iszero, x].
    elif node.value == 'eq' and int_at(argz, 1) and argz[1].value == 0:
        return LLLnode(
            'iszero',
            [argz[0]],
            node.typ,
            node.location,
            node.pos,
            node.annotation,
            add_gas_estimate=node.add_gas_estimate,
            valency=node.valency,
        )
    # [ne, x, y] has the same truthyness as [xor, x, y]
    # rewrite 'ne' as 'xor' in places where truthy is accepted.
    elif has_cond_arg(node) and argz[0].value == 'ne':
        argz[0] = LLLnode.from_list(['xor'] + argz[0].args)
        return LLLnode.from_list(
            [node.value] + argz,
            typ=node.typ,
            location=node.location,
            pos=node.pos,
            annotation=node.annotation,
            # let from_list handle valency and gas_estimate
        )
    elif _is_with_without_set(node, argz):
        # TODO: This block is currently unreachable due to
        # `_is_with_without_set` unconditionally returning `False` this appears
        # to be because this "is actually not such a good optimization after
        # all" accordiing to previous comment.
        o = replace_with_value(argz[2], argz[0].value, argz[1].value)
        return o
    elif node.value == "seq":
        o = []
        for arg in argz:
            if arg.value == "seq":
                o.extend(arg.args)
            elif arg.value != "pass":
                o.append(arg)
        return LLLnode(
            node.value,
            o,
            node.typ,
            node.location,
            node.pos,
            node.annotation,
            add_gas_estimate=node.add_gas_estimate,
            valency=node.valency,
        )
    elif node.total_gas is not None:
        o = LLLnode(
            node.value,
            argz,
            node.typ,
            node.location,
            node.pos,
            node.annotation,
            add_gas_estimate=node.add_gas_estimate,
            valency=node.valency,
        )
        o.total_gas = node.total_gas - node.gas + o.gas
        o.func_name = node.func_name
        return o
    else:
        return LLLnode(
            node.value,
            argz,
            node.typ,
            node.location,
            node.pos,
            node.annotation,
            add_gas_estimate=node.add_gas_estimate,
            valency=node.valency,
        )
예제 #2
0
def optimize(node: LLLnode) -> LLLnode:
    argz = [optimize(arg) for arg in node.args]
    if node.value in arith and int_at(argz, 0) and int_at(argz, 1):
        left, right = get_int_at(argz, 0), get_int_at(argz, 1)
        # `node.value in arith` implies that `node.value` is a `str`
        calcer, symb = arith[str(node.value)]
        new_value = calcer(left, right)
        if argz[0].annotation and argz[1].annotation:
            annotation = argz[0].annotation + symb + argz[1].annotation
        elif argz[0].annotation or argz[1].annotation:
            annotation = ((argz[0].annotation or str(left)) + symb +
                          (argz[1].annotation or str(right)))
        else:
            annotation = ""
        return LLLnode(
            new_value,
            [],
            node.typ,
            None,
            node.pos,
            annotation,
            add_gas_estimate=node.add_gas_estimate,
            valency=node.valency,
        )
    elif _is_constant_add(node, argz):
        # `node.value in arith` implies that `node.value` is a `str`
        calcer, symb = arith[str(node.value)]
        if argz[0].annotation and argz[1].args[0].annotation:
            annotation = argz[0].annotation + symb + argz[1].args[0].annotation
        elif argz[0].annotation or argz[1].args[0].annotation:
            annotation = (
                (argz[0].annotation or str(argz[0].value)) + symb +
                (argz[1].args[0].annotation or str(argz[1].args[0].value)))
        else:
            annotation = ""
        return LLLnode(
            "add",
            [
                LLLnode(int(argz[0].value) + int(argz[1].args[0].value),
                        annotation=annotation),
                argz[1].args[1],
            ],
            node.typ,
            None,
            annotation=node.annotation,
            add_gas_estimate=node.add_gas_estimate,
            valency=node.valency,
        )
    elif node.value == "add" and get_int_at(argz, 0) == 0:
        return LLLnode(
            argz[1].value,
            argz[1].args,
            node.typ,
            node.location,
            node.pos,
            annotation=argz[1].annotation,
            add_gas_estimate=node.add_gas_estimate,
            valency=node.valency,
        )
    elif node.value == "add" and get_int_at(argz, 1) == 0:
        return LLLnode(
            argz[0].value,
            argz[0].args,
            node.typ,
            node.location,
            node.pos,
            argz[0].annotation,
            add_gas_estimate=node.add_gas_estimate,
            valency=node.valency,
        )
    elif node.value == "clamp" and int_at(argz, 0) and int_at(
            argz, 1) and int_at(argz, 2):
        if get_int_at(argz, 0, True) > get_int_at(argz, 1,
                                                  True):  # type: ignore
            raise Exception("Clamp always fails")
        elif get_int_at(argz, 1, True) > get_int_at(argz, 2,
                                                    True):  # type: ignore
            raise Exception("Clamp always fails")
        else:
            return argz[1]
    elif node.value == "clamp" and int_at(argz, 0) and int_at(argz, 1):
        if get_int_at(argz, 0, True) > get_int_at(argz, 1,
                                                  True):  # type: ignore
            raise Exception("Clamp always fails")
        else:
            return LLLnode(
                "clample",
                [argz[1], argz[2]],
                node.typ,
                node.location,
                node.pos,
                node.annotation,
                add_gas_estimate=node.add_gas_estimate,
                valency=node.valency,
            )
    elif node.value == "clamp_nonzero" and int_at(argz, 0):
        if get_int_at(argz, 0) != 0:
            return LLLnode(
                argz[0].value,
                [],
                node.typ,
                node.location,
                node.pos,
                node.annotation,
                add_gas_estimate=node.add_gas_estimate,
                valency=node.valency,
            )
        else:
            raise Exception("Clamp always fails")
    # [eq, x, 0] is the same as [iszero, x].
    elif node.value == "eq" and int_at(argz, 1) and argz[1].value == 0:
        return LLLnode(
            "iszero",
            [argz[0]],
            node.typ,
            node.location,
            node.pos,
            node.annotation,
            add_gas_estimate=node.add_gas_estimate,
            valency=node.valency,
        )
    # [ne, x, y] has the same truthyness as [xor, x, y]
    # rewrite 'ne' as 'xor' in places where truthy is accepted.
    elif has_cond_arg(node) and argz[0].value == "ne":
        argz[0] = LLLnode.from_list(["xor"] + argz[0].args)  # type: ignore
        return LLLnode.from_list(
            [node.value] + argz,  # type: ignore
            typ=node.typ,
            location=node.location,
            pos=node.pos,
            annotation=node.annotation,
            # let from_list handle valency and gas_estimate
        )
    elif node.value == "seq":
        xs: List[Any] = []
        for arg in argz:
            if arg.value == "seq":
                xs.extend(arg.args)
            else:
                xs.append(arg)
        return LLLnode(
            node.value,
            xs,
            node.typ,
            node.location,
            node.pos,
            node.annotation,
            add_gas_estimate=node.add_gas_estimate,
            valency=node.valency,
        )
    elif node.total_gas is not None:
        o = LLLnode(
            node.value,
            argz,
            node.typ,
            node.location,
            node.pos,
            node.annotation,
            add_gas_estimate=node.add_gas_estimate,
            valency=node.valency,
        )
        o.total_gas = node.total_gas - node.gas + o.gas
        o.func_name = node.func_name
        return o
    else:
        return LLLnode(
            node.value,
            argz,
            node.typ,
            node.location,
            node.pos,
            node.annotation,
            add_gas_estimate=node.add_gas_estimate,
            valency=node.valency,
        )
예제 #3
0
def optimize(node):
    argz = [optimize(arg) for arg in node.args]
    if node.value in arith and int_at(argz, 0) and int_at(argz, 1):
        left, right = get_int_at(argz, 0), get_int_at(argz, 1)
        calcer, symb = arith[node.value]
        new_value = calcer(left, right)
        if argz[0].annotation and argz[1].annotation:
            annotation = argz[0].annotation + symb + argz[1].annotation
        elif argz[0].annotation or argz[1].annotation:
            annotation = (argz[0].annotation or str(left)) + symb + (
                argz[1].annotation or str(right))
        else:
            annotation = ''
        return LLLnode(new_value, [],
                       node.typ,
                       None,
                       node.pos,
                       annotation,
                       add_gas_estimate=node.add_gas_estimate)
    elif node.value == "add" and int_at(
            argz, 0) and argz[1].value == "add" and int_at(argz[1].args, 0):
        calcer, symb = arith[node.value]
        if argz[0].annotation and argz[1].args[0].annotation:
            annotation = argz[0].annotation + symb + argz[1].args[0].annotation
        elif argz[0].annotation or argz[1].args[0].annotation:
            annotation = (argz[0].annotation or str(argz[0].value)) + symb + (
                argz[1].args[0].annotation or str(argz[1].args[0].value))
        else:
            annotation = ''
        return LLLnode("add", [
            LLLnode(argz[0].value + argz[1].args[0].value,
                    annotation=annotation), argz[1].args[1]
        ],
                       node.typ,
                       None,
                       node.annotation,
                       add_gas_estimate=node.add_gas_estimate)
    elif node.value == "add" and get_int_at(argz, 0) == 0:
        return LLLnode(argz[1].value,
                       argz[1].args,
                       node.typ,
                       node.location,
                       node.pos,
                       argz[1].annotation,
                       add_gas_estimate=node.add_gas_estimate)
    elif node.value == "add" and get_int_at(argz, 1) == 0:
        return LLLnode(argz[0].value,
                       argz[0].args,
                       node.typ,
                       node.location,
                       node.pos,
                       argz[0].annotation,
                       add_gas_estimate=node.add_gas_estimate)
    elif node.value == "clamp" and int_at(argz, 0) and int_at(
            argz, 1) and int_at(argz, 2):
        if get_int_at(argz, 0, True) > get_int_at(argz, 1, True):
            raise Exception("Clamp always fails")
        elif get_int_at(argz, 1, True) > get_int_at(argz, 2, True):
            raise Exception("Clamp always fails")
        else:
            return argz[1]
    elif node.value == "clamp" and int_at(argz, 0) and int_at(argz, 1):
        if get_int_at(argz, 0, True) > get_int_at(argz, 1, True):
            raise Exception("Clamp always fails")
        else:
            return LLLnode("clample", [argz[1], argz[2]],
                           node.typ,
                           node.location,
                           node.pos,
                           node.annotation,
                           add_gas_estimate=node.add_gas_estimate)
    elif node.value == "clamp_nonzero" and int_at(argz, 0):
        if get_int_at(argz, 0) != 0:
            return LLLnode(argz[0].value, [],
                           node.typ,
                           node.location,
                           node.pos,
                           node.annotation,
                           add_gas_estimate=node.add_gas_estimate)
        else:
            raise Exception("Clamp always fails")
    # Turns out this is actually not such a good optimization after all
    elif node.value == "with" and int_at(
            argz, 1) and not search_for_set(argz[2], argz[0].value) and False:
        o = replace_with_value(argz[2], argz[0].value, argz[1].value)
        return o
    elif node.value == "seq":
        o = []
        for arg in argz:
            if arg.value == "seq":
                o.extend(arg.args)
            elif arg.value != "pass":
                o.append(arg)
        return LLLnode(node.value,
                       o,
                       node.typ,
                       node.location,
                       node.pos,
                       node.annotation,
                       add_gas_estimate=node.add_gas_estimate)
    elif hasattr(node, 'total_gas'):
        o = LLLnode(node.value,
                    argz,
                    node.typ,
                    node.location,
                    node.pos,
                    node.annotation,
                    add_gas_estimate=node.add_gas_estimate)
        o.total_gas = node.total_gas - node.gas + o.gas
        o.func_name = node.func_name
        return o
    else:
        return LLLnode(node.value,
                       argz,
                       node.typ,
                       node.location,
                       node.pos,
                       node.annotation,
                       add_gas_estimate=node.add_gas_estimate)
예제 #4
0
def apply_general_optimizations(node: LLLnode) -> LLLnode:
    # TODO refactor this into several functions
    argz = [apply_general_optimizations(arg) for arg in node.args]

    if node.value == "seq":
        # look for sequential mzero / calldatacopy operations that are zero'ing memory
        # and merge them into a single calldatacopy
        mzero_nodes: List = []
        initial_offset = 0
        total_length = 0
        for lll_node in [i for i in argz if i.value != "pass"]:
            if (lll_node.value == "mstore"
                    and isinstance(lll_node.args[0].value, int)
                    and lll_node.args[1].value == 0):
                # mstore of a zero value
                offset = lll_node.args[0].value
                if not mzero_nodes:
                    initial_offset = offset
                if initial_offset + total_length == offset:
                    mzero_nodes.append(lll_node)
                    total_length += 32
                    continue

            if (lll_node.value == "calldatacopy"
                    and isinstance(lll_node.args[0].value, int)
                    and lll_node.args[1].value == "calldatasize"
                    and isinstance(lll_node.args[2].value, int)):
                # calldatacopy from the end of calldata - efficient zero'ing via `empty()`
                offset, length = lll_node.args[0].value, lll_node.args[2].value
                if not mzero_nodes:
                    initial_offset = offset
                if initial_offset + total_length == offset:
                    mzero_nodes.append(lll_node)
                    total_length += length
                    continue

            # if we get this far, the current node is not a zero'ing operation
            # it's time to apply the optimization if possible
            if len(mzero_nodes) > 1:
                new_lll = LLLnode.from_list(
                    [
                        "calldatacopy", initial_offset, "calldatasize",
                        total_length
                    ],
                    pos=mzero_nodes[0].pos,
                )
                # replace first zero'ing operation with optimized node and remove the rest
                idx = argz.index(mzero_nodes[0])
                argz[idx] = new_lll
                for i in mzero_nodes[1:]:
                    argz.remove(i)

            initial_offset = 0
            total_length = 0
            mzero_nodes.clear()

    if node.value in arith and int_at(argz, 0) and int_at(argz, 1):
        left, right = get_int_at(argz, 0), get_int_at(argz, 1)
        # `node.value in arith` implies that `node.value` is a `str`
        calcer, symb = arith[str(node.value)]
        new_value = calcer(left, right)
        if argz[0].annotation and argz[1].annotation:
            annotation = argz[0].annotation + symb + argz[1].annotation
        elif argz[0].annotation or argz[1].annotation:
            annotation = ((argz[0].annotation or str(left)) + symb +
                          (argz[1].annotation or str(right)))
        else:
            annotation = ""
        return LLLnode(
            new_value,
            [],
            node.typ,
            None,
            node.pos,
            annotation,
            add_gas_estimate=node.add_gas_estimate,
            valency=node.valency,
        )
    elif _is_constant_add(node, argz):
        # `node.value in arith` implies that `node.value` is a `str`
        calcer, symb = arith[str(node.value)]
        if argz[0].annotation and argz[1].args[0].annotation:
            annotation = argz[0].annotation + symb + argz[1].args[0].annotation
        elif argz[0].annotation or argz[1].args[0].annotation:
            annotation = (
                (argz[0].annotation or str(argz[0].value)) + symb +
                (argz[1].args[0].annotation or str(argz[1].args[0].value)))
        else:
            annotation = ""
        return LLLnode(
            "add",
            [
                LLLnode(int(argz[0].value) + int(argz[1].args[0].value),
                        annotation=annotation),
                argz[1].args[1],
            ],
            node.typ,
            None,
            annotation=node.annotation,
            add_gas_estimate=node.add_gas_estimate,
            valency=node.valency,
        )
    elif node.value == "add" and get_int_at(argz, 0) == 0:
        return LLLnode(
            argz[1].value,
            argz[1].args,
            node.typ,
            node.location,
            node.pos,
            annotation=argz[1].annotation,
            add_gas_estimate=node.add_gas_estimate,
            valency=node.valency,
        )
    elif node.value == "add" and get_int_at(argz, 1) == 0:
        return LLLnode(
            argz[0].value,
            argz[0].args,
            node.typ,
            node.location,
            node.pos,
            argz[0].annotation,
            add_gas_estimate=node.add_gas_estimate,
            valency=node.valency,
        )
    elif node.value == "clamp" and int_at(argz, 0) and int_at(
            argz, 1) and int_at(argz, 2):
        if get_int_at(argz, 0, True) > get_int_at(argz, 1,
                                                  True):  # type: ignore
            raise Exception("Clamp always fails")
        elif get_int_at(argz, 1, True) > get_int_at(argz, 2,
                                                    True):  # type: ignore
            raise Exception("Clamp always fails")
        else:
            return argz[1]
    elif node.value == "clamp" and int_at(argz, 0) and int_at(argz, 1):
        if get_int_at(argz, 0, True) > get_int_at(argz, 1,
                                                  True):  # type: ignore
            raise Exception("Clamp always fails")
        else:
            return LLLnode(
                "clample",
                [argz[1], argz[2]],
                node.typ,
                node.location,
                node.pos,
                node.annotation,
                add_gas_estimate=node.add_gas_estimate,
                valency=node.valency,
            )
    elif node.value == "clamp_nonzero" and int_at(argz, 0):
        if get_int_at(argz, 0) != 0:
            return LLLnode(
                argz[0].value,
                [],
                node.typ,
                node.location,
                node.pos,
                node.annotation,
                add_gas_estimate=node.add_gas_estimate,
                valency=node.valency,
            )
        else:
            raise Exception("Clamp always fails")
    # [eq, x, 0] is the same as [iszero, x].
    elif node.value == "eq" and int_at(argz, 1) and argz[1].value == 0:
        return LLLnode(
            "iszero",
            [argz[0]],
            node.typ,
            node.location,
            node.pos,
            node.annotation,
            add_gas_estimate=node.add_gas_estimate,
            valency=node.valency,
        )
    # [ne, x, y] has the same truthyness as [xor, x, y]
    # rewrite 'ne' as 'xor' in places where truthy is accepted.
    elif node.value in ("if", "if_unchecked",
                        "assert") and argz[0].value == "ne":
        argz[0] = LLLnode.from_list(["xor"] + argz[0].args)  # type: ignore
        return LLLnode.from_list(
            [node.value] + argz,  # type: ignore
            typ=node.typ,
            location=node.location,
            pos=node.pos,
            annotation=node.annotation,
            # let from_list handle valency and gas_estimate
        )
    elif node.value == "seq":
        xs: List[Any] = []
        for arg in argz:
            if arg.value == "seq":
                xs.extend(arg.args)
            else:
                xs.append(arg)
        return LLLnode(
            node.value,
            xs,
            node.typ,
            node.location,
            node.pos,
            node.annotation,
            add_gas_estimate=node.add_gas_estimate,
            valency=node.valency,
        )
    elif node.total_gas is not None:
        o = LLLnode(
            node.value,
            argz,
            node.typ,
            node.location,
            node.pos,
            node.annotation,
            add_gas_estimate=node.add_gas_estimate,
            valency=node.valency,
        )
        o.total_gas = node.total_gas - node.gas + o.gas
        o.func_name = node.func_name
        return o
    else:
        return LLLnode(
            node.value,
            argz,
            node.typ,
            node.location,
            node.pos,
            node.annotation,
            add_gas_estimate=node.add_gas_estimate,
            valency=node.valency,
        )