diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 153ab547e9a8..fdc27241550b 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -1396,6 +1396,7 @@ LinkerObject const& Assembly::assembleEOF() const m_tagPositionsInBytecode = std::vector(m_usedTags, std::numeric_limits::max()); std::map dataSectionRef; + std::map tagRef; for (auto&& [codeSectionIndex, codeSection]: m_codeSections | ranges::views::enumerate) { @@ -1412,7 +1413,9 @@ LinkerObject const& Assembly::assembleEOF() const solAssert( item.instruction() != Instruction::DATALOADN && item.instruction() != Instruction::RETURNCONTRACT && - item.instruction() != Instruction::EOFCREATE + item.instruction() != Instruction::EOFCREATE && + item.instruction() != Instruction::RJUMP && + item.instruction() != Instruction::RJUMPI ); solAssert(!(item.instruction() >= Instruction::PUSH0 && item.instruction() <= Instruction::PUSH32)); ret.bytecode += assembleOperation(item); @@ -1427,6 +1430,14 @@ LinkerObject const& Assembly::assembleEOF() const ret.linkReferences.insert(linkRef); break; } + case RelativeJump: + case ConditionalRelativeJump: + { + ret.bytecode.push_back(static_cast(item.instruction())); + tagRef[ret.bytecode.size()] = item.relativeJumpTagID(); + appendBigEndianUint16(ret.bytecode, 0u); + break; + } case EOFCreate: { ret.bytecode.push_back(static_cast(Instruction::EOFCREATE)); @@ -1465,6 +1476,18 @@ LinkerObject const& Assembly::assembleEOF() const setBigEndianUint16(ret.bytecode, codeSectionSizePositions[codeSectionIndex], ret.bytecode.size() - sectionStart); } + for (auto const& [refPos, tagId]: tagRef) + { + solAssert(tagId < m_tagPositionsInBytecode.size(), "Reference to non-existing tag."); + size_t tagPos = m_tagPositionsInBytecode[tagId]; + solAssert(tagPos != std::numeric_limits::max(), "Reference to tag without position."); + + ptrdiff_t const relativeJumpOffset = static_cast(tagPos) - (static_cast(refPos) + 2); + solRequire(-0x8000 <= relativeJumpOffset && relativeJumpOffset <= 0x7FFF, AssemblyException, "Relative jump too far"); + solAssert(relativeJumpOffset < -2 || 0 <= relativeJumpOffset, "Relative jump offset into immediate argument."); + setBigEndianUint16(ret.bytecode, refPos, static_cast(static_cast(relativeJumpOffset))); + } + for (auto i: referencedSubIds) ret.bytecode += m_subs[i]->assemble().bytecode; diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index daeea6e8cf0e..4b52ca3d7970 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -65,6 +65,7 @@ class Assembly } std::optional eofVersion() const { return m_eofVersion; } + bool supportsRelativeJumps() const { return m_eofVersion.has_value(); } AssemblyItem newTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(Tag, m_usedTags++); } AssemblyItem newPushTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(PushTag, m_usedTags++); } /// Returns a tag identified by the given name. Creates it if it does not yet exist. diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp index d26e01f8a7ab..96c007ea5ce7 100644 --- a/libevmasm/AssemblyItem.cpp +++ b/libevmasm/AssemblyItem.cpp @@ -62,13 +62,21 @@ AssemblyItem AssemblyItem::toSubAssemblyTag(size_t _subId) const std::pair AssemblyItem::splitForeignPushTag() const { - assertThrow(m_type == PushTag || m_type == Tag, util::Exception, ""); + solAssert(m_type == PushTag || m_type == Tag || m_type == RelativeJump || m_type == ConditionalRelativeJump); u256 combined = u256(data()); size_t subId = static_cast((combined >> 64) - 1); size_t tag = static_cast(combined & 0xffffffffffffffffULL); return std::make_pair(subId, tag); } +size_t AssemblyItem::relativeJumpTagID() const +{ + solAssert(m_type == RelativeJump || m_type == ConditionalRelativeJump); + auto const [subId, tagId] = splitForeignPushTag(); + solAssert(subId == std::numeric_limits::max(), "Relative jump to sub"); + return tagId; +} + std::pair AssemblyItem::nameAndData(langutil::EVMVersion _evmVersion) const { switch (type()) @@ -76,6 +84,8 @@ std::pair AssemblyItem::nameAndData(langutil::EVMVersi case Operation: case EOFCreate: case ReturnContract: + case RelativeJump: + case ConditionalRelativeJump: return {instructionInfo(instruction(), _evmVersion).name, m_data != nullptr ? toStringInHex(*m_data) : ""}; case Push: return {"PUSH", toStringInHex(data())}; @@ -115,7 +125,8 @@ std::pair AssemblyItem::nameAndData(langutil::EVMVersi void AssemblyItem::setPushTagSubIdAndTag(size_t _subId, size_t _tag) { - assertThrow(m_type == PushTag || m_type == Tag, util::Exception, ""); + solAssert(m_type == PushTag || m_type == Tag || m_type == RelativeJump || m_type == ConditionalRelativeJump); + solAssert(!(m_type == RelativeJump || m_type == ConditionalRelativeJump) || _subId == std::numeric_limits::max()); u256 data = _tag; if (_subId != std::numeric_limits::max()) data |= (u256(_subId) + 1) << 64; @@ -167,6 +178,8 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength, langutil::EVMVersion _ } case VerbatimBytecode: return std::get<2>(*m_verbatimBytecode).size(); + case RelativeJump: + case ConditionalRelativeJump: case AuxDataLoadN: return 1 + 2; case EOFCreate: @@ -201,6 +214,8 @@ size_t AssemblyItem::returnValues() const case Operation: case EOFCreate: case ReturnContract: + case RelativeJump: + case ConditionalRelativeJump: // The latest EVMVersion is used here, since the InstructionInfo is assumed to be // the same across all EVM versions except for the instruction name. return static_cast(instructionInfo(instruction(), EVMVersion()).ret); @@ -236,6 +251,8 @@ bool AssemblyItem::canBeFunctional() const case Operation: case EOFCreate: case ReturnContract: + case RelativeJump: + case ConditionalRelativeJump: return !isDupInstruction(instruction()) && !isSwapInstruction(instruction()); case Push: case PushTag: @@ -360,6 +377,12 @@ std::string AssemblyItem::toAssemblyText(Assembly const& _assembly) const case ReturnContract: text = "returcontract{" + std::to_string(static_cast(data())) + "}"; break; + case RelativeJump: + text = "rjump{" + std::string("tag_") + std::to_string(relativeJumpTagID()) + "}"; + break; + case ConditionalRelativeJump: + text = "rjumpi{" + std::string("tag_") + std::to_string(relativeJumpTagID()) + "}"; + break; } if (m_jumpType == JumpType::IntoFunction || m_jumpType == JumpType::OutOfFunction) { @@ -380,6 +403,8 @@ std::ostream& solidity::evmasm::operator<<(std::ostream& _out, AssemblyItem cons case Operation: case EOFCreate: case ReturnContract: + case RelativeJump: + case ConditionalRelativeJump: _out << " " << instructionInfo(_item.instruction(), EVMVersion()).name; if (_item.instruction() == Instruction::JUMP || _item.instruction() == Instruction::JUMPI) _out << "\t" << _item.getJumpTypeAsString(); diff --git a/libevmasm/AssemblyItem.h b/libevmasm/AssemblyItem.h index e51d07b16150..a9c46bb14edb 100644 --- a/libevmasm/AssemblyItem.h +++ b/libevmasm/AssemblyItem.h @@ -57,6 +57,8 @@ enum AssemblyItemType AuxDataLoadN, EOFCreate, ///< Creates new contract using subcontainer as initcode ReturnContract, ///< Returns new container (with auxiliary data filled in) to be deployed + RelativeJump, ///< Jumps to relative position accordingly to its argument + ConditionalRelativeJump, ///< Same as RelativeJump but takes condition from the stack VerbatimBytecode ///< Contains data that is inserted into the bytecode code section without modification. }; @@ -111,20 +113,32 @@ class AssemblyItem { return AssemblyItem(ReturnContract, Instruction::RETURNCONTRACT, _containerID, std::move(_debugData)); } + static AssemblyItem relativeJumpTo(AssemblyItem _tag, langutil::DebugData::ConstPtr _debugData = langutil::DebugData::create()) + { + solAssert(_tag.type() == Tag); + return AssemblyItem(RelativeJump, Instruction::RJUMP, _tag.data(), _debugData); + } + static AssemblyItem conditionalRelativeJumpTo(AssemblyItem _tag, langutil::DebugData::ConstPtr _debugData = langutil::DebugData::create()) + { + solAssert(_tag.type() == Tag); + return AssemblyItem(ConditionalRelativeJump, Instruction::RJUMPI, _tag.data(), _debugData); + } AssemblyItem(AssemblyItem const&) = default; AssemblyItem(AssemblyItem&&) = default; 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 { solAssert(m_type == PushTag || m_type == Tag || m_type == RelativeJump || m_type == ConditionalRelativeJump); return AssemblyItem(Tag, data()); } + AssemblyItem pushTag() const { solAssert(m_type == PushTag || m_type == Tag || m_type == RelativeJump || m_type == ConditionalRelativeJump); 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; /// @returns splits the data of the push tag into sub assembly id and actual tag id. /// The sub assembly id of non-foreign push tags is -1. std::pair splitForeignPushTag() const; + /// @returns relative jump target tag ID. Asserts that it is not foreign tag. + size_t relativeJumpTagID() const; /// Sets sub-assembly part and tag for a push tag. void setPushTagSubIdAndTag(size_t _subId, size_t _tag); @@ -145,9 +159,14 @@ class AssemblyItem /// @returns true if the item has m_instruction properly set. bool hasInstruction() const { - return m_type == Operation || m_type == EOFCreate || m_type == ReturnContract; + return + m_type == Operation || + m_type == EOFCreate || + m_type == ReturnContract || + m_type == RelativeJump || + m_type == ConditionalRelativeJump; } - /// @returns the instruction of this item (only valid if type() == Operation || EOFCreate || ReturnContract) + /// @returns the instruction of this item (only valid if hasInstruction returns true) Instruction instruction() const { solAssert(hasInstruction()); diff --git a/libevmasm/BlockDeduplicator.cpp b/libevmasm/BlockDeduplicator.cpp index c978bae2bb39..3ba8cfcd3523 100644 --- a/libevmasm/BlockDeduplicator.cpp +++ b/libevmasm/BlockDeduplicator.cpp @@ -106,7 +106,7 @@ bool BlockDeduplicator::applyTagReplacement( { bool changed = false; for (AssemblyItem& item: _items) - if (item.type() == PushTag) + if (item.type() == PushTag || item.type() == RelativeJump || item.type() == ConditionalRelativeJump) { size_t subId; size_t tagId; @@ -131,7 +131,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/GasMeter.cpp b/libevmasm/GasMeter.cpp index a501b487f45a..3ee3a39cbdc4 100644 --- a/libevmasm/GasMeter.cpp +++ b/libevmasm/GasMeter.cpp @@ -272,6 +272,8 @@ unsigned GasMeter::runGas(Instruction _instruction, langutil::EVMVersion _evmVer { case Tier::Zero: return GasCosts::tier0Gas; case Tier::Base: return GasCosts::tier1Gas; + case Tier::RJump: return GasCosts::tier1Gas; + case Tier::RJumpI: return GasCosts::rjumpiGas; case Tier::VeryLow: return GasCosts::tier2Gas; case Tier::Low: return GasCosts::tier3Gas; case Tier::Mid: return GasCosts::tier4Gas; diff --git a/libevmasm/GasMeter.h b/libevmasm/GasMeter.h index 50a783e1a6e5..f202b358e5f0 100644 --- a/libevmasm/GasMeter.h +++ b/libevmasm/GasMeter.h @@ -177,6 +177,7 @@ namespace GasCosts return _evmVersion >= langutil::EVMVersion::istanbul() ? 16 : 68; } static unsigned const copyGas = 3; + static unsigned const rjumpiGas = 4; } /** diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index 5122fb01f604..60ec75f9284e 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -169,6 +169,8 @@ std::map const solidity::evmasm::c_instructions = { "LOG3", Instruction::LOG3 }, { "LOG4", Instruction::LOG4 }, { "DATALOADN", Instruction::DATALOADN }, + { "RJUMP", Instruction::RJUMP }, + { "RJUMPI", Instruction::RJUMPI }, { "EOFCREATE", Instruction::EOFCREATE }, { "RETURNCONTRACT", Instruction::RETURNCONTRACT }, { "CREATE", Instruction::CREATE }, @@ -256,6 +258,8 @@ static std::map const c_instructionInfo = {Instruction::GAS, {"GAS", 0, 0, 1, false, Tier::Base}}, {Instruction::JUMPDEST, {"JUMPDEST", 0, 0, 0, true, Tier::Special}}, {Instruction::DATALOADN, {"DATALOADN", 2, 0, 1, true, Tier::Low}}, + {Instruction::RJUMP, {"RJUMP", 2, 0, 0, true, Tier::RJump}}, + {Instruction::RJUMPI, {"RJUMPI", 2, 1, 0, true, Tier::RJumpI}}, {Instruction::PUSH0, {"PUSH0", 0, 0, 1, false, Tier::Base}}, {Instruction::PUSH1, {"PUSH1", 1, 0, 1, false, Tier::VeryLow}}, {Instruction::PUSH2, {"PUSH2", 2, 0, 1, false, Tier::VeryLow}}, diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index d9e96c49ad48..76998c538d5f 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -183,6 +183,9 @@ enum class Instruction: uint8_t LOG4, ///< Makes a log entry; 4 topics. DATALOADN = 0xd1, ///< load data from EOF data section + + RJUMP = 0xe0, ///< relative jump + RJUMPI = 0xe1, ///< conditional relative jump EOFCREATE = 0xec, ///< create a new account with associated container code. RETURNCONTRACT = 0xee, ///< return container to be deployed with axiliary data filled in. CREATE = 0xf0, ///< create a new account with associated code @@ -298,7 +301,9 @@ enum class Tier // NOTE: Tiers should be ordered by cost, since we sometimes perform comparisons between them. Zero = 0, // 0, Zero Base, // 2, Quick + RJump, // 2, RJump VeryLow, // 3, Fastest + RJumpI, // 4, Low, // 5, Fast Mid, // 8, Mid High, // 10, Slow diff --git a/libevmasm/JumpdestRemover.cpp b/libevmasm/JumpdestRemover.cpp index 7afa715a88df..7ded5ae90889 100644 --- a/libevmasm/JumpdestRemover.cpp +++ b/libevmasm/JumpdestRemover.cpp @@ -58,7 +58,7 @@ std::set JumpdestRemover::referencedTags(AssemblyItems const& _items, si { std::set ret; for (auto const& item: _items) - if (item.type() == PushTag) + if (item.type() == PushTag || item.type() == RelativeJump || item.type() == ConditionalRelativeJump) { auto subAndTag = item.splitForeignPushTag(); if (subAndTag.first == _subId) diff --git a/libevmasm/PeepholeOptimiser.cpp b/libevmasm/PeepholeOptimiser.cpp index 40937e73f107..64650eaeea9f 100644 --- a/libevmasm/PeepholeOptimiser.cpp +++ b/libevmasm/PeepholeOptimiser.cpp @@ -328,6 +328,29 @@ struct IsZeroIsZeroJumpI: SimplePeepholeOptimizerMethod } }; +struct IsZeroIsZeroRJumpI: SimplePeepholeOptimizerMethod +{ + static size_t applySimple( + AssemblyItem const& _iszero1, + AssemblyItem const& _iszero2, + AssemblyItem const& _rjumpi, + std::back_insert_iterator _out + ) + { + if ( + _iszero1 == Instruction::ISZERO && + _iszero2 == Instruction::ISZERO && + _rjumpi.type() == ConditionalRelativeJump + ) + { + *_out = _rjumpi; + return true; + } + else + return false; + } +}; + struct EqIsZeroJumpI: SimplePeepholeOptimizerMethod { static size_t applySimple( @@ -355,6 +378,30 @@ struct EqIsZeroJumpI: SimplePeepholeOptimizerMethod } }; +struct EqIsZeroRJumpI: SimplePeepholeOptimizerMethod +{ + static size_t applySimple( + AssemblyItem const& _eq, + AssemblyItem const& _iszero, + AssemblyItem const& _rjumpi, + std::back_insert_iterator _out + ) + { + if ( + _eq == Instruction::EQ && + _iszero == Instruction::ISZERO && + _rjumpi.type() == ConditionalRelativeJump + ) + { + *_out = AssemblyItem(Instruction::SUB, _eq.debugData()); + *_out = _rjumpi; + return true; + } + else + return false; + } +}; + // push_tag_1 jumpi push_tag_2 jump tag_1: -> iszero push_tag_2 jumpi tag_1: struct DoubleJump: SimplePeepholeOptimizerMethod { @@ -387,6 +434,33 @@ struct DoubleJump: SimplePeepholeOptimizerMethod } }; +// rjumpi(tag_1) rjump(tag_2) tag_1: -> iszero rjumpi(tag_2) tag_1: +struct DoubleRJump: SimplePeepholeOptimizerMethod +{ + static size_t applySimple( + AssemblyItem const& _rjumpi, + AssemblyItem const& _rjump, + AssemblyItem const& _tag1, + std::back_insert_iterator _out + ) + { + if ( + _rjumpi.type() == ConditionalRelativeJump && + _rjump.type() == RelativeJump && + _tag1.type() == Tag && + _rjumpi.data() == _tag1.data() + ) + { + *_out = AssemblyItem(Instruction::ISZERO, _rjumpi.debugData()); + *_out = AssemblyItem::conditionalRelativeJumpTo(_rjump.tag(), _rjump.debugData()); + *_out = _tag1; + return true; + } + else + return false; + } +}; + struct JumpToNext: SimplePeepholeOptimizerMethod { static size_t applySimple( @@ -413,6 +487,30 @@ struct JumpToNext: SimplePeepholeOptimizerMethod } }; +struct RJumpToNext: SimplePeepholeOptimizerMethod +{ + static size_t applySimple( + AssemblyItem const& _rjump, + AssemblyItem const& _tag, + std::back_insert_iterator _out + ) + { + if ( + (_rjump.type() == ConditionalRelativeJump || _rjump.type() == RelativeJump) && + _tag.type() == Tag && + _rjump.data() == _tag.data() + ) + { + if (_rjump.type() == ConditionalRelativeJump) + *_out = AssemblyItem(Instruction::POP, _rjump.debugData()); + *_out = _tag; + return true; + } + else + return false; + } +}; + struct TagConjunctions: SimplePeepholeOptimizerMethod { static bool applySimple( @@ -476,6 +574,7 @@ struct UnreachableCode return false; if ( it[0] != Instruction::JUMP && + it[0] != Instruction::RJUMP && it[0] != Instruction::RETURN && it[0] != Instruction::STOP && it[0] != Instruction::INVALID && @@ -618,9 +717,30 @@ bool PeepholeOptimiser::optimise() while (state.i < m_items.size()) applyMethods( state, - PushPop(), OpPop(), OpStop(), OpReturnRevert(), DoublePush(), DoubleSwap(), CommutativeSwap(), SwapComparison(), - DupSwap(), IsZeroIsZeroJumpI(), EqIsZeroJumpI(), DoubleJump(), JumpToNext(), UnreachableCode(), DeduplicateNextTagSize3(), - DeduplicateNextTagSize2(), DeduplicateNextTagSize1(), TagConjunctions(), TruthyAnd(), Identity() + PushPop(), + OpPop(), + OpStop(), + OpReturnRevert(), + DoublePush(), + DoubleSwap(), + CommutativeSwap(), + SwapComparison(), + DupSwap(), + IsZeroIsZeroJumpI(), + IsZeroIsZeroRJumpI(), // EOF specific + EqIsZeroJumpI(), + EqIsZeroRJumpI(), // EOF specific + DoubleJump(), + DoubleRJump(), // EOF specific + JumpToNext(), + RJumpToNext(), // EOF specific + UnreachableCode(), + DeduplicateNextTagSize3(), + DeduplicateNextTagSize2(), + DeduplicateNextTagSize1(), + TagConjunctions(), + TruthyAnd(), + Identity() ); if (m_optimisedItems.size() < m_items.size() || ( m_optimisedItems.size() == m_items.size() && ( diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index e909a7074dbb..99a5a0f4348e 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -303,7 +303,13 @@ bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item) bool SemanticInformation::isJumpInstruction(AssemblyItem const& _item) { - return _item == Instruction::JUMP || _item == Instruction::JUMPI; + return + _item == Instruction::JUMP || + _item == Instruction::JUMPI || + _item == Instruction::RJUMP || + _item == Instruction::RJUMPI || + _item.type() == RelativeJump || + _item.type() == ConditionalRelativeJump; } bool SemanticInformation::altersControlFlow(AssemblyItem const& _item) @@ -317,6 +323,8 @@ 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::RETURN: case Instruction::SELFDESTRUCT: case Instruction::STOP: @@ -613,6 +621,8 @@ bool SemanticInformation::invalidInPureFunctions(Instruction _instruction) bool SemanticInformation::invalidInViewFunctions(Instruction _instruction) { + // Relative jumps cannot jump out of the current code section of EOF so they are valid in view functions + // (under the assumption that every Solidity function actually gets its own code section). switch (_instruction) { case Instruction::SSTORE: diff --git a/liblangutil/EVMVersion.cpp b/liblangutil/EVMVersion.cpp index 9041ae793492..f469f337d632 100644 --- a/liblangutil/EVMVersion.cpp +++ b/liblangutil/EVMVersion.cpp @@ -80,6 +80,8 @@ bool EVMVersion::hasOpcode(Instruction _opcode, std::optional _eofVersi case Instruction::EOFCREATE: case Instruction::RETURNCONTRACT: case Instruction::DATALOADN: + case Instruction::RJUMP: + case Instruction::RJUMPI: return _eofVersion.has_value(); default: return true; diff --git a/libyul/backends/evm/EthAssemblyAdapter.cpp b/libyul/backends/evm/EthAssemblyAdapter.cpp index 604177c4f78a..d8810b261db3 100644 --- a/libyul/backends/evm/EthAssemblyAdapter.cpp +++ b/libyul/backends/evm/EthAssemblyAdapter.cpp @@ -106,14 +106,31 @@ void EthAssemblyAdapter::appendJump(int _stackDiffAfter, JumpType _jumpType) void EthAssemblyAdapter::appendJumpTo(LabelID _labelId, int _stackDiffAfter, JumpType _jumpType) { - appendLabelReference(_labelId); - appendJump(_stackDiffAfter, _jumpType); + if (m_assembly.supportsRelativeJumps()) + { + m_assembly.append(evmasm::AssemblyItem::relativeJumpTo(evmasm::AssemblyItem(evmasm::Tag, _labelId))); + yulAssert(_jumpType == JumpType::Ordinary); + m_assembly.adjustDeposit(_stackDiffAfter); + } + else + { + appendLabelReference(_labelId); + appendJump(_stackDiffAfter, _jumpType); + } } void EthAssemblyAdapter::appendJumpToIf(LabelID _labelId, JumpType _jumpType) { - appendLabelReference(_labelId); - appendJumpInstruction(evmasm::Instruction::JUMPI, _jumpType); + if (m_assembly.supportsRelativeJumps()) + { + m_assembly.append(evmasm::AssemblyItem::conditionalRelativeJumpTo(evmasm::AssemblyItem(evmasm::Tag, _labelId))); + yulAssert(_jumpType == JumpType::Ordinary); + } + else + { + appendLabelReference(_labelId); + appendJumpInstruction(evmasm::Instruction::JUMPI, _jumpType); + } } void EthAssemblyAdapter::appendAssemblySize() diff --git a/test/libyul/objectCompiler/eof/rjumps.yul b/test/libyul/objectCompiler/eof/rjumps.yul new file mode 100644 index 000000000000..b13f3c3625e5 --- /dev/null +++ b/test/libyul/objectCompiler/eof/rjumps.yul @@ -0,0 +1,40 @@ +object "a" { + code { + if true { + mstore(0, 1) + } + + return(0, 32) + } +} + +// ==== +// EVMVersion: >=prague +// bytecodeFormat: >=EOFv1 +// ---- +// Assembly: +// /* "source":49:53 */ +// 0x01 +// /* "source":46:70 */ +// rjumpi{tag_1} +// /* "source":22:112 */ +// tag_2: +// /* "source":93:95 */ +// 0x20 +// /* "source":90:91 */ +// 0x00 +// /* "source":83:96 */ +// return +// /* "source":54:70 */ +// tag_1: +// /* "source":66:67 */ +// 0x01 +// /* "source":63:64 */ +// 0x00 +// /* "source":56:68 */ +// mstore +// /* "source":54:70 */ +// rjump{tag_2} +// Bytecode: ef00010100040200010010040000000080ffff6001e1000460205ff360015f52e0fff5 +// Opcodes: 0xEF STOP ADD ADD STOP DIV MUL STOP ADD STOP LT DIV STOP STOP STOP STOP DUP1 SELFDESTRUCT SELFDESTRUCT PUSH1 0x1 RJUMPI 0x4 PUSH1 0x20 PUSH0 RETURN PUSH1 0x1 PUSH0 MSTORE RJUMP 0xFFF5 +// SourceMappings: 49:4:0:-:0;46:24;22:90;93:2;90:1;83:13;54:16;66:1;63;56:12;54:16 diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp index 0f1f37451569..044fff8ce6f4 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp @@ -490,6 +490,8 @@ u256 EVMInstructionInterpreter::eval( case Instruction::DATALOADN: case Instruction::EOFCREATE: case Instruction::RETURNCONTRACT: + case Instruction::RJUMP: + case Instruction::RJUMPI: solUnimplemented("EOF not yet supported by Yul interpreter."); }