Skip to content

Integrate numerical label IDs to Yul #15969

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 17 commits into
base: add_node_id_dispenser
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 0 additions & 2 deletions libsolc/libsolc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
#include <libsolc/libsolc.h>
#include <libsolidity/interface/StandardCompiler.h>
#include <libsolidity/interface/Version.h>
#include <libyul/YulName.h>

#include <cstdlib>
#include <list>
Expand Down Expand Up @@ -153,7 +152,6 @@ extern void solidity_reset() noexcept
{
// This is called right before each compilation, but not at the end, so additional memory
// can be freed here.
yul::YulStringRepository::reset();
solidityAllocations.clear();
}
}
7 changes: 4 additions & 3 deletions libsolidity/analysis/PostTypeChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ struct ReservedErrorSelector: public PostTypeChecker::Checker
class YulLValueChecker : public solidity::yul::ASTWalker
{
public:
YulLValueChecker(ASTString const& _identifierName): m_identifierName(_identifierName) {}
YulLValueChecker(ASTString const& _identifierName, yul::ASTLabelRegistry const& _yulLabels): m_identifierName(_identifierName), m_yulLabels(_yulLabels) {}
bool willBeWrittenTo() const { return m_willBeWrittenTo; }
using solidity::yul::ASTWalker::operator();
void operator()(solidity::yul::Assignment const& _assignment) override
Expand All @@ -472,12 +472,13 @@ class YulLValueChecker : public solidity::yul::ASTWalker

if (ranges::any_of(
_assignment.variableNames,
[&](auto const& yulIdentifier) { return yulIdentifier.name.str() == m_identifierName; }
[&](auto const& yulIdentifier) { return m_yulLabels[yulIdentifier.name] == m_identifierName; }
))
m_willBeWrittenTo = true;
}
private:
ASTString const& m_identifierName;
yul::ASTLabelRegistry const& m_yulLabels;
bool m_willBeWrittenTo = false;
};

Expand Down Expand Up @@ -505,7 +506,7 @@ class LValueChecker: public ASTConstVisitor
if (m_willBeWrittenTo)
return;

YulLValueChecker yulChecker{m_declaration->name()};
YulLValueChecker yulChecker{m_declaration->name(), _inlineAssembly.operations().labels()};
yulChecker(_inlineAssembly.operations().root());
m_willBeWrittenTo = yulChecker.willBeWrittenTo();
}
Expand Down
34 changes: 20 additions & 14 deletions libsolidity/analysis/ReferencesResolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -235,10 +235,9 @@ bool ReferencesResolver::visit(UsingForDirective const& _usingFor)

bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
{
m_yulAnnotation = &_inlineAssembly.annotation();
ScopedSaveAndRestore saveAnnotation{m_yulAnnotation, &_inlineAssembly.annotation()};
ScopedSaveAndRestore saveYulLabels{m_yulLabels, &_inlineAssembly.operations().labels()};
(*this)(_inlineAssembly.operations().root());
m_yulAnnotation = nullptr;

return false;
}

Expand Down Expand Up @@ -286,12 +285,14 @@ void ReferencesResolver::operator()(yul::FunctionDefinition const& _function)

void ReferencesResolver::operator()(yul::Identifier const& _identifier)
{
solAssert(m_yulLabels);
solAssert(m_yulAnnotation);
solAssert(nativeLocationOf(_identifier) == originLocationOf(_identifier), "");

if (m_resolver.experimentalSolidity())
{
std::vector<std::string> splitName;
boost::split(splitName, _identifier.name.str(), boost::is_any_of("."));
boost::split(splitName, (*m_yulLabels)[_identifier.name], boost::is_any_of("."));
solAssert(!splitName.empty());
if (splitName.size() > 2)
{
Expand Down Expand Up @@ -332,22 +333,23 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier)
static std::set<std::string> suffixes{"slot", "offset", "length", "address", "selector"};
std::string suffix;
for (std::string const& s: suffixes)
if (boost::algorithm::ends_with(_identifier.name.str(), "." + s))
if (boost::algorithm::ends_with((*m_yulLabels)[_identifier.name], "." + s))
suffix = s;

// Could also use `pathFromCurrentScope`, split by '.'.
// If we do that, suffix should only be set for when it has a special
// meaning, not for normal identifierPaths.
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name.str());
auto declarations = m_resolver.nameFromCurrentScope(std::string{(*m_yulLabels)[_identifier.name]});
if (!suffix.empty())
{
// special mode to access storage variables
if (!declarations.empty())
// the special identifier exists itself, we should not allow that.
return;
std::string realName = _identifier.name.str().substr(0, _identifier.name.str().size() - suffix.size() - 1);
std::string_view const identifierLabel = (*m_yulLabels)[_identifier.name];
std::string_view const realName = identifierLabel.substr(0, identifierLabel.size() - suffix.size() - 1);
solAssert(!realName.empty(), "Empty name.");
declarations = m_resolver.nameFromCurrentScope(realName);
declarations = m_resolver.nameFromCurrentScope(std::string{realName});
if (!declarations.empty())
// To support proper path resolution, we have to use pathFromCurrentScope.
solAssert(!util::contains(realName, '.'), "");
Expand All @@ -363,9 +365,10 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier)
}
else if (declarations.size() == 0)
{
std::string_view const identifierLabel = (*m_yulLabels)[_identifier.name];
if (
boost::algorithm::ends_with(_identifier.name.str(), "_slot") ||
boost::algorithm::ends_with(_identifier.name.str(), "_offset")
boost::algorithm::ends_with(identifierLabel, "_slot") ||
boost::algorithm::ends_with(identifierLabel, "_offset")
)
m_errorReporter.declarationError(
9467_error,
Expand All @@ -391,13 +394,14 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier)

void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl)
{
solAssert(m_yulLabels);
for (auto const& identifier: _varDecl.variables)
{
solAssert(nativeLocationOf(identifier) == originLocationOf(identifier), "");
validateYulIdentifierName(identifier.name, nativeLocationOf(identifier));

if (
auto declarations = m_resolver.nameFromCurrentScope(identifier.name.str());
auto declarations = m_resolver.nameFromCurrentScope(std::string{(*m_yulLabels)[identifier.name]});
!declarations.empty()
)
{
Expand Down Expand Up @@ -490,17 +494,19 @@ void ReferencesResolver::resolveInheritDoc(StructuredDocumentation const& _docum

void ReferencesResolver::validateYulIdentifierName(yul::YulName _name, SourceLocation const& _location)
{
if (util::contains(_name.str(), '.'))
solAssert(m_yulLabels);
std::string_view const label = (*m_yulLabels)[_name];
if (util::contains(label, '.'))
m_errorReporter.declarationError(
3927_error,
_location,
"User-defined identifiers in inline assembly cannot contain '.'."
);

if (std::set<std::string>{"this", "super", "_"}.count(_name.str()))
if (std::set<std::string_view>{"this", "super", "_"}.contains(label))
m_errorReporter.declarationError(
4113_error,
_location,
"The identifier name \"" + _name.str() + "\" is reserved."
fmt::format("The identifier name \"{}\" is reserved.", label)
);
}
1 change: 1 addition & 0 deletions libsolidity/analysis/ReferencesResolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ class ReferencesResolver: private ASTConstVisitor, private yul::ASTWalker
bool const m_resolveInsideCode;

InlineAssemblyAnnotation* m_yulAnnotation = nullptr;
yul::ASTLabelRegistry const* m_yulLabels = nullptr;
bool m_yulInsideFunction = false;
bool m_typeContext = false;
};
Expand Down
1 change: 1 addition & 0 deletions libsolidity/analysis/TypeChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
*_inlineAssembly.annotation().analysisInfo,
m_errorReporter,
_inlineAssembly.dialect(),
_inlineAssembly.operations().labels(),
identifierAccess
);
if (!analyzer.analyze(_inlineAssembly.operations().root()))
Expand Down
8 changes: 4 additions & 4 deletions libsolidity/ast/ASTJsonExporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -670,10 +670,10 @@ bool ASTJsonExporter::visit(InlineAssembly const& _node)

for (auto const& it: _node.annotation().externalReferences)
if (it.first)
externalReferences.emplace_back(std::make_pair(
it.first->name.str(),
externalReferences.emplace_back(
_node.operations().labelOf(it.first->name),
inlineAssemblyIdentifierToJson(it)
));
);

Json externalReferencesJson = Json::array();

Expand All @@ -684,7 +684,7 @@ bool ASTJsonExporter::visit(InlineAssembly const& _node)
auto const& evmDialect = dynamic_cast<solidity::yul::EVMDialect const&>(_node.dialect());

std::vector<std::pair<std::string, Json>> attributes = {
std::make_pair("AST", Json(yul::AsmJsonConverter(evmDialect, sourceIndexFromLocation(_node.location()))(_node.operations().root()))),
std::make_pair("AST", Json(yul::AsmJsonConverter(evmDialect, _node.operations().labels(), sourceIndexFromLocation(_node.location()))(_node.operations().root()))),
std::make_pair("externalReferences", std::move(externalReferencesJson)),
std::make_pair("evmVersion", evmDialect.evmVersion().name())
};
Expand Down
39 changes: 18 additions & 21 deletions libsolidity/codegen/CompilerContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
#include <libyul/backends/evm/EVMMetrics.h>
#include <libyul/optimiser/Suite.h>
#include <libyul/Object.h>
#include <libyul/YulName.h>
#include <libyul/Utilities.h>

#include <libsolutil/Whiskers.h>
Expand All @@ -49,6 +48,8 @@
#include <liblangutil/Scanner.h>
#include <liblangutil/SourceReferenceFormatter.h>

#include <range/v3/algorithm/find.hpp>

#include <utility>

// Change to "define" to output all intermediate code
Expand Down Expand Up @@ -388,13 +389,18 @@ void CompilerContext::appendInlineAssembly(
{
unsigned startStackHeight = stackHeight();

std::set<yul::YulName> externallyUsedIdentifiers;
for (auto const& fun: _externallyUsedFunctions)
externallyUsedIdentifiers.insert(yul::YulName(fun));
for (auto const& var: _localVariables)
externallyUsedIdentifiers.insert(yul::YulName(var));

std::set<std::string> const externallyUsedIdentifiers = _externallyUsedFunctions + _localVariables;
yul::ExternalIdentifierAccess identifierAccess;

ErrorList errors;
ErrorReporter errorReporter(errors);
langutil::CharStream charStream(_assembly, _sourceName);
yul::EVMDialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(m_evmVersion, std::nullopt);
std::optional<langutil::SourceLocation> locationOverride;
if (!_system)
locationOverride = m_asm->currentSourceLocation();
std::shared_ptr<yul::AST const> parserResult
= yul::Parser(errorReporter, dialect, std::move(locationOverride)).parse(charStream);
identifierAccess.resolve = [&](
yul::Identifier const& _identifier,
yul::IdentifierContext,
Expand All @@ -403,7 +409,7 @@ void CompilerContext::appendInlineAssembly(
{
if (_insideFunction)
return false;
return util::contains(_localVariables, _identifier.name.str());
return util::contains(_localVariables, parserResult->labelOf(_identifier.name));
};
identifierAccess.generateCode = [&](
yul::Identifier const& _identifier,
Expand All @@ -412,7 +418,7 @@ void CompilerContext::appendInlineAssembly(
)
{
solAssert(_context == yul::IdentifierContext::RValue || _context == yul::IdentifierContext::LValue, "");
auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name.str());
auto const it = ranges::find(_localVariables, parserResult->labelOf(_identifier.name));
solAssert(it != _localVariables.end(), "");
auto stackDepth = static_cast<size_t>(distance(it, _localVariables.end()));
size_t stackDiff = static_cast<size_t>(_assembly.stackHeight()) - startStackHeight + stackDepth;
Expand All @@ -433,16 +439,6 @@ void CompilerContext::appendInlineAssembly(
}
};

ErrorList errors;
ErrorReporter errorReporter(errors);
langutil::CharStream charStream(_assembly, _sourceName);
yul::EVMDialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(m_evmVersion, std::nullopt);
std::optional<langutil::SourceLocation> locationOverride;
if (!_system)
locationOverride = m_asm->currentSourceLocation();
std::shared_ptr<yul::AST> parserResult =
yul::Parser(errorReporter, dialect, std::move(locationOverride))
.parse(charStream);
#ifdef SOL_OUTPUT_ASM
std::cout << yul::AsmPrinter::format(*parserResult) << std::endl;
#endif
Expand Down Expand Up @@ -471,6 +467,7 @@ void CompilerContext::appendInlineAssembly(
analysisInfo,
errorReporter,
dialect,
parserResult->labels(),
identifierAccess.resolve
).analyze(parserResult->root());
if (!parserResult || errorReporter.hasErrorsWarningsOrInfos() || !analyzerResult)
Expand Down Expand Up @@ -517,7 +514,7 @@ void CompilerContext::appendInlineAssembly(

solAssert(!errorReporter.hasErrorsWarningsOrInfos(), "Failed to analyze inline assembly block.");
yul::CodeGenerator::assemble(
toBeAssembledAST->root(),
*toBeAssembledAST,
analysisInfo,
*m_asm,
m_evmVersion,
Expand All @@ -532,7 +529,7 @@ void CompilerContext::appendInlineAssembly(
}


void CompilerContext::optimizeYul(yul::Object& _object, OptimiserSettings const& _optimiserSettings, std::set<yul::YulName> const& _externalIdentifiers)
void CompilerContext::optimizeYul(yul::Object& _object, OptimiserSettings const& _optimiserSettings, std::set<std::string> const& _externalIdentifiers)
{
yulAssert(_object.dialect());
auto const* evmDialect = dynamic_cast<yul::EVMDialect const*>(_object.dialect());
Expand Down
2 changes: 1 addition & 1 deletion libsolidity/codegen/CompilerContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ class CompilerContext
/// Otherwise returns "revert(0, 0)".
std::string revertReasonIfDebug(std::string const& _message = "");

void optimizeYul(yul::Object& _object, OptimiserSettings const& _optimiserSetting, std::set<yul::YulName> const& _externalIdentifiers = {});
void optimizeYul(yul::Object& _object, OptimiserSettings const& _optimiserSetting, std::set<std::string> const& _externalIdentifiers = {});

/// Appends arbitrary data to the end of the bytecode.
void appendToAuxiliaryData(bytes const& _data) { m_asm->appendToAuxiliaryData(_data); }
Expand Down
5 changes: 2 additions & 3 deletions libsolidity/codegen/ContractCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
#include <libyul/optimiser/Suite.h>
#include <libyul/Object.h>
#include <libyul/optimiser/ASTCopier.h>
#include <libyul/YulName.h>

#include <libevmasm/Instruction.h>
#include <libevmasm/Assembly.h>
Expand Down Expand Up @@ -944,7 +943,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
solAssert(dialect, "");

// Create a modifiable copy of the code and analysis
object.setCode(std::make_shared<yul::AST>(_inlineAssembly.dialect(), yul::ASTCopier().translate(code->root())));
object.setCode(std::make_shared<yul::AST>(_inlineAssembly.dialect(), code->labels(), yul::ASTCopier().translate(code->root())));
object.analysisInfo = std::make_shared<yul::AsmAnalysisInfo>(yul::AsmAnalyzer::analyzeStrictAssertCorrect(object));

m_context.optimizeYul(object, m_optimiserSettings);
Expand All @@ -954,7 +953,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
}

yul::CodeGenerator::assemble(
code->root(),
*code,
*analysisInfo,
*m_context.assemblyPtr(),
m_context.evmVersion(),
Expand Down
28 changes: 21 additions & 7 deletions libsolidity/codegen/ir/IRGeneratorForStatements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,18 @@ struct CopyTranslate: public yul::ASTCopier
{
using ExternalRefsMap = std::map<yul::Identifier const*, InlineAssemblyAnnotation::ExternalIdentifierInfo>;

CopyTranslate(IRGenerationContext& _context, ExternalRefsMap const& _references):
m_context(_context), m_references(_references) {}
CopyTranslate(
yul::ASTLabelRegistry const& _originalLabels,
yul::ASTLabelRegistryBuilder& _labelBuilder,
IRGenerationContext& _context,
ExternalRefsMap const& _references
):
m_originalLabels(_originalLabels),
m_labelBuilder(_labelBuilder),
m_context(_context),
m_references(_references)
{
}

using ASTCopier::operator();

Expand All @@ -84,7 +94,7 @@ struct CopyTranslate: public yul::ASTCopier
// from the Yul dialect we are compiling to. By only translating `YulName`s which correspond to Identifiers,
// we are implicitly excluding builtins together with the assumption, that numerical builtin handles
// stay identical. Special care has to be taken, that these numerical handles stay consistent.
return yul::YulName{"usr$" + _name.str()};
return m_labelBuilder.define(fmt::format("usr${}", m_originalLabels[_name]));
}

yul::Identifier translate(yul::Identifier const& _identifier) override
Expand Down Expand Up @@ -203,9 +213,11 @@ struct CopyTranslate: public yul::ASTCopier
if (isDigit(value.front()))
return yul::Literal{_identifier.debugData, yul::LiteralKind::Number, yul::valueOfNumberLiteral(value)};
else
return yul::Identifier{_identifier.debugData, yul::YulName{value}};
return yul::Identifier{_identifier.debugData, m_labelBuilder.define(value)};
}

yul::ASTLabelRegistry const& m_originalLabels;
yul::ASTLabelRegistryBuilder& m_labelBuilder;
IRGenerationContext& m_context;
ExternalRefsMap const& m_references;
};
Expand Down Expand Up @@ -2289,13 +2301,15 @@ bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm)
setLocation(_inlineAsm);
if (*_inlineAsm.annotation().hasMemoryEffects && !_inlineAsm.annotation().markedMemorySafe)
m_context.setMemoryUnsafeInlineAssemblySeen();
CopyTranslate bodyCopier{m_context, _inlineAsm.annotation().externalReferences};

auto const& originalLabels = _inlineAsm.operations().labels();
yul::ASTLabelRegistryBuilder labelBuilder{originalLabels};
CopyTranslate bodyCopier{originalLabels, labelBuilder, m_context, _inlineAsm.annotation().externalReferences};
yul::Statement modified = bodyCopier(_inlineAsm.operations().root());
auto const modifiedLabels = labelBuilder.build();

solAssert(std::holds_alternative<yul::Block>(modified));

appendCode() << yul::AsmPrinter(_inlineAsm.dialect())(std::get<yul::Block>(modified)) << "\n";
appendCode() << yul::AsmPrinter(_inlineAsm.dialect(), modifiedLabels)(std::get<yul::Block>(modified)) << "\n";
return false;
}

Expand Down
Loading