Skip to content
This repository has been archived by the owner on Oct 28, 2021. It is now read-only.

Implement bitwise shifting (EIP145) #4054

Merged
merged 8 commits into from
Feb 22, 2018
Merged

Implement bitwise shifting (EIP145) #4054

merged 8 commits into from
Feb 22, 2018

Conversation

axic
Copy link
Member

@axic axic commented Apr 23, 2017

Implements ethereum/EIPs#215

@codecov-io
Copy link

codecov-io commented Apr 23, 2017

Codecov Report

Merging #4054 into develop will decrease coverage by 0.08%.
The diff coverage is 24.39%.

Impacted file tree graph

@@             Coverage Diff             @@
##           develop    #4054      +/-   ##
===========================================
- Coverage    60.36%   60.28%   -0.09%     
===========================================
  Files          352      352              
  Lines        27891    27912      +21     
  Branches      2884     2894      +10     
===========================================
- Hits         16836    16826      -10     
- Misses       10060    10089      +29     
- Partials       995      997       +2

libevm/VM.cpp Outdated
ON_OP();
updateIOGas();

/// TODO: confirm shift >= 256 results in 0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add unit tests for edge cases in boost::multiprecision.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is done in #4064. So we should add appropriate boundary checks here (and masking).

@axic axic force-pushed the bitshift branch 2 times, most recently from 9d11d0f to d5f342e Compare May 4, 2017 17:09
libevm/VM.cpp Outdated
updateIOGas();

/// This workarounds a bug in Boost, ...
u256 mask = (u256(1) << (256 - m_SP[1])) - 1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know what happens when m_SP[1] is more than 256.

  • Is the underflown subtraction defined?
  • Isn't the shift very slow when 256 - m_SP[1] is a big number?

Copy link
Member Author

@axic axic Jul 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The EIP says If the shift amount (`arg2`) is greater or equal 256 the result is 0. so the above code should be updated accordingly.

@axic
Copy link
Member Author

axic commented Sep 25, 2017

Need to swap operands as per latest changes.

@axic axic mentioned this pull request Oct 2, 2017
@gcolvin
Copy link
Contributor

gcolvin commented Oct 3, 2017

@axic @chfast There is substantial overlap with my #4569. This one is the one that should be pulled. However, you will want libevm/Instruction.h and Instruction.cpp from #4569. Also the convention I am using for an experimental feature like this is to place

#ifndef EIP_145
	#define EIP_145 false
#endif

in libevm/VMConfig.h and wall off the code for the feature with #if EIP_145.

@gcolvin
Copy link
Contributor

gcolvin commented Oct 3, 2017

@axic @chfast Inspecting our code there are many differences. For starters. I am taking the item on the top of the stack SP[0] as the amount to shift by, which is how I read the spec. E.g.

The SHL instruction (shift left) pops 2 values from the stack, arg1 and arg2, and pushes on the stack the second popped value arg2 shifted to the left by the number of bits in the first popped value arg1.

You are shifting by SP[1], so I think that's wrong.

Otherwise our code is just different.

  • For SHL I'm not working around whatever Boost bug you are, but I don't know if my approach triggers the bug.
  • For SHR I explicitly test for a shift amount >= 256, which might not be needed, but I suspect the test is much cheaper than the shift.
  • For SAR I use two shifts and a mask rather than division and exponentiation.

So perhaps my code is wrong, but please look.

libevm/VM.cpp Outdated
{
uint64_t amount = uint64_t(m_SP[0]);
m_SPP[0] = shiftee >> amount;
m_SPP[0] |= allbits << (256 - amount);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you have to check shiftee & hibit here as well.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@axic axic force-pushed the bitshift branch 3 times, most recently from afafed4 to 7e4fbf5 Compare October 3, 2017 16:11
libevm/VM.cpp Outdated
if (m_SP[0] >= 256)
m_SPP[0] = 0;
else
m_SPP[0] = m_SP[1] << uint64_t(m_SP[0]);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could also just use unsigned and not the specific uint64_t since it always fits into 8 bits.

libevm/VM.cpp Outdated
ON_OP();
updateIOGas();

static u256 const hibit = u256(1) << 255;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd prefer a slower, but definitely correct implementation for this case to ensure everything is working properly. Once multiple clients agree on tests, it can be optimised for speed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think the code I have now is mathematically incorrect? Or are you worried about bugs in boost's library?

libevm/VM.cpp Outdated
m_SPP[0] = s2u(-1);
}
else
m_SPP[0] = s2u(divWorkaround(value, exp256(2, shift)));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was broken due to EVM DIV not rounding the same way.

@axic
Copy link
Member Author

axic commented Oct 4, 2017

I wonder if it is possible to do the RC5 changes in a separate pull request as it doesn't seem related to this PR?

@@ -25,9 +25,13 @@ namespace eth
//
// interpreter configuration macros for development, optimizations and tracing
//
// EIP_145 - bitwise shifting
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, why is EIP_145 better/more expressive than EVM_USE_BITSHIFT ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It tells the reader where to find the spec for the feature. And it's consistent with the others.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer using a meaning names, because I'm usually confused by numbers.
As a compromise, can it be EIP145_BITSHIFT or EVM_EIP145_BITSHIFT?

Copy link
Contributor

@gcolvin gcolvin Jan 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I much prefer the compromise.

I chose to use the EIP numbers for a reason--if you are going into the code looking for all configuration macros specific to an EIP all you need to know is the number. Conversely, when you come upon an unnumbered macro in the code you might know EIP it relates to.

If we must remove the numbers from the configuration macros, please add comments at the point of definition as to what EIP they configure.

@axic
Copy link
Member Author

axic commented Oct 4, 2017

Where are the unit tests being run? What did change in jsontests submodule?

@gcolvin
Copy link
Contributor

gcolvin commented Oct 4, 2017

The relationship is the change to use the solidity shift operators rather than exponentiation. The shifts in rc5 are data-dependent, and will be a good stress test.

@gcolvin
Copy link
Contributor

gcolvin commented Oct 4, 2017

If you mean libevm/tests/unittests/performance the tests there are run manually with tests.mk - testing performance on virtualized machines is useless. shift-tests.asm doesn't really belong there so far as being a performance test, but I don't know where else to put it.

@gcolvin
Copy link
Contributor

gcolvin commented Oct 4, 2017

As far as I know nothing changed in jsontests; I just did git submodule update --init. But apparently git thinks something changed, and I'm guessing doing git commit -a ... might have caused the problem?

@gcolvin
Copy link
Contributor

gcolvin commented Oct 4, 2017

@axic It appears my attempt to remove jsontests from the PR failed, although it did allow for an automatic merge again. I will refrain from further attempts to back this out and, with apologies, leave it to you.

@pirapira
Copy link
Member

Please rebase.

@axic
Copy link
Member Author

axic commented Jan 5, 2018

@gumb0 @pirapira @chfast is it possible to include a genesis.json "enable option" field in this PR (e.g. EIP145ForkBlock)?

@gumb0
Copy link
Member

gumb0 commented Jan 5, 2018

It should be possible, you'd need to add EIP145ForkBlok to ChainOperationParams, parse it in ChainParams.cpp, also add a flag to EVMSchedule and define EIP145Schedule and check the flag in EVM

@axic
Copy link
Member Author

axic commented Feb 9, 2018

How about adding support for the constantinople schedule in this PR then? Shouldn't take too long.

@axic
Copy link
Member Author

axic commented Feb 9, 2018

Or could merge this and help @tcsiwula finish #4838

@axic
Copy link
Member Author

axic commented Feb 22, 2018

@gumb0 @chfast I think this is ready for merging now.

@axic axic merged commit 0913b23 into develop Feb 22, 2018
@axic
Copy link
Member Author

axic commented Feb 22, 2018

Merged this as every test succeeded except the code coverage :)

@axic
Copy link
Member Author

axic commented Feb 22, 2018

@gcolvin @tcsiwula thank you for your help finishing this!

@axic axic deleted the bitshift branch February 22, 2018 11:11
@axic
Copy link
Member Author

axic commented Feb 22, 2018

@winsvega @pirapira @holiman everything's ready for writing bitwise shifting state tests now 😉

@gcolvin
Copy link
Contributor

gcolvin commented Feb 22, 2018

@axic Sorry to be late to the game. I believe the runtime tests in VM.cpp can more efficiently be handled in VM::optimize by conditionally replacing the unimplemented instructions with Instruction::INVALID.
https://github.com/ethereum/cpp-ethereum/blob/a3d93a8fb80c2620449e4d399507dd5ac77ecaae/libevm/VMOpt.cpp#L86

@axic
Copy link
Member Author

axic commented Feb 22, 2018

@gcolvin it seems that the optimize function is not even used for the byzantium opcodes. I think it may be a good idea to implement that in a separate PR.

@gcolvin
Copy link
Contributor

gcolvin commented Feb 22, 2018

@axic Unless jump destinations are no longer verified the loop I pointed at is needed, and conditionally invalid opcodes can be tested for there, whether or not my work on bytecode optimization is being abandoned. Once replaced with INVALID these opcodes will be detected by the opcode dispatch without incurring extra overhead on every opcode execution. It would be small change to this PR, or a small new PR, but I haven't time to make it right now.

@axic
Copy link
Member Author

axic commented Feb 22, 2018

it seems that the optimize function is not even used for the byzantium opcodes.

... aka it doesn't even replace Byzantium opcodes with INVALID on non-Byzantium mode.

@gcolvin
Copy link
Contributor

gcolvin commented Feb 22, 2018

Also Known As what? I'm not understanding you, sorry.

@axic
Copy link
Member Author

axic commented Feb 22, 2018

The code you have linked to does not replace any Byzantium opcode with the invalid opcode. Not sure how can I write this sentence differently.

@gcolvin
Copy link
Contributor

gcolvin commented Feb 23, 2018

Something like this, and remove the tests in VM.cpp

	for (size_t pc = 0; pc < nBytes; ++pc)
	{
		Instruction op = Instruction(m_code[pc]);
		TRACE_OP(2, pc, op);

		if (
			op == Instruction:: SHL ||
			op == Instruction::SHR ||
			op == Instruction::SAR
		)
		{
			if (!m_schedule->haveBitwiseShifting)
				m_code[pc] = (byte)Instruction::INVALID;
		}
	. . . 

@axic
Copy link
Member Author

axic commented Feb 23, 2018

I understand how it can be added, all I am saying that it is not present for returndatacopy and returndatasize at the moment.

As a result, this isn't something which was missed in this PR, rather than it is a new improvement, which can be done separately.

@gcolvin
Copy link
Contributor

gcolvin commented Feb 24, 2018

Aha. I didn't understand what you didn't understand ;) I agree it can be done separately. I just wanted to make note of it. I haven't had much time for this code, but try to notice when changes go in.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants