Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
60fdfe0
Add SHL, SHR and SAR implementations and benchmarks for EVM v2
ahamlat Apr 1, 2026
1dd435b
Use more accurate benchmarks on shift operations
ahamlat Apr 1, 2026
3cac741
Merge branch 'main' into stack_artithmetic
ahamlat Apr 1, 2026
45f61e5
Use existing implementation on SAR, SHR and SHL
ahamlat Apr 2, 2026
531a87b
Add unit tests.
ahamlat Apr 2, 2026
9b88b6d
Address review comments.
ahamlat Apr 2, 2026
189a3c8
Add property based testing for v2 shift operations
ahamlat Apr 2, 2026
9ccfea9
spotless
ahamlat Apr 2, 2026
fdcdb26
Merge branch 'main' into stack_artithmetic
ahamlat Apr 2, 2026
0b688ff
Fix javadoc
ahamlat Apr 2, 2026
be07eab
Add MulModOperationV2 and Benchmarks
siladu Apr 2, 2026
28b94ff
Extra benchmarks
siladu Apr 2, 2026
2553d6f
Phase 1: Expand StackArithmetic with full StackMath method set and V2…
siladu Apr 3, 2026
1c1c1c4
Migrate all EVM operations to V2 flat long[] stack architecture
siladu Apr 3, 2026
0b4f1de
Fix javadoc errors in V2 StackArithmetic and operation classes
siladu Apr 4, 2026
52971c5
Fix NPE when V2 dispatch runs frames not built with enableEvmV2(true)
siladu Apr 4, 2026
b366fcf
Add missing V2 bitwise operations: AND, OR, XOR, BYTE, SIGNEXTEND
siladu Apr 5, 2026
a55629f
Introduce AbstractOperationV2 and fix spurious account tracking in re…
siladu Apr 5, 2026
4a094d9
Fix V2 CALL/CREATE stack check and SSTORE minimum gas
siladu Apr 5, 2026
b173b7a
Fix SelfDestructOperationV2 to use AbstractOperationV2 account helpers
siladu Apr 5, 2026
3926718
Fix gasAvailableForChildCall ordering in AbstractCallOperationV2
siladu Apr 5, 2026
a1e3add
Add test.evm.v2 system property to enable EVM v2 in reference tests
siladu Apr 5, 2026
cb5d2c9
Fix EVM V2 reference test failures: gas costs, fork gates, state gas
siladu Apr 5, 2026
f4d7757
Fix CallDataLoadOperationV2 int-overflow when offset > Integer.MAX_VALUE
siladu Apr 5, 2026
a9a4d97
Fix EVM V2 fork gates: REVERT (Byzantium+) and SLOTNUM (Amsterdam+)
siladu Apr 6, 2026
31b756a
Add StackPool
siladu Apr 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* Copyright contributors to Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.vm.operations.v2;

import static org.hyperledger.besu.ethereum.vm.operations.v2.BenchmarkHelperV2.bytesToUInt256;
import static org.hyperledger.besu.ethereum.vm.operations.v2.BenchmarkHelperV2.randomUInt256Value;

import org.hyperledger.besu.evm.UInt256;

import java.util.concurrent.ThreadLocalRandom;

import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Setup;

/**
* Abstract base class for shift operation benchmarks (SHL, SHR, SAR).
*
* <p>Provides shared test case definitions and setup logic.
*/
public abstract class AbstractShiftOperationBenchmarkV2 extends BinaryOperationBenchmarkV2 {

/** Test cases covering different execution paths for shift operations. */
public enum Case {
/** Shift by 0 - no shift needed. */
SHIFT_0,
/** Small shift by 1 bit. */
SHIFT_1,
/** Medium shift by 128 bits (half word). */
SHIFT_128,
/** Large shift by 255 bits (max valid). */
SHIFT_255,
/** Overflow: shift of exactly 256. */
OVERFLOW_SHIFT_256,
/** Overflow: shift amount > 4 bytes. */
OVERFLOW_LARGE_SHIFT,
/** Random values (original behavior). */
FULL_RANDOM
}

@Param({
"SHIFT_0",
"SHIFT_1",
"SHIFT_128",
"SHIFT_255",
"OVERFLOW_SHIFT_256",
"OVERFLOW_LARGE_SHIFT",
"FULL_RANDOM"
})
protected String caseName;

@Setup(Level.Iteration)
@Override
public void setUp() {
frame = BenchmarkHelperV2.createMessageCallFrame();

final Case scenario = Case.valueOf(caseName);
aPool = new UInt256[SAMPLE_SIZE]; // shift amount (pushed second, popped first)
bPool = new UInt256[SAMPLE_SIZE]; // value (pushed first, popped second)

final ThreadLocalRandom random = ThreadLocalRandom.current();

for (int i = 0; i < SAMPLE_SIZE; i++) {
switch (scenario) {
case SHIFT_0:
aPool[i] = UInt256.ZERO;
bPool[i] = randomUInt256Value(random);
break;

case SHIFT_1:
aPool[i] = UInt256.ONE;
bPool[i] = randomUInt256Value(random);
break;

case SHIFT_128:
aPool[i] = UInt256.fromInt(128);
bPool[i] = randomUInt256Value(random);
break;

case SHIFT_255:
aPool[i] = UInt256.fromInt(255);
bPool[i] = randomUInt256Value(random);
break;

case OVERFLOW_SHIFT_256:
aPool[i] = UInt256.fromInt(256); // 256
bPool[i] = randomUInt256Value(random);
break;

case OVERFLOW_LARGE_SHIFT:
aPool[i] = bytesToUInt256(new byte[] {0x01, 0x00, 0x00, 0x00, 0x00, 0x00});
bPool[i] = randomUInt256Value(random);
break;

case FULL_RANDOM:
default:
final byte[] shift = new byte[1 + random.nextInt(4)];
final byte[] value = new byte[1 + random.nextInt(32)];
random.nextBytes(shift);
random.nextBytes(value);
aPool[i] = bytesToUInt256(shift);
bPool[i] = bytesToUInt256(value);
break;
}
}
index = 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/*
* Copyright contributors to Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.vm.operations.v2;

import org.hyperledger.besu.evm.UInt256;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.operation.Operation;
import org.hyperledger.besu.evm.v2.operation.AddModOperationV2;

import java.util.concurrent.ThreadLocalRandom;

import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Setup;

public class AddModOperationBenchmarkV2 extends TernaryOperationBenchmarkV2 {

// Benches for (a + b) % c

// Define available scenarios
public enum Case {
MULMOD_32_32_32(1, 1, 1),
MULMOD_64_32_32(2, 1, 1),
MULMOD_64_64_32(2, 2, 1),
MULMOD_64_64_64(2, 2, 2),
MULMOD_128_32_32(4, 1, 1),
MULMOD_128_64_32(4, 2, 1),
MULMOD_128_64_64(4, 2, 2),
MULMOD_128_128_32(4, 4, 1),
MULMOD_128_128_64(4, 4, 2),
MULMOD_128_128_128(4, 4, 3),
MULMOD_192_32_32(6, 1, 1),
MULMOD_192_64_32(6, 2, 1),
MULMOD_192_64_64(6, 2, 2),
MULMOD_192_128_32(6, 4, 1),
MULMOD_192_128_64(6, 4, 2),
MULMOD_192_128_128(6, 4, 4),
MULMOD_192_192_32(6, 6, 1),
MULMOD_192_192_64(6, 6, 2),
MULMOD_192_192_128(6, 6, 4),
MULMOD_192_192_192(6, 6, 6),
MULMOD_256_32_32(8, 1, 1),
MULMOD_256_64_32(8, 2, 1),
MULMOD_256_64_64(8, 2, 2),
MULMOD_256_64_128(8, 2, 4),
MULMOD_256_64_192(8, 2, 6),
MULMOD_256_128_32(8, 4, 1),
MULMOD_256_128_64(8, 4, 2),
MULMOD_256_128_128(8, 4, 4),
MULMOD_256_192_32(8, 6, 1),
MULMOD_256_192_64(8, 6, 2),
MULMOD_256_192_128(8, 6, 4),
MULMOD_256_192_192(8, 6, 6),
MULMOD_256_256_32(8, 8, 1),
MULMOD_256_256_64(8, 8, 2),
MULMOD_256_256_128(8, 8, 4),
MULMOD_256_256_192(8, 8, 6),
MULMOD_256_256_256(8, 8, 8),
LARGER_MULMOD_64_64_128(2, 2, 4),
LARGER_MULMOD_192_192_256(6, 6, 8),
ZERO_MULMOD_128_256_0(4, 8, 0),
FULL_RANDOM(-1, -1, -1);

final int aSize;
final int bSize;
final int cSize;

Case(final int aSize, final int bSize, final int cSize) {
this.aSize = aSize;
this.bSize = bSize;
this.cSize = cSize;
}
}

@Param({
"MULMOD_32_32_32",
"MULMOD_64_32_32",
"MULMOD_64_64_32",
"MULMOD_64_64_64",
"MULMOD_128_32_32",
"MULMOD_128_64_32",
"MULMOD_128_64_64",
"MULMOD_128_128_32",
"MULMOD_128_128_64",
"MULMOD_128_128_128",
"MULMOD_192_32_32",
"MULMOD_192_64_32",
"MULMOD_192_64_64",
"MULMOD_192_128_32",
"MULMOD_192_128_64",
"MULMOD_192_128_128",
"MULMOD_192_192_32",
"MULMOD_192_192_64",
"MULMOD_192_192_128",
"MULMOD_192_192_192",
"MULMOD_256_32_32",
"MULMOD_256_64_32",
"MULMOD_256_64_64",
"MULMOD_256_64_128",
"MULMOD_256_64_192",
"MULMOD_256_128_32",
"MULMOD_256_128_64",
"MULMOD_256_128_128",
"MULMOD_256_192_32",
"MULMOD_256_192_64",
"MULMOD_256_192_128",
"MULMOD_256_192_192",
"MULMOD_256_256_32",
"MULMOD_256_256_64",
"MULMOD_256_256_128",
"MULMOD_256_256_192",
"MULMOD_256_256_256",
"LARGER_MULMOD_64_64_128",
"LARGER_MULMOD_192_192_256",
"ZERO_MULMOD_128_256_0",
"FULL_RANDOM"
})
private String caseName;

@Setup(Level.Iteration)
@Override
public void setUp() {
frame = BenchmarkHelperV2.createMessageCallFrame();

AddModOperationBenchmarkV2.Case scenario = AddModOperationBenchmarkV2.Case.valueOf(caseName);
aPool = new UInt256[SAMPLE_SIZE];
bPool = new UInt256[SAMPLE_SIZE];
cPool = new UInt256[SAMPLE_SIZE];

final ThreadLocalRandom random = ThreadLocalRandom.current();
int aSize;
int bSize;
int cSize;

for (int i = 0; i < SAMPLE_SIZE; i++) {
if (scenario.aSize < 0) aSize = random.nextInt(1, 33);
else aSize = scenario.aSize * 4;
if (scenario.bSize < 0) bSize = random.nextInt(1, 33);
else bSize = scenario.bSize * 4;
if (scenario.cSize < 0) cSize = random.nextInt(1, 33);
else cSize = scenario.cSize * 4;

final byte[] a = new byte[aSize];
final byte[] b = new byte[bSize];
final byte[] c = new byte[cSize];
random.nextBytes(a);
random.nextBytes(b);
random.nextBytes(c);
aPool[i] = BenchmarkHelperV2.bytesToUInt256(a);
bPool[i] = BenchmarkHelperV2.bytesToUInt256(b);
cPool[i] = BenchmarkHelperV2.bytesToUInt256(c);
}
index = 0;
}

@Override
protected Operation.OperationResult invoke(final MessageFrame frame) {
return AddModOperationV2.staticOperation(frame, frame.stackDataV2());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.operation.Operation;
import org.hyperledger.besu.evm.operation.v2.AddOperationV2;
import org.hyperledger.besu.evm.v2.operation.AddOperationV2;

public class AddOperationBenchmarkV2 extends BinaryOperationBenchmarkV2 {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.hyperledger.besu.evm.worldstate.WorldUpdater;

import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;

import org.apache.tuweni.bytes.Bytes32;

Expand Down Expand Up @@ -101,4 +102,44 @@ static void pushUInt256(final MessageFrame frame, final org.hyperledger.besu.evm
s[dst + 3] = value.u0();
frame.setTopV2(top + 1);
}

/**
* Generates a random UInt256 value.
*
* @param random thread-local random source
* @return random UInt256 value
*/
static org.hyperledger.besu.evm.UInt256 randomUInt256Value(final ThreadLocalRandom random) {
final byte[] value = new byte[32];
random.nextBytes(value);
return org.hyperledger.besu.evm.UInt256.fromBytesBE(value);
}

/**
* Generates a random positive signed 256-bit UInt256 value (sign bit cleared).
*
* @param random thread-local random source
* @return random positive UInt256 value
*/
static org.hyperledger.besu.evm.UInt256 randomPositiveUInt256Value(
final ThreadLocalRandom random) {
final byte[] value = new byte[32];
random.nextBytes(value);
value[0] = (byte) (value[0] & 0x7F);
return org.hyperledger.besu.evm.UInt256.fromBytesBE(value);
}

/**
* Generates a random negative signed 256-bit UInt256 value (sign bit set).
*
* @param random thread-local random source
* @return random negative UInt256 value
*/
static org.hyperledger.besu.evm.UInt256 randomNegativeUInt256Value(
final ThreadLocalRandom random) {
final byte[] value = new byte[32];
random.nextBytes(value);
value[0] = (byte) (value[0] | 0x80);
return org.hyperledger.besu.evm.UInt256.fromBytesBE(value);
}
}
Loading