Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ are provided with different values, using input as per the execution-apis spec i
- 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)
<<<<<<< optimize/register-based-shift
- Use cache locality to improve Shift opcodes [#9878](https://github.com/besu-eth/besu/pull/9878)
=======
- Plugin API: pass pending block header when creating selectors [#10034](https://github.com/besu-eth/besu/pull/10034)
>>>>>>> main

## 26.2.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@

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 static org.hyperledger.besu.evm.operation.Shift256Operations.shiftRight;

import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.frame.MessageFrame;
Expand Down Expand Up @@ -94,30 +97,46 @@ 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;
// 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 = shiftRight(w2, w1, bitShift);
w2 = shiftRight(w1, w0, bitShift);
w1 = shiftRight(w0, fill, bitShift);
w0 = fill;
break;
case 2:
w3 = shiftRight(w1, w0, bitShift);
w2 = shiftRight(w0, fill, bitShift);
w1 = fill;
w0 = fill;
break;
case 3:
w3 = shiftRight(w0, fill, bitShift);
w2 = fill;
w1 = fill;
w0 = fill;
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)));
}
}

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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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];

Expand All @@ -50,4 +56,42 @@ 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);
}

/**
* Shifts a 64-bit word right and carries in bits from the previous more-significant word.
*
* <p>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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -89,29 +91,63 @@ 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);
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) {
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 = shiftLeft(w1, w2, bitShift);
w1 = shiftLeft(w2, w3, bitShift);
w2 = shiftLeft(w3, 0, bitShift);
w3 = 0;
break;
case 2:
w0 = shiftLeft(w2, w3, bitShift);
w1 = shiftLeft(w3, 0, bitShift);
w2 = 0;
w3 = 0;
break;
case 3:
w0 = shiftLeft(w3, 0, bitShift);
w1 = 0;
w2 = 0;
w3 = 0;
break;
}

final int shiftBytes = shift >>> 3; // /8
final int shiftBits = shift & 7; // %8

final byte[] out = new byte[32];

// 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)));
}
}

putLong(out, 0, w0);
putLong(out, 8, w1);
putLong(out, 16, w2);
putLong(out, 24, w3);
return Bytes.wrap(out);
}

/**
* Shifts a 64-bit word left and carries in bits from the next less-significant word.
*
* <p>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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
*/
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 static org.hyperledger.besu.evm.operation.Shift256Operations.shiftRight;

import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.frame.MessageFrame;
Expand Down Expand Up @@ -93,24 +96,48 @@ 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];

// 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)));
}
long w0 = getLong(in, 0);
long w1 = getLong(in, 8);
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) {
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 = shiftRight(w2, w1, bitShift);
w2 = shiftRight(w1, w0, bitShift);
w1 = shiftRight(w0, 0, bitShift);
w0 = 0;
break;
case 2:
w3 = shiftRight(w1, w0, bitShift);
w2 = shiftRight(w0, 0, bitShift);
w1 = 0;
w0 = 0;
break;
case 3:
w3 = shiftRight(w0, 0, bitShift);
w2 = 0;
w1 = 0;
w0 = 0;
break;
}
Comment thread
siladu marked this conversation as resolved.

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);
}
}
Loading