Skip to content

Commit

Permalink
Merge pull request #15467 from ipsilon/eof-pass-flag-to-evm-dialect
Browse files Browse the repository at this point in the history
eof: Pass `eofVersion` flag to `EVMDialect`
  • Loading branch information
cameel authored Oct 1, 2024
2 parents 885c916 + 8d8ac1c commit 4ab3e36
Show file tree
Hide file tree
Showing 45 changed files with 371 additions and 222 deletions.
26 changes: 22 additions & 4 deletions liblangutil/EVMVersion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,26 @@ using namespace solidity;
using namespace solidity::evmasm;
using namespace solidity::langutil;

bool EVMVersion::hasOpcode(Instruction _opcode) const
bool EVMVersion::hasOpcode(Instruction _opcode, std::optional<uint8_t> _eofVersion) const
{
// EOF version can be only defined since prague
assert(!_eofVersion.has_value() || this->m_version >= prague());

switch (_opcode)
{
case Instruction::RETURNDATACOPY:
case Instruction::RETURNDATASIZE:
return supportsReturndata();
case Instruction::STATICCALL:
return hasStaticCall();
return !_eofVersion.has_value() && hasStaticCall();
case Instruction::SHL:
case Instruction::SHR:
case Instruction::SAR:
return hasBitwiseShifting();
case Instruction::CREATE2:
return hasCreate2();
return !_eofVersion.has_value() && hasCreate2();
case Instruction::EXTCODEHASH:
return hasExtCodeHash();
return !_eofVersion.has_value() && hasExtCodeHash();
case Instruction::CHAINID:
return hasChainID();
case Instruction::SELFBALANCE:
Expand All @@ -58,6 +61,21 @@ bool EVMVersion::hasOpcode(Instruction _opcode) const
case Instruction::TSTORE:
case Instruction::TLOAD:
return supportsTransientStorage();
// Instructions below are deprecated in EOF
case Instruction::CALL:
case Instruction::CALLCODE:
case Instruction::DELEGATECALL:
case Instruction::SELFDESTRUCT:
case Instruction::JUMP:
case Instruction::JUMPI:
case Instruction::PC:
case Instruction::CREATE:
case Instruction::CODESIZE:
case Instruction::CODECOPY:
case Instruction::EXTCODESIZE:
case Instruction::EXTCODECOPY:
case Instruction::GAS:
return !_eofVersion.has_value();
default:
return true;
}
Expand Down
2 changes: 1 addition & 1 deletion liblangutil/EVMVersion.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ class EVMVersion:
bool hasMcopy() const { return *this >= cancun(); }
bool supportsTransientStorage() const { return *this >= cancun(); }

bool hasOpcode(evmasm::Instruction _opcode) const;
bool hasOpcode(evmasm::Instruction _opcode, std::optional<uint8_t> _eofVersion) const;

/// Whether we have to retain the costs for the call opcode itself (false),
/// or whether we can just forward easily all remaining gas (true).
Expand Down
11 changes: 10 additions & 1 deletion libsolidity/ast/ASTJsonExporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -664,12 +664,21 @@ bool ASTJsonExporter::visit(InlineAssembly const& _node)
for (Json& it: externalReferences | ranges::views::values)
externalReferencesJson.emplace_back(std::move(it));

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(sourceIndexFromLocation(_node.location()))(_node.operations().root()))),
std::make_pair("externalReferences", std::move(externalReferencesJson)),
std::make_pair("evmVersion", dynamic_cast<solidity::yul::EVMDialect const&>(_node.dialect()).evmVersion().name())
std::make_pair("evmVersion", evmDialect.evmVersion().name())
};

// TODO: Add test in test/linsolidity/ASTJSON/assembly. This requires adding support for eofVersion in ASTJSONTest
if (evmDialect.eofVersion())
{
solAssert(*evmDialect.eofVersion() > 0);
attributes.push_back(std::make_pair("eofVersion", *evmDialect.eofVersion()));
}

if (_node.flags())
{
Json flags = Json::array();
Expand Down
11 changes: 10 additions & 1 deletion libsolidity/ast/ASTJsonImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,16 @@ ASTPointer<InlineAssembly> ASTJsonImporter::createInlineAssembly(Json const& _no
astAssert(evmVersion.has_value(), "Invalid EVM version!");
astAssert(m_evmVersion == evmVersion, "Imported tree evm version differs from configured evm version!");

yul::Dialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(evmVersion.value());
// TODO: Add test in test/linsolidity/ASTJSON/assembly. This requires adding support for eofVersion in ASTJSONTest
std::optional<uint8_t> eofVersion;
if (auto const it = _node.find("eofVersion"); it != _node.end())
{
eofVersion = it->get<uint8_t>();
astAssert(eofVersion > 0);
}
astAssert(m_eofVersion == eofVersion, "Imported tree EOF version differs from configured EOF version!");

yul::Dialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(evmVersion.value(), eofVersion);
ASTPointer<std::vector<ASTPointer<ASTString>>> flags;
if (_node.contains("flags"))
{
Expand Down
6 changes: 4 additions & 2 deletions libsolidity/ast/ASTJsonImporter.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ namespace solidity::frontend
class ASTJsonImporter
{
public:
ASTJsonImporter(langutil::EVMVersion _evmVersion)
:m_evmVersion(_evmVersion)
ASTJsonImporter(langutil::EVMVersion _evmVersion, std::optional<uint8_t> _eofVersion)
:m_evmVersion(_evmVersion), m_eofVersion(_eofVersion)
{}

/// Converts the AST from JSON-format to ASTPointer
Expand Down Expand Up @@ -166,6 +166,8 @@ class ASTJsonImporter
std::set<int64_t> m_usedIDs;
/// Configured EVM version
langutil::EVMVersion m_evmVersion;
/// Configured EOF version. Equals std::nullopt if non-EOF
std::optional<uint8_t> m_eofVersion;
};

}
3 changes: 2 additions & 1 deletion libsolidity/codegen/CompilerContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ void CompilerContext::appendInlineAssembly(
ErrorList errors;
ErrorReporter errorReporter(errors);
langutil::CharStream charStream(_assembly, _sourceName);
yul::EVMDialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(m_evmVersion);
yul::EVMDialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(m_evmVersion, std::nullopt);
std::optional<langutil::SourceLocation> locationOverride;
if (!_system)
locationOverride = m_asm->currentSourceLocation();
Expand Down Expand Up @@ -522,6 +522,7 @@ void CompilerContext::appendInlineAssembly(
analysisInfo,
*m_asm,
m_evmVersion,
std::nullopt,
identifierAccess.generateCode,
_system,
_optimiserSettings.optimizeStackAllocation
Expand Down
1 change: 1 addition & 0 deletions libsolidity/codegen/ContractCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
*analysisInfo,
*m_context.assemblyPtr(),
m_context.evmVersion(),
std::nullopt,
identifierAccessCodeGen,
false,
m_optimiserSettings.optimizeStackAllocation
Expand Down
7 changes: 4 additions & 3 deletions libsolidity/interface/CompilerStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ bool CompilerStack::parse()

try
{
Parser parser{m_errorReporter, m_evmVersion};
Parser parser{m_errorReporter, m_evmVersion, m_eofVersion};

std::vector<std::string> sourcesToParse;
for (auto const& s: m_sources)
Expand Down Expand Up @@ -427,7 +427,8 @@ bool CompilerStack::parse()
void CompilerStack::importASTs(std::map<std::string, Json> const& _sources)
{
solAssert(m_stackState == Empty, "Must call importASTs only before the SourcesSet state.");
std::map<std::string, ASTPointer<SourceUnit>> reconstructedSources = ASTJsonImporter(m_evmVersion).jsonToSourceUnit(_sources);
std::map<std::string, ASTPointer<SourceUnit>> reconstructedSources =
ASTJsonImporter(m_evmVersion, m_eofVersion).jsonToSourceUnit(_sources);
for (auto& src: reconstructedSources)
{
solUnimplementedAssert(!src.second->experimentalSolidity());
Expand Down Expand Up @@ -896,7 +897,7 @@ Json CompilerStack::generatedSources(std::string const& _contractName, bool _run
ErrorList errors;
ErrorReporter errorReporter(errors);
CharStream charStream(source, sourceName);
yul::EVMDialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(m_evmVersion);
yul::EVMDialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(m_evmVersion, m_eofVersion);
std::shared_ptr<yul::AST> parserResult = yul::Parser{errorReporter, dialect}.parse(charStream);
solAssert(parserResult);
sources[0]["ast"] = yul::AsmJsonConverter{sourceIndex}(parserResult->root());
Expand Down
2 changes: 1 addition & 1 deletion libsolidity/parsing/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1448,7 +1448,7 @@ ASTPointer<InlineAssembly> Parser::parseInlineAssembly(ASTPointer<ASTString> con
SourceLocation location = currentLocation();

expectToken(Token::Assembly);
yul::Dialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(m_evmVersion);
yul::Dialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(m_evmVersion, m_eofVersion);
if (m_scanner->currentToken() == Token::StringLiteral)
{
if (m_scanner->currentLiteral() != "evmasm")
Expand Down
7 changes: 5 additions & 2 deletions libsolidity/parsing/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,12 @@ class Parser: public langutil::ParserBase
public:
explicit Parser(
langutil::ErrorReporter& _errorReporter,
langutil::EVMVersion _evmVersion
langutil::EVMVersion _evmVersion,
std::optional<uint8_t> _eofVersion
):
ParserBase(_errorReporter),
m_evmVersion(_evmVersion)
m_evmVersion(_evmVersion),
m_eofVersion(_eofVersion)
{}

ASTPointer<SourceUnit> parse(langutil::CharStream& _charStream);
Expand Down Expand Up @@ -247,6 +249,7 @@ class Parser: public langutil::ParserBase
/// Flag that signifies whether '_' is parsed as a PlaceholderStatement or a regular identifier.
bool m_insideModifier = false;
langutil::EVMVersion m_evmVersion;
std::optional<uint8_t> m_eofVersion;
/// Counter for the next AST node ID
int64_t m_currentNodeID = 0;
/// Flag that indicates whether experimental mode is enabled in the current source unit
Expand Down
3 changes: 2 additions & 1 deletion libyul/AsmAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,8 @@ void AsmAnalyzer::expectValidIdentifier(YulName _identifier, SourceLocation cons
bool AsmAnalyzer::validateInstructions(std::string const& _instructionIdentifier, langutil::SourceLocation const& _location)
{
// NOTE: This function uses the default EVM version instead of the currently selected one.
auto const builtin = EVMDialect::strictAssemblyForEVM(EVMVersion{}).builtin(YulName(_instructionIdentifier));
// TODO: Add EOF support
auto const builtin = EVMDialect::strictAssemblyForEVM(EVMVersion{}, std::nullopt).builtin(YulName(_instructionIdentifier));
if (builtin && builtin->instruction.has_value())
return validateInstructions(builtin->instruction.value(), _location);
else
Expand Down
8 changes: 5 additions & 3 deletions libyul/ObjectOptimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@ using namespace solidity::util;
using namespace solidity::yul;


Dialect const& yul::languageToDialect(Language _language, EVMVersion _version)
Dialect const& yul::languageToDialect(Language _language, EVMVersion _version, std::optional<uint8_t> _eofVersion)
{
switch (_language)
{
case Language::Assembly:
case Language::StrictAssembly:
return EVMDialect::strictAssemblyForEVMObjects(_version);
return EVMDialect::strictAssemblyForEVMObjects(_version, _eofVersion);
}
util::unreachable();
}
Expand Down Expand Up @@ -77,7 +77,7 @@ void ObjectOptimizer::optimize(Object& _object, Settings const& _settings, bool
);
}

Dialect const& dialect = languageToDialect(_settings.language, _settings.evmVersion);
Dialect const& dialect = languageToDialect(_settings.language, _settings.evmVersion, _settings.eofVersion);
std::unique_ptr<GasMeter> meter;
if (EVMDialect const* evmDialect = dynamic_cast<EVMDialect const*>(&dialect))
meter = std::make_unique<GasMeter>(*evmDialect, _isCreation, _settings.expectedExecutionsPerDeployment);
Expand Down Expand Up @@ -158,6 +158,8 @@ std::optional<h256> ObjectOptimizer::calculateCacheKey(
rawKey += h256(u256(_settings.expectedExecutionsPerDeployment)).asBytes();
rawKey += FixedHash<1>(uint8_t(_isCreation ? 0 : 1)).asBytes();
rawKey += keccak256(_settings.evmVersion.name()).asBytes();
yulAssert(!_settings.eofVersion.has_value() || *_settings.eofVersion > 0);
rawKey += FixedHash<1>(uint8_t(_settings.eofVersion ? 0 : *_settings.eofVersion)).asBytes();
rawKey += keccak256(_settings.yulOptimiserSteps).asBytes();
rawKey += keccak256(_settings.yulOptimiserCleanupSteps).asBytes();

Expand Down
3 changes: 2 additions & 1 deletion libyul/ObjectOptimizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ enum class Language
StrictAssembly,
};

Dialect const& languageToDialect(Language _language, langutil::EVMVersion _version);
Dialect const& languageToDialect(Language _language, langutil::EVMVersion _version, std::optional<uint8_t> _eofVersion);

/// Encapsulates logic for applying @a yul::OptimiserSuite to a whole hierarchy of Yul objects.
/// Also, acts as a transparent cache for optimized objects.
Expand All @@ -58,6 +58,7 @@ class ObjectOptimizer
{
Language language;
langutil::EVMVersion evmVersion;
std::optional<uint8_t> eofVersion;
bool optimizeStackAllocation;
std::string yulOptimiserSteps;
std::string yulOptimiserCleanupSteps;
Expand Down
13 changes: 7 additions & 6 deletions libyul/YulStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ bool YulStack::parse(std::string const& _sourceName, std::string const& _source)
{
m_charStream = std::make_unique<CharStream>(_source, _sourceName);
std::shared_ptr<Scanner> scanner = std::make_shared<Scanner>(*m_charStream);
m_parserResult = ObjectParser(m_errorReporter, languageToDialect(m_language, m_evmVersion)).parse(scanner, false);
m_parserResult = ObjectParser(m_errorReporter, languageToDialect(m_language, m_evmVersion, m_eofVersion)).parse(scanner, false);
}
catch (UnimplementedFeatureError const& _error)
{
Expand Down Expand Up @@ -94,7 +94,7 @@ void YulStack::optimize()
{
if (
!m_optimiserSettings.runYulOptimiser &&
yul::MSizeFinder::containsMSize(languageToDialect(m_language, m_evmVersion), *m_parserResult)
yul::MSizeFinder::containsMSize(languageToDialect(m_language, m_evmVersion, m_eofVersion), *m_parserResult)
)
return;

Expand Down Expand Up @@ -130,6 +130,7 @@ void YulStack::optimize()
ObjectOptimizer::Settings{
m_language,
m_evmVersion,
m_eofVersion,
optimizeStackAllocation,
yulOptimiserSteps,
yulOptimiserCleanupSteps,
Expand Down Expand Up @@ -163,7 +164,7 @@ bool YulStack::analyzeParsed(Object& _object)
AsmAnalyzer analyzer(
*_object.analysisInfo,
m_errorReporter,
languageToDialect(m_language, m_evmVersion),
languageToDialect(m_language, m_evmVersion, m_eofVersion),
{},
_object.qualifiedDataNames()
);
Expand Down Expand Up @@ -196,7 +197,7 @@ void YulStack::compileEVM(AbstractAssembly& _assembly, bool _optimize) const
{
case Language::Assembly:
case Language::StrictAssembly:
dialect = &EVMDialect::strictAssemblyForEVMObjects(m_evmVersion);
dialect = &EVMDialect::strictAssemblyForEVMObjects(m_evmVersion, m_eofVersion);
break;
default:
yulAssert(false, "Invalid language.");
Expand Down Expand Up @@ -316,7 +317,7 @@ YulStack::assembleEVMWithDeployed(std::optional<std::string_view> _deployName)
// it with the minimal steps required to avoid "stack too deep".
bool optimize = m_optimiserSettings.optimizeStackAllocation || (
!m_optimiserSettings.runYulOptimiser &&
!yul::MSizeFinder::containsMSize(languageToDialect(m_language, m_evmVersion), *m_parserResult)
!yul::MSizeFinder::containsMSize(languageToDialect(m_language, m_evmVersion, m_eofVersion), *m_parserResult)
);
try
{
Expand Down Expand Up @@ -385,7 +386,7 @@ Json YulStack::cfgJson() const
// NOTE: The block Ids are reset for each object
std::unique_ptr<ControlFlow> controlFlow = SSAControlFlowGraphBuilder::build(
*_object.analysisInfo.get(),
languageToDialect(m_language, m_evmVersion),
languageToDialect(m_language, m_evmVersion, m_eofVersion),
_object.code()->root()
);
YulControlFlowGraphExporter exporter(*controlFlow);
Expand Down
3 changes: 2 additions & 1 deletion libyul/backends/evm/AsmCodeGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ void CodeGenerator::assemble(
AsmAnalysisInfo& _analysisInfo,
evmasm::Assembly& _assembly,
langutil::EVMVersion _evmVersion,
std::optional<uint8_t> _eofVersion,
ExternalIdentifierAccess::CodeGenerator _identifierAccessCodeGen,
bool _useNamedLabelsForFunctions,
bool _optimizeStackAllocation
Expand All @@ -49,7 +50,7 @@ void CodeGenerator::assemble(
assemblyAdapter,
_analysisInfo,
_parsedData,
EVMDialect::strictAssemblyForEVM(_evmVersion),
EVMDialect::strictAssemblyForEVM(_evmVersion, _eofVersion),
builtinContext,
_optimizeStackAllocation,
_identifierAccessCodeGen,
Expand Down
1 change: 1 addition & 0 deletions libyul/backends/evm/AsmCodeGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class CodeGenerator
AsmAnalysisInfo& _analysisInfo,
evmasm::Assembly& _assembly,
langutil::EVMVersion _evmVersion,
std::optional<uint8_t> _eofVersion,
ExternalIdentifierAccess::CodeGenerator _identifierAccess = {},
bool _useNamedLabelsForFunctions = false,
bool _optimizeStackAllocation = false
Expand Down
Loading

0 comments on commit 4ab3e36

Please sign in to comment.