More fixes.

This commit is contained in:
Daniel Kirchner 2022-12-21 14:56:38 +01:00
parent f260b91001
commit 1364253dd9
12 changed files with 35 additions and 39 deletions

View File

@ -551,8 +551,12 @@ LinkerObject const& Assembly::assemble() const
"Cannot push and assign immutables in the same assembly subroutine." "Cannot push and assign immutables in the same assembly subroutine."
); );
// TODO: assert zero inputs/outputs on code section zero assertThrow(!m_codeSections.empty(), AssemblyException, "Expected at least one code section.");
// TODO: assert one code section being present and *only* one being present unless EOF 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; unsigned bytesRequiredForSubs = 0;
// TODO: consider fully producing all sub and data refs in this pass already. // 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. // Insert EOF1 header.
vector<size_t> codeSectionSizeOffsets; vector<size_t> codeSectionSizeOffsets;
auto setCodeSectionSize = [&](size_t _section, size_t _size) { auto setCodeSectionSize = [&](size_t _section, size_t _size) {
bytesRef length(ret.bytecode.data() + codeSectionSizeOffsets.at(_section), 2); toBigEndian(_size, bytesRef(ret.bytecode.data() + codeSectionSizeOffsets.at(_section), 2));
toBigEndian(_size, length);
}; };
std::optional<size_t> dataSectionSizeOffset; std::optional<size_t> dataSectionSizeOffset;
auto setDataSectionSize = [&](size_t _size) { auto setDataSectionSize = [&](size_t _size) {
assertThrow(dataSectionSizeOffset.has_value(), AssemblyException, ""); assertThrow(dataSectionSizeOffset.has_value(), AssemblyException, "");
assertThrow(_size <= 0xFFFF, AssemblyException, ""); assertThrow(_size <= 0xFFFF, AssemblyException, "");
bytesRef length(ret.bytecode.data() + *dataSectionSizeOffset, 2); toBigEndian(static_cast<uint16_t>(_size), bytesRef(ret.bytecode.data() + *dataSectionSizeOffset, 2));
toBigEndian(static_cast<uint16_t>(_size), length);
}; };
if (eof) if (eof)
{ {
@ -591,8 +593,7 @@ LinkerObject const& Assembly::assemble() const
ret.bytecode.push_back(0x03); // kind=type ret.bytecode.push_back(0x03); // kind=type
ret.bytecode.push_back(0x00); // length of type section ret.bytecode.push_back(0x00); // length of type section
ret.bytecode.push_back(0x00); ret.bytecode.push_back(0x00);
bytesRef length(&ret.bytecode.back() + 1 - 2, 2); toBigEndian(m_codeSections.size() * 2, bytesRef(&ret.bytecode.back() + 1 - 2, 2));
toBigEndian(m_codeSections.size() * 2, length);
} }
for (auto const& codeSection: m_codeSections) 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(m_eofVersion.has_value(), AssemblyException, "Relative jump outside EOF");
assertThrow(subId == numeric_limits<size_t>::max(), AssemblyException, "Relative jump to sub"); assertThrow(subId == numeric_limits<size_t>::max(), AssemblyException, "Relative jump to sub");
bytesRef r(ret.bytecode.data() + bytecodeOffset, 2);
assertThrow( assertThrow(
static_cast<ssize_t>(pos) - static_cast<ssize_t>(bytecodeOffset + 2u) < 0x7FFF && static_cast<ssize_t>(pos) - static_cast<ssize_t>(bytecodeOffset + 2u) < 0x7FFF &&
static_cast<ssize_t>(pos) - static_cast<ssize_t>(bytecodeOffset + 2u) >= -0x8000, static_cast<ssize_t>(pos) - static_cast<ssize_t>(bytecodeOffset + 2u) >= -0x8000,
@ -864,13 +864,12 @@ LinkerObject const& Assembly::assemble() const
"Relative jump too far" "Relative jump too far"
); );
uint16_t relativeOffset = static_cast<uint16_t>(pos - (bytecodeOffset + 2u)); uint16_t relativeOffset = static_cast<uint16_t>(pos - (bytecodeOffset + 2u));
toBigEndian(relativeOffset, r); toBigEndian(relativeOffset, bytesRef(ret.bytecode.data() + bytecodeOffset, 2));
} }
else else
{ {
assertThrow(!m_eofVersion.has_value(), AssemblyException, "Dynamic tag reference within EOF"); assertThrow(!m_eofVersion.has_value(), AssemblyException, "Dynamic tag reference within EOF");
bytesRef r(ret.bytecode.data() + bytecodeOffset, bytesPerTag); toBigEndian(pos, bytesRef(ret.bytecode.data() + bytecodeOffset, bytesPerTag));
toBigEndian(pos, r);
} }
} }
for (auto const& [name, tagInfo]: m_namedTags) for (auto const& [name, tagInfo]: m_namedTags)
@ -899,15 +898,13 @@ LinkerObject const& Assembly::assemble() const
if (references.first == references.second) if (references.first == references.second)
continue; continue;
for (auto ref = references.first; ref != references.second; ++ref) for (auto ref = references.first; ref != references.second; ++ref)
{ toBigEndian(ret.bytecode.size(), bytesRef(ret.bytecode.data() + ref->second, bytesPerDataRef));
bytesRef r(ret.bytecode.data() + ref->second, bytesPerDataRef);
toBigEndian(ret.bytecode.size(), r);
}
ret.bytecode += dataItem.second; ret.bytecode += dataItem.second;
} }
ret.bytecode += m_auxiliaryData; ret.bytecode += m_auxiliaryData;
// TODO: remove this when transitioning to unified spec headers
if (eof && bytesRequiredForDataAndSubsUpperBound > 0 && ret.bytecode.size() == dataStart) 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. // We have commited to a data section, but not actually needed it, so create a fake one.

View File

@ -157,7 +157,6 @@ public:
langutil::SourceLocation const& currentSourceLocation() const { return m_currentSourceLocation; } 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. /// 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; LinkerObject const& assemble() const;
struct OptimiserSettings struct OptimiserSettings

View File

@ -113,8 +113,8 @@ public:
AssemblyItem& operator=(AssemblyItem const&) = default; AssemblyItem& operator=(AssemblyItem const&) = default;
AssemblyItem& operator=(AssemblyItem&&) = default; AssemblyItem& operator=(AssemblyItem&&) = default;
AssemblyItem tag() const { assertThrow(m_type == PushTag || m_type == Tag, util::Exception, ""); return AssemblyItem(Tag, 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, util::Exception, ""); return AssemblyItem(PushTag, 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. /// 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. /// @param _subId the identifier of the subassembly the tag is taken from.
AssemblyItem toSubAssemblyTag(size_t _subId) const; AssemblyItem toSubAssemblyTag(size_t _subId) const;

View File

@ -132,7 +132,7 @@ BlockDeduplicator::BlockIterator& BlockDeduplicator::BlockIterator::operator++()
{ {
if (it == end) if (it == end)
return *this; return *this;
if (SemanticInformation::altersControlFlow(*it) && *it != AssemblyItem{Instruction::JUMPI}) if (SemanticInformation::altersControlFlow(*it) && *it != AssemblyItem{Instruction::JUMPI} && it->type() != ConditionalRelativeJump)
it = end; it = end;
else else
{ {

View File

@ -165,6 +165,9 @@ std::map<std::string, Instruction> const solidity::evmasm::c_instructions =
{ "LOG2", Instruction::LOG2 }, { "LOG2", Instruction::LOG2 },
{ "LOG3", Instruction::LOG3 }, { "LOG3", Instruction::LOG3 },
{ "LOG4", Instruction::LOG4 }, { "LOG4", Instruction::LOG4 },
{ "CALLF", Instruction::CALLF },
{ "RETF", Instruction::RETF },
{ "JUMPF", Instruction::JUMPF },
{ "CREATE", Instruction::CREATE }, { "CREATE", Instruction::CREATE },
{ "CALL", Instruction::CALL }, { "CALL", Instruction::CALL },
{ "CALLCODE", Instruction::CALLCODE }, { "CALLCODE", Instruction::CALLCODE },
@ -243,9 +246,9 @@ static std::map<Instruction, InstructionInfo> const c_instructionInfo =
{ Instruction::MSIZE, { "MSIZE", 0, 0, 1, false, Tier::Base } }, { Instruction::MSIZE, { "MSIZE", 0, 0, 1, false, Tier::Base } },
{ Instruction::GAS, { "GAS", 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::JUMPDEST, { "JUMPDEST", 0, 0, 0, true, Tier::Special } },
{ Instruction::RJUMP, { "RJUMP", 0, 0, 0, true, Tier::Low } }, { Instruction::RJUMP, { "RJUMP", 2, 0, 0, true, Tier::Low } },
{ Instruction::RJUMPI, { "RJUMPI", 0, 0, 0, true, Tier::Low } }, { Instruction::RJUMPI, { "RJUMPI", 2, 1, 0, true, Tier::Low } },
{ Instruction::RJUMPV, { "RJUMPV", 0, 1, 0, true, Tier::Low } }, { Instruction::RJUMPV, { "RJUMPV", 2, 1, 0, true, Tier::Low } },
{ Instruction::PUSH1, { "PUSH1", 1, 0, 1, false, Tier::VeryLow } }, { Instruction::PUSH1, { "PUSH1", 1, 0, 1, false, Tier::VeryLow } },
{ Instruction::PUSH2, { "PUSH2", 2, 0, 1, false, Tier::VeryLow } }, { Instruction::PUSH2, { "PUSH2", 2, 0, 1, false, Tier::VeryLow } },
{ Instruction::PUSH3, { "PUSH3", 3, 0, 1, false, Tier::VeryLow } }, { Instruction::PUSH3, { "PUSH3", 3, 0, 1, false, Tier::VeryLow } },
@ -315,6 +318,9 @@ static std::map<Instruction, InstructionInfo> const c_instructionInfo =
{ Instruction::LOG2, { "LOG2", 0, 4, 0, true, Tier::Special } }, { Instruction::LOG2, { "LOG2", 0, 4, 0, true, Tier::Special } },
{ Instruction::LOG3, { "LOG3", 0, 5, 0, true, Tier::Special } }, { Instruction::LOG3, { "LOG3", 0, 5, 0, true, Tier::Special } },
{ Instruction::LOG4, { "LOG4", 0, 6, 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::CREATE, { "CREATE", 0, 3, 1, true, Tier::Special } },
{ Instruction::CALL, { "CALL", 0, 7, 1, true, Tier::Special } }, { Instruction::CALL, { "CALL", 0, 7, 1, true, Tier::Special } },
{ Instruction::CALLCODE, { "CALLCODE", 0, 7, 1, true, Tier::Special } }, { Instruction::CALLCODE, { "CALLCODE", 0, 7, 1, true, Tier::Special } },

View File

@ -291,7 +291,7 @@ struct IsZeroIsZeroJumpI: SimplePeepholeOptimizerMethod<IsZeroIsZeroJumpI>
} }
}; };
struct IsZeroIsZeroRJumpI: SimplePeepholeOptimizerMethod<IsZeroIsZeroJumpI> struct IsZeroIsZeroRJumpI: SimplePeepholeOptimizerMethod<IsZeroIsZeroRJumpI>
{ {
static size_t applySimple( static size_t applySimple(
AssemblyItem const& _iszero1, AssemblyItem const& _iszero1,
@ -341,7 +341,7 @@ struct EqIsZeroJumpI: SimplePeepholeOptimizerMethod<EqIsZeroJumpI>
} }
}; };
struct EqIsZeroRJumpI: SimplePeepholeOptimizerMethod<EqIsZeroJumpI> struct EqIsZeroRJumpI: SimplePeepholeOptimizerMethod<EqIsZeroRJumpI>
{ {
static size_t applySimple( static size_t applySimple(
AssemblyItem const& _eq, AssemblyItem const& _eq,
@ -398,7 +398,7 @@ struct DoubleJump: SimplePeepholeOptimizerMethod<DoubleJump>
}; };
// rjumpi(tag_1) rjump(tag_2) tag_1: -> iszero rjumpi(tag_2) tag_1: // rjumpi(tag_1) rjump(tag_2) tag_1: -> iszero rjumpi(tag_2) tag_1:
struct DoubleRJump: SimplePeepholeOptimizerMethod<DoubleJump> struct DoubleRJump: SimplePeepholeOptimizerMethod<DoubleRJump>
{ {
static size_t applySimple( static size_t applySimple(
AssemblyItem const& _rjumpi, AssemblyItem const& _rjumpi,
@ -450,7 +450,7 @@ struct JumpToNext: SimplePeepholeOptimizerMethod<JumpToNext>
} }
}; };
struct RJumpToNext: SimplePeepholeOptimizerMethod<JumpToNext> struct RJumpToNext: SimplePeepholeOptimizerMethod<RJumpToNext>
{ {
static size_t applySimple( static size_t applySimple(
AssemblyItem const& _rjump, AssemblyItem const& _rjump,

View File

@ -239,7 +239,7 @@ bool SemanticInformation::isJumpInstruction(AssemblyItem const& _item)
bool SemanticInformation::altersControlFlow(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; return true;
if (_item.type() != evmasm::Operation) if (_item.type() != evmasm::Operation)
return false; return false;
@ -249,6 +249,9 @@ bool SemanticInformation::altersControlFlow(AssemblyItem const& _item)
// continue on the next instruction // continue on the next instruction
case Instruction::JUMP: case Instruction::JUMP:
case Instruction::JUMPI: case Instruction::JUMPI:
case Instruction::RJUMP:
case Instruction::RJUMPI:
case Instruction::RJUMPV:
case Instruction::RETURN: case Instruction::RETURN:
case Instruction::SELFDESTRUCT: case Instruction::SELFDESTRUCT:
case Instruction::STOP: case Instruction::STOP:

View File

@ -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 Out will typically be either std::string or bytes.
/// @a T will typically by unsigned, u160, u256 or bigint. /// @a T will typically by unsigned, u160, u256 or bigint.
template <class T, class Out> template <class T, class Out>
inline void toBigEndian(T _val, Out& o_out) inline void toBigEndian(T _val, Out&& o_out)
{ {
static_assert(std::is_same<bigint, T>::value || !std::numeric_limits<T>::is_signed, "only unsigned types or bigint supported"); //bigint does not carry sign bit on shift static_assert(std::is_same<bigint, T>::value || !std::numeric_limits<T>::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--) for (auto i = o_out.size(); i != 0; _val >>= 8, i--)
{ {
T v = _val & (T)0xff; 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<Out>::value_type)(uint8_t)v;
} }
} }

View File

@ -60,7 +60,6 @@ public:
virtual ~AbstractAssembly() = default; virtual ~AbstractAssembly() = default;
virtual bool supportsFunctions() const = 0;
/// Set a new source location valid starting from the next instruction. /// Set a new source location valid starting from the next instruction.
virtual void setSourceLocation(langutil::SourceLocation const& _location) = 0; virtual void setSourceLocation(langutil::SourceLocation const& _location) = 0;
/// Retrieve the current height of the stack. This does not have to be zero /// Retrieve the current height of the stack. This does not have to be zero

View File

@ -44,11 +44,6 @@ EthAssemblyAdapter::EthAssemblyAdapter(evmasm::Assembly& _assembly):
{ {
} }
bool EthAssemblyAdapter::supportsFunctions() const
{
return m_assembly.supportsFunctions();
}
void EthAssemblyAdapter::setSourceLocation(SourceLocation const& _location) void EthAssemblyAdapter::setSourceLocation(SourceLocation const& _location)
{ {
m_assembly.setSourceLocation(_location); m_assembly.setSourceLocation(_location);

View File

@ -40,7 +40,6 @@ class EthAssemblyAdapter: public AbstractAssembly
{ {
public: public:
explicit EthAssemblyAdapter(evmasm::Assembly& _assembly); explicit EthAssemblyAdapter(evmasm::Assembly& _assembly);
bool supportsFunctions() const override;
void setSourceLocation(langutil::SourceLocation const& _location) override; void setSourceLocation(langutil::SourceLocation const& _location) override;
int stackHeight() const override; int stackHeight() const override;
void setStackHeight(int height) override; void setStackHeight(int height) override;

View File

@ -57,8 +57,6 @@ public:
~NoOutputAssembly() override = default; ~NoOutputAssembly() override = default;
bool supportsFunctions() const override { return m_hasFunctions; }
void setSourceLocation(langutil::SourceLocation const&) override {} void setSourceLocation(langutil::SourceLocation const&) override {}
int stackHeight() const override { return m_stackHeight; } int stackHeight() const override { return m_stackHeight; }
void setStackHeight(int height) override { m_stackHeight = height; } void setStackHeight(int height) override { m_stackHeight = height; }