Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 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
c4c7c3d
address comments.
ahamlat Apr 7, 2026
5e1eb9f
Merge branch 'main' into stack_artithmetic
ahamlat Apr 7, 2026
18bd044
Add MulModOperationV2 and Benchmarks
siladu Apr 2, 2026
6b74364
Extra benchmarks
siladu Apr 2, 2026
2621c6e
Merge branch 'main' into evmv2-mulmod
siladu Apr 8, 2026
a0decd2
javadoc
siladu Apr 8, 2026
9f4fc64
Review comments - fixup benchmark param and comments
siladu Apr 8, 2026
0b9adc1
review comments - var names
siladu Apr 8, 2026
5ef4135
spotless
siladu Apr 8, 2026
d709900
Merge branch 'main' into evmv2-mulmod
siladu Apr 9, 2026
7278b9c
Add MulModOperationV2Test covering stack management and underflow
siladu Apr 9, 2026
995d5e6
spotless
siladu Apr 9, 2026
dd580d1
Javadoc formatting
siladu Apr 9, 2026
5456d98
spotless
siladu Apr 9, 2026
2a6b476
Add finals
siladu Apr 10, 2026
48e3dbd
Merge remote-tracking branch 'upstream/main' into evmv2-mulmod
siladu Apr 10, 2026
e11a9cc
Move StackArithmetic.mulMod to MulModOperationV2
siladu Apr 12, 2026
36d9779
Merge branch 'main' into evmv2-mulmod
siladu Apr 15, 2026
f67ceae
Use internal void method
siladu Apr 15, 2026
bbb1c58
Merge branch 'main' into evmv2-mulmod
siladu Apr 16, 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
Expand Up @@ -133,7 +133,7 @@ public void setUp() {
Case scenario = Case.valueOf(caseName);
aPool = new Bytes[SAMPLE_SIZE];
bPool = new Bytes[SAMPLE_SIZE];
cPool = new Bytes[SAMPLE_SIZE];
mPool = new Bytes[SAMPLE_SIZE];

final ThreadLocalRandom random = ThreadLocalRandom.current();
int aSize;
Expand All @@ -156,7 +156,7 @@ public void setUp() {
random.nextBytes(c);
aPool[i] = Bytes.wrap(a);
bPool[i] = Bytes.wrap(b);
cPool[i] = Bytes.wrap(c);
mPool[i] = Bytes.wrap(c);
}
index = 0;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

public class MulModOperationBenchmark extends TernaryOperationBenchmark {

// Benches for (a + b) % c
// Benches for (a * b) % m

// Define available scenarios
public enum Case {
Expand All @@ -40,7 +40,7 @@ public enum Case {
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),
Copy link
Copy Markdown
Contributor

@lu-pinto lu-pinto Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

damn! maybe that's why I couldn't understand some results with modulus 128...Maybe we should just pass in the # bits as arguments instead of having them in the name 😓

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're suggesting something like MULMOD(128, 128, 128) I guess, but we'd still need a unique name for the enum. If we don't use an enum then might be harder to run targeted cases, which I have found quite useful.

MULMOD_128_128_128(4, 4, 4),
MULMOD_192_32_32(6, 1, 1),
MULMOD_192_64_32(6, 2, 1),
MULMOD_192_64_64(6, 2, 2),
Expand Down Expand Up @@ -75,12 +75,12 @@ public enum Case {

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

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

Expand Down Expand Up @@ -137,30 +137,30 @@ public void setUp() {
Case scenario = Case.valueOf(caseName);
aPool = new Bytes[SAMPLE_SIZE];
bPool = new Bytes[SAMPLE_SIZE];
cPool = new Bytes[SAMPLE_SIZE];
mPool = new Bytes[SAMPLE_SIZE];

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

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;
if (scenario.mSize < 0) mSize = random.nextInt(1, 33);
else mSize = scenario.mSize * 4;

final byte[] a = new byte[aSize];
final byte[] b = new byte[bSize];
final byte[] c = new byte[cSize];
final byte[] m = new byte[mSize];
random.nextBytes(a);
random.nextBytes(b);
random.nextBytes(c);
random.nextBytes(m);
aPool[i] = Bytes.wrap(a);
bPool[i] = Bytes.wrap(b);
cPool[i] = Bytes.wrap(c);
mPool[i] = Bytes.wrap(m);
}
index = 0;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public abstract class TernaryOperationBenchmark {

protected Bytes[] aPool;
protected Bytes[] bPool;
protected Bytes[] cPool;
protected Bytes[] mPool;
protected int index;
protected MessageFrame frame;

Expand All @@ -51,16 +51,16 @@ public void setUp() {
frame = BenchmarkHelper.createMessageCallFrame();
aPool = new Bytes[SAMPLE_SIZE];
bPool = new Bytes[SAMPLE_SIZE];
cPool = new Bytes[SAMPLE_SIZE];
mPool = new Bytes[SAMPLE_SIZE];
BenchmarkHelper.fillPool(aPool);
BenchmarkHelper.fillPool(bPool);
BenchmarkHelper.fillPool(cPool);
BenchmarkHelper.fillPool(mPool);
index = 0;
}

@Benchmark
public void executeOperation(final Blackhole blackhole) {
frame.pushStackItem(cPool[index]);
frame.pushStackItem(mPool[index]);
frame.pushStackItem(bPool[index]);
frame.pushStackItem(aPool[index]);

Expand Down
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.MulModOperationV2;

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 MulModOperationBenchmarkV2 extends TernaryOperationBenchmarkV2 {

// Benches for (a * b) % m

// 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, 4),
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 mSize;

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

@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();

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

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

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.mSize < 0) mSize = random.nextInt(1, 33);
else mSize = scenario.mSize * 4;

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

@Override
protected Operation.OperationResult invoke(final MessageFrame frame) {
return MulModOperationV2.staticOperation(frame, frame.stackDataV2());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* 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 java.util.concurrent.TimeUnit;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;

@State(Scope.Thread)
@Warmup(iterations = 2, time = 1, timeUnit = TimeUnit.SECONDS)
@OutputTimeUnit(value = TimeUnit.NANOSECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@BenchmarkMode(Mode.AverageTime)
public abstract class TernaryOperationBenchmarkV2 {

protected static final int SAMPLE_SIZE = 30_000;

protected UInt256[] aPool;
protected UInt256[] bPool;
protected UInt256[] mPool;
protected int index;
protected MessageFrame frame;

@Setup()
public void setUp() {
frame = BenchmarkHelperV2.createMessageCallFrame();
aPool = new UInt256[SAMPLE_SIZE];
bPool = new UInt256[SAMPLE_SIZE];
mPool = new UInt256[SAMPLE_SIZE];
BenchmarkHelperV2.fillUInt256Pool(aPool);
BenchmarkHelperV2.fillUInt256Pool(bPool);
BenchmarkHelperV2.fillUInt256Pool(mPool);
index = 0;
}

@Benchmark
public void executeOperation(final Blackhole blackhole) {
BenchmarkHelperV2.pushUInt256(frame, mPool[index]);
BenchmarkHelperV2.pushUInt256(frame, bPool[index]);
BenchmarkHelperV2.pushUInt256(frame, aPool[index]);

blackhole.consume(invoke(frame));

frame.setTopV2(frame.stackTopV2() - 1);

index = (index + 1) % SAMPLE_SIZE;
}

protected abstract Operation.OperationResult invoke(MessageFrame frame);
}
2 changes: 2 additions & 0 deletions evm/src/main/java/org/hyperledger/besu/evm/EVM.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
import org.hyperledger.besu.evm.operation.XorOperationOptimized;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.v2.operation.AddOperationV2;
import org.hyperledger.besu.evm.v2.operation.MulModOperationV2;
import org.hyperledger.besu.evm.v2.operation.SarOperationV2;
import org.hyperledger.besu.evm.v2.operation.ShlOperationV2;
import org.hyperledger.besu.evm.v2.operation.ShrOperationV2;
Expand Down Expand Up @@ -488,6 +489,7 @@ private void runToHaltV2(final MessageFrame frame, final OperationTracer tracing
result =
switch (opcode) {
case 0x01 -> AddOperationV2.staticOperation(frame, frame.stackDataV2());
case 0x09 -> MulModOperationV2.staticOperation(frame, frame.stackDataV2());
case 0x1b ->
enableConstantinople
? ShlOperationV2.staticOperation(frame)
Expand Down
Loading
Loading