Skip to content

Commit

Permalink
eof: Support EOF asm analysis
Browse files Browse the repository at this point in the history
  • Loading branch information
rodiazet committed Oct 9, 2024
1 parent fde19a5 commit a96b80a
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 5 deletions.
43 changes: 40 additions & 3 deletions libyul/AsmAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -625,12 +625,16 @@ 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.
// 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
return false;

// TODO: Change `prague()` to `EVMVersion{}` once EOF gets deployed
auto const eofBuiltin = EVMDialect::strictAssemblyForEVM(EVMVersion::prague(), 1).builtin(YulName(_instructionIdentifier));
if (eofBuiltin && eofBuiltin->instruction.has_value())
return validateInstructions(eofBuiltin->instruction.value(), _location);

return false;
}

bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocation const& _location)
Expand Down Expand Up @@ -702,9 +706,42 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio
"PC instruction is a low-level EVM feature. "
"Because of that PC is disallowed in strict assembly."
);
else if (m_eofVersion.has_value() && (
_instr == evmasm::Instruction::CALL ||
_instr == evmasm::Instruction::CALLCODE ||
_instr == evmasm::Instruction::DELEGATECALL ||
_instr == evmasm::Instruction::SELFDESTRUCT ||
_instr == evmasm::Instruction::JUMP ||
_instr == evmasm::Instruction::JUMPI ||
_instr == evmasm::Instruction::PC ||
_instr == evmasm::Instruction::CREATE ||
_instr == evmasm::Instruction::CODESIZE ||
_instr == evmasm::Instruction::CODECOPY ||
_instr == evmasm::Instruction::EXTCODESIZE ||
_instr == evmasm::Instruction::EXTCODECOPY ||
_instr == evmasm::Instruction::GAS
))
{
m_errorReporter.typeError(
9132_error,
_location,
fmt::format(
"The \"{instruction}\" instruction is {kind} VMs (you are currently compiling to EOF).",
fmt::arg("instruction", boost::to_lower_copy(instructionInfo(_instr, m_evmVersion).name)),
fmt::arg("kind", "only available in legacy bytecode")
)
);
}
else
{
// Sanity check
solAssert(m_evmVersion.hasOpcode(_instr, m_eofVersion));
return false;
}

// Sanity check
// PC is not available in strict assembly but it is always valid opcode in legacy evm.
solAssert(_instr == evmasm::Instruction::PC || !m_evmVersion.hasOpcode(_instr, m_eofVersion));
return true;
}

Expand Down
4 changes: 4 additions & 0 deletions libyul/AsmAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@ class AsmAnalyzer
m_dataNames(std::move(_dataNames))
{
if (EVMDialect const* evmDialect = dynamic_cast<EVMDialect const*>(&m_dialect))
{
m_evmVersion = evmDialect->evmVersion();
m_eofVersion = evmDialect->eofVersion();
}
}

bool analyze(Block const& _block);
Expand Down Expand Up @@ -125,6 +128,7 @@ class AsmAnalyzer
AsmAnalysisInfo& m_info;
langutil::ErrorReporter& m_errorReporter;
langutil::EVMVersion m_evmVersion;
std::optional<uint8_t> m_eofVersion;
Dialect const& m_dialect;
/// Names of data objects to be referenced by builtin functions with literal arguments.
std::set<std::string> m_dataNames;
Expand Down
2 changes: 2 additions & 0 deletions test/Common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ void CommonOptions::validate() const
std::cout << "- ABI coder: v1 (default: v2)" << std::endl;
std::cout << std::endl << "DO NOT COMMIT THE UPDATED EXPECTATIONS." << std::endl << std::endl;
}

assertThrow(!eofVersion().has_value() || evmVersion() >= langutil::EVMVersion::prague(), ConfigException, "EOF is unavailable before Prague fork.");
}

bool CommonOptions::parse(int argc, char const* const* argv)
Expand Down
30 changes: 28 additions & 2 deletions test/TestCase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,7 @@ TestCase::TestResult TestCase::checkResult(std::ostream& _stream, const std::str
return TestResult::Success;
}

EVMVersionRestrictedTestCase::EVMVersionRestrictedTestCase(std::string const& _filename):
TestCase(_filename)
void EVMVersionRestrictedTestCase::processEVMVersionSetting()
{
std::string versionString = m_reader.stringSetting("EVMVersion", "any");
if (versionString == "any")
Expand Down Expand Up @@ -139,3 +138,30 @@ EVMVersionRestrictedTestCase::EVMVersionRestrictedTestCase(std::string const& _f
if (!comparisonResult)
m_shouldRun = false;
}

void EVMVersionRestrictedTestCase::processBytecodeFormatSetting()
{
std::optional<uint8_t> eofVersion = solidity::test::CommonOptions::get().eofVersion();
// TODO: Update if EOF moved to Osaka
// EOF only available since Prague
solAssert(!eofVersion.has_value() ||solidity::test::CommonOptions::get().evmVersion() >= langutil::EVMVersion::prague());

std::string bytecodeFormatString = m_reader.stringSetting("bytecodeFormat", "legacy");
if (bytecodeFormatString == "legacy,>=EOFv1" || bytecodeFormatString == ">=EOFv1,legacy")
return;

// TODO: This is naive implementation because for now we support only one EOF version.
if (bytecodeFormatString == "legacy" && eofVersion.has_value())
m_shouldRun = false;
else if (bytecodeFormatString == ">=EOFv1" && !eofVersion.has_value())
m_shouldRun = false;
else if (bytecodeFormatString != "legacy" && bytecodeFormatString != ">=EOFv1" )
BOOST_THROW_EXCEPTION(std::runtime_error{"Invalid bytecodeFormat flag: \"" + bytecodeFormatString + "\""});
}

EVMVersionRestrictedTestCase::EVMVersionRestrictedTestCase(std::string const& _filename):
TestCase(_filename)
{
processEVMVersionSetting();
processBytecodeFormatSetting();
}
4 changes: 4 additions & 0 deletions test/TestCase.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ class TestCase

class EVMVersionRestrictedTestCase: public TestCase
{
private:
void processEVMVersionSetting();
void processBytecodeFormatSetting();

protected:
EVMVersionRestrictedTestCase(std::string const& _filename);
};
Expand Down
13 changes: 13 additions & 0 deletions test/libyul/yulSyntaxTests/eof/call_intruction_in_eof.yul
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
object "a" {
code {
let success := call(gas(), 0x1, 0, 128, 4, 128, 0)
}
}
// ====
// EVMVersion: >=prague
// bytecodeFormat: >=EOFv1
// ----
// TypeError 9132: (47-51): The "call" instruction is only available in legacy bytecode VMs (you are currently compiling to EOF).
// TypeError 9132: (52-55): The "gas" instruction is only available in legacy bytecode VMs (you are currently compiling to EOF).
// TypeError 3950: (52-57): Expected expression to evaluate to one value, but got 0 values instead.
// DeclarationError 3812: (32-82): Variable count mismatch for declaration of "success": 1 variables and 0 values.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
object "a" {
code {
let success := call(gas(), 0x1, 0, 128, 4, 128, 0)
}
}
// ====
// EVMVersion: <prague
// bytecodeFormat: >=EOFv1
// ----
// TypeError 9132: (47-51): The "call" instruction is only available in legacy bytecode VMs (you are currently compiling to EOF).
// TypeError 9132: (52-55): The "gas" instruction is only available in legacy bytecode VMs (you are currently compiling to EOF).
// TypeError 3950: (52-57): Expected expression to evaluate to one value, but got 0 values instead.
// DeclarationError 3812: (32-82): Variable count mismatch for declaration of "success": 1 variables and 0 values.

0 comments on commit a96b80a

Please sign in to comment.