diff --git a/ethereum/core/src/jmh/java/org/hyperledger/besu/ethereum/vm/operations/v2/SarOperationBenchmarkV2.java b/ethereum/core/src/jmh/java/org/hyperledger/besu/ethereum/vm/operations/v2/SarOperationBenchmarkV2.java index e617026a236..2cd81b507dd 100644 --- a/ethereum/core/src/jmh/java/org/hyperledger/besu/ethereum/vm/operations/v2/SarOperationBenchmarkV2.java +++ b/ethereum/core/src/jmh/java/org/hyperledger/besu/ethereum/vm/operations/v2/SarOperationBenchmarkV2.java @@ -151,6 +151,6 @@ public void setUp() { @Override protected Operation.OperationResult invoke(final MessageFrame frame) { - return SarOperationV2.staticOperation(frame, frame.stackDataV2()); + return SarOperationV2.staticOperation(frame); } } diff --git a/ethereum/core/src/jmh/java/org/hyperledger/besu/ethereum/vm/operations/v2/ShlOperationBenchmarkV2.java b/ethereum/core/src/jmh/java/org/hyperledger/besu/ethereum/vm/operations/v2/ShlOperationBenchmarkV2.java index 117f8148036..61bf64c255c 100644 --- a/ethereum/core/src/jmh/java/org/hyperledger/besu/ethereum/vm/operations/v2/ShlOperationBenchmarkV2.java +++ b/ethereum/core/src/jmh/java/org/hyperledger/besu/ethereum/vm/operations/v2/ShlOperationBenchmarkV2.java @@ -22,6 +22,6 @@ public class ShlOperationBenchmarkV2 extends AbstractShiftOperationBenchmarkV2 { @Override protected Operation.OperationResult invoke(final MessageFrame frame) { - return ShlOperationV2.staticOperation(frame, frame.stackDataV2()); + return ShlOperationV2.staticOperation(frame); } } diff --git a/ethereum/core/src/jmh/java/org/hyperledger/besu/ethereum/vm/operations/v2/ShrOperationBenchmarkV2.java b/ethereum/core/src/jmh/java/org/hyperledger/besu/ethereum/vm/operations/v2/ShrOperationBenchmarkV2.java index fa3dd952609..e8dc5d0968b 100644 --- a/ethereum/core/src/jmh/java/org/hyperledger/besu/ethereum/vm/operations/v2/ShrOperationBenchmarkV2.java +++ b/ethereum/core/src/jmh/java/org/hyperledger/besu/ethereum/vm/operations/v2/ShrOperationBenchmarkV2.java @@ -22,6 +22,6 @@ public class ShrOperationBenchmarkV2 extends AbstractShiftOperationBenchmarkV2 { @Override protected Operation.OperationResult invoke(final MessageFrame frame) { - return ShrOperationV2.staticOperation(frame, frame.stackDataV2()); + return ShrOperationV2.staticOperation(frame); } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/EVM.java b/evm/src/main/java/org/hyperledger/besu/evm/EVM.java index 4384c5e3320..97577d59572 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/EVM.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/EVM.java @@ -490,15 +490,15 @@ private void runToHaltV2(final MessageFrame frame, final OperationTracer tracing case 0x01 -> AddOperationV2.staticOperation(frame, frame.stackDataV2()); case 0x1b -> enableConstantinople - ? ShlOperationV2.staticOperation(frame, frame.stackDataV2()) + ? ShlOperationV2.staticOperation(frame) : InvalidOperation.invalidOperationResult(opcode); case 0x1c -> enableConstantinople - ? ShrOperationV2.staticOperation(frame, frame.stackDataV2()) + ? ShrOperationV2.staticOperation(frame) : InvalidOperation.invalidOperationResult(opcode); case 0x1d -> enableConstantinople - ? SarOperationV2.staticOperation(frame, frame.stackDataV2()) + ? SarOperationV2.staticOperation(frame) : InvalidOperation.invalidOperationResult(opcode); // TODO: implement remaining opcodes in v2; until then fall through to v1 default -> { diff --git a/evm/src/main/java/org/hyperledger/besu/evm/UInt256.java b/evm/src/main/java/org/hyperledger/besu/evm/UInt256.java index 293d36cdd72..6f3eda6ae74 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/UInt256.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/UInt256.java @@ -625,6 +625,186 @@ public static byte[] sub(final byte[] x, final byte[] y) { return sbb(x, y); } + /** + * Performs EVM SAR (arithmetic shift right) on the two top stack items. + * + *
Reads the shift amount (unsigned) and the value (signed) from the top two stack slots, + * writes {@code value >> shift} back into the value slot and decrements the top. Shifts >= 256 + * produce 0 for positive values and -1 for negative values. + * + * @param shift 256 bit value storing the amount of bits to shift + * @return the result + */ + public UInt256 sar(final UInt256 shift) { + int bitShift; + if (shift.u3() != 0 + || shift.u2() != 0 + || shift.u1() != 0 + || Long.compareUnsigned(shift.u0(), 256) >= 0) { + bitShift = 256; + } else { + bitShift = (int) shift.u0(); + } + long fill = (u3 < 0 ? -1L : 0); + return sar0(bitShift, fill); + } + + /** + * Performs EVM SHR (logical shift right) on the two top stack items. + * + *
Reads the shift amount (unsigned) and the value from the top two stack slots, writes {@code + * value >>> shift} back into the value slot and decrements the top. Shifts >= 256 or a zero value + * produce 0. + * + * @param shift 256 bit value storing the amount of bits to shift + * @return the result + */ + public UInt256 shr(final UInt256 shift) { + int bitShift; + if (shift.u3() != 0 + || shift.u2() != 0 + || shift.u1() != 0 + || Long.compareUnsigned(shift.u0(), 256) >= 0) { + bitShift = 256; + } else { + bitShift = (int) shift.u0(); + } + return sar0(bitShift, 0); + } + + /** + * Arithmetic right-shifts a 256-bit value in place by 0..255 bits, sign-extending with {@code + * fill}. + * + * @param shift number of bits to shift + * @param fill value to prepend while shifting + * @return the result + */ + // TODO: check perf - wiring shiftRight callers with this one + private UInt256 sar0(final int shift, final long fill) { + long w3 = u3, w2 = u2, w1 = u1, w0 = u0; + if (shift == 256) { + w3 = fill; + w2 = fill; + w1 = fill; + w0 = fill; + } else if (shift != 0) { + // Number of whole 64-bit words to shift (shift / 64) + final int wordShift = shift >>> 6; + // Remaining intra-word bit shift (shift % 64) + final int bitShift = shift & 63; + switch (wordShift) { + case 0: + w0 = shiftRightWord(w0, w1, bitShift); + w1 = shiftRightWord(w1, w2, bitShift); + w2 = shiftRightWord(w2, w3, bitShift); + w3 = shiftRightWord(w3, fill, bitShift); + break; + case 1: + w0 = shiftRightWord(w1, w2, bitShift); + w1 = shiftRightWord(w2, w3, bitShift); + w2 = shiftRightWord(w3, fill, bitShift); + w3 = fill; + break; + case 2: + w0 = shiftRightWord(w2, w3, bitShift); + w1 = shiftRightWord(w3, fill, bitShift); + w2 = fill; + w3 = fill; + break; + case 3: + w0 = shiftRightWord(w3, fill, bitShift); + w1 = fill; + w2 = fill; + w3 = fill; + break; + } + } + return new UInt256(w3, w2, w1, w0); + } + + /** + * Performs EVM SHL (shift left) on the two top stack items. + * + *
Reads the shift amount (unsigned) and the value from the top two stack slots, writes {@code + * value << shift} back into the value slot and decrements the top. Shifts >= 256 or a zero value + * produce 0. + * + * @param shift 256 bit value storing the amount of bits to shift + * @return the result + */ + public UInt256 shl(final UInt256 shift) { + int bitShift; + if (shift.u3() != 0 + || shift.u2() != 0 + || shift.u1() != 0 + || Long.compareUnsigned(shift.u0(), 256) >= 0) { + bitShift = 256; + } else { + bitShift = (int) shift.u0(); + } + return shl0(bitShift); + } + + /** + * Left-shifts a 256-bit value in place by 1..255 bits, zero-filling from the right. + * + * @param shift number of bits to shift + * @return the result + */ + // TODO: check perf - wiring shiftLeft callers with this one + private UInt256 shl0(final int shift) { + long w3 = u3, w2 = u2, w1 = u1, w0 = u0; + if (shift == 256) { + w3 = 0; + w2 = 0; + w1 = 0; + w0 = 0; + } else if (shift != 0) { + // Number of whole 64-bit words to shift (shift / 64) + final int wordShift = shift >>> 6; + // Remaining intra-word bit shift (shift % 64) + final int bitShift = shift & 63; + switch (wordShift) { + case 0: + w3 = shiftLeftWord(w3, w2, bitShift); + w2 = shiftLeftWord(w2, w1, bitShift); + w1 = shiftLeftWord(w1, w0, bitShift); + w0 = shiftLeftWord(w0, 0, bitShift); + break; + case 1: + w3 = shiftLeftWord(w2, w1, bitShift); + w2 = shiftLeftWord(w1, w0, bitShift); + w1 = shiftLeftWord(w0, 0, bitShift); + w0 = 0; + break; + case 2: + w3 = shiftLeftWord(w1, w0, bitShift); + w2 = shiftLeftWord(w0, 0, bitShift); + w1 = 0; + w0 = 0; + break; + case 3: + w3 = shiftLeftWord(w0, 0, bitShift); + w2 = 0; + w1 = 0; + w0 = 0; + break; + } + } + return new UInt256(w3, w2, w1, w0); + } + + private static long shiftLeftWord(final long value, final long nextValue, final int bitShift) { + if (bitShift == 0) return value; + return (value << bitShift) | (nextValue >>> (64 - bitShift)); + } + + private static long shiftRightWord(final long value, final long prevValue, final int bitShift) { + if (bitShift == 0) return value; + return (value >>> bitShift) | (prevValue << (64 - bitShift)); + } + private static boolean isZero(final byte[] arr) { int index = Arrays.mismatch(arr, ZERO_BYTES); return (index == -1 || index >= arr.length); @@ -1915,4 +2095,5 @@ record UInt576(long u8, long u7, long u6, long u5, long u4, long u3, long u2, lo // -------------------------------------------------------------------------- // endregion + } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/v2/StackArithmetic.java b/evm/src/main/java/org/hyperledger/besu/evm/v2/StackArithmetic.java deleted file mode 100644 index 6a9f808f3dc..00000000000 --- a/evm/src/main/java/org/hyperledger/besu/evm/v2/StackArithmetic.java +++ /dev/null @@ -1,324 +0,0 @@ -/* - * 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.evm.v2; - -/** - * Static utility operating directly on the flat {@code long[]} operand stack. Each slot occupies 4 - * consecutive longs in big-endian limb order: {@code [u3, u2, u1, u0]} where u3 is the most - * significant limb. - * - *
All methods take {@code (long[] s, int top)} and return the new {@code top}. The caller - * (operation) is responsible for underflow/overflow checks before calling. - */ -public class StackArithmetic { - - /** Utility class — not instantiable. */ - private StackArithmetic() {} - - // region SHL (Shift Left) - // --------------------------------------------------------------------------- - - /** - * Performs EVM SHL (shift left) on the two top stack items. - * - *
Reads the shift amount (unsigned) and the value from the top two stack slots, writes {@code - * value << shift} back into the value slot and decrements the top. Shifts >= 256 or a zero value - * produce 0. - * - * @param stack the flat limb array - * @param top current stack-top (item count) - * @return the new stack-top after consuming one item - */ - public static int shl(final long[] stack, final int top) { - final int shiftOffset = (top - 1) << 2; - final int valueOffset = (top - 2) << 2; - // If shift amount > 255 or value is zero, result is zero - if (stack[shiftOffset] != 0 - || stack[shiftOffset + 1] != 0 - || stack[shiftOffset + 2] != 0 - || Long.compareUnsigned(stack[shiftOffset + 3], 256) >= 0 - || (stack[valueOffset] == 0 - && stack[valueOffset + 1] == 0 - && stack[valueOffset + 2] == 0 - && stack[valueOffset + 3] == 0)) { - stack[valueOffset] = 0; - stack[valueOffset + 1] = 0; - stack[valueOffset + 2] = 0; - stack[valueOffset + 3] = 0; - return top - 1; - } - int shift = (int) stack[shiftOffset + 3]; - shiftLeftInPlace(stack, valueOffset, shift); - return top - 1; - } - - /** - * Left-shifts a 256-bit value in place by 1..255 bits, zero-filling from the right. - * - * @param stack the flat limb array - * @param valueOffset index of the value's most-significant limb - * @param shift number of bits to shift (must be in [1, 255]) - */ - private static void shiftLeftInPlace(final long[] stack, final int valueOffset, final int shift) { - if (shift == 0) return; - long w0 = stack[valueOffset], - w1 = stack[valueOffset + 1], - w2 = stack[valueOffset + 2], - w3 = stack[valueOffset + 3]; - // Number of whole 64-bit words to shift (shift / 64) - final int wordShift = shift >>> 6; - // Remaining intra-word bit shift (shift % 64) - final int bitShift = shift & 63; - switch (wordShift) { - case 0: - w0 = shiftLeftWord(w0, w1, bitShift); - w1 = shiftLeftWord(w1, w2, bitShift); - w2 = shiftLeftWord(w2, w3, bitShift); - w3 = shiftLeftWord(w3, 0, bitShift); - break; - case 1: - w0 = shiftLeftWord(w1, w2, bitShift); - w1 = shiftLeftWord(w2, w3, bitShift); - w2 = shiftLeftWord(w3, 0, bitShift); - w3 = 0; - break; - case 2: - w0 = shiftLeftWord(w2, w3, bitShift); - w1 = shiftLeftWord(w3, 0, bitShift); - w2 = 0; - w3 = 0; - break; - case 3: - w0 = shiftLeftWord(w3, 0, bitShift); - w1 = 0; - w2 = 0; - w3 = 0; - break; - } - stack[valueOffset] = w0; - stack[valueOffset + 1] = w1; - stack[valueOffset + 2] = w2; - stack[valueOffset + 3] = w3; - } - - /** - * Shifts a 64-bit word left and carries in bits from the next less-significant word. - * - * @param value the current word - * @param nextValue the next less-significant word (bits carry in from its top) - * @param bitShift the intra-word shift amount in [0, 63]; 0 returns {@code value} unchanged to - * avoid Java's mod-64 shift semantics on {@code nextValue >>> 64} - * @return the shifted word - */ - private static long shiftLeftWord(final long value, final long nextValue, final int bitShift) { - if (bitShift == 0) return value; - return (value << bitShift) | (nextValue >>> (64 - bitShift)); - } - - // endregion - - // region SHR (Shift Right) - // --------------------------------------------------------------------------- - - /** - * Performs EVM SHR (logical shift right) on the two top stack items. - * - *
Reads the shift amount (unsigned) and the value from the top two stack slots, writes {@code - * value >>> shift} back into the value slot and decrements the top. Shifts >= 256 or a zero value - * produce 0. - * - * @param stack the flat limb array - * @param top current stack-top (item count) - * @return the new stack-top after consuming one item - */ - public static int shr(final long[] stack, final int top) { - final int shiftOffset = (top - 1) << 2; - final int valueOffset = (top - 2) << 2; - if (stack[shiftOffset] != 0 - || stack[shiftOffset + 1] != 0 - || stack[shiftOffset + 2] != 0 - || Long.compareUnsigned(stack[shiftOffset + 3], 256) >= 0 - || (stack[valueOffset] == 0 - && stack[valueOffset + 1] == 0 - && stack[valueOffset + 2] == 0 - && stack[valueOffset + 3] == 0)) { - stack[valueOffset] = 0; - stack[valueOffset + 1] = 0; - stack[valueOffset + 2] = 0; - stack[valueOffset + 3] = 0; - return top - 1; - } - int shift = (int) stack[shiftOffset + 3]; - shiftRightInPlace(stack, valueOffset, shift); - return top - 1; - } - - /** - * Logically right-shifts a 256-bit value in place by 1..255 bits, zero-filling from the left. - * - * @param stack the flat limb array - * @param valueOffset index of the value's most-significant limb - * @param shift number of bits to shift (must be in [1, 255]) - */ - private static void shiftRightInPlace( - final long[] stack, final int valueOffset, final int shift) { - if (shift == 0) return; - long w0 = stack[valueOffset], - w1 = stack[valueOffset + 1], - w2 = stack[valueOffset + 2], - w3 = stack[valueOffset + 3]; - // Number of whole 64-bit words to shift (shift / 64) - final int wordShift = shift >>> 6; - // Remaining intra-word bit shift (shift % 64) - final int bitShift = shift & 63; - switch (wordShift) { - case 0: - w3 = shiftRightWord(w3, w2, bitShift); - w2 = shiftRightWord(w2, w1, bitShift); - w1 = shiftRightWord(w1, w0, bitShift); - w0 = shiftRightWord(w0, 0, bitShift); - break; - case 1: - w3 = shiftRightWord(w2, w1, bitShift); - w2 = shiftRightWord(w1, w0, bitShift); - w1 = shiftRightWord(w0, 0, bitShift); - w0 = 0; - break; - case 2: - w3 = shiftRightWord(w1, w0, bitShift); - w2 = shiftRightWord(w0, 0, bitShift); - w1 = 0; - w0 = 0; - break; - case 3: - w3 = shiftRightWord(w0, 0, bitShift); - w2 = 0; - w1 = 0; - w0 = 0; - break; - } - stack[valueOffset] = w0; - stack[valueOffset + 1] = w1; - stack[valueOffset + 2] = w2; - stack[valueOffset + 3] = w3; - } - - // endregion - - // region SAR (Shift Arithmetic Right) - // --------------------------------------------------------------------------- - - /** - * Performs EVM SAR (arithmetic shift right) on the two top stack items. - * - *
Reads the shift amount (unsigned) and the value (signed) from the top two stack slots, - * writes {@code value >> shift} back into the value slot and decrements the top. Shifts >= 256 - * produce 0 for positive values and -1 for negative values. - * - * @param stack the flat limb array - * @param top current stack-top (item count) - * @return the new stack-top after consuming one item - */ - public static int sar(final long[] stack, final int top) { - final int shiftOffset = (top - 1) << 2; - final int valueOffset = (top - 2) << 2; - boolean negative = stack[valueOffset] < 0; - if (stack[shiftOffset] != 0 - || stack[shiftOffset + 1] != 0 - || stack[shiftOffset + 2] != 0 - || Long.compareUnsigned(stack[shiftOffset + 3], 256) >= 0) { - long fill = negative ? -1L : 0L; - stack[valueOffset] = fill; - stack[valueOffset + 1] = fill; - stack[valueOffset + 2] = fill; - stack[valueOffset + 3] = fill; - return top - 1; - } - int shift = (int) stack[shiftOffset + 3]; - sarInPlace(stack, valueOffset, shift, negative); - return top - 1; - } - - /** - * Arithmetic right-shifts a 256-bit value in place by 0..255 bits, sign-extending with {@code - * fill}. - * - * @param stack the flat limb array - * @param valueOffset index of the value's most-significant limb - * @param shift number of bits to shift (must be in [0, 255]) - * @param negative true if the original value is negative (fill = -1) - */ - private static void sarInPlace( - final long[] stack, final int valueOffset, final int shift, final boolean negative) { - if (shift == 0) return; - long w0 = stack[valueOffset], - w1 = stack[valueOffset + 1], - w2 = stack[valueOffset + 2], - w3 = stack[valueOffset + 3]; - final long fill = negative ? -1L : 0L; - // Number of whole 64-bit words to shift (shift / 64) - final int wordShift = shift >>> 6; - // Remaining intra-word bit shift (shift % 64) - final int bitShift = shift & 63; - switch (wordShift) { - case 0: - w3 = shiftRightWord(w3, w2, bitShift); - w2 = shiftRightWord(w2, w1, bitShift); - w1 = shiftRightWord(w1, w0, bitShift); - w0 = shiftRightWord(w0, fill, bitShift); - break; - case 1: - w3 = shiftRightWord(w2, w1, bitShift); - w2 = shiftRightWord(w1, w0, bitShift); - w1 = shiftRightWord(w0, fill, bitShift); - w0 = fill; - break; - case 2: - w3 = shiftRightWord(w1, w0, bitShift); - w2 = shiftRightWord(w0, fill, bitShift); - w1 = fill; - w0 = fill; - break; - case 3: - w3 = shiftRightWord(w0, fill, bitShift); - w2 = fill; - w1 = fill; - w0 = fill; - break; - } - stack[valueOffset] = w0; - stack[valueOffset + 1] = w1; - stack[valueOffset + 2] = w2; - stack[valueOffset + 3] = w3; - } - - /** - * Shifts a 64-bit word right and carries in bits from the previous more-significant word. - * - *
The {@code bitShift == 0} fast path avoids Java long-shift masking, where a shift by 64 is
- * treated as a shift by 0.
- *
- * @param value the current word
- * @param prevValue the previous more-significant word
- * @param bitShift the intra-word shift amount in the range {@code [0..63]}
- * @return the shifted word
- */
- private static long shiftRightWord(final long value, final long prevValue, final int bitShift) {
- if (bitShift == 0) return value;
- return (value >>> bitShift) | (prevValue << (64 - bitShift));
- }
-
- // endregion
-}
diff --git a/evm/src/main/java/org/hyperledger/besu/evm/v2/operation/SarOperationV2.java b/evm/src/main/java/org/hyperledger/besu/evm/v2/operation/SarOperationV2.java
index d488886c5df..986a2d0655f 100644
--- a/evm/src/main/java/org/hyperledger/besu/evm/v2/operation/SarOperationV2.java
+++ b/evm/src/main/java/org/hyperledger/besu/evm/v2/operation/SarOperationV2.java
@@ -15,10 +15,10 @@
package org.hyperledger.besu.evm.v2.operation;
import org.hyperledger.besu.evm.EVM;
+import org.hyperledger.besu.evm.UInt256;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.operation.Operation;
-import org.hyperledger.besu.evm.v2.StackArithmetic;
/** The Sar operation. */
public class SarOperationV2 extends AbstractFixedCostOperationV2 {
@@ -38,19 +38,40 @@ public SarOperationV2(final GasCalculator gasCalculator) {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
- return staticOperation(frame, frame.stackDataV2());
+ return staticOperation(frame);
}
/**
* Performs sar operation.
*
* @param frame the frame
- * @param stack the v2 operand stack ({@code long[]} in big-endian limb order)
* @return the operation result
*/
- public static OperationResult staticOperation(final MessageFrame frame, final long[] stack) {
+ public static OperationResult staticOperation(final MessageFrame frame) {
if (!frame.stackHasItems(2)) return UNDERFLOW_RESPONSE;
- frame.setTopV2(StackArithmetic.sar(stack, frame.stackTopV2()));
+ long[] stack = frame.stackDataV2();
+ int top = frame.stackTopV2();
+ final int shiftOffset = (--top) << 2;
+ final UInt256 shift =
+ new UInt256(
+ stack[shiftOffset],
+ stack[shiftOffset + 1],
+ stack[shiftOffset + 2],
+ stack[shiftOffset + 3]);
+ final int valueOffset = (--top) << 2;
+ final UInt256 value =
+ new UInt256(
+ stack[valueOffset],
+ stack[valueOffset + 1],
+ stack[valueOffset + 2],
+ stack[valueOffset + 3]);
+ final UInt256 result = value.sar(shift);
+ int resultOffset = top << 2;
+ stack[resultOffset] = result.u3();
+ stack[resultOffset + 1] = result.u2();
+ stack[resultOffset + 2] = result.u1();
+ stack[resultOffset + 3] = result.u0();
+ frame.setTopV2(++top);
return sarSuccess;
}
}
diff --git a/evm/src/main/java/org/hyperledger/besu/evm/v2/operation/ShlOperationV2.java b/evm/src/main/java/org/hyperledger/besu/evm/v2/operation/ShlOperationV2.java
index 2f765e94269..62b56aef7dd 100644
--- a/evm/src/main/java/org/hyperledger/besu/evm/v2/operation/ShlOperationV2.java
+++ b/evm/src/main/java/org/hyperledger/besu/evm/v2/operation/ShlOperationV2.java
@@ -15,10 +15,10 @@
package org.hyperledger.besu.evm.v2.operation;
import org.hyperledger.besu.evm.EVM;
+import org.hyperledger.besu.evm.UInt256;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.operation.Operation;
-import org.hyperledger.besu.evm.v2.StackArithmetic;
/** The Shl (Shift Left) operation. */
public class ShlOperationV2 extends AbstractFixedCostOperationV2 {
@@ -38,19 +38,40 @@ public ShlOperationV2(final GasCalculator gasCalculator) {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
- return staticOperation(frame, frame.stackDataV2());
+ return staticOperation(frame);
}
/**
* Performs Shift Left operation.
*
* @param frame the frame
- * @param stack the v2 operand stack ({@code long[]} in big-endian limb order)
* @return the operation result
*/
- public static OperationResult staticOperation(final MessageFrame frame, final long[] stack) {
+ public static OperationResult staticOperation(final MessageFrame frame) {
if (!frame.stackHasItems(2)) return UNDERFLOW_RESPONSE;
- frame.setTopV2(StackArithmetic.shl(stack, frame.stackTopV2()));
+ long[] stack = frame.stackDataV2();
+ int top = frame.stackTopV2();
+ final int shiftOffset = (--top) << 2;
+ final UInt256 shift =
+ new UInt256(
+ stack[shiftOffset],
+ stack[shiftOffset + 1],
+ stack[shiftOffset + 2],
+ stack[shiftOffset + 3]);
+ final int valueOffset = (--top) << 2;
+ final UInt256 value =
+ new UInt256(
+ stack[valueOffset],
+ stack[valueOffset + 1],
+ stack[valueOffset + 2],
+ stack[valueOffset + 3]);
+ final UInt256 result = value.shl(shift);
+ int resultOffset = top << 2;
+ stack[resultOffset] = result.u3();
+ stack[resultOffset + 1] = result.u2();
+ stack[resultOffset + 2] = result.u1();
+ stack[resultOffset + 3] = result.u0();
+ frame.setTopV2(++top);
return shlSuccess;
}
}
diff --git a/evm/src/main/java/org/hyperledger/besu/evm/v2/operation/ShrOperationV2.java b/evm/src/main/java/org/hyperledger/besu/evm/v2/operation/ShrOperationV2.java
index 71c412a04ab..f8eb164b16f 100644
--- a/evm/src/main/java/org/hyperledger/besu/evm/v2/operation/ShrOperationV2.java
+++ b/evm/src/main/java/org/hyperledger/besu/evm/v2/operation/ShrOperationV2.java
@@ -15,10 +15,10 @@
package org.hyperledger.besu.evm.v2.operation;
import org.hyperledger.besu.evm.EVM;
+import org.hyperledger.besu.evm.UInt256;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.operation.Operation;
-import org.hyperledger.besu.evm.v2.StackArithmetic;
/** The Shr (Shift Right) operation. */
public class ShrOperationV2 extends AbstractFixedCostOperationV2 {
@@ -38,19 +38,40 @@ public ShrOperationV2(final GasCalculator gasCalculator) {
@Override
public Operation.OperationResult executeFixedCostOperation(
final MessageFrame frame, final EVM evm) {
- return staticOperation(frame, frame.stackDataV2());
+ return staticOperation(frame);
}
/**
* Performs SHR operation.
*
* @param frame the frame
- * @param stack the v2 operand stack ({@code long[]} in big-endian limb order)
* @return the operation result
*/
- public static OperationResult staticOperation(final MessageFrame frame, final long[] stack) {
+ public static OperationResult staticOperation(final MessageFrame frame) {
if (!frame.stackHasItems(2)) return UNDERFLOW_RESPONSE;
- frame.setTopV2(StackArithmetic.shr(stack, frame.stackTopV2()));
+ long[] stack = frame.stackDataV2();
+ int top = frame.stackTopV2();
+ final int shiftOffset = (--top) << 2;
+ final UInt256 shift =
+ new UInt256(
+ stack[shiftOffset],
+ stack[shiftOffset + 1],
+ stack[shiftOffset + 2],
+ stack[shiftOffset + 3]);
+ final int valueOffset = (--top) << 2;
+ final UInt256 value =
+ new UInt256(
+ stack[valueOffset],
+ stack[valueOffset + 1],
+ stack[valueOffset + 2],
+ stack[valueOffset + 3]);
+ final UInt256 result = value.shr(shift);
+ int resultOffset = top << 2;
+ stack[resultOffset] = result.u3();
+ stack[resultOffset + 1] = result.u2();
+ stack[resultOffset + 2] = result.u1();
+ stack[resultOffset + 3] = result.u0();
+ frame.setTopV2(++top);
return shrSuccess;
}
}
diff --git a/evm/src/test/java/org/hyperledger/besu/evm/v2/operation/SarOperationV2Test.java b/evm/src/test/java/org/hyperledger/besu/evm/v2/operation/SarOperationV2Test.java
index f48f58c794f..0b0f801d2fb 100644
--- a/evm/src/test/java/org/hyperledger/besu/evm/v2/operation/SarOperationV2Test.java
+++ b/evm/src/test/java/org/hyperledger/besu/evm/v2/operation/SarOperationV2Test.java
@@ -25,6 +25,7 @@
import java.util.List;
import org.apache.tuweni.bytes.Bytes32;
+import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
@@ -70,15 +71,15 @@ static Iterable