-
Notifications
You must be signed in to change notification settings - Fork 937
[DO NOT MERGE] feat: EIP-7939 Implement counting leading zero operation #8771
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[DO NOT MERGE] feat: EIP-7939 Implement counting leading zero operation #8771
Conversation
Signed-off-by: Gabriel-Trintinalia <[email protected]>
70e1799
to
fb8fcaf
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This pull request adds support for the EIP-7939 CLZ opcode, implementing the operation itself, adding corresponding tests, and properly registering the new opcode in the mainnet EVM operations.
- Introduces CountLeadingZerosOperation which computes the number of leading zeros for a given input.
- Provides parameterized tests to verify correct opcode functionality.
- Updates MainnetEVMs to register the new CLZ operation.
Reviewed Changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
File | Description |
---|---|
evm/src/test/java/org/hyperledger/besu/evm/operation/CountLeadingZerosOperationTest.java | Adds tests for CLZ opcode functionality. |
evm/src/main/java/org/hyperledger/besu/evm/operation/CountLeadingZerosOperation.java | Implements the CLZ operation following EIP-7939. |
evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java | Registers the new opcode and cleans up obsolete imports. |
Comments suppressed due to low confidence (1)
evm/src/main/java/org/hyperledger/besu/evm/operation/CountLeadingZerosOperation.java:28
- [nitpick] Consider renaming 'clzSuccess' to 'CLZ_SUCCESS' to better reflect that it is a constant value.
static final OperationResult clzSuccess = new OperationResult(3, null);
Signed-off-by: Gabriel-Trintinalia <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to add a case 0x1e
in the switch statement EVM.runToHalt() in order to be able to be called by the EVM
Signed-off-by: Gabriel-Trintinalia <[email protected]>
Signed-off-by: Gabriel-Trintinalia <[email protected]>
Signed-off-by: Gabriel-Trintinalia <[email protected]>
@@ -252,6 +255,10 @@ public void runToHalt(final MessageFrame frame, final OperationTracer tracing) { | |||
case 0x18 -> XorOperation.staticOperation(frame); | |||
case 0x19 -> NotOperation.staticOperation(frame); | |||
case 0x1a -> ByteOperation.staticOperation(frame); | |||
case 0x1e -> | |||
enableOsaka |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why can't this be part of the ProtocolSpecs? Don't like to have fork specific info inside these classes. It seems we can load all of the opcodes during configuration. But maybe this is a bigger refactoring than this PR should address
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add an end to end test for this opcode to make sure it fully works for the fork. Suggestion: look into EvmToolSpecTests
Signed-off-by: Gabriel-Trintinalia <[email protected]>
@lu-pinto done |
Please don't merge before performance benchmarking. |
public static OperationResult staticOperation(final MessageFrame frame) { | ||
final Bytes value = frame.popStackItem(); | ||
final int numberOfLeadingZeros = UInt256.fromBytes(value).numberOfLeadingZeros(); | ||
frame.pushStackItem(Words.intBytes(numberOfLeadingZeros)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's benchmark two different implementations, the one suggested here and this one
frame.pushStackItem(UInt256.valueOf(value.numberOfLeadingZeros()))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's benchmark two different implementations, the one suggested here and this one
frame.pushStackItem(UInt256.valueOf(value.numberOfLeadingZeros()))
Thanks for looking into this. The reference tests do not pass with Bytes.numberOfLeadingZeros()
. I initially was using it but replaced in this commit: e82709e
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do you know why were they not passing? The implementation is literally the same - I'm surprised
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lu-pinto It works if Bytes.size() == 32. Wrapping into a Bytes32
works fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok so @ahamlat you could probably do:
final int numberOfLeadingZeros = Bytes32.wrap(value).numberOfLeadingZeros();
frame.pushStackItem(Words.intBytes(numberOfLeadingZeros));
but I agree, my guess, is less performant.
So I ran some benchmarks for both cases (I actually had to change the suggestion implementation slightly to make it work):
Benchmark results show 2-3x slower for the impl in this PR:
branch where I got my benchmarks published: lu-pinto/benchmark-8770-clz-opcode |
We can even simplify the code and avoid the check.
we can have, as in the case where the check is false, value.size() is called twice, and we already that this call can be time consuming
|
The benchmarks show that when useBase32Wrapping is true, the results is much better in terms of performance. Which means that the fast implementation is this one
Could you replace with this implementation ? remove DO NOT MERGE and make it ready for review ? |
Signed-off-by: Gabriel-Trintinalia <[email protected]>
Unfortunately, It does not pass the reference tests. I added a failing test to the PR:
The proposed change above returns 1. |
Fixed on the new tip 5607b1c. Was partially counting bytes instead of bits :) |
We had a chat about it with @lu-pinto . There's one main issue and the unit test is made in a way to reproduce the reference test :
This should be
You can use
But may be we should keep using Bytes instead just to be sure we're handling correctly this case. |
Signed-off-by: Luis Pinto <[email protected]>
Implements https://eips.ethereum.org/EIPS/eip-7939
This pull request adds support for the EIP-7939 CLZ opcode, implementing the operation itself, adding corresponding tests, and properly registering the new opcode in the mainnet EVM operations.
Introduces CountLeadingZerosOperation which computes the number of leading zeros for a given input.
Provides parameterized tests to verify correct opcode functionality.
Updates MainnetEVMs to register the new CLZ operation.