Skip to content

Commit

Permalink
eof: Implement relative jumps support
Browse files Browse the repository at this point in the history
  • Loading branch information
rodiazet committed Oct 25, 2024
1 parent 5e691b4 commit 713beaa
Show file tree
Hide file tree
Showing 14 changed files with 261 additions and 15 deletions.
23 changes: 23 additions & 0 deletions libevmasm/Assembly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1396,6 +1396,7 @@ LinkerObject const& Assembly::assembleEOF() const

m_tagPositionsInBytecode = std::vector<size_t>(m_usedTags, std::numeric_limits<size_t>::max());
std::map<size_t, uint16_t> dataSectionRef;
std::map<size_t, size_t> tagRef;

for (auto&& [codeSectionIndex, codeSection]: m_codeSections | ranges::views::enumerate)
{
Expand Down Expand Up @@ -1427,6 +1428,16 @@ LinkerObject const& Assembly::assembleEOF() const
ret.linkReferences.insert(linkRef);
break;
}
case RelativeJump:
case ConditionalRelativeJump:
{
ret.bytecode.push_back(static_cast<uint8_t>(item.type() == RelativeJump ? Instruction::RJUMP : Instruction::RJUMPI));
auto [subId, tagId] = item.splitForeignPushTag();
assertThrow(subId == std::numeric_limits<size_t>::max(), AssemblyException, "Relative jump to sub");
tagRef[ret.bytecode.size()] = tagId;
appendBigEndianUint16(ret.bytecode, 0u);
break;
}
case EOFCreate:
{
ret.bytecode.push_back(static_cast<uint8_t>(Instruction::EOFCREATE));
Expand Down Expand Up @@ -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<size_t>::max(), "Reference to tag without position.");

ptrdiff_t const relativeJumpOffset = static_cast<ptrdiff_t>(tagPos) - (static_cast<ptrdiff_t>(refPos) + 2);
solRequire(relativeJumpOffset < 0x7FFF && relativeJumpOffset >= -0x8000, AssemblyException, "Relative jump too far");

setBigEndianUint16(ret.bytecode, refPos, static_cast<uint16_t>(relativeJumpOffset));
}

for (auto i: referencedSubIds)
ret.bytecode += m_subs[i]->assemble().bytecode;

Expand Down
1 change: 1 addition & 0 deletions libevmasm/Assembly.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class Assembly
}

std::optional<uint8_t> 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.
Expand Down
36 changes: 34 additions & 2 deletions libevmasm/AssemblyItem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ AssemblyItem AssemblyItem::toSubAssemblyTag(size_t _subId) const

std::pair<size_t, size_t> 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<size_t>((combined >> 64) - 1);
size_t tag = static_cast<size_t>(combined & 0xffffffffffffffffULL);
Expand Down Expand Up @@ -108,6 +108,10 @@ std::pair<std::string, std::string> AssemblyItem::nameAndData(langutil::EVMVersi
return {"EOFCREATE", util::toString(data())};
case ReturnContract:
return {"RETURNCONTRACT", util::toString(data())};
case RelativeJump:
return {"RJUMP", util::toString(data())};
case ConditionalRelativeJump:
return {"RJUMPI", util::toString(data())};
case UndefinedItem:
solAssert(false);
}
Expand All @@ -117,7 +121,7 @@ std::pair<std::string, std::string> 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);
u256 data = _tag;
if (_subId != std::numeric_limits<size_t>::max())
data |= (u256(_subId) + 1) << 64;
Expand Down Expand Up @@ -169,6 +173,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:
Expand All @@ -192,6 +198,8 @@ size_t AssemblyItem::arguments() const
return std::get<0>(*m_verbatimBytecode);
else if (type() == AssignImmutable)
return 2;
else if (type() == ConditionalRelativeJump)
return 1;
else if (type() == EOFCreate)
return 4;
else if (type() == ReturnContract)
Expand Down Expand Up @@ -223,6 +231,8 @@ size_t AssemblyItem::returnValues() const
case VerbatimBytecode:
return std::get<1>(*m_verbatimBytecode);
case ReturnContract:
case RelativeJump:
case ConditionalRelativeJump:
return 0;
case AuxDataLoadN:
case EOFCreate:
Expand Down Expand Up @@ -256,6 +266,8 @@ bool AssemblyItem::canBeFunctional() const
return true;
case Tag:
case ReturnContract:
case RelativeJump:
case ConditionalRelativeJump:
return false;
case AssignImmutable:
case VerbatimBytecode:
Expand Down Expand Up @@ -361,6 +373,12 @@ std::string AssemblyItem::toAssemblyText(Assembly const& _assembly) const
assertThrow(data() <= std::numeric_limits<size_t>::max(), AssemblyException, "Invalid auxdataloadn argument.");
text = "auxdataloadn(" + std::to_string(static_cast<size_t>(data())) + ")";
break;
case RelativeJump:
text = "rjump{" + std::string("tag_") + std::to_string(static_cast<size_t>(data())) + "}";
break;
case ConditionalRelativeJump:
text = "rjumpi{" + std::string("tag_") + std::to_string(static_cast<size_t>(data())) + "}";
break;
case EOFCreate:
text = "eofcreate{" + std::to_string(static_cast<size_t>(data())) + "}";
break;
Expand Down Expand Up @@ -401,6 +419,20 @@ std::ostream& solidity::evmasm::operator<<(std::ostream& _out, AssemblyItem cons
_out << " PushTag " << subId << ":" << _item.splitForeignPushTag().second;
break;
}
case RelativeJump:
{
auto const& [subId, tagId] = _item.splitForeignPushTag();
solAssert(subId == std::numeric_limits<size_t>::max(), "Relative jump to sub");
_out << " RelativeJump " << tagId;
break;
}
case ConditionalRelativeJump:
{
auto const& [subId, tagId] = _item.splitForeignPushTag();
solAssert(subId == std::numeric_limits<size_t>::max(), "Conditional relative jump to sub");
_out << " ConditionalRelativeJump " << tagId;
break;
}
case Tag:
_out << " Tag " << _item.data();
break;
Expand Down
14 changes: 12 additions & 2 deletions libevmasm/AssemblyItem.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
};

Expand Down Expand Up @@ -103,14 +105,22 @@ class AssemblyItem
{
return AssemblyItem(ReturnContract, _containerID, std::move(_debugData));
}
static AssemblyItem jumpTo(AssemblyItem _tag, langutil::DebugData::ConstPtr _debugData = langutil::DebugData::create())
{
return AssemblyItem(RelativeJump, _tag.data(), _debugData);
}
static AssemblyItem conditionalJumpTo(AssemblyItem _tag, langutil::DebugData::ConstPtr _debugData = langutil::DebugData::create())
{
return AssemblyItem(ConditionalRelativeJump, _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 { 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;
Expand Down
4 changes: 2 additions & 2 deletions libevmasm/BlockDeduplicator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
{
Expand Down
4 changes: 4 additions & 0 deletions libevmasm/Instruction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ std::map<std::string, Instruction> 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 },
Expand Down Expand Up @@ -256,6 +258,8 @@ static std::map<Instruction, InstructionInfo> 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::Low }},
{Instruction::RJUMPI, {"RJUMPI", 2, 1, 0, true, Tier::Low }},
{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}},
Expand Down
3 changes: 3 additions & 0 deletions libevmasm/Instruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -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, ///< conidtional 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
Expand Down
2 changes: 1 addition & 1 deletion libevmasm/JumpdestRemover.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ std::set<size_t> JumpdestRemover::referencedTags(AssemblyItems const& _items, si
{
std::set<size_t> 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)
Expand Down
104 changes: 102 additions & 2 deletions libevmasm/PeepholeOptimiser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,29 @@ struct IsZeroIsZeroJumpI: SimplePeepholeOptimizerMethod<IsZeroIsZeroJumpI>
}
};

struct IsZeroIsZeroRJumpI: SimplePeepholeOptimizerMethod<IsZeroIsZeroRJumpI>
{
static size_t applySimple(
AssemblyItem const& _iszero1,
AssemblyItem const& _iszero2,
AssemblyItem const& _rjumpi,
std::back_insert_iterator<AssemblyItems> _out
)
{
if (
_iszero1 == Instruction::ISZERO &&
_iszero2 == Instruction::ISZERO &&
_rjumpi.type() == ConditionalRelativeJump
)
{
*_out = _rjumpi;
return true;
}
else
return false;
}
};

struct EqIsZeroJumpI: SimplePeepholeOptimizerMethod<EqIsZeroJumpI>
{
static size_t applySimple(
Expand Down Expand Up @@ -355,6 +378,30 @@ struct EqIsZeroJumpI: SimplePeepholeOptimizerMethod<EqIsZeroJumpI>
}
};

struct EqIsZeroRJumpI: SimplePeepholeOptimizerMethod<EqIsZeroRJumpI>
{
static size_t applySimple(
AssemblyItem const& _eq,
AssemblyItem const& _iszero,
AssemblyItem const& _rjumpi,
std::back_insert_iterator<AssemblyItems> _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<DoubleJump>
{
Expand Down Expand Up @@ -387,6 +434,33 @@ struct DoubleJump: SimplePeepholeOptimizerMethod<DoubleJump>
}
};

// rjumpi(tag_1) rjump(tag_2) tag_1: -> iszero rjumpi(tag_2) tag_1:
struct DoubleRJump: SimplePeepholeOptimizerMethod<DoubleRJump>
{
static size_t applySimple(
AssemblyItem const& _rjumpi,
AssemblyItem const& _rjump,
AssemblyItem const& _tag1,
std::back_insert_iterator<AssemblyItems> _out
)
{
if (
_rjumpi.type() == ConditionalRelativeJump &&
_rjump.type() == RelativeJump &&
_tag1.type() == Tag &&
_rjumpi.data() == _tag1.data()
)
{
*_out = AssemblyItem(Instruction::ISZERO, _rjumpi.debugData());
*_out = AssemblyItem::conditionalJumpTo(_rjump.tag(), _rjump.debugData());
*_out = _tag1;
return true;
}
else
return false;
}
};

struct JumpToNext: SimplePeepholeOptimizerMethod<JumpToNext>
{
static size_t applySimple(
Expand All @@ -413,6 +487,30 @@ struct JumpToNext: SimplePeepholeOptimizerMethod<JumpToNext>
}
};

struct RJumpToNext: SimplePeepholeOptimizerMethod<RJumpToNext>
{
static size_t applySimple(
AssemblyItem const& _rjump,
AssemblyItem const& _tag,
std::back_insert_iterator<AssemblyItems> _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<TagConjunctions>
{
static bool applySimple(
Expand Down Expand Up @@ -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 &&
Expand Down Expand Up @@ -619,8 +718,9 @@ bool PeepholeOptimiser::optimise()
applyMethods(
state,
PushPop(), OpPop(), OpStop(), OpReturnRevert(), DoublePush(), DoubleSwap(), CommutativeSwap(), SwapComparison(),
DupSwap(), IsZeroIsZeroJumpI(), EqIsZeroJumpI(), DoubleJump(), JumpToNext(), UnreachableCode(), DeduplicateNextTagSize3(),
DeduplicateNextTagSize2(), DeduplicateNextTagSize1(), TagConjunctions(), TruthyAnd(), Identity()
DupSwap(), IsZeroIsZeroJumpI(), IsZeroIsZeroRJumpI(), EqIsZeroJumpI(), DoubleJump(), DoubleRJump(), JumpToNext(),
RJumpToNext(), UnreachableCode(), DeduplicateNextTagSize3(), DeduplicateNextTagSize2(), DeduplicateNextTagSize1(),
TagConjunctions(), TruthyAnd(), Identity()
);
if (m_optimisedItems.size() < m_items.size() || (
m_optimisedItems.size() == m_items.size() && (
Expand Down
Loading

0 comments on commit 713beaa

Please sign in to comment.