Skip to content

test_shifts()

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

Generate fixtures for these test cases for Amsterdam 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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
@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 3 forks.