Skip to content

Commit

Permalink
Transport debug data to assemlby item & preserve yul source locations.
Browse files Browse the repository at this point in the history
  • Loading branch information
aarlt committed Jan 31, 2025
1 parent 348cd32 commit 1f48910
Show file tree
Hide file tree
Showing 25 changed files with 480 additions and 464 deletions.
9 changes: 5 additions & 4 deletions libevmasm/Assembly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ using namespace solidity::util;

std::map<std::string, std::shared_ptr<std::string const>> Assembly::s_sharedSourceNames;

AssemblyItem const& Assembly::append(AssemblyItem _i)
AssemblyItem const& Assembly::append(AssemblyItem _i, langutil::DebugData::ConstPtr _debugData)
{
assertThrow(m_deposit >= 0, AssemblyException, "Stack underflow.");
m_deposit += static_cast<int>(_i.deposit());
Expand All @@ -65,6 +65,7 @@ AssemblyItem const& Assembly::append(AssemblyItem _i)
if (!currentItems.back().location().isValid() && m_currentSourceLocation.isValid())
currentItems.back().setLocation(m_currentSourceLocation);
currentItems.back().m_modifierDepth = m_currentModifierDepth;
currentItems.back().debugData() = _debugData;
return currentItems.back();
}

Expand Down Expand Up @@ -189,7 +190,7 @@ AssemblyItem Assembly::createAssemblyItemFromJSON(Json const& _json, std::vector

if (c_instructions.count(name))
{
AssemblyItem item{c_instructions.at(name), langutil::DebugData::create(location)};
AssemblyItem item{c_instructions.at(name), langutil::DebugData::create(location, {})};
if (!jumpType.empty())
{
if (item.instruction() == Instruction::JUMP || item.instruction() == Instruction::JUMPI)
Expand Down Expand Up @@ -689,7 +690,7 @@ std::shared_ptr<std::string const> Assembly::sharedSourceName(std::string const&
return s_sharedSourceNames[_name];
}

AssemblyItem Assembly::namedTag(std::string const& _name, size_t _params, size_t _returns, std::optional<uint64_t> _sourceID)
AssemblyItem Assembly::namedTag(std::string const& _name, size_t _params, size_t _returns, std::optional<uint64_t> _sourceID, langutil::DebugData::ConstPtr _debugData)
{
assertThrow(!_name.empty(), AssemblyException, "Empty named tag.");
if (m_namedTags.count(_name))
Expand All @@ -699,7 +700,7 @@ AssemblyItem Assembly::namedTag(std::string const& _name, size_t _params, size_t
assertThrow(m_namedTags.at(_name).sourceID == _sourceID, AssemblyException, "");
}
else
m_namedTags[_name] = {static_cast<size_t>(newTag().data()), _sourceID, _params, _returns};
m_namedTags[_name] = {static_cast<size_t>(newTag(_debugData).data()), _sourceID, _params, _returns};
return AssemblyItem{Tag, m_namedTags.at(_name).id};
}

Expand Down
56 changes: 28 additions & 28 deletions libevmasm/Assembly.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ class Assembly
std::optional<uint8_t> eofVersion() const { return m_eofVersion; }
bool supportsFunctions() const { return m_eofVersion.has_value(); }
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++); }
AssemblyItem newTag(langutil::DebugData::ConstPtr _debugData) { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(Tag, m_usedTags++, _debugData); }
AssemblyItem newPushTag(langutil::DebugData::ConstPtr _debugData) { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(PushTag, m_usedTags++, _debugData); }

AssemblyItem newFunctionCall(uint16_t _functionID) const;
AssemblyItem newFunctionReturn() const;
Expand All @@ -77,7 +77,7 @@ class Assembly
void endFunction();

/// Returns a tag identified by the given name. Creates it if it does not yet exist.
AssemblyItem namedTag(std::string const& _name, size_t _params, size_t _returns, std::optional<uint64_t> _sourceID);
AssemblyItem namedTag(std::string const& _name, size_t _params, size_t _returns, std::optional<uint64_t> _sourceID, langutil::DebugData::ConstPtr _debugData);
AssemblyItem newData(bytes const& _data) { util::h256 h(util::keccak256(util::asString(_data))); m_data[h] = _data; return AssemblyItem(PushData, h); }
bytes const& data(util::h256 const& _i) const { return m_data.at(_i); }
AssemblyItem newSub(AssemblyPointer const& _sub) { m_subs.push_back(_sub); return AssemblyItem(PushSub, m_subs.size() - 1); }
Expand All @@ -90,57 +90,57 @@ class Assembly
AssemblyItem newImmutableAssignment(std::string const& _identifier);
AssemblyItem newAuxDataLoadN(size_t offset);

AssemblyItem const& append(AssemblyItem _i);
AssemblyItem const& append(bytes const& _data) { return append(newData(_data)); }
AssemblyItem const& append(AssemblyItem _i,langutil::DebugData::ConstPtr _debugData);
AssemblyItem const& append(bytes const& _data, langutil::DebugData::ConstPtr _debugData) { return append(newData(_data), _debugData); }

template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; }
template <class T> Assembly& operator<<(T const& _d) { append(_d, {}); return *this; }

/// Pushes the final size of the current assembly itself. Use this when the code is modified
/// after compilation and CODESIZE is not an option.
void appendProgramSize() { append(AssemblyItem(PushProgramSize)); }
void appendLibraryAddress(std::string const& _identifier) { append(newPushLibraryAddress(_identifier)); }
void appendImmutable(std::string const& _identifier) { append(newPushImmutable(_identifier)); }
void appendImmutableAssignment(std::string const& _identifier) { append(newImmutableAssignment(_identifier)); }
void appendAuxDataLoadN(uint16_t _offset) { append(newAuxDataLoadN(_offset));}
void appendProgramSize(langutil::DebugData::ConstPtr _debugData) { append(AssemblyItem(PushProgramSize), _debugData); }
void appendLibraryAddress(std::string const& _identifier, langutil::DebugData::ConstPtr _debugData) { append(newPushLibraryAddress(_identifier), _debugData); }
void appendImmutable(std::string const& _identifier, langutil::DebugData::ConstPtr _debugData) { append(newPushImmutable(_identifier), _debugData); }
void appendImmutableAssignment(std::string const& _identifier, langutil::DebugData::ConstPtr _debugData) { append(newImmutableAssignment(_identifier), _debugData); }
void appendAuxDataLoadN(uint16_t _offset, langutil::DebugData::ConstPtr _debugData) { append(newAuxDataLoadN(_offset), _debugData);}

void appendVerbatim(bytes _data, size_t _arguments, size_t _returnVariables)
void appendVerbatim(bytes _data, size_t _arguments, size_t _returnVariables, langutil::DebugData::ConstPtr _debugData)
{
append(AssemblyItem(std::move(_data), _arguments, _returnVariables));
append(AssemblyItem(std::move(_data), _arguments, _returnVariables), _debugData);
}

AssemblyItem appendEOFCreate(ContainerID _containerId)
AssemblyItem appendEOFCreate(ContainerID _containerId, langutil::DebugData::ConstPtr _debugData)
{
solAssert(_containerId < m_subs.size(), "EOF Create of undefined container.");
return append(AssemblyItem::eofCreate(_containerId));
return append(AssemblyItem::eofCreate(_containerId), _debugData);
}
AssemblyItem appendReturnContract(ContainerID _containerId)
AssemblyItem appendReturnContract(ContainerID _containerId, langutil::DebugData::ConstPtr _debugData)
{
solAssert(_containerId < m_subs.size(), "Return undefined container ID.");
return append(AssemblyItem::returnContract(_containerId));
return append(AssemblyItem::returnContract(_containerId), _debugData);
}

AssemblyItem appendFunctionCall(uint16_t _functionID)
AssemblyItem appendFunctionCall(uint16_t _functionID, langutil::DebugData::ConstPtr _debugData)
{
return append(newFunctionCall(_functionID));
return append(newFunctionCall(_functionID), _debugData);
}

AssemblyItem appendFunctionReturn()
AssemblyItem appendFunctionReturn(langutil::DebugData::ConstPtr _debugData)
{
solAssert(m_currentCodeSection != 0, "Appending function return without begin function.");
return append(newFunctionReturn());
return append(newFunctionReturn(), _debugData);
}

AssemblyItem appendJump() { auto ret = append(newPushTag()); append(Instruction::JUMP); return ret; }
AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(Instruction::JUMPI); return ret; }
AssemblyItem appendJump(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(Instruction::JUMP); return ret; }
AssemblyItem appendJumpI(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(Instruction::JUMPI); return ret; }
AssemblyItem appendJump(langutil::DebugData::ConstPtr _debugData) { auto ret = append(newPushTag(_debugData), _debugData); append(Instruction::JUMP, _debugData); return ret; }
AssemblyItem appendJumpI(langutil::DebugData::ConstPtr _debugData) { auto ret = append(newPushTag(_debugData), _debugData); append(Instruction::JUMPI, _debugData); return ret; }
AssemblyItem appendJump(AssemblyItem const& _tag, langutil::DebugData::ConstPtr _debugData) { auto ret = append(_tag.pushTag(), _debugData); append(Instruction::JUMP, _debugData); return ret; }
AssemblyItem appendJumpI(AssemblyItem const& _tag, langutil::DebugData::ConstPtr _debugData) { auto ret = append(_tag.pushTag(), _debugData); append(Instruction::JUMPI, _debugData); return ret; }

/// Adds a subroutine to the code (in the data section) and pushes its size (via a tag)
/// on the stack. @returns the pushsub assembly item.
AssemblyItem appendSubroutine(AssemblyPointer const& _assembly) { auto sub = newSub(_assembly); append(newPushSubSize(size_t(sub.data()))); return sub; }
void pushSubroutineSize(size_t _subRoutine) { append(newPushSubSize(_subRoutine)); }
AssemblyItem appendSubroutine(AssemblyPointer const& _assembly, langutil::DebugData::ConstPtr _debugData) { auto sub = newSub(_assembly); append(newPushSubSize(size_t(sub.data())), _debugData); return sub; }
void pushSubroutineSize(size_t _subRoutine, langutil::DebugData::ConstPtr _debugData) { append(newPushSubSize(_subRoutine), _debugData); }
/// Pushes the offset of the subroutine.
void pushSubroutineOffset(size_t _subRoutine) { append(AssemblyItem(PushSub, _subRoutine)); }
void pushSubroutineOffset(size_t _subRoutine, langutil::DebugData::ConstPtr _debugData) { append(AssemblyItem(PushSub, _subRoutine, _debugData), _debugData); }

/// Appends @a _data literally to the very end of the bytecode.
void appendToAuxiliaryData(bytes const& _data) { m_auxiliaryData += _data; }
Expand Down
18 changes: 3 additions & 15 deletions libevmasm/AssemblyItem.h
Original file line number Diff line number Diff line change
Expand Up @@ -252,21 +252,8 @@ class AssemblyItem
/// @returns true if the assembly item can be used in a functional context.
bool canBeFunctional() const;

void setLocation(langutil::SourceLocation const& _location)
{
solAssert(m_debugData);
m_debugData = langutil::DebugData::create(
_location,
m_debugData->originLocation,
m_debugData->astID
);
}

langutil::SourceLocation const& location() const
{
solAssert(m_debugData);
return m_debugData->nativeLocation;
}
void setLocation(langutil::SourceLocation const& _location) { m_location = _location; }
langutil::SourceLocation const& location() const { return m_location; }

void setDebugData(langutil::DebugData::ConstPtr _debugData)
{
Expand Down Expand Up @@ -315,6 +302,7 @@ class AssemblyItem
/// If m_type == VerbatimBytecode, this holds number of arguments, number of
/// return variables and verbatim bytecode.
std::optional<std::tuple<size_t, size_t, bytes>> m_verbatimBytecode;
langutil::SourceLocation m_location;
langutil::DebugData::ConstPtr m_debugData;
JumpType m_jumpType = JumpType::Ordinary;
/// Pushed value for operations with data to be determined during assembly stage,
Expand Down
2 changes: 1 addition & 1 deletion libevmasm/CommonSubexpressionEliminator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ void CommonSubexpressionEliminator::optimizeBreakingItem()

ExpressionClasses& classes = m_state.expressionClasses();
SourceLocation const& itemLocation = m_breakingItem->location();
langutil::DebugData::ConstPtr debugData{langutil::DebugData::create(itemLocation)};
langutil::DebugData::ConstPtr debugData{langutil::DebugData::create(itemLocation, {})};
if (*m_breakingItem == AssemblyItem(Instruction::JUMPI))
{
AssemblyItem::JumpType jumpType = m_breakingItem->getJumpType();
Expand Down
8 changes: 4 additions & 4 deletions liblangutil/DebugData.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ struct DebugData
typedef typename std::shared_ptr<DebugData const> ConstPtr;

explicit DebugData(
langutil::SourceLocation _nativeLocation = {},
langutil::SourceLocation _originLocation = {},
langutil::SourceLocation _nativeLocation,
langutil::SourceLocation _originLocation,
std::optional<int64_t> _astID = {}
):
nativeLocation(std::move(_nativeLocation)),
Expand All @@ -41,7 +41,7 @@ struct DebugData

static DebugData::ConstPtr create(
langutil::SourceLocation _nativeLocation,
langutil::SourceLocation _originLocation = {},
langutil::SourceLocation _originLocation,
std::optional<int64_t> _astID = {}
)
{
Expand All @@ -54,7 +54,7 @@ struct DebugData

static DebugData::ConstPtr create()
{
static DebugData::ConstPtr emptyDebugData = create({});
static DebugData::ConstPtr emptyDebugData = create({}, {}, {});
return emptyDebugData;
}

Expand Down
Loading

0 comments on commit 1f48910

Please sign in to comment.