diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index a1a42f971..61726e1cf 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -551,8 +551,12 @@ LinkerObject const& Assembly::assemble() const "Cannot push and assign immutables in the same assembly subroutine." ); - // TODO: assert zero inputs/outputs on code section zero - // TODO: assert one code section being present and *only* one being present unless EOF + assertThrow(!m_codeSections.empty(), AssemblyException, "Expected at least one code section."); + assertThrow(eof || m_codeSections.size() > 1, AssemblyException, "Expected exactly one code section in non-EOF code."); + assertThrow( + m_codeSections.front().inputs == 0 && m_codeSections.front().outputs == 0, AssemblyException, + "Expected the first code section to have zero inputs and outputs." + ); unsigned bytesRequiredForSubs = 0; // TODO: consider fully producing all sub and data refs in this pass already. @@ -569,15 +573,13 @@ LinkerObject const& Assembly::assemble() const // Insert EOF1 header. vector codeSectionSizeOffsets; auto setCodeSectionSize = [&](size_t _section, size_t _size) { - bytesRef length(ret.bytecode.data() + codeSectionSizeOffsets.at(_section), 2); - toBigEndian(_size, length); + toBigEndian(_size, bytesRef(ret.bytecode.data() + codeSectionSizeOffsets.at(_section), 2)); }; std::optional dataSectionSizeOffset; auto setDataSectionSize = [&](size_t _size) { assertThrow(dataSectionSizeOffset.has_value(), AssemblyException, ""); assertThrow(_size <= 0xFFFF, AssemblyException, ""); - bytesRef length(ret.bytecode.data() + *dataSectionSizeOffset, 2); - toBigEndian(static_cast(_size), length); + toBigEndian(static_cast(_size), bytesRef(ret.bytecode.data() + *dataSectionSizeOffset, 2)); }; if (eof) { @@ -591,8 +593,7 @@ LinkerObject const& Assembly::assemble() const ret.bytecode.push_back(0x03); // kind=type ret.bytecode.push_back(0x00); // length of type section ret.bytecode.push_back(0x00); - bytesRef length(&ret.bytecode.back() + 1 - 2, 2); - toBigEndian(m_codeSections.size() * 2, length); + toBigEndian(m_codeSections.size() * 2, bytesRef(&ret.bytecode.back() + 1 - 2, 2)); } for (auto const& codeSection: m_codeSections) { @@ -856,7 +857,6 @@ LinkerObject const& Assembly::assemble() const { assertThrow(m_eofVersion.has_value(), AssemblyException, "Relative jump outside EOF"); assertThrow(subId == numeric_limits::max(), AssemblyException, "Relative jump to sub"); - bytesRef r(ret.bytecode.data() + bytecodeOffset, 2); assertThrow( static_cast(pos) - static_cast(bytecodeOffset + 2u) < 0x7FFF && static_cast(pos) - static_cast(bytecodeOffset + 2u) >= -0x8000, @@ -864,13 +864,12 @@ LinkerObject const& Assembly::assemble() const "Relative jump too far" ); uint16_t relativeOffset = static_cast(pos - (bytecodeOffset + 2u)); - toBigEndian(relativeOffset, r); + toBigEndian(relativeOffset, bytesRef(ret.bytecode.data() + bytecodeOffset, 2)); } else { assertThrow(!m_eofVersion.has_value(), AssemblyException, "Dynamic tag reference within EOF"); - bytesRef r(ret.bytecode.data() + bytecodeOffset, bytesPerTag); - toBigEndian(pos, r); + toBigEndian(pos, bytesRef(ret.bytecode.data() + bytecodeOffset, bytesPerTag)); } } for (auto const& [name, tagInfo]: m_namedTags) @@ -899,15 +898,13 @@ LinkerObject const& Assembly::assemble() const if (references.first == references.second) continue; for (auto ref = references.first; ref != references.second; ++ref) - { - bytesRef r(ret.bytecode.data() + ref->second, bytesPerDataRef); - toBigEndian(ret.bytecode.size(), r); - } + toBigEndian(ret.bytecode.size(), bytesRef(ret.bytecode.data() + ref->second, bytesPerDataRef)); ret.bytecode += dataItem.second; } ret.bytecode += m_auxiliaryData; + // TODO: remove this when transitioning to unified spec headers if (eof && bytesRequiredForDataAndSubsUpperBound > 0 && ret.bytecode.size() == dataStart) { // We have commited to a data section, but not actually needed it, so create a fake one. diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index d8c3ed7d9..40c61f458 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -157,7 +157,6 @@ public: langutil::SourceLocation const& currentSourceLocation() const { return m_currentSourceLocation; } /// Assembles the assembly into bytecode. The assembly should not be modified after this call, since the assembled version is cached. - /// @param eof If true, assemble for EOF, otherwise for legacy EVM output. LinkerObject const& assemble() const; struct OptimiserSettings diff --git a/libevmasm/AssemblyItem.h b/libevmasm/AssemblyItem.h index 2278d2491..ce648cea4 100644 --- a/libevmasm/AssemblyItem.h +++ b/libevmasm/AssemblyItem.h @@ -113,8 +113,8 @@ public: AssemblyItem& operator=(AssemblyItem const&) = default; AssemblyItem& operator=(AssemblyItem&&) = default; - AssemblyItem tag() const { assertThrow(m_type == PushTag || m_type == Tag, util::Exception, ""); return AssemblyItem(Tag, data()); } - AssemblyItem pushTag() const { assertThrow(m_type == PushTag || m_type == Tag, util::Exception, ""); return AssemblyItem(PushTag, data()); } + AssemblyItem tag() const { assertThrow(m_type == PushTag || m_type == Tag || m_type == RelativeJump || m_type == ConditionalRelativeJump, util::Exception, ""); return AssemblyItem(Tag, data()); } + AssemblyItem pushTag() const { assertThrow(m_type == PushTag || m_type == Tag || m_type == RelativeJump || m_type == ConditionalRelativeJump, util::Exception, ""); return AssemblyItem(PushTag, data()); } /// Converts the tag to a subassembly tag. This has to be called in order to move a tag across assemblies. /// @param _subId the identifier of the subassembly the tag is taken from. AssemblyItem toSubAssemblyTag(size_t _subId) const; diff --git a/libevmasm/BlockDeduplicator.cpp b/libevmasm/BlockDeduplicator.cpp index 382d79e6e..ff0f2089d 100644 --- a/libevmasm/BlockDeduplicator.cpp +++ b/libevmasm/BlockDeduplicator.cpp @@ -132,7 +132,7 @@ BlockDeduplicator::BlockIterator& BlockDeduplicator::BlockIterator::operator++() { if (it == end) return *this; - if (SemanticInformation::altersControlFlow(*it) && *it != AssemblyItem{Instruction::JUMPI}) + if (SemanticInformation::altersControlFlow(*it) && *it != AssemblyItem{Instruction::JUMPI} && it->type() != ConditionalRelativeJump) it = end; else { diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index 21a5111a2..c5b2146e3 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -165,6 +165,9 @@ std::map const solidity::evmasm::c_instructions = { "LOG2", Instruction::LOG2 }, { "LOG3", Instruction::LOG3 }, { "LOG4", Instruction::LOG4 }, + { "CALLF", Instruction::CALLF }, + { "RETF", Instruction::RETF }, + { "JUMPF", Instruction::JUMPF }, { "CREATE", Instruction::CREATE }, { "CALL", Instruction::CALL }, { "CALLCODE", Instruction::CALLCODE }, @@ -243,9 +246,9 @@ static std::map const c_instructionInfo = { Instruction::MSIZE, { "MSIZE", 0, 0, 1, false, Tier::Base } }, { Instruction::GAS, { "GAS", 0, 0, 1, false, Tier::Base } }, { Instruction::JUMPDEST, { "JUMPDEST", 0, 0, 0, true, Tier::Special } }, - { Instruction::RJUMP, { "RJUMP", 0, 0, 0, true, Tier::Low } }, - { Instruction::RJUMPI, { "RJUMPI", 0, 0, 0, true, Tier::Low } }, - { Instruction::RJUMPV, { "RJUMPV", 0, 1, 0, true, Tier::Low } }, + { Instruction::RJUMP, { "RJUMP", 2, 0, 0, true, Tier::Low } }, + { Instruction::RJUMPI, { "RJUMPI", 2, 1, 0, true, Tier::Low } }, + { Instruction::RJUMPV, { "RJUMPV", 2, 1, 0, true, Tier::Low } }, { Instruction::PUSH1, { "PUSH1", 1, 0, 1, false, Tier::VeryLow } }, { Instruction::PUSH2, { "PUSH2", 2, 0, 1, false, Tier::VeryLow } }, { Instruction::PUSH3, { "PUSH3", 3, 0, 1, false, Tier::VeryLow } }, @@ -315,6 +318,9 @@ static std::map const c_instructionInfo = { Instruction::LOG2, { "LOG2", 0, 4, 0, true, Tier::Special } }, { Instruction::LOG3, { "LOG3", 0, 5, 0, true, Tier::Special } }, { Instruction::LOG4, { "LOG4", 0, 6, 0, true, Tier::Special } }, + { Instruction::CALLF, { "CALLF", 2, 0, 0, true, Tier::Special } }, + { Instruction::RETF, { "RETF", 0, 0, 0, true, Tier::Special } }, + { Instruction::JUMPF, { "JUMPF", 2, 0, 0, true, Tier::Special } }, { Instruction::CREATE, { "CREATE", 0, 3, 1, true, Tier::Special } }, { Instruction::CALL, { "CALL", 0, 7, 1, true, Tier::Special } }, { Instruction::CALLCODE, { "CALLCODE", 0, 7, 1, true, Tier::Special } }, diff --git a/libevmasm/PeepholeOptimiser.cpp b/libevmasm/PeepholeOptimiser.cpp index c53676254..f1cbd8c81 100644 --- a/libevmasm/PeepholeOptimiser.cpp +++ b/libevmasm/PeepholeOptimiser.cpp @@ -291,7 +291,7 @@ struct IsZeroIsZeroJumpI: SimplePeepholeOptimizerMethod } }; -struct IsZeroIsZeroRJumpI: SimplePeepholeOptimizerMethod +struct IsZeroIsZeroRJumpI: SimplePeepholeOptimizerMethod { static size_t applySimple( AssemblyItem const& _iszero1, @@ -341,7 +341,7 @@ struct EqIsZeroJumpI: SimplePeepholeOptimizerMethod } }; -struct EqIsZeroRJumpI: SimplePeepholeOptimizerMethod +struct EqIsZeroRJumpI: SimplePeepholeOptimizerMethod { static size_t applySimple( AssemblyItem const& _eq, @@ -398,7 +398,7 @@ struct DoubleJump: SimplePeepholeOptimizerMethod }; // rjumpi(tag_1) rjump(tag_2) tag_1: -> iszero rjumpi(tag_2) tag_1: -struct DoubleRJump: SimplePeepholeOptimizerMethod +struct DoubleRJump: SimplePeepholeOptimizerMethod { static size_t applySimple( AssemblyItem const& _rjumpi, @@ -450,7 +450,7 @@ struct JumpToNext: SimplePeepholeOptimizerMethod } }; -struct RJumpToNext: SimplePeepholeOptimizerMethod +struct RJumpToNext: SimplePeepholeOptimizerMethod { static size_t applySimple( AssemblyItem const& _rjump, diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index 14a717e5f..e8e8f9bc6 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -239,7 +239,7 @@ bool SemanticInformation::isJumpInstruction(AssemblyItem const& _item) bool SemanticInformation::altersControlFlow(AssemblyItem const& _item) { - if (_item.type() == evmasm::RetF) + if (_item.type() == evmasm::RetF || _item.type() == evmasm::RelativeJump || _item.type() == evmasm::ConditionalRelativeJump) return true; if (_item.type() != evmasm::Operation) return false; @@ -249,6 +249,9 @@ bool SemanticInformation::altersControlFlow(AssemblyItem const& _item) // continue on the next instruction case Instruction::JUMP: case Instruction::JUMPI: + case Instruction::RJUMP: + case Instruction::RJUMPI: + case Instruction::RJUMPV: case Instruction::RETURN: case Instruction::SELFDESTRUCT: case Instruction::STOP: diff --git a/libsolutil/Numeric.h b/libsolutil/Numeric.h index c0f33e490..f9a678da3 100644 --- a/libsolutil/Numeric.h +++ b/libsolutil/Numeric.h @@ -99,13 +99,13 @@ bool fitsPrecisionBaseX(bigint const& _mantissa, double _log2OfBase, uint32_t _e /// @a Out will typically be either std::string or bytes. /// @a T will typically by unsigned, u160, u256 or bigint. template -inline void toBigEndian(T _val, Out& o_out) +inline void toBigEndian(T _val, Out&& o_out) { static_assert(std::is_same::value || !std::numeric_limits::is_signed, "only unsigned types or bigint supported"); //bigint does not carry sign bit on shift for (auto i = o_out.size(); i != 0; _val >>= 8, i--) { T v = _val & (T)0xff; - o_out[i - 1] = (typename Out::value_type)(uint8_t)v; + o_out[i - 1] = (typename std::remove_reference_t::value_type)(uint8_t)v; } } diff --git a/libyul/backends/evm/AbstractAssembly.h b/libyul/backends/evm/AbstractAssembly.h index f6acc6b76..2825ed8c5 100644 --- a/libyul/backends/evm/AbstractAssembly.h +++ b/libyul/backends/evm/AbstractAssembly.h @@ -60,7 +60,6 @@ public: virtual ~AbstractAssembly() = default; - virtual bool supportsFunctions() const = 0; /// Set a new source location valid starting from the next instruction. virtual void setSourceLocation(langutil::SourceLocation const& _location) = 0; /// Retrieve the current height of the stack. This does not have to be zero diff --git a/libyul/backends/evm/EthAssemblyAdapter.cpp b/libyul/backends/evm/EthAssemblyAdapter.cpp index aea6fe38f..27bcf4f97 100644 --- a/libyul/backends/evm/EthAssemblyAdapter.cpp +++ b/libyul/backends/evm/EthAssemblyAdapter.cpp @@ -44,11 +44,6 @@ EthAssemblyAdapter::EthAssemblyAdapter(evmasm::Assembly& _assembly): { } -bool EthAssemblyAdapter::supportsFunctions() const -{ - return m_assembly.supportsFunctions(); -} - void EthAssemblyAdapter::setSourceLocation(SourceLocation const& _location) { m_assembly.setSourceLocation(_location); diff --git a/libyul/backends/evm/EthAssemblyAdapter.h b/libyul/backends/evm/EthAssemblyAdapter.h index 2bb382700..771c79a28 100644 --- a/libyul/backends/evm/EthAssemblyAdapter.h +++ b/libyul/backends/evm/EthAssemblyAdapter.h @@ -40,7 +40,6 @@ class EthAssemblyAdapter: public AbstractAssembly { public: explicit EthAssemblyAdapter(evmasm::Assembly& _assembly); - bool supportsFunctions() const override; void setSourceLocation(langutil::SourceLocation const& _location) override; int stackHeight() const override; void setStackHeight(int height) override; diff --git a/libyul/backends/evm/NoOutputAssembly.h b/libyul/backends/evm/NoOutputAssembly.h index 0c69ec135..b78d68eaf 100644 --- a/libyul/backends/evm/NoOutputAssembly.h +++ b/libyul/backends/evm/NoOutputAssembly.h @@ -57,8 +57,6 @@ public: ~NoOutputAssembly() override = default; - bool supportsFunctions() const override { return m_hasFunctions; } - void setSourceLocation(langutil::SourceLocation const&) override {} int stackHeight() const override { return m_stackHeight; } void setStackHeight(int height) override { m_stackHeight = height; }