Skip to content

eof: Enable eofcreate inline assembly #15981

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

Draft
wants to merge 2 commits into
base: develop
Choose a base branch
from
Draft
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion libsolidity/analysis/ReferencesResolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier)
return;
}

static std::set<std::string> suffixes{"slot", "offset", "length", "address", "selector"};
static std::set<std::string> suffixes{"slot", "offset", "length", "address", "selector", "objectName"};
std::string suffix;
for (std::string const& s: suffixes)
if (boost::algorithm::ends_with(_identifier.name.str(), "." + s))
Expand Down
33 changes: 30 additions & 3 deletions libsolidity/analysis/TypeChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -815,7 +815,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
if (!identifierInfo.suffix.empty())
{
std::string const& suffix = identifierInfo.suffix;
solAssert((std::set<std::string>{"offset", "slot", "length", "selector", "address"}).count(suffix), "");
solAssert((std::set<std::string>{"offset", "slot", "length", "selector", "address", "objectName"}).count(suffix), "");
if (!var->isConstant() && (var->isStateVariable() || var->type()->dataStoredIn(DataLocation::Storage)))
{
if (suffix != "slot" && suffix != "offset")
Expand Down Expand Up @@ -896,9 +896,36 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
return false;
}
}
else if (dynamic_cast<ContractDefinition const*>(declaration) && !identifierInfo.suffix.empty())
{
if (identifierInfo.suffix != "objectName" || !m_eofVersion.has_value())
{
m_errorReporter.typeError(
1342_error,
nativeLocationOf(_identifier),
"\".objectName\" suffix is supported only for contract name identifier when compiling to EOF."
);
return false;
}
}
else if (!identifierInfo.suffix.empty())
{
m_errorReporter.typeError(7944_error, nativeLocationOf(_identifier), "The suffixes \".offset\", \".slot\" and \".length\" can only be used with variables.");
if (m_eofVersion.has_value())
{
m_errorReporter.typeError(
9479_error,
nativeLocationOf(_identifier),
"The suffixes \".offset\", \".slot\" and \".length\" can only be used with variables or the suffix \"objectName\" can be used with a contract name identifier."
);
}
else
{
m_errorReporter.typeError(
7944_error,
nativeLocationOf(_identifier),
"The suffixes \".offset\", \".slot\" and \".length\" can only be used with variables."
);
}
return false;
}
else if (_context == yul::IdentifierContext::LValue)
Expand All @@ -923,7 +950,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
}
else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration))
{
if (!contract->isLibrary())
if (!contract->isLibrary() && identifierInfo.suffix != "objectName")
{
m_errorReporter.typeError(4977_error, nativeLocationOf(_identifier), "Expected a library.");
return false;
Expand Down
3 changes: 3 additions & 0 deletions libsolidity/analysis/ViewPureChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,11 @@ class AssemblyViewPureChecker
{
if (yul::EVMDialect const* dialect = dynamic_cast<decltype(dialect)>(&m_dialect))
if (yul::BuiltinFunctionForEVM const* builtin = resolveBuiltinFunctionForEVM(_funCall.functionName, *dialect))
{
solAssert(builtin->instruction.has_value());
if (builtin->instruction)
checkInstruction(nativeLocationOf(_funCall), *builtin->instruction);
}

for (auto const& arg: _funCall.arguments)
std::visit(*this, arg);
Expand Down
18 changes: 17 additions & 1 deletion libsolidity/codegen/ir/IRGeneratorForStatements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,27 @@ struct CopyTranslate: public yul::ASTCopier
yul::Expression translateReference(yul::Identifier const& _identifier)
{
auto const& reference = m_references.at(&_identifier);
std::string value;

if (auto const contractDefinition = dynamic_cast<ContractDefinition const*>(reference.declaration))
{
// Already verified in TypeChecker
solAssert(reference.suffix == "objectName");
solAssert(m_context.eofVersion().has_value());

// Add sub-object as it's referenced from this context
m_context.addSubObject(contractDefinition);
return yul::Literal{
_identifier.debugData,
yul::LiteralKind::String,
yul::LiteralValue{IRNames::creationObject(*contractDefinition)}
};
}

auto const varDecl = dynamic_cast<VariableDeclaration const*>(reference.declaration);
solUnimplementedAssert(varDecl);
std::string const& suffix = reference.suffix;

std::string value;
if (suffix.empty() && varDecl->isLocalVariable())
{
auto const& var = m_context.localVariable(*varDecl);
Expand Down
5 changes: 3 additions & 2 deletions libyul/AsmAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -790,12 +790,12 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio
yulAssert(m_evmVersion.hasBitwiseShifting() == m_evmVersion.hasCreate2(), "");

// These instructions are disabled in the dialect.
// EOFCREATE instruction is enabled to run ViewPureChecker validation.
yulAssert(
_instr != evmasm::Instruction::JUMP &&
_instr != evmasm::Instruction::JUMPI &&
_instr != evmasm::Instruction::JUMPDEST &&
_instr != evmasm::Instruction::DATALOADN &&
_instr != evmasm::Instruction::EOFCREATE &&
_instr != evmasm::Instruction::RETURNCONTRACT &&
_instr != evmasm::Instruction::RJUMP &&
_instr != evmasm::Instruction::RJUMPI &&
Expand Down Expand Up @@ -863,7 +863,8 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio
else if (!m_eofVersion.has_value() && (
_instr == evmasm::Instruction::EXTCALL ||
_instr == evmasm::Instruction::EXTDELEGATECALL ||
_instr == evmasm::Instruction::EXTSTATICCALL
_instr == evmasm::Instruction::EXTSTATICCALL ||
_instr == evmasm::Instruction::EOFCREATE
))
{
m_errorReporter.typeError(
Expand Down
48 changes: 25 additions & 23 deletions libyul/backends/evm/EVMDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -404,29 +404,6 @@ std::vector<std::optional<BuiltinFunctionForEVM>> createBuiltins(langutil::EVMVe
}
));

builtins.emplace_back(createFunction(
"eofcreate",
5,
1,
EVMDialect::sideEffectsOfInstruction(evmasm::Instruction::EOFCREATE),
ControlFlowSideEffects::fromInstruction(evmasm::Instruction::EOFCREATE),
{LiteralKind::String, std::nullopt, std::nullopt, std::nullopt, std::nullopt},
[](
FunctionCall const& _call,
AbstractAssembly& _assembly,
BuiltinContext& context
) {
yulAssert(_call.arguments.size() == 5);
Literal const* literal = std::get_if<Literal>(&_call.arguments.front());
auto const formattedLiteral = formatLiteral(*literal);
yulAssert(!util::contains(formattedLiteral, '.'));
auto const* containerID = valueOrNullptr(context.subIDs, formattedLiteral);
yulAssert(containerID != nullptr);
yulAssert(*containerID <= std::numeric_limits<AbstractAssembly::ContainerID>::max());
_assembly.appendEOFCreate(static_cast<AbstractAssembly::ContainerID>(*containerID));
}
));

builtins.emplace_back(createFunction(
"returncontract",
3,
Expand All @@ -451,6 +428,31 @@ std::vector<std::optional<BuiltinFunctionForEVM>> createBuiltins(langutil::EVMVe
}
));
}

builtins.emplace_back(createFunction(
"eofcreate",
5,
1,
EVMDialect::sideEffectsOfInstruction(evmasm::Instruction::EOFCREATE),
ControlFlowSideEffects::fromInstruction(evmasm::Instruction::EOFCREATE),
// TODO: Personally I don't like this solution but cannot find any better way to prevent 9114 error in asm analysis.
{_objectAccess ? LiteralKind::String : std::optional<LiteralKind>{}, std::nullopt, std::nullopt, std::nullopt, std::nullopt},
[](
FunctionCall const& _call,
AbstractAssembly& _assembly,
BuiltinContext& context
) {
yulAssert(_call.arguments.size() == 5);
Literal const* literal = std::get_if<Literal>(&_call.arguments.front());
auto const formattedLiteral = formatLiteral(*literal);
yulAssert(!util::contains(formattedLiteral, '.'));
auto const* containerID = valueOrNullptr(context.subIDs, formattedLiteral);
yulAssert(containerID != nullptr);
yulAssert(*containerID <= std::numeric_limits<AbstractAssembly::ContainerID>::max());
_assembly.appendEOFCreate(static_cast<AbstractAssembly::ContainerID>(*containerID));
}
));
(*builtins.back()).instruction = evmasm::Instruction::EOFCREATE;
}
yulAssert(
ranges::all_of(builtins, [](std::optional<BuiltinFunctionForEVM> const& _builtinFunction){
Expand Down
28 changes: 28 additions & 0 deletions test/libsolidity/semanticTests/inlineAssembly/objectName.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
contract B
{
function f() public returns (uint ret){
ret = 123;
}
}

contract C {
B b;

function f() public {
B t;
assembly {
t := eofcreate(B.objectName, 0, 0, 0, 0)
}

b = t;
}

function run_B_f() public returns (uint) {
return b.f();
}
}
// ====
// bytecodeFormat: >=EOFv1
// ----
// f()
// run_B_f() -> 123
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
library Lib {
struct S {
uint v;
}
function add(uint a, uint b) external pure returns(uint ret) {
ret = a + b;
}
}

interface ILib {
function add(uint a, uint b) external pure returns (uint);
}

contract C {
address addr;

function create_lib() public {
address l;
assembly {
l := eofcreate(Lib.objectName, 0, 0, 0, 0)

}
addr = l;
}

function add() public returns(uint){
return ILib(addr).add(5, 7);
}
}
// ====
// bytecodeFormat: >=EOFv1
// ----
// create_lib()
// add() -> 12
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
contract C {
function f() pure public {
assembly {
let x := f.slot
}
}
}
// ====
// bytecodeFormat: >=EOFv1
// ----
// TypeError 9479: (84-90): The suffixes ".offset", ".slot" and ".length" can only be used with variables or the suffix "objectName" can be used with a contract name identifier.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ contract C {
// ====
// bytecodeFormat: legacy
// ----
// DeclarationError 7223: (75-84): Builtin function "eofcreate" is only available in EOF.
// TypeError 4328: (75-84): The "eofcreate" instruction is only available in EOF.
// DeclarationError 7223: (114-128): Builtin function "returncontract" is only available in EOF.
// DeclarationError 7223: (149-161): Builtin function "auxdataloadn" is only available in EOF.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
contract C {
function f() view public {
assembly {
eofcreate("a", 0, 0, 0, 0)
returncontract("a", 0)
auxdataloadn(0)
}
Expand All @@ -11,6 +10,5 @@ contract C {
// ====
// bytecodeFormat: >=EOFv1
// ----
// DeclarationError 4619: (186-195): Function "eofcreate" not found.
// DeclarationError 4619: (225-239): Function "returncontract" not found.
// DeclarationError 4619: (260-272): Function "auxdataloadn" not found.
// DeclarationError 4619: (186-200): Function "returncontract" not found.
// DeclarationError 4619: (221-233): Function "auxdataloadn" not found.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
contract B {}

contract C {
function f() public {
B b;
assembly {
b := eofcreate(B.objectId, 0, 0, 0, 0)
}
}
}
// ====
// bytecodeFormat: >=EOFv1
// ----
// DeclarationError 8198: (113-123): Identifier "B.objectId" not found.
12 changes: 12 additions & 0 deletions test/libsolidity/syntaxTests/inlineAssembly/objectName.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
contract B {}

contract C {
function f() public {
B b;
assembly {
b := eofcreate(B.objectName, 0, 0, 0, 0)
}
}
}
// ====
// bytecodeFormat: >=EOFv1
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
contract C {
uint s;
function f() public {
uint b;
assembly {
b := s.objectName
b := b.objectName
}
}
}
// ====
// bytecodeFormat: >=EOFv1
// ----
// TypeError 4656: (103-115): State variables only support ".slot" and ".offset".
// TypeError 3622: (133-145): The suffix ".objectName" is not supported by this variable or type.
16 changes: 16 additions & 0 deletions test/libsolidity/syntaxTests/inlineAssembly/objectNameInLegacy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
contract B {}

contract C {
function f() public {
B b;
assembly {
b := eofcreate(B.objectName, 0, 0, 0, 0)
}
}
}
// ====
// bytecodeFormat: legacy
// ----
// TypeError 4328: (103-112): The "eofcreate" instruction is only available in EOF.
// TypeError 1342: (113-125): ".objectName" suffix is supported only for contract name identifier when compiling to EOF.
// DeclarationError 8678: (98-138): Variable count for assignment to "b" does not match number of values (1 vs. 0)
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
contract B {}

contract C {
function f() public view {
B b;
assembly {
b := eofcreate(B.objectName, 0, 0, 0, 0)
}
}

function g() public pure {
B b;
assembly {
b := eofcreate(B.objectName, 0, 0, 0, 0)
}
}

}
// ====
// bytecodeFormat: >=EOFv1
// ----
// TypeError 8961: (108-143): Function cannot be declared as view because this expression (potentially) modifies the state.
// TypeError 8961: (241-276): Function cannot be declared as pure because this expression (potentially) modifies the state.
11 changes: 11 additions & 0 deletions test/libsolidity/syntaxTests/inlineAssembly/objectNameLibrary.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
library Lib {}

contract C {
function f() public {
assembly {
pop(eofcreate(Lib.objectName, 0, 0, 0, 0))
}
}
}
// ====
// bytecodeFormat: >=EOFv1
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@ contract C {
}
}
}
// ====
// bytecodeFormat: legacy
// ----
// TypeError 7944: (84-90): The suffixes ".offset", ".slot" and ".length" can only be used with variables.
Loading