Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -2987,6 +2987,7 @@ private String generateConfigurationOverview() {
.setTxPoolImplementation(buildTransactionPoolConfiguration().getTxPoolImplementation())
.setWorldStateUpdateMode(unstableEvmOptions.toDomainObject().worldUpdaterMode())
.setEnabledOpcodeOptimizations(unstableEvmOptions.toDomainObject().enableOptimizedOpcodes())
.setEvmV2(unstableEvmOptions.toDomainObject().enableEvmV2())
.setPluginContext(this.besuPluginContext)
.setHistoryExpiryPruneEnabled(getDataStorageConfiguration().getHistoryExpiryPruneEnabled())
.setBlobDBSettings(rocksDBPlugin.getBlobDBSettings());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public class ConfigurationOverviewBuilder {
private TransactionPoolConfiguration.Implementation txPoolImplementation;
private EvmConfiguration.WorldUpdaterMode worldStateUpdateMode;
private boolean enabledOpcodeOptimizations;
private boolean evmV2 = false;
private Map<String, String> environment;
private BesuPluginContextImpl besuPluginContext;
private boolean isHistoryExpiryPruneEnabled = false;
Expand Down Expand Up @@ -320,6 +321,17 @@ public ConfigurationOverviewBuilder setEnabledOpcodeOptimizations(
return this;
}

/**
* Sets whether the experimental EVM v2 (long[] stack) is enabled.
*
* @param evmV2 true if --Xevm-go-fast / --Xevm-v2 is enabled
* @return the builder
*/
public ConfigurationOverviewBuilder setEvmV2(final boolean evmV2) {
this.evmV2 = evmV2;
return this;
}

/**
* Sets the engine jwt file path.
*
Expand Down Expand Up @@ -513,7 +525,11 @@ public String build() {

lines.add("Using " + worldStateUpdateMode + " worldstate update mode");

lines.add("Opcode optimizations " + (enabledOpcodeOptimizations ? "enabled" : "disabled"));
if (evmV2) {
lines.add("Experimental EVM v2 (long[] stack) enabled");
} else {
lines.add("Opcode optimizations " + (enabledOpcodeOptimizations ? "enabled" : "disabled"));
}

if (isParallelTxProcessingEnabled) {
lines.add("Parallel transaction processing enabled");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ public class EvmOptions implements CLIOptions<EvmConfiguration> {
/** The constant OPTIMIZED_OP_CODES. */
public static final String OPTIMIZED_OP_CODES = "--Xevm-optimized-opcodes";

/** The constant EVM_V2. */
public static final String EVM_V2 = "--Xevm-v2";

/** Default constructor. */
EvmOptions() {}

Expand Down Expand Up @@ -72,10 +75,18 @@ public static EvmOptions create() {
arity = "1")
private boolean enableOptimizedOpcodes = true;

@CommandLine.Option(
names = {EVM_V2, "--Xevm-go-fast"},
description = "Enable experimental EVM v2 with long[] stack representation (default: false)",
fallbackValue = "false",
hidden = true,
arity = "1")
private boolean enableEvmV2 = false;

@Override
public EvmConfiguration toDomainObject() {
return new EvmConfiguration(
jumpDestCacheWeightKilobytes, worldstateUpdateMode, enableOptimizedOpcodes);
jumpDestCacheWeightKilobytes, worldstateUpdateMode, enableOptimizedOpcodes, enableEvmV2);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* 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.frame.MessageFrame;
import org.hyperledger.besu.evm.operation.Operation;
import org.hyperledger.besu.evm.operation.v2.AddOperationV2;

public class AddOperationBenchmarkV2 extends BinaryOperationBenchmarkV2 {

@Override
protected Operation.OperationResult invoke(final MessageFrame frame) {
return AddOperationV2.staticOperation(frame, frame.stackDataV2());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* 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.mockito.Mockito.mock;

import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.frame.BlockValues;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;

import java.util.Random;

import org.apache.tuweni.bytes.Bytes32;

public class BenchmarkHelperV2 {
/**
* Creates a minimal {@link MessageFrame} suitable for opcode benchmarks.
*
* <p>The frame is configured with mocked dependencies and deterministic zero/default values.
*
* @return a message-call frame ready to use in benchmark setup
*/
public static MessageFrame createMessageCallFrame() {
return MessageFrame.builder()
.enableEvmV2(true)
.worldUpdater(mock(WorldUpdater.class))
.originator(Address.ZERO)
.gasPrice(Wei.ONE)
.blobGasPrice(Wei.ONE)
.blockValues(mock(BlockValues.class))
.miningBeneficiary(Address.ZERO)
.blockHashLookup((__, ___) -> Hash.ZERO)
.type(MessageFrame.Type.MESSAGE_CALL)
.initialGas(Long.MAX_VALUE)
.address(Address.ZERO)
.contract(Address.ZERO)
.inputData(Bytes32.ZERO)
.sender(Address.ZERO)
.value(Wei.ZERO)
.apparentValue(Wei.ZERO)
.code(Code.EMPTY_CODE)
.completer(__ -> {})
.build();
}

/**
* Fills an array with random UInt256 values.
*
* @param pool the destination array
*/
public static void fillUInt256Pool(final org.hyperledger.besu.evm.UInt256[] pool) {
final Random random = new Random();
for (int i = 0; i < pool.length; i++) {
final byte[] a = new byte[1 + random.nextInt(32)];
random.nextBytes(a);
pool[i] = bytesToUInt256(a);
}
}

/**
* Converts a byte array to UInt256 (left-pads to 32 bytes).
*
* @param bytes the byte array
* @return the UInt256 value
*/
static org.hyperledger.besu.evm.UInt256 bytesToUInt256(final byte[] bytes) {
final byte[] padded = new byte[32];
System.arraycopy(bytes, 0, padded, 32 - bytes.length, bytes.length);
return org.hyperledger.besu.evm.UInt256.fromBytesBE(padded);
}

/**
* Pushes a UInt256 value onto the frame's stack by writing limbs directly.
*
* @param frame the message frame
* @param value the UInt256 value to push
*/
static void pushUInt256(final MessageFrame frame, final org.hyperledger.besu.evm.UInt256 value) {
final long[] s = frame.stackDataV2();
final int top = frame.stackTopV2();
final int dst = top << 2;
s[dst] = value.u3();
s[dst + 1] = value.u2();
s[dst + 2] = value.u1();
s[dst + 3] = value.u0();
frame.setTopV2(top + 1);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* 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 = 3, time = 1, timeUnit = TimeUnit.SECONDS)
@OutputTimeUnit(value = TimeUnit.NANOSECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@BenchmarkMode(Mode.AverageTime)
public abstract class BinaryOperationBenchmarkV2 {

protected static final int SAMPLE_SIZE = 30_000;

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

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

@Benchmark
public void executeOperation(final Blackhole blackhole) {
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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,13 @@ void execute(final InputStream input, final PrintWriter output, final String[] a
addForkHelp(commandLine.getSubcommands().get("t8n"));
addForkHelp(commandLine.getSubcommands().get("t8n-server"));

commandLine.setExecutionStrategy(new CommandLine.RunLast());
commandLine.setExecutionStrategy(
parseResult -> {
if (daggerOptions.isEvmV2Enabled()) {
out.println("EVM v2 (long[] stack) enabled");
}
return new CommandLine.RunLast().execute(parseResult);
});
commandLine.execute(args);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,29 @@ BlockParameter provideBlockParameter() {
arity = "1")
private boolean enableOptimizedOpcodes = true;

@CommandLine.Option(
names = {"--Xevm-go-fast", "--Xevm-v2"},
description = "Enable experimental EVM v2 with long[] stack representation (default: false)",
fallbackValue = "false",
defaultValue = "false",
hidden = true,
arity = "1")
private boolean enableEvmV2 = false;

@Provides
@Singleton
EvmConfiguration provideEvmConfiguration() {
return new EvmConfiguration(
jumpDestCacheWeightKilobytes, worldstateUpdateMode, enableOptimizedOpcodes);
jumpDestCacheWeightKilobytes, worldstateUpdateMode, enableOptimizedOpcodes, enableEvmV2);
}

/**
* Returns whether experimental EVM v2 is enabled. Used by the tool to emit a startup notice.
*
* @return true if EVM v2 is enabled
*/
public boolean isEvmV2Enabled() {
return enableEvmV2;
}

/** Default constructor for the EvmToolCommandOptionsModule class. */
Expand Down
Loading
Loading