Add experimental EOF options for CLI and Standard JSON.

Co-authored-by: Kamil Śliwak <kamil.sliwak@codepoets.it>
This commit is contained in:
Daniel Kirchner 2022-11-22 13:05:20 +01:00
parent 3109ce2dbc
commit bf26d3be5a
38 changed files with 198 additions and 22 deletions

View File

@ -97,6 +97,7 @@ pair<string, string> IRGenerator::run(
yul::YulStack asmStack( yul::YulStack asmStack(
m_evmVersion, m_evmVersion,
m_eofVersion,
yul::YulStack::Language::StrictAssembly, yul::YulStack::Language::StrictAssembly,
m_optimiserSettings, m_optimiserSettings,
m_context.debugInfoSelection() m_context.debugInfoSelection()

View File

@ -46,6 +46,7 @@ public:
IRGenerator( IRGenerator(
langutil::EVMVersion _evmVersion, langutil::EVMVersion _evmVersion,
std::optional<uint8_t> _eofVersion,
RevertStrings _revertStrings, RevertStrings _revertStrings,
OptimiserSettings _optimiserSettings, OptimiserSettings _optimiserSettings,
std::map<std::string, unsigned> _sourceIndices, std::map<std::string, unsigned> _sourceIndices,
@ -53,6 +54,7 @@ public:
langutil::CharStreamProvider const* _soliditySourceProvider langutil::CharStreamProvider const* _soliditySourceProvider
): ):
m_evmVersion(_evmVersion), m_evmVersion(_evmVersion),
m_eofVersion(_eofVersion),
m_optimiserSettings(_optimiserSettings), m_optimiserSettings(_optimiserSettings),
m_context( m_context(
_evmVersion, _evmVersion,
@ -138,6 +140,7 @@ private:
std::string dispenseLocationComment(ASTNode const& _node); std::string dispenseLocationComment(ASTNode const& _node);
langutil::EVMVersion const m_evmVersion; langutil::EVMVersion const m_evmVersion;
std::optional<uint8_t> const m_eofVersion;
OptimiserSettings const m_optimiserSettings; OptimiserSettings const m_optimiserSettings;
IRGenerationContext m_context; IRGenerationContext m_context;

View File

@ -225,6 +225,15 @@ void CompilerStack::setEVMVersion(langutil::EVMVersion _version)
m_evmVersion = _version; m_evmVersion = _version;
} }
void CompilerStack::setEOFVersion(std::optional<uint8_t> _version)
{
if (m_stackState >= CompilationSuccessful)
solThrow(CompilerError, "Must set EOF version before compiling.");
if (_version && _version != 1)
solThrow(CompilerError, "Invalid EOF version.");
m_eofVersion = _version;
}
void CompilerStack::setModelCheckerSettings(ModelCheckerSettings _settings) void CompilerStack::setModelCheckerSettings(ModelCheckerSettings _settings)
{ {
if (m_stackState >= ParsedAndImported) if (m_stackState >= ParsedAndImported)
@ -1299,6 +1308,7 @@ void CompilerStack::compileContract(
) )
{ {
solAssert(!m_viaIR, ""); solAssert(!m_viaIR, "");
solUnimplementedAssert(!m_eofVersion.has_value(), "Experimental EOF support is only available for via-IR compilation.");
solAssert(m_stackState >= AnalysisPerformed, ""); solAssert(m_stackState >= AnalysisPerformed, "");
if (m_hasError) if (m_hasError)
solThrow(CompilerError, "Called compile with errors."); solThrow(CompilerError, "Called compile with errors.");
@ -1364,7 +1374,15 @@ void CompilerStack::generateIR(ContractDefinition const& _contract)
for (auto const& pair: m_contracts) for (auto const& pair: m_contracts)
otherYulSources.emplace(pair.second.contract, pair.second.yulIR); otherYulSources.emplace(pair.second.contract, pair.second.yulIR);
IRGenerator generator(m_evmVersion, m_revertStrings, m_optimiserSettings, sourceIndices(), m_debugInfoSelection, this); IRGenerator generator(
m_evmVersion,
m_eofVersion,
m_revertStrings,
m_optimiserSettings,
sourceIndices(),
m_debugInfoSelection,
this
);
tie(compiledContract.yulIR, compiledContract.yulIROptimized) = generator.run( tie(compiledContract.yulIR, compiledContract.yulIROptimized) = generator.run(
_contract, _contract,
createCBORMetadata(compiledContract, /* _forIR */ true), createCBORMetadata(compiledContract, /* _forIR */ true),
@ -1389,6 +1407,7 @@ void CompilerStack::generateEVMFromIR(ContractDefinition const& _contract)
// Re-parse the Yul IR in EVM dialect // Re-parse the Yul IR in EVM dialect
yul::YulStack stack( yul::YulStack stack(
m_evmVersion, m_evmVersion,
m_eofVersion,
yul::YulStack::Language::StrictAssembly, yul::YulStack::Language::StrictAssembly,
m_optimiserSettings, m_optimiserSettings,
m_debugInfoSelection m_debugInfoSelection
@ -1421,6 +1440,7 @@ void CompilerStack::generateEwasm(ContractDefinition const& _contract)
// Re-parse the Yul IR in EVM dialect // Re-parse the Yul IR in EVM dialect
yul::YulStack stack( yul::YulStack stack(
m_evmVersion, m_evmVersion,
m_eofVersion,
yul::YulStack::Language::StrictAssembly, yul::YulStack::Language::StrictAssembly,
m_optimiserSettings, m_optimiserSettings,
m_debugInfoSelection m_debugInfoSelection
@ -1571,6 +1591,8 @@ string CompilerStack::createMetadata(Contract const& _contract, bool _forIR) con
if (_forIR) if (_forIR)
meta["settings"]["viaIR"] = _forIR; meta["settings"]["viaIR"] = _forIR;
meta["settings"]["evmVersion"] = m_evmVersion.name(); meta["settings"]["evmVersion"] = m_evmVersion.name();
if (m_eofVersion.has_value())
meta["settings"]["eofVersion"] = *m_eofVersion;
meta["settings"]["compilationTarget"][_contract.contract->sourceUnitName()] = meta["settings"]["compilationTarget"][_contract.contract->sourceUnitName()] =
*_contract.contract->annotation().canonicalName; *_contract.contract->annotation().canonicalName;
@ -1695,7 +1717,7 @@ bytes CompilerStack::createCBORMetadata(Contract const& _contract, bool _forIR)
else else
solAssert(m_metadataHash == MetadataHash::None, "Invalid metadata hash"); solAssert(m_metadataHash == MetadataHash::None, "Invalid metadata hash");
if (experimentalMode) if (experimentalMode || m_eofVersion.has_value())
encoder.pushBool("experimental", true); encoder.pushBool("experimental", true);
if (m_metadataFormat == MetadataFormat::WithReleaseVersionTag) if (m_metadataFormat == MetadataFormat::WithReleaseVersionTag)
encoder.pushBytes("solc", VersionCompactBytes); encoder.pushBytes("solc", VersionCompactBytes);

View File

@ -181,6 +181,10 @@ public:
/// Must be set before parsing. /// Must be set before parsing.
void setEVMVersion(langutil::EVMVersion _version = langutil::EVMVersion{}); void setEVMVersion(langutil::EVMVersion _version = langutil::EVMVersion{});
/// Set the EOF version used before running compile.
/// If set to std::nullopt (the default), legacy non-EOF bytecode is generated.
void setEOFVersion(std::optional<uint8_t> version);
/// Set model checker settings. /// Set model checker settings.
void setModelCheckerSettings(ModelCheckerSettings _settings); void setModelCheckerSettings(ModelCheckerSettings _settings);
@ -498,6 +502,7 @@ private:
State m_stopAfter = State::CompilationSuccessful; State m_stopAfter = State::CompilationSuccessful;
bool m_viaIR = false; bool m_viaIR = false;
langutil::EVMVersion m_evmVersion; langutil::EVMVersion m_evmVersion;
std::optional<uint8_t> m_eofVersion;
ModelCheckerSettings m_modelCheckerSettings; ModelCheckerSettings m_modelCheckerSettings;
std::map<std::string, std::set<std::string>> m_requestedContractNames; std::map<std::string, std::set<std::string>> m_requestedContractNames;
bool m_generateEvmBytecode = true; bool m_generateEvmBytecode = true;

View File

@ -800,6 +800,16 @@ std::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompiler:
ret.evmVersion = *version; ret.evmVersion = *version;
} }
if (settings.isMember("eofVersion"))
{
if (!settings["eofVersion"].isUInt())
return formatFatalError(Error::Type::JSONError, "eofVersion must be an unsigned integer.");
auto eofVersion = settings["evmVersion"].asUInt();
if (eofVersion != 1)
return formatFatalError(Error::Type::JSONError, "Invalid EOF version requested.");
ret.eofVersion = 1;
}
if (settings.isMember("debug")) if (settings.isMember("debug"))
{ {
if (auto result = checkKeys(settings["debug"], {"revertStrings", "debugInfo"}, "settings.debug")) if (auto result = checkKeys(settings["debug"], {"revertStrings", "debugInfo"}, "settings.debug"))
@ -1413,6 +1423,7 @@ Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings)
YulStack stack( YulStack stack(
_inputsAndSettings.evmVersion, _inputsAndSettings.evmVersion,
_inputsAndSettings.eofVersion,
YulStack::Language::StrictAssembly, YulStack::Language::StrictAssembly,
_inputsAndSettings.optimiserSettings, _inputsAndSettings.optimiserSettings,
_inputsAndSettings.debugInfoSelection.has_value() ? _inputsAndSettings.debugInfoSelection.has_value() ?

View File

@ -77,6 +77,7 @@ private:
std::map<std::string, std::string> sources; std::map<std::string, std::string> sources;
std::map<util::h256, std::string> smtLib2Responses; std::map<util::h256, std::string> smtLib2Responses;
langutil::EVMVersion evmVersion; langutil::EVMVersion evmVersion;
std::optional<uint8_t> eofVersion;
std::vector<ImportRemapper::Remapping> remappings; std::vector<ImportRemapper::Remapping> remappings;
RevertStrings revertStrings = RevertStrings::Default; RevertStrings revertStrings = RevertStrings::Default;
OptimiserSettings optimiserSettings = OptimiserSettings::minimal(); OptimiserSettings optimiserSettings = OptimiserSettings::minimal();

View File

@ -164,7 +164,7 @@ void YulStack::compileEVM(AbstractAssembly& _assembly, bool _optimize) const
break; break;
} }
EVMObjectCompiler::compile(*m_parserResult, _assembly, *dialect, _optimize); EVMObjectCompiler::compile(*m_parserResult, _assembly, *dialect, _optimize, m_eofVersion);
} }
void YulStack::optimize(Object& _object, bool _isCreation) void YulStack::optimize(Object& _object, bool _isCreation)

View File

@ -72,6 +72,7 @@ public:
YulStack(): YulStack():
YulStack( YulStack(
langutil::EVMVersion{}, langutil::EVMVersion{},
std::nullopt,
Language::Assembly, Language::Assembly,
solidity::frontend::OptimiserSettings::none(), solidity::frontend::OptimiserSettings::none(),
langutil::DebugInfoSelection::Default() langutil::DebugInfoSelection::Default()
@ -80,12 +81,14 @@ public:
YulStack( YulStack(
langutil::EVMVersion _evmVersion, langutil::EVMVersion _evmVersion,
std::optional<uint8_t> _eofVersion,
Language _language, Language _language,
solidity::frontend::OptimiserSettings _optimiserSettings, solidity::frontend::OptimiserSettings _optimiserSettings,
langutil::DebugInfoSelection const& _debugInfoSelection langutil::DebugInfoSelection const& _debugInfoSelection
): ):
m_language(_language), m_language(_language),
m_evmVersion(_evmVersion), m_evmVersion(_evmVersion),
m_eofVersion(_eofVersion),
m_optimiserSettings(std::move(_optimiserSettings)), m_optimiserSettings(std::move(_optimiserSettings)),
m_debugInfoSelection(_debugInfoSelection), m_debugInfoSelection(_debugInfoSelection),
m_errorReporter(m_errors) m_errorReporter(m_errors)
@ -146,6 +149,7 @@ private:
Language m_language = Language::Assembly; Language m_language = Language::Assembly;
langutil::EVMVersion m_evmVersion; langutil::EVMVersion m_evmVersion;
std::optional<uint8_t> m_eofVersion;
solidity::frontend::OptimiserSettings m_optimiserSettings; solidity::frontend::OptimiserSettings m_optimiserSettings;
langutil::DebugInfoSelection m_debugInfoSelection{}; langutil::DebugInfoSelection m_debugInfoSelection{};

View File

@ -35,9 +35,15 @@
using namespace solidity::yul; using namespace solidity::yul;
using namespace std; using namespace std;
void EVMObjectCompiler::compile(Object& _object, AbstractAssembly& _assembly, EVMDialect const& _dialect, bool _optimize) void EVMObjectCompiler::compile(
Object& _object,
AbstractAssembly& _assembly,
EVMDialect const& _dialect,
bool _optimize,
std::optional<uint8_t> _eofVersion
)
{ {
EVMObjectCompiler compiler(_assembly, _dialect); EVMObjectCompiler compiler(_assembly, _dialect, _eofVersion);
compiler.run(_object, _optimize); compiler.run(_object, _optimize);
} }
@ -54,7 +60,7 @@ void EVMObjectCompiler::run(Object& _object, bool _optimize)
auto subAssemblyAndID = m_assembly.createSubAssembly(isCreation, subObject->name.str()); auto subAssemblyAndID = m_assembly.createSubAssembly(isCreation, subObject->name.str());
context.subIDs[subObject->name] = subAssemblyAndID.second; context.subIDs[subObject->name] = subAssemblyAndID.second;
subObject->subId = subAssemblyAndID.second; subObject->subId = subAssemblyAndID.second;
compile(*subObject, *subAssemblyAndID.first, m_dialect, _optimize); compile(*subObject, *subAssemblyAndID.first, m_dialect, _optimize, m_eofVersion);
} }
else else
{ {
@ -68,6 +74,11 @@ void EVMObjectCompiler::run(Object& _object, bool _optimize)
yulAssert(_object.analysisInfo, "No analysis info."); yulAssert(_object.analysisInfo, "No analysis info.");
yulAssert(_object.code, "No code."); yulAssert(_object.code, "No code.");
if (m_eofVersion.has_value())
yulAssert(
_optimize && (m_dialect.evmVersion() == langutil::EVMVersion()),
"Experimental EOF support is only available for optimized via-IR compilation and the most recent EVM version."
);
if (_optimize && m_dialect.evmVersion().canOverchargeGasForCall()) if (_optimize && m_dialect.evmVersion().canOverchargeGasForCall())
{ {
auto stackErrors = OptimizedEVMCodeTransform::run( auto stackErrors = OptimizedEVMCodeTransform::run(

View File

@ -21,6 +21,9 @@
#pragma once #pragma once
#include <optional>
#include <cstdint>
namespace solidity::yul namespace solidity::yul
{ {
struct Object; struct Object;
@ -30,16 +33,23 @@ struct EVMDialect;
class EVMObjectCompiler class EVMObjectCompiler
{ {
public: public:
static void compile(Object& _object, AbstractAssembly& _assembly, EVMDialect const& _dialect, bool _optimize); static void compile(
Object& _object,
AbstractAssembly& _assembly,
EVMDialect const& _dialect,
bool _optimize,
std::optional<uint8_t> _eofVersion
);
private: private:
EVMObjectCompiler(AbstractAssembly& _assembly, EVMDialect const& _dialect): EVMObjectCompiler(AbstractAssembly& _assembly, EVMDialect const& _dialect, std::optional<uint8_t> _eofVersion):
m_assembly(_assembly), m_dialect(_dialect) m_assembly(_assembly), m_dialect(_dialect), m_eofVersion(_eofVersion)
{} {}
void run(Object& _object, bool _optimize); void run(Object& _object, bool _optimize);
AbstractAssembly& m_assembly; AbstractAssembly& m_assembly;
EVMDialect const& m_dialect; EVMDialect const& m_dialect;
std::optional<uint8_t> m_eofVersion;
}; };
} }

View File

@ -708,6 +708,7 @@ void CommandLineInterface::compile()
m_compiler->setLibraries(m_options.linker.libraries); m_compiler->setLibraries(m_options.linker.libraries);
m_compiler->setViaIR(m_options.output.viaIR); m_compiler->setViaIR(m_options.output.viaIR);
m_compiler->setEVMVersion(m_options.output.evmVersion); m_compiler->setEVMVersion(m_options.output.evmVersion);
m_compiler->setEOFVersion(m_options.output.eofVersion);
m_compiler->setRevertStringBehaviour(m_options.output.revertStrings); m_compiler->setRevertStringBehaviour(m_options.output.revertStrings);
if (m_options.output.debugInfoSelection.has_value()) if (m_options.output.debugInfoSelection.has_value())
m_compiler->selectDebugInfo(m_options.output.debugInfoSelection.value()); m_compiler->selectDebugInfo(m_options.output.debugInfoSelection.value());
@ -1042,6 +1043,7 @@ void CommandLineInterface::assemble(yul::YulStack::Language _language, yul::YulS
auto& stack = yulStacks[src.first] = yul::YulStack( auto& stack = yulStacks[src.first] = yul::YulStack(
m_options.output.evmVersion, m_options.output.evmVersion,
m_options.output.eofVersion,
_language, _language,
m_options.optimiserSettings(), m_options.optimiserSettings(),
m_options.output.debugInfoSelection.has_value() ? m_options.output.debugInfoSelection.has_value() ?

View File

@ -47,6 +47,7 @@ static string const g_strErrorRecovery = "error-recovery";
static string const g_strEVM = "evm"; static string const g_strEVM = "evm";
static string const g_strEVMVersion = "evm-version"; static string const g_strEVMVersion = "evm-version";
static string const g_strEwasm = "ewasm"; static string const g_strEwasm = "ewasm";
static string const g_strEOFVersion = "experimental-eof-version";
static string const g_strViaIR = "via-ir"; static string const g_strViaIR = "via-ir";
static string const g_strExperimentalViaIR = "experimental-via-ir"; static string const g_strExperimentalViaIR = "experimental-via-ir";
static string const g_strGas = "gas"; static string const g_strGas = "gas";
@ -231,6 +232,7 @@ bool CommandLineOptions::operator==(CommandLineOptions const& _other) const noex
output.revertStrings == _other.output.revertStrings && output.revertStrings == _other.output.revertStrings &&
output.debugInfoSelection == _other.output.debugInfoSelection && output.debugInfoSelection == _other.output.debugInfoSelection &&
output.stopAfter == _other.output.stopAfter && output.stopAfter == _other.output.stopAfter &&
output.eofVersion == _other.output.eofVersion &&
input.mode == _other.input.mode && input.mode == _other.input.mode &&
assembly.targetMachine == _other.assembly.targetMachine && assembly.targetMachine == _other.assembly.targetMachine &&
assembly.inputLanguage == _other.assembly.inputLanguage && assembly.inputLanguage == _other.assembly.inputLanguage &&
@ -509,9 +511,11 @@ void CommandLineParser::parseOutputSelection()
"The following outputs are not supported in " + g_inputModeName.at(m_options.input.mode) + " mode: " + "The following outputs are not supported in " + g_inputModeName.at(m_options.input.mode) + " mode: " +
joinOptionNames(unsupportedOutputs) + "." joinOptionNames(unsupportedOutputs) + "."
); );
// TODO: restrict EOF version to correct EVM version.
} }
po::options_description CommandLineParser::optionsDescription() po::options_description CommandLineParser::optionsDescription(bool _forHelp)
{ {
// Declare the supported options. // Declare the supported options.
po::options_description desc((R"(solc, the Solidity commandline compiler. po::options_description desc((R"(solc, the Solidity commandline compiler.
@ -588,6 +592,18 @@ General Information)").c_str(),
"Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon, " "Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon, "
"byzantium, constantinople, petersburg, istanbul, berlin, london or paris." "byzantium, constantinople, petersburg, istanbul, berlin, london or paris."
) )
;
if (!_forHelp) // Note: We intentionally keep this undocumented for now.
outputOptions.add_options()
(
g_strEOFVersion.c_str(),
// Declared as uint64_t, since uint8_t will be parsed as character by boost.
po::value<uint64_t>()->value_name("version")->implicit_value(1),
"Select desired EOF version. Currently the only valid value is 1. "
"If not specified, legacy non-EOF bytecode will be generated."
)
;
outputOptions.add_options()
( (
g_strExperimentalViaIR.c_str(), g_strExperimentalViaIR.c_str(),
"Deprecated synonym of --via-ir." "Deprecated synonym of --via-ir."
@ -1113,6 +1129,15 @@ void CommandLineParser::processArgs()
m_options.output.evmVersion = *versionOption; m_options.output.evmVersion = *versionOption;
} }
if (m_args.count(g_strEOFVersion))
{
// Request as uint64_t, since uint8_t will be parsed as character by boost.
uint64_t versionOption = m_args[g_strEOFVersion].as<uint64_t>();
if (versionOption != 1)
solThrow(CommandLineValidationError, "Invalid option for --" + g_strEOFVersion + ": " + to_string(versionOption));
m_options.output.eofVersion = 1;
}
m_options.optimizer.enabled = (m_args.count(g_strOptimize) > 0); m_options.optimizer.enabled = (m_args.count(g_strOptimize) > 0);
m_options.optimizer.noOptimizeYul = (m_args.count(g_strNoOptimizeYul) > 0); m_options.optimizer.noOptimizeYul = (m_args.count(g_strNoOptimizeYul) > 0);
if (!m_args[g_strOptimizeRuns].defaulted()) if (!m_args[g_strOptimizeRuns].defaulted())

View File

@ -186,6 +186,7 @@ struct CommandLineOptions
RevertStrings revertStrings = RevertStrings::Default; RevertStrings revertStrings = RevertStrings::Default;
std::optional<langutil::DebugInfoSelection> debugInfoSelection; std::optional<langutil::DebugInfoSelection> debugInfoSelection;
CompilerStack::State stopAfter = CompilerStack::State::CompilationSuccessful; CompilerStack::State stopAfter = CompilerStack::State::CompilationSuccessful;
std::optional<uint8_t> eofVersion;
} output; } output;
struct struct
@ -247,12 +248,12 @@ public:
CommandLineOptions const& options() const { return m_options; } CommandLineOptions const& options() const { return m_options; }
static void printHelp(std::ostream& _out) { _out << optionsDescription(); } static void printHelp(std::ostream& _out) { _out << optionsDescription(true /* _forHelp */); }
private: private:
/// @returns a specification of all named command-line options accepted by the compiler. /// @returns a specification of all named command-line options accepted by the compiler.
/// The object can be used to parse command-line arguments or to generate the help screen. /// The object can be used to parse command-line arguments or to generate the help screen.
static boost::program_options::options_description optionsDescription(); static boost::program_options::options_description optionsDescription(bool _forHelp = false);
/// @returns a specification of all positional command-line arguments accepted by the compiler. /// @returns a specification of all positional command-line arguments accepted by the compiler.
/// The object can be used to parse command-line arguments or to generate the help screen. /// The object can be used to parse command-line arguments or to generate the help screen.

View File

@ -103,7 +103,9 @@ CommonOptions::CommonOptions(std::string _caption):
void CommonOptions::addOptions() void CommonOptions::addOptions()
{ {
options.add_options() options.add_options()
("evm-version", po::value(&evmVersionString), "which evm version to use") ("evm-version", po::value(&evmVersionString), "which EVM version to use")
// "eof-version" is declared as uint64_t, since uint8_t will be parsed as character by boost.
("eof-version", po::value<uint64_t>()->implicit_value(1u), "which EOF version to use")
("testpath", po::value<fs::path>(&this->testPath)->default_value(solidity::test::testPath()), "path to test files") ("testpath", po::value<fs::path>(&this->testPath)->default_value(solidity::test::testPath()), "path to test files")
("vm", po::value<std::vector<fs::path>>(&vmPaths), "path to evmc library, can be supplied multiple times.") ("vm", po::value<std::vector<fs::path>>(&vmPaths), "path to evmc library, can be supplied multiple times.")
("ewasm", po::bool_switch(&ewasm)->default_value(ewasm), "tries to automatically find an ewasm vm and enable ewasm test-execution.") ("ewasm", po::bool_switch(&ewasm)->default_value(ewasm), "tries to automatically find an ewasm vm and enable ewasm test-execution.")
@ -170,6 +172,14 @@ bool CommonOptions::parse(int argc, char const* const* argv)
auto parsedOptions = cmdLineParser.run(); auto parsedOptions = cmdLineParser.run();
po::store(parsedOptions, arguments); po::store(parsedOptions, arguments);
po::notify(arguments); po::notify(arguments);
if (arguments.count("eof-version"))
{
// Request as uint64_t, since uint8_t will be parsed as character by boost.
uint64_t eofVersion = arguments["eof-version"].as<uint64_t>();
if (eofVersion != 1)
BOOST_THROW_EXCEPTION(std::runtime_error("Invalid EOF version: " + to_string(eofVersion)));
m_eofVersion = 1;
}
for (auto const& parsedOption: parsedOptions.options) for (auto const& parsedOption: parsedOptions.options)
if (parsedOption.position_key >= 0) if (parsedOption.position_key >= 0)
@ -261,7 +271,6 @@ langutil::EVMVersion CommonOptions::evmVersion() const
return langutil::EVMVersion(); return langutil::EVMVersion();
} }
CommonOptions const& CommonOptions::get() CommonOptions const& CommonOptions::get()
{ {
if (!m_singleton) if (!m_singleton)

View File

@ -72,6 +72,7 @@ struct CommonOptions
size_t selectedBatch = 0; size_t selectedBatch = 0;
langutil::EVMVersion evmVersion() const; langutil::EVMVersion evmVersion() const;
std::optional<uint8_t> eofVersion() const { return m_eofVersion; }
virtual void addOptions(); virtual void addOptions();
// @returns true if the program should continue, false if it should exit immediately without // @returns true if the program should continue, false if it should exit immediately without
@ -98,6 +99,7 @@ protected:
private: private:
std::string evmVersionString; std::string evmVersionString;
std::optional<uint8_t> m_eofVersion;
static std::unique_ptr<CommonOptions const> m_singleton; static std::unique_ptr<CommonOptions const> m_singleton;
}; };

View File

@ -39,6 +39,7 @@ public:
{ {
std::string filename; std::string filename;
langutil::EVMVersion evmVersion; langutil::EVMVersion evmVersion;
std::optional<uint8_t> eofVersion;
std::vector<boost::filesystem::path> vmPaths; std::vector<boost::filesystem::path> vmPaths;
bool enforceCompileToEwasm = false; bool enforceCompileToEwasm = false;
bool enforceGasCost = false; bool enforceGasCost = false;

View File

@ -62,6 +62,7 @@ std::optional<Error> parseAndReturnFirstError(
{ {
YulStack stack( YulStack stack(
solidity::test::CommonOptions::get().evmVersion(), solidity::test::CommonOptions::get().evmVersion(),
solidity::test::CommonOptions::get().eofVersion(),
_language, _language,
solidity::frontend::OptimiserSettings::none(), solidity::frontend::OptimiserSettings::none(),
DebugInfoSelection::None() DebugInfoSelection::None()
@ -133,6 +134,7 @@ void parsePrintCompare(string const& _source, bool _canWarn = false)
{ {
YulStack stack( YulStack stack(
solidity::test::CommonOptions::get().evmVersion(), solidity::test::CommonOptions::get().evmVersion(),
solidity::test::CommonOptions::get().eofVersion(),
YulStack::Language::Assembly, YulStack::Language::Assembly,
OptimiserSettings::none(), OptimiserSettings::none(),
DebugInfoSelection::None() DebugInfoSelection::None()
@ -223,6 +225,7 @@ BOOST_AUTO_TEST_CASE(print_string_literal_unicode)
string parsed = "object \"object\" {\n code { let x := \"\\xe1\\xae\\xac\" }\n}\n"; string parsed = "object \"object\" {\n code { let x := \"\\xe1\\xae\\xac\" }\n}\n";
YulStack stack( YulStack stack(
solidity::test::CommonOptions::get().evmVersion(), solidity::test::CommonOptions::get().evmVersion(),
solidity::test::CommonOptions::get().eofVersion(),
YulStack::Language::Assembly, YulStack::Language::Assembly,
OptimiserSettings::none(), OptimiserSettings::none(),
DebugInfoSelection::None() DebugInfoSelection::None()

View File

@ -226,6 +226,45 @@ BOOST_AUTO_TEST_CASE(metadata_stamp_experimental)
} }
} }
BOOST_AUTO_TEST_CASE(metadata_eof_experimental)
{
// Check that setting an EOF version results in the experimental flag being set.
char const* sourceCode = R"(
pragma solidity >=0.0;
contract test {
function g(function(uint) external returns (uint) x) public {}
}
)";
for (auto metadataFormat: std::set<CompilerStack::MetadataFormat>{
CompilerStack::MetadataFormat::NoMetadata,
CompilerStack::MetadataFormat::WithReleaseVersionTag,
CompilerStack::MetadataFormat::WithPrereleaseVersionTag
})
{
CompilerStack compilerStack;
compilerStack.setMetadataFormat(metadataFormat);
compilerStack.setSources({{"", sourceCode}});
compilerStack.setEVMVersion({});
compilerStack.setViaIR(true);
compilerStack.setEOFVersion(1);
compilerStack.setOptimiserSettings(true);
BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed");
bytes const& bytecode = compilerStack.runtimeObject("test").bytecode;
string const& metadata = compilerStack.metadata("test");
BOOST_CHECK(solidity::test::isValidMetadata(metadata));
auto const cborMetadata = requireParsedCBORMetadata(bytecode, metadataFormat);
if (metadataFormat == CompilerStack::MetadataFormat::NoMetadata)
BOOST_CHECK(cborMetadata.count("experimental") == 0);
else
{
BOOST_CHECK(cborMetadata.count("experimental") == 1);
BOOST_CHECK(cborMetadata.at("experimental") == "true");
}
}
}
BOOST_AUTO_TEST_CASE(metadata_relevant_sources) BOOST_AUTO_TEST_CASE(metadata_relevant_sources)
{ {
CompilerStack compilerStack; CompilerStack compilerStack;

View File

@ -48,12 +48,13 @@ namespace fs = boost::filesystem;
SemanticTest::SemanticTest( SemanticTest::SemanticTest(
string const& _filename, string const& _filename,
langutil::EVMVersion _evmVersion, langutil::EVMVersion _evmVersion,
optional<uint8_t> _eofVersion,
vector<boost::filesystem::path> const& _vmPaths, vector<boost::filesystem::path> const& _vmPaths,
bool _enforceCompileToEwasm, bool _enforceCompileToEwasm,
bool _enforceGasCost, bool _enforceGasCost,
u256 _enforceGasCostMinValue u256 _enforceGasCostMinValue
): ):
SolidityExecutionFramework(_evmVersion, _vmPaths, false), SolidityExecutionFramework(_evmVersion, _eofVersion, _vmPaths, false),
EVMVersionRestrictedTestCase(_filename), EVMVersionRestrictedTestCase(_filename),
m_sources(m_reader.sources()), m_sources(m_reader.sources()),
m_lineOffset(m_reader.lineNumber()), m_lineOffset(m_reader.lineNumber()),
@ -296,13 +297,13 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref
{ {
TestResult result = TestResult::Success; TestResult result = TestResult::Success;
if (m_testCaseWantsLegacyRun) if (m_testCaseWantsLegacyRun && !m_eofVersion.has_value())
result = runTest(_stream, _linePrefix, _formatted, false, false); result = runTest(_stream, _linePrefix, _formatted, false, false);
if (m_testCaseWantsYulRun && result == TestResult::Success) if (m_testCaseWantsYulRun && result == TestResult::Success)
result = runTest(_stream, _linePrefix, _formatted, true, false); result = runTest(_stream, _linePrefix, _formatted, true, false);
if ((m_testCaseWantsEwasmRun || m_enforceCompileToEwasm) && result == TestResult::Success) if (!m_eofVersion.has_value() && (m_testCaseWantsEwasmRun || m_enforceCompileToEwasm) && result == TestResult::Success)
{ {
// TODO: Once we have full Ewasm support, we could remove try/catch here. // TODO: Once we have full Ewasm support, we could remove try/catch here.
try try

View File

@ -51,6 +51,7 @@ public:
return std::make_unique<SemanticTest>( return std::make_unique<SemanticTest>(
_options.filename, _options.filename,
_options.evmVersion, _options.evmVersion,
_options.eofVersion,
_options.vmPaths, _options.vmPaths,
_options.enforceCompileToEwasm, _options.enforceCompileToEwasm,
_options.enforceGasCost, _options.enforceGasCost,
@ -61,6 +62,7 @@ public:
explicit SemanticTest( explicit SemanticTest(
std::string const& _filename, std::string const& _filename,
langutil::EVMVersion _evmVersion, langutil::EVMVersion _evmVersion,
std::optional<uint8_t> _eofVersion,
std::vector<boost::filesystem::path> const& _vmPaths, std::vector<boost::filesystem::path> const& _vmPaths,
bool _enforceCompileToEwasm = false, bool _enforceCompileToEwasm = false,
bool _enforceGasCost = false, bool _enforceGasCost = false,

View File

@ -58,6 +58,7 @@ bytes SolidityExecutionFramework::multiSourceCompileContract(
m_compiler.setLibraries(_libraryAddresses); m_compiler.setLibraries(_libraryAddresses);
m_compiler.setRevertStringBehaviour(m_revertStrings); m_compiler.setRevertStringBehaviour(m_revertStrings);
m_compiler.setEVMVersion(m_evmVersion); m_compiler.setEVMVersion(m_evmVersion);
m_compiler.setEOFVersion(m_eofVersion);
m_compiler.setOptimiserSettings(m_optimiserSettings); m_compiler.setOptimiserSettings(m_optimiserSettings);
m_compiler.enableEvmBytecodeGeneration(!m_compileViaYul); m_compiler.enableEvmBytecodeGeneration(!m_compileViaYul);
m_compiler.enableIRGeneration(m_compileViaYul); m_compiler.enableIRGeneration(m_compileViaYul);
@ -102,6 +103,7 @@ bytes SolidityExecutionFramework::multiSourceCompileContract(
yul::YulStack asmStack( yul::YulStack asmStack(
m_evmVersion, m_evmVersion,
m_eofVersion,
yul::YulStack::Language::StrictAssembly, yul::YulStack::Language::StrictAssembly,
optimiserSettings, optimiserSettings,
DebugInfoSelection::All() DebugInfoSelection::All()

View File

@ -42,10 +42,12 @@ public:
SolidityExecutionFramework(): m_showMetadata(solidity::test::CommonOptions::get().showMetadata) {} SolidityExecutionFramework(): m_showMetadata(solidity::test::CommonOptions::get().showMetadata) {}
explicit SolidityExecutionFramework( explicit SolidityExecutionFramework(
langutil::EVMVersion _evmVersion, langutil::EVMVersion _evmVersion,
std::optional<uint8_t> _eofVersion,
std::vector<boost::filesystem::path> const& _vmPaths, std::vector<boost::filesystem::path> const& _vmPaths,
bool _appendCBORMetadata = true bool _appendCBORMetadata = true
): ):
ExecutionFramework(_evmVersion, _vmPaths), ExecutionFramework(_evmVersion, _vmPaths),
m_eofVersion(_eofVersion),
m_showMetadata(solidity::test::CommonOptions::get().showMetadata), m_showMetadata(solidity::test::CommonOptions::get().showMetadata),
m_appendCBORMetadata(_appendCBORMetadata) m_appendCBORMetadata(_appendCBORMetadata)
{} {}
@ -82,6 +84,7 @@ public:
static std::string addPreamble(std::string const& _sourceCode); static std::string addPreamble(std::string const& _sourceCode);
protected: protected:
using CompilerStack = solidity::frontend::CompilerStack; using CompilerStack = solidity::frontend::CompilerStack;
std::optional<uint8_t> m_eofVersion;
CompilerStack m_compiler; CompilerStack m_compiler;
bool m_compileViaYul = false; bool m_compileViaYul = false;
bool m_compileToEwasm = false; bool m_compileToEwasm = false;

View File

@ -57,6 +57,7 @@ pair<shared_ptr<Block>, shared_ptr<yul::AsmAnalysisInfo>> yul::test::parse(strin
{ {
YulStack stack( YulStack stack(
solidity::test::CommonOptions::get().evmVersion(), solidity::test::CommonOptions::get().evmVersion(),
solidity::test::CommonOptions::get().eofVersion(),
_yul ? YulStack::Language::Yul : YulStack::Language::StrictAssembly, _yul ? YulStack::Language::Yul : YulStack::Language::StrictAssembly,
solidity::test::CommonOptions::get().optimize ? solidity::test::CommonOptions::get().optimize ?
solidity::frontend::OptimiserSettings::standard() : solidity::frontend::OptimiserSettings::standard() :

View File

@ -53,6 +53,7 @@ TestCase::TestResult EVMCodeTransformTest::run(ostream& _stream, string const& _
settings.optimizeStackAllocation = m_stackOpt; settings.optimizeStackAllocation = m_stackOpt;
YulStack stack( YulStack stack(
EVMVersion{}, EVMVersion{},
nullopt,
YulStack::Language::StrictAssembly, YulStack::Language::StrictAssembly,
settings, settings,
DebugInfoSelection::All() DebugInfoSelection::All()
@ -71,7 +72,8 @@ TestCase::TestResult EVMCodeTransformTest::run(ostream& _stream, string const& _
*stack.parserResult(), *stack.parserResult(),
adapter, adapter,
EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}), EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}),
m_stackOpt m_stackOpt,
nullopt
); );
std::ostringstream output; std::ostringstream output;

View File

@ -82,6 +82,7 @@ bool EwasmTranslationTest::parse(ostream& _stream, string const& _linePrefix, bo
{ {
m_stack = YulStack( m_stack = YulStack(
solidity::test::CommonOptions::get().evmVersion(), solidity::test::CommonOptions::get().evmVersion(),
solidity::test::CommonOptions::get().eofVersion(),
YulStack::Language::StrictAssembly, YulStack::Language::StrictAssembly,
solidity::frontend::OptimiserSettings::none(), solidity::frontend::OptimiserSettings::none(),
DebugInfoSelection::All() DebugInfoSelection::All()

View File

@ -65,6 +65,7 @@ TestCase::TestResult ObjectCompilerTest::run(ostream& _stream, string const& _li
{ {
YulStack stack( YulStack stack(
EVMVersion(), EVMVersion(),
nullopt,
m_wasm ? YulStack::Language::Ewasm : YulStack::Language::StrictAssembly, m_wasm ? YulStack::Language::Ewasm : YulStack::Language::StrictAssembly,
OptimiserSettings::preset(m_optimisationPreset), OptimiserSettings::preset(m_optimisationPreset),
DebugInfoSelection::All() DebugInfoSelection::All()

View File

@ -59,6 +59,7 @@ pair<bool, ErrorList> parse(string const& _source)
{ {
YulStack asmStack( YulStack asmStack(
solidity::test::CommonOptions::get().evmVersion(), solidity::test::CommonOptions::get().evmVersion(),
solidity::test::CommonOptions::get().eofVersion(),
YulStack::Language::StrictAssembly, YulStack::Language::StrictAssembly,
solidity::frontend::OptimiserSettings::none(), solidity::frontend::OptimiserSettings::none(),
DebugInfoSelection::All() DebugInfoSelection::All()
@ -181,6 +182,7 @@ BOOST_AUTO_TEST_CASE(to_string)
expectation = boost::replace_all_copy(expectation, "\t", " "); expectation = boost::replace_all_copy(expectation, "\t", " ");
YulStack asmStack( YulStack asmStack(
solidity::test::CommonOptions::get().evmVersion(), solidity::test::CommonOptions::get().evmVersion(),
solidity::test::CommonOptions::get().eofVersion(),
YulStack::Language::StrictAssembly, YulStack::Language::StrictAssembly,
solidity::frontend::OptimiserSettings::none(), solidity::frontend::OptimiserSettings::none(),
DebugInfoSelection::All() DebugInfoSelection::All()

View File

@ -68,6 +68,7 @@ bool YulInterpreterTest::parse(ostream& _stream, string const& _linePrefix, bool
{ {
YulStack stack( YulStack stack(
solidity::test::CommonOptions::get().evmVersion(), solidity::test::CommonOptions::get().evmVersion(),
solidity::test::CommonOptions::get().eofVersion(),
YulStack::Language::StrictAssembly, YulStack::Language::StrictAssembly,
solidity::frontend::OptimiserSettings::none(), solidity::frontend::OptimiserSettings::none(),
DebugInfoSelection::All() DebugInfoSelection::All()

View File

@ -144,6 +144,7 @@ int registerTests(
TestCase::Config config{ TestCase::Config config{
fullpath.string(), fullpath.string(),
solidity::test::CommonOptions::get().evmVersion(), solidity::test::CommonOptions::get().evmVersion(),
solidity::test::CommonOptions::get().eofVersion(),
solidity::test::CommonOptions::get().vmPaths, solidity::test::CommonOptions::get().vmPaths,
_enforceCompileToEwasm, _enforceCompileToEwasm,
solidity::test::CommonOptions::get().enforceGasTest, solidity::test::CommonOptions::get().enforceGasTest,

View File

@ -159,6 +159,7 @@ TestTool::Result TestTool::process()
m_test = m_testCaseCreator(TestCase::Config{ m_test = m_testCaseCreator(TestCase::Config{
m_path.string(), m_path.string(),
m_options.evmVersion(), m_options.evmVersion(),
m_options.eofVersion(),
m_options.vmPaths, m_options.vmPaths,
m_options.enforceCompileToEwasm, m_options.enforceCompileToEwasm,
m_options.enforceGasTest, m_options.enforceGasTest,

View File

@ -80,7 +80,7 @@ DEFINE_PROTO_FUZZER(Program const& _input)
bytes unoptimisedByteCode; bytes unoptimisedByteCode;
try try
{ {
unoptimisedByteCode = YulAssembler{version, settings, yul_source}.assemble(); unoptimisedByteCode = YulAssembler{version, nullopt, settings, yul_source}.assemble();
} }
catch (solidity::yul::StackTooDeepError const&) catch (solidity::yul::StackTooDeepError const&)
{ {
@ -123,7 +123,7 @@ DEFINE_PROTO_FUZZER(Program const& _input)
bytes optimisedByteCode; bytes optimisedByteCode;
try try
{ {
optimisedByteCode = YulAssembler{version, settings, yul_source}.assemble(); optimisedByteCode = YulAssembler{version, nullopt, settings, yul_source}.assemble();
} }
catch (solidity::yul::StackTooDeepError const&) catch (solidity::yul::StackTooDeepError const&)
{ {

View File

@ -31,12 +31,14 @@ class YulAssembler
{ {
public: public:
YulAssembler( YulAssembler(
langutil::EVMVersion _version, langutil::EVMVersion _evmVersion,
std::optional<uint8_t> _eofVersion,
solidity::frontend::OptimiserSettings _optSettings, solidity::frontend::OptimiserSettings _optSettings,
std::string const& _yulSource std::string const& _yulSource
): ):
m_stack( m_stack(
_version, _evmVersion,
_eofVersion,
solidity::yul::YulStack::Language::StrictAssembly, solidity::yul::YulStack::Language::StrictAssembly,
_optSettings, _optSettings,
langutil::DebugInfoSelection::All() langutil::DebugInfoSelection::All()

View File

@ -39,6 +39,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size)
string input(reinterpret_cast<char const*>(_data), _size); string input(reinterpret_cast<char const*>(_data), _size);
YulStack stack( YulStack stack(
langutil::EVMVersion(), langutil::EVMVersion(),
nullopt,
YulStack::Language::StrictAssembly, YulStack::Language::StrictAssembly,
solidity::frontend::OptimiserSettings::full(), solidity::frontend::OptimiserSettings::full(),
langutil::DebugInfoSelection::All() langutil::DebugInfoSelection::All()

View File

@ -62,6 +62,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size)
YulStack stack( YulStack stack(
langutil::EVMVersion(), langutil::EVMVersion(),
nullopt,
YulStack::Language::StrictAssembly, YulStack::Language::StrictAssembly,
solidity::frontend::OptimiserSettings::full(), solidity::frontend::OptimiserSettings::full(),
DebugInfoSelection::All() DebugInfoSelection::All()

View File

@ -40,6 +40,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size)
string input(reinterpret_cast<char const*>(_data), _size); string input(reinterpret_cast<char const*>(_data), _size);
YulStack stack( YulStack stack(
langutil::EVMVersion(), langutil::EVMVersion(),
nullopt,
YulStack::Language::StrictAssembly, YulStack::Language::StrictAssembly,
solidity::frontend::OptimiserSettings::full(), solidity::frontend::OptimiserSettings::full(),
DebugInfoSelection::All() DebugInfoSelection::All()

View File

@ -64,6 +64,7 @@ DEFINE_PROTO_FUZZER(Program const& _input)
// YulStack entry point // YulStack entry point
YulStack stack( YulStack stack(
version, version,
nullopt,
YulStack::Language::StrictAssembly, YulStack::Language::StrictAssembly,
solidity::frontend::OptimiserSettings::full(), solidity::frontend::OptimiserSettings::full(),
DebugInfoSelection::All() DebugInfoSelection::All()

View File

@ -63,6 +63,7 @@ DEFINE_PROTO_FUZZER(Program const& _input)
// YulStack entry point // YulStack entry point
YulStack stack( YulStack stack(
version, version,
nullopt,
YulStack::Language::StrictAssembly, YulStack::Language::StrictAssembly,
solidity::frontend::OptimiserSettings::full(), solidity::frontend::OptimiserSettings::full(),
DebugInfoSelection::All() DebugInfoSelection::All()

View File

@ -59,6 +59,7 @@ pair<shared_ptr<Block>, shared_ptr<AsmAnalysisInfo>> parse(string const& _source
{ {
YulStack stack( YulStack stack(
langutil::EVMVersion(), langutil::EVMVersion(),
nullopt,
YulStack::Language::StrictAssembly, YulStack::Language::StrictAssembly,
solidity::frontend::OptimiserSettings::none(), solidity::frontend::OptimiserSettings::none(),
DebugInfoSelection::Default() DebugInfoSelection::Default()