Skip to content

test_shifts()

Documentation for tests/benchmark/compute/instruction/test_bitwise.py::test_shifts@ffe48572.

Generate fixtures for these test cases for Bogota with:

fill -v tests/benchmark/compute/instruction/test_bitwise.py::test_shifts --gas-benchmark-values 1

Benchmark shift instructions with non-trivial arguments.

This generates left-right pairs of shifts to avoid zeroing the argument. Shift amounts are randomly selected from constant pool of 15 stack values.

Source code in tests/benchmark/compute/instruction/test_bitwise.py
@pytest.mark.parametrize("opcode", [Op.SHR, Op.SAR])
def test_shifts(
    benchmark_test: BenchmarkTestFiller,
    pre: Alloc,
    fork: Fork,
    opcode: Op,
    gas_benchmark_value: int,
) -> None:
    """
    Benchmark shift instructions with non-trivial arguments.

    This generates left-right pairs of shifts to avoid zeroing the argument.
    Shift amounts are randomly selected from constant pool of 15 stack values.
    """
    max_code_size = fork.max_code_size()

    match opcode:
        case Op.SHR:
            shift_right_fn = shr
        case Op.SAR:
            shift_right_fn = sar
        case _:
            raise ValueError(f"Unexpected shift op: {opcode}")

    rng = random.Random(1)  # Use random with a fixed seed.
    initial_value = 2**256 - 1  # The initial value to be shifted; should be
    # negative for SAR.

    # Create the list of shift amounts with 15 elements (max reachable by DUPs
    # instructions). For the worst case keep the values small and omit values
    # divisible by 8.
    shift_amounts = [x + (x >= 8) + (x >= 15) for x in range(1, 16)]

    code_prefix = (
        sum(Op.PUSH1[sh] for sh in shift_amounts)
        + Op.JUMPDEST
        + Op.CALLDATALOAD(0)
    )
    code_suffix = Op.POP + Op.JUMP(len(shift_amounts) * 2)
    code_body_len = max_code_size - len(code_prefix) - len(code_suffix)

    def select_shift_amount(
        shift_fn: Callable[[int, int], int], v: int
    ) -> tuple[int, int]:
        """Select a shift amount that will produce a non-zero result."""
        while True:
            index = rng.randint(0, len(shift_amounts) - 1)
            sh = shift_amounts[index]
            new_v = shift_fn(v, sh) % 2**256
            if new_v != 0:
                return new_v, index

    code_body = Bytecode()
    v = initial_value
    while len(code_body) <= code_body_len - 4:
        v, i = select_shift_amount(shl, v)
        code_body += make_dup(len(shift_amounts) - i) + Op.SHL
        v, i = select_shift_amount(shift_right_fn, v)
        code_body += make_dup(len(shift_amounts) - i) + opcode

    code = code_prefix + code_body + code_suffix
    assert len(code) == max_code_size - 2

    tx = Transaction(
        to=pre.deploy_contract(code=code),
        data=initial_value.to_bytes(32, byteorder="big"),
        gas_limit=gas_benchmark_value,
        sender=pre.fund_eoa(),
    )

    benchmark_test(
        target_opcode=opcode,
        tx=tx,
    )

Parametrized Test Cases

This test generates 2 parametrized test cases across 4 forks.