From 8f65eae60d25e4f4200fd9918cd160c5011da7b8 Mon Sep 17 00:00:00 2001 From: "Ameziane H." Date: Tue, 24 Feb 2026 09:52:37 +0100 Subject: [PATCH 1/5] Use CPU cache locality and avoid on-CPU cache misses Signed-off-by: Ameziane H. --- .../evm/operation/SarOperationOptimized.java | 62 +++++++++++++------ .../evm/operation/Shift256Operations.java | 28 +++++++++ .../evm/operation/ShlOperationOptimized.java | 59 +++++++++++++----- .../evm/operation/ShrOperationOptimized.java | 58 ++++++++++++----- 4 files changed, 157 insertions(+), 50 deletions(-) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/SarOperationOptimized.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/SarOperationOptimized.java index adbea0cb47a..2d55c76d60e 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/SarOperationOptimized.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/SarOperationOptimized.java @@ -16,7 +16,9 @@ import static org.hyperledger.besu.evm.operation.Shift256Operations.ALL_ONES; import static org.hyperledger.besu.evm.operation.Shift256Operations.ALL_ONES_BYTES; +import static org.hyperledger.besu.evm.operation.Shift256Operations.getLong; import static org.hyperledger.besu.evm.operation.Shift256Operations.isShiftOverflow; +import static org.hyperledger.besu.evm.operation.Shift256Operations.putLong; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; @@ -95,29 +97,51 @@ public static OperationResult staticOperation(final MessageFrame frame) { private static Bytes sar256(final byte[] in, final int shift, final boolean negative) { if (shift == 0) return Bytes.wrap(in); - final int shiftBytes = shift >>> 3; // /8 - final int shiftBits = shift & 7; // %8 - final int fill = negative ? 0xFF : 0x00; - - final byte[] out = new byte[32]; - - // Pre-fill sign-extended bytes (indices below shiftBytes are fully sign-extended) - if (negative && shiftBytes > 0) { - Arrays.fill(out, 0, shiftBytes, (byte) 0xFF); + long w0 = getLong(in, 0); + long w1 = getLong(in, 8); + long w2 = getLong(in, 16); + long w3 = getLong(in, 24); + + final long fill = negative ? -1L : 0L; + final int wordShift = shift >>> 6; + final int bitShift = shift & 63; + + switch (wordShift) { + case 1: + w3 = w2; + w2 = w1; + w1 = w0; + w0 = fill; + break; + case 2: + w3 = w1; + w2 = w0; + w1 = fill; + w0 = fill; + break; + case 3: + w3 = w0; + w2 = fill; + w1 = fill; + w0 = fill; + break; + default: + break; } - // Only iterate bytes that receive shifted data from the input - for (int i = 31; i >= shiftBytes; i--) { - final int srcIndex = i - shiftBytes; - final int curr = in[srcIndex] & 0xFF; - if (shiftBits == 0) { - out[i] = (byte) curr; - } else { - final int prev = (srcIndex - 1 >= 0) ? (in[srcIndex - 1] & 0xFF) : fill; - out[i] = (byte) ((curr >>> shiftBits) | (prev << (8 - shiftBits))); - } + if (bitShift > 0) { + final int inv = 64 - bitShift; + w3 = (w3 >>> bitShift) | (w2 << inv); + w2 = (w2 >>> bitShift) | (w1 << inv); + w1 = (w1 >>> bitShift) | (w0 << inv); + w0 = (w0 >>> bitShift) | (fill << inv); } + final byte[] out = new byte[32]; + putLong(out, 0, w0); + putLong(out, 8, w1); + putLong(out, 16, w2); + putLong(out, 24, w3); return Bytes.wrap(out); } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/Shift256Operations.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/Shift256Operations.java index edfdab6be03..e0fe0056762 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/Shift256Operations.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/Shift256Operations.java @@ -14,6 +14,9 @@ */ package org.hyperledger.besu.evm.operation; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.nio.ByteOrder; import java.util.Arrays; import org.apache.tuweni.bytes.Bytes; @@ -24,6 +27,9 @@ */ public final class Shift256Operations { + private static final VarHandle LONG_HANDLE = + MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.BIG_ENDIAN); + /** An array of 31 0 bytes */ private static final byte[] ZERO_31 = new byte[31]; @@ -50,4 +56,26 @@ public static boolean isShiftOverflow(final byte[] shiftBytes) { if (len <= 0) return false; return !Arrays.equals(shiftBytes, 0, len, ZERO_31, 0, len); } + + /** + * Reads a big-endian long from a byte array at the given offset. + * + * @param arr the byte array (must have at least offset + 8 bytes) + * @param offset the byte offset to read from + * @return the long value + */ + static long getLong(final byte[] arr, final int offset) { + return (long) LONG_HANDLE.get(arr, offset); + } + + /** + * Writes a big-endian long to a byte array at the given offset. + * + * @param arr the byte array (must have at least offset + 8 bytes) + * @param offset the byte offset to write to + * @param value the long value to write + */ + static void putLong(final byte[] arr, final int offset, final long value) { + LONG_HANDLE.set(arr, offset, value); + } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ShlOperationOptimized.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ShlOperationOptimized.java index a691636e060..8a6c3010f3b 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ShlOperationOptimized.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ShlOperationOptimized.java @@ -14,7 +14,9 @@ */ package org.hyperledger.besu.evm.operation; +import static org.hyperledger.besu.evm.operation.Shift256Operations.getLong; import static org.hyperledger.besu.evm.operation.Shift256Operations.isShiftOverflow; +import static org.hyperledger.besu.evm.operation.Shift256Operations.putLong; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; @@ -93,25 +95,50 @@ private static Bytes shl256(final byte[] in, final int shift) { return Bytes.wrap(in); } - final int shiftBytes = shift >>> 3; // /8 - final int shiftBits = shift & 7; // %8 - - final byte[] out = new byte[32]; + long w0 = getLong(in, 0); + long w1 = getLong(in, 8); + long w2 = getLong(in, 16); + long w3 = getLong(in, 24); + + final int wordShift = shift >>> 6; + final int bitShift = shift & 63; + + switch (wordShift) { + case 1: + w0 = w1; + w1 = w2; + w2 = w3; + w3 = 0; + break; + case 2: + w0 = w2; + w1 = w3; + w2 = 0; + w3 = 0; + break; + case 3: + w0 = w3; + w1 = 0; + w2 = 0; + w3 = 0; + break; + default: + break; + } - // Shift left: bytes move to lower indices (towards index 0) - // Bytes at index >= (32 - shiftBytes) are guaranteed zero (already from new byte[32]) - final int limit = 32 - shiftBytes; - for (int i = 0; i < limit; i++) { - final int srcIndex = i + shiftBytes; - final int curr = in[srcIndex] & 0xFF; - if (shiftBits == 0) { - out[i] = (byte) curr; - } else { - final int next = (srcIndex + 1 < 32) ? (in[srcIndex + 1] & 0xFF) : 0; - out[i] = (byte) ((curr << shiftBits) | (next >>> (8 - shiftBits))); - } + if (bitShift > 0) { + final int inv = 64 - bitShift; + w0 = (w0 << bitShift) | (w1 >>> inv); + w1 = (w1 << bitShift) | (w2 >>> inv); + w2 = (w2 << bitShift) | (w3 >>> inv); + w3 = w3 << bitShift; } + final byte[] out = new byte[32]; + putLong(out, 0, w0); + putLong(out, 8, w1); + putLong(out, 16, w2); + putLong(out, 24, w3); return Bytes.wrap(out); } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ShrOperationOptimized.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ShrOperationOptimized.java index 1eeb1b1ca5c..e2ef63d4425 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ShrOperationOptimized.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ShrOperationOptimized.java @@ -14,7 +14,9 @@ */ package org.hyperledger.besu.evm.operation; +import static org.hyperledger.besu.evm.operation.Shift256Operations.getLong; import static org.hyperledger.besu.evm.operation.Shift256Operations.isShiftOverflow; +import static org.hyperledger.besu.evm.operation.Shift256Operations.putLong; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; @@ -93,24 +95,50 @@ private static Bytes shr256(final byte[] in, final int shift) { return Bytes.wrap(in); } - final int shiftBytes = shift >>> 3; // /8 - final int shiftBits = shift & 7; // %8 - - final byte[] out = new byte[32]; + long w0 = getLong(in, 0); + long w1 = getLong(in, 8); + long w2 = getLong(in, 16); + long w3 = getLong(in, 24); + + final int wordShift = shift >>> 6; + final int bitShift = shift & 63; + + switch (wordShift) { + case 1: + w3 = w2; + w2 = w1; + w1 = w0; + w0 = 0; + break; + case 2: + w3 = w1; + w2 = w0; + w1 = 0; + w0 = 0; + break; + case 3: + w3 = w0; + w2 = 0; + w1 = 0; + w0 = 0; + break; + default: + break; + } - // Shift right: bytes move to higher indices (towards index 31) - // Bytes below shiftBytes are guaranteed zero (already from new byte[32]) - for (int i = 31; i >= shiftBytes; i--) { - final int srcIndex = i - shiftBytes; - final int curr = in[srcIndex] & 0xFF; - if (shiftBits == 0) { - out[i] = (byte) curr; - } else { - final int prev = (srcIndex - 1 >= 0) ? (in[srcIndex - 1] & 0xFF) : 0; - out[i] = (byte) ((curr >>> shiftBits) | (prev << (8 - shiftBits))); - } + if (bitShift > 0) { + final int inv = 64 - bitShift; + w3 = (w3 >>> bitShift) | (w2 << inv); + w2 = (w2 >>> bitShift) | (w1 << inv); + w1 = (w1 >>> bitShift) | (w0 << inv); + w0 = w0 >>> bitShift; } + final byte[] out = new byte[32]; + putLong(out, 0, w0); + putLong(out, 8, w1); + putLong(out, 16, w2); + putLong(out, 24, w3); return Bytes.wrap(out); } } From e288bbc1100af9b60c8600d6ebcea2304a7d8796 Mon Sep 17 00:00:00 2001 From: "Ameziane H." Date: Tue, 24 Feb 2026 11:24:33 +0100 Subject: [PATCH 2/5] Add comments Signed-off-by: Ameziane H. --- .../hyperledger/besu/evm/operation/SarOperationOptimized.java | 2 ++ .../hyperledger/besu/evm/operation/ShlOperationOptimized.java | 2 ++ .../hyperledger/besu/evm/operation/ShrOperationOptimized.java | 2 ++ 3 files changed, 6 insertions(+) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/SarOperationOptimized.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/SarOperationOptimized.java index 2d55c76d60e..6dbe66232b7 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/SarOperationOptimized.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/SarOperationOptimized.java @@ -103,7 +103,9 @@ private static Bytes sar256(final byte[] in, final int shift, final boolean nega long w3 = getLong(in, 24); 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) { diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ShlOperationOptimized.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ShlOperationOptimized.java index 8a6c3010f3b..4751ae26da9 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ShlOperationOptimized.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ShlOperationOptimized.java @@ -100,7 +100,9 @@ private static Bytes shl256(final byte[] in, final int shift) { long w2 = getLong(in, 16); long w3 = getLong(in, 24); + // 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) { diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ShrOperationOptimized.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ShrOperationOptimized.java index e2ef63d4425..65a80f1aee7 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ShrOperationOptimized.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ShrOperationOptimized.java @@ -100,7 +100,9 @@ private static Bytes shr256(final byte[] in, final int shift) { long w2 = getLong(in, 16); long w3 = getLong(in, 24); + // 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) { From d6abbe20d84a5339dda522b8c691c995093eb765 Mon Sep 17 00:00:00 2001 From: "Ameziane H." Date: Thu, 12 Mar 2026 09:56:44 +0100 Subject: [PATCH 3/5] Add changelog Signed-off-by: Ameziane H. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49e3b77e978..914251669ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ - Use JDK SHA-256 provider to leverage hardware SHA-NI instructions instead of BouncyCastle [#9924](https://github.com/hyperledger/besu/pull/9924) - Support [EIP-7975](https://eips.ethereum.org/EIPS/eip-7975): eth/70 - partial block receipt lists - Limit pooled tx requests by size and remove pre-eth/68 transaction announcement support [#9990](https://github.com/besu-eth/besu/pull/9990) +- Use cache locality to improve Shift opcodes [9878](https://github.com/besu-eth/besu/pull/9878) ## 26.2.0 From a7bf5b12769290a0691edbb705c5c8f4aec73e0a Mon Sep 17 00:00:00 2001 From: "Ameziane H." Date: Thu, 12 Mar 2026 17:03:49 +0100 Subject: [PATCH 4/5] Address comments Signed-off-by: Ameziane H. --- .../evm/operation/ShrOperationOptimized.java | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ShrOperationOptimized.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ShrOperationOptimized.java index 65a80f1aee7..ae7d3328637 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ShrOperationOptimized.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ShrOperationOptimized.java @@ -106,34 +106,30 @@ private static Bytes shr256(final byte[] in, final int shift) { final int bitShift = shift & 63; switch (wordShift) { + case 0: + w3 = shiftRight(w3, w2, bitShift); + w2 = shiftRight(w2, w1, bitShift); + w1 = shiftRight(w1, w0, bitShift); + w0 = shiftRight(w0, 0, bitShift); + break; case 1: - w3 = w2; - w2 = w1; - w1 = w0; + w3 = shiftRight(w2, w1, bitShift); + w2 = shiftRight(w1, w0, bitShift); + w1 = shiftRight(w0, 0, bitShift); w0 = 0; break; case 2: - w3 = w1; - w2 = w0; + w3 = shiftRight(w1, w0, bitShift); + w2 = shiftRight(w0, 0, bitShift); w1 = 0; w0 = 0; break; case 3: - w3 = w0; + w3 = shiftRight(w0, 0, bitShift); w2 = 0; w1 = 0; w0 = 0; break; - default: - break; - } - - if (bitShift > 0) { - final int inv = 64 - bitShift; - w3 = (w3 >>> bitShift) | (w2 << inv); - w2 = (w2 >>> bitShift) | (w1 << inv); - w1 = (w1 >>> bitShift) | (w0 << inv); - w0 = w0 >>> bitShift; } final byte[] out = new byte[32]; @@ -143,4 +139,9 @@ private static Bytes shr256(final byte[] in, final int shift) { putLong(out, 24, w3); return Bytes.wrap(out); } + + private static long shiftRight(final long value, final long prevValue, final int bitShift) { + if (bitShift == 0) return value; + return (value >>> bitShift) | (prevValue << (64 - bitShift)); + } } From d7e56558ba8a64a16d17a44a75fb236988cb7103 Mon Sep 17 00:00:00 2001 From: "Ameziane H." Date: Mon, 16 Mar 2026 14:51:32 +0100 Subject: [PATCH 5/5] Make shift implementation operations consistant Signed-off-by: Ameziane H. --- CHANGELOG.md | 2 +- .../evm/operation/SarOperationOptimized.java | 33 +++++------- .../evm/operation/Shift256Operations.java | 16 ++++++ .../evm/operation/ShlOperationOptimized.java | 51 +++++++++++-------- .../evm/operation/ShrOperationOptimized.java | 6 +-- 5 files changed, 60 insertions(+), 48 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d8278a6c62..e344c0d3b7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,7 +37,7 @@ - Implement `txpool_status` RPC method [#10002](https://github.com/hyperledger/besu/pull/10002) - Support [EIP-7975](https://eips.ethereum.org/EIPS/eip-7975): eth/70 - partial block receipt lists - Limit pooled tx requests by size and remove pre-eth/68 transaction announcement support [#9990](https://github.com/besu-eth/besu/pull/9990) -- Use cache locality to improve Shift opcodes [9878](https://github.com/besu-eth/besu/pull/9878) +- Use cache locality to improve Shift opcodes [#9878](https://github.com/besu-eth/besu/pull/9878) ## 26.2.0 diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/SarOperationOptimized.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/SarOperationOptimized.java index 6dbe66232b7..70815987f84 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/SarOperationOptimized.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/SarOperationOptimized.java @@ -19,6 +19,7 @@ import static org.hyperledger.besu.evm.operation.Shift256Operations.getLong; import static org.hyperledger.besu.evm.operation.Shift256Operations.isShiftOverflow; import static org.hyperledger.besu.evm.operation.Shift256Operations.putLong; +import static org.hyperledger.besu.evm.operation.Shift256Operations.shiftRight; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; @@ -96,49 +97,41 @@ public static OperationResult staticOperation(final MessageFrame frame) { */ private static Bytes sar256(final byte[] in, final int shift, final boolean negative) { if (shift == 0) return Bytes.wrap(in); - long w0 = getLong(in, 0); long w1 = getLong(in, 8); long w2 = getLong(in, 16); long w3 = getLong(in, 24); - 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 = shiftRight(w3, w2, bitShift); + w2 = shiftRight(w2, w1, bitShift); + w1 = shiftRight(w1, w0, bitShift); + w0 = shiftRight(w0, fill, bitShift); + break; case 1: - w3 = w2; - w2 = w1; - w1 = w0; + w3 = shiftRight(w2, w1, bitShift); + w2 = shiftRight(w1, w0, bitShift); + w1 = shiftRight(w0, fill, bitShift); w0 = fill; break; case 2: - w3 = w1; - w2 = w0; + w3 = shiftRight(w1, w0, bitShift); + w2 = shiftRight(w0, fill, bitShift); w1 = fill; w0 = fill; break; case 3: - w3 = w0; + w3 = shiftRight(w0, fill, bitShift); w2 = fill; w1 = fill; w0 = fill; break; - default: - break; - } - - if (bitShift > 0) { - final int inv = 64 - bitShift; - w3 = (w3 >>> bitShift) | (w2 << inv); - w2 = (w2 >>> bitShift) | (w1 << inv); - w1 = (w1 >>> bitShift) | (w0 << inv); - w0 = (w0 >>> bitShift) | (fill << inv); } - final byte[] out = new byte[32]; putLong(out, 0, w0); putLong(out, 8, w1); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/Shift256Operations.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/Shift256Operations.java index e0fe0056762..8e4075bb3b7 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/Shift256Operations.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/Shift256Operations.java @@ -78,4 +78,20 @@ static long getLong(final byte[] arr, final int offset) { static void putLong(final byte[] arr, final int offset, final long value) { LONG_HANDLE.set(arr, offset, value); } + + /** + * 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 + */ + static long shiftRight(final long value, final long prevValue, final int bitShift) { + if (bitShift == 0) return value; + return (value >>> bitShift) | (prevValue << (64 - bitShift)); + } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ShlOperationOptimized.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ShlOperationOptimized.java index 4751ae26da9..0b9804ae6ee 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ShlOperationOptimized.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ShlOperationOptimized.java @@ -91,10 +91,7 @@ public static OperationResult staticOperation(final MessageFrame frame) { * @return the shifted 256-bit value */ private static Bytes shl256(final byte[] in, final int shift) { - if (shift == 0) { - return Bytes.wrap(in); - } - + if (shift == 0) return Bytes.wrap(in); long w0 = getLong(in, 0); long w1 = getLong(in, 8); long w2 = getLong(in, 16); @@ -104,38 +101,32 @@ private static Bytes shl256(final byte[] in, final int shift) { final int wordShift = shift >>> 6; // Remaining intra-word bit shift (shift % 64). final int bitShift = shift & 63; - switch (wordShift) { + case 0: + w0 = shiftLeft(w0, w1, bitShift); + w1 = shiftLeft(w1, w2, bitShift); + w2 = shiftLeft(w2, w3, bitShift); + w3 = shiftLeft(w3, 0, bitShift); + break; case 1: - w0 = w1; - w1 = w2; - w2 = w3; + w0 = shiftLeft(w1, w2, bitShift); + w1 = shiftLeft(w2, w3, bitShift); + w2 = shiftLeft(w3, 0, bitShift); w3 = 0; break; case 2: - w0 = w2; - w1 = w3; + w0 = shiftLeft(w2, w3, bitShift); + w1 = shiftLeft(w3, 0, bitShift); w2 = 0; w3 = 0; break; case 3: - w0 = w3; + w0 = shiftLeft(w3, 0, bitShift); w1 = 0; w2 = 0; w3 = 0; break; - default: - break; - } - - if (bitShift > 0) { - final int inv = 64 - bitShift; - w0 = (w0 << bitShift) | (w1 >>> inv); - w1 = (w1 << bitShift) | (w2 >>> inv); - w2 = (w2 << bitShift) | (w3 >>> inv); - w3 = w3 << bitShift; } - final byte[] out = new byte[32]; putLong(out, 0, w0); putLong(out, 8, w1); @@ -143,4 +134,20 @@ private static Bytes shl256(final byte[] in, final int shift) { putLong(out, 24, w3); return Bytes.wrap(out); } + + /** + * Shifts a 64-bit word left and carries in bits from the next less-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 nextValue the next less-significant word + * @param bitShift the intra-word shift amount in the range {@code [0..63]} + * @return the shifted word + */ + private static long shiftLeft(final long value, final long nextValue, final int bitShift) { + if (bitShift == 0) return value; + return (value << bitShift) | (nextValue >>> (64 - bitShift)); + } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ShrOperationOptimized.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ShrOperationOptimized.java index ae7d3328637..e32bd22b875 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ShrOperationOptimized.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ShrOperationOptimized.java @@ -17,6 +17,7 @@ import static org.hyperledger.besu.evm.operation.Shift256Operations.getLong; import static org.hyperledger.besu.evm.operation.Shift256Operations.isShiftOverflow; import static org.hyperledger.besu.evm.operation.Shift256Operations.putLong; +import static org.hyperledger.besu.evm.operation.Shift256Operations.shiftRight; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; @@ -139,9 +140,4 @@ private static Bytes shr256(final byte[] in, final int shift) { putLong(out, 24, w3); return Bytes.wrap(out); } - - private static long shiftRight(final long value, final long prevValue, final int bitShift) { - if (bitShift == 0) return value; - return (value >>> bitShift) | (prevValue << (64 - bitShift)); - } }