Alternative design for shift opcodes#10216
Conversation
da22999 to
82337b0
Compare
| 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(); |
There was a problem hiding this comment.
I would suggest to just use stack instead of _stack to be inline with the naming used in the project.
| if (!frame.stackHasItems(2)) return UNDERFLOW_RESPONSE; | ||
| frame.setTopV2(StackArithmetic.shl(stack, frame.stackTopV2())); | ||
| long[] _stack = frame.stackDataV2(); |
There was a problem hiding this comment.
I would suggest to just use stack instead of _stack to be inline with the naming used in the project.
| 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(); |
There was a problem hiding this comment.
I would suggest to just use stack instead of _stack to be inline with the naming used in the project.
There was a problem hiding this comment.
Sure, it is a leftover from the previous method argument before I removed it
| || shift.u2() != 0 | ||
| || shift.u1() != 0 | ||
| || Long.compareUnsigned(shift.u0(), 256) >= 0) { | ||
| bytesToShift = 256; |
There was a problem hiding this comment.
I guess this is bitsToShift ?
There was a problem hiding this comment.
We use bitShift in private methods below
There was a problem hiding this comment.
yes it is bits, well spotted
| || shift.u2() != 0 | ||
| || shift.u1() != 0 | ||
| || Long.compareUnsigned(shift.u0(), 256) >= 0) { | ||
| bytesToShift = 256; |
| return new UInt256(w3, w2, w1, w0); | ||
| } | ||
|
|
||
| private static long shiftLeftWord(final long value, final long nextValue, final int bitShift) { |
| return (value << bitShift) | (nextValue >>> (64 - bitShift)); | ||
| } | ||
|
|
||
| private static long shiftRightWord(final long value, final long prevValue, final int bitShift) { |
| final long[] s = new long[8]; | ||
| writeLimbs(s, 0, valueVal); | ||
| writeLimbs(s, 4, shiftVal); | ||
| final UInt256 result = executor.execute(valueVal, shiftVal); |
There was a problem hiding this comment.
👍 (this is a good argument that this design is better)
Signed-off-by: Luis Pinto <luis.pinto@consensys.net>
Signed-off-by: Luis Pinto <luis.pinto@consensys.net>
Signed-off-by: Luis Pinto <luis.pinto@consensys.net>
Signed-off-by: Luis Pinto <luis.pinto@consensys.net>
82337b0 to
f4ed77d
Compare
Signed-off-by: Luis Pinto <luis.pinto@consensys.net>
Signed-off-by: Luis Pinto <luis.pinto@consensys.net>
Looked into it and optimised a little more - but I'm going to park it here. Worst cases (FULL_RANDOM) are much closer or have improved significantly. IMO these are prob the most realistic ones. |
| Arguments.of( | ||
| "0x8000000000000000000000000000000000000000000000000000000000000000", | ||
| "0x100", | ||
| "0x0100", |
There was a problem hiding this comment.
Why do make this change and all the changes below on unit tests ?
There was a problem hiding this comment.
Is it related not using anymore fromHexStringLenient ?
There was a problem hiding this comment.
changed fromHexStringLenient to fromHexString in the test to make the hexadecimal exact without having to guess if there will be a zero or not prepended. Hard to know if you don't know what lenient does. Since we are providing the values hardcoded does it make sense to "disguise" them? For instance 0x0 is half a byte so it seems lenient would put a zero to complete the byte.
I can revert it if you feel strongly about it.
PR description
I was honestly not convinced with the current design of the opcodes on EVM V2 from #10154 so I did some experimentation and I would like to challenge the existing design.
I managed to achieve the same performance level while splitting up duties between the arithmetic/bitwise computations from the opcodes themselves. Opcodes should be the ones fetching/updating the stack, and not the code that does computations - this should be strictly decoupled from one another.
IMO code looks much cleaner and easier to read. It also benefits from code reuse with already existing arithmetics in UInt256. I will take a look at repurposing shl and shr for modulus arithmetics in another PR as well as I believe we might be able to reuse them.
Performance stats:
Issue(s)
#10131
Thanks for sending a pull request! Have you done the following?
doc-change-requiredlabel to this PR if updates are required.Locally, you can run these tests to catch failures early:
./gradlew spotlessApply./gradlew build./gradlew acceptanceTest./gradlew integrationTest./gradlew ethereum:referenceTests:referenceTests