Adding origin location notation to yul ir.

This commit is contained in:
Djordje Mijovic
2021-06-21 18:48:20 +02:00
committed by chriseth
parent 921e21c7cb
commit 838f59fa5b
32 changed files with 1552 additions and 177 deletions
+22 -1
View File
@@ -16,8 +16,9 @@
*/
// SPDX-License-Identifier: GPL-3.0
#include <libsolidity/codegen/ir/Common.h>
#include <libsolidity/ast/TypeProvider.h>
#include <libsolidity/codegen/ir/Common.h>
#include <libsolidity/codegen/ir/IRGenerationContext.h>
#include <libsolutil/CommonIO.h>
@@ -25,6 +26,9 @@ using namespace std;
using namespace solidity::util;
using namespace solidity::frontend;
namespace solidity::frontend
{
YulArity YulArity::fromType(FunctionType const& _functionType)
{
return YulArity{
@@ -122,3 +126,20 @@ string IRNames::zeroValue(Type const& _type, string const& _variableName)
{
return "zero_" + _type.identifier() + _variableName;
}
string sourceLocationComment(langutil::SourceLocation const& _location, IRGenerationContext const& _context)
{
return "/// @src "
+ to_string(_context.sourceIndices().at(_location.source->name()))
+ ":"
+ to_string(_location.start)
+ ","
+ to_string(_location.end);
}
string sourceLocationComment(ASTNode const& _node, IRGenerationContext const& _context)
{
return sourceLocationComment(_node.location(), _context);
}
}
+10
View File
@@ -29,6 +29,8 @@
namespace solidity::frontend
{
class IRGenerationContext;
/**
* Structure that describes arity and co-arity of a Yul function, i.e. the number of its inputs and outputs.
*/
@@ -66,6 +68,14 @@ struct IRNames
static std::string zeroValue(Type const& _type, std::string const& _variableName);
};
/**
* @returns a source location comment in the form of
* `/// @src <sourceIndex>:<locationStart>:<locationEnd>`.
*/
std::string sourceLocationComment(langutil::SourceLocation const& _location, IRGenerationContext const& _context);
std::string sourceLocationComment(ASTNode const& _node, IRGenerationContext const& _context);
}
// Overloading std::less() makes it possible to use YulArity as a map key. We could define operator<
+7 -2
View File
@@ -68,11 +68,13 @@ public:
IRGenerationContext(
langutil::EVMVersion _evmVersion,
RevertStrings _revertStrings,
OptimiserSettings _optimiserSettings
OptimiserSettings _optimiserSettings,
std::map<std::string, unsigned> _sourceIndices
):
m_evmVersion(_evmVersion),
m_revertStrings(_revertStrings),
m_optimiserSettings(std::move(_optimiserSettings))
m_optimiserSettings(std::move(_optimiserSettings)),
m_sourceIndices(std::move(_sourceIndices))
{}
MultiUseYulFunctionCollector& functionCollector() { return m_functions; }
@@ -150,10 +152,13 @@ public:
bool inlineAssemblySeen() const { return m_inlineAssemblySeen; }
void setInlineAssemblySeen() { m_inlineAssemblySeen = true; }
std::map<std::string, unsigned> const& sourceIndices() const { return m_sourceIndices; }
private:
langutil::EVMVersion m_evmVersion;
RevertStrings m_revertStrings;
OptimiserSettings m_optimiserSettings;
std::map<std::string, unsigned> m_sourceIndices;
ContractDefinition const* m_mostDerivedContract = nullptr;
std::map<VariableDeclaration const*, IRVariable> m_localVariables;
/// Memory offsets reserved for the values of immutable variables during contract creation.
+36 -7
View File
@@ -21,8 +21,8 @@
* Component that translates Solidity code into Yul.
*/
#include <libsolidity/codegen/ir/Common.h>
#include <libsolidity/codegen/ir/IRGenerator.h>
#include <libsolidity/codegen/ir/IRGeneratorForStatements.h>
#include <libsolidity/ast/AST.h>
@@ -107,12 +107,12 @@ pair<string, string> IRGenerator::run(
asmStack.optimize();
string warning =
"/*******************************************************\n"
"/*=====================================================*\n"
" * WARNING *\n"
" * Solidity to Yul compilation is still EXPERIMENTAL *\n"
" * It can result in LOSS OF FUNDS or worse *\n"
" * !USE AT YOUR OWN RISK! *\n"
" *******************************************************/\n\n";
" *=====================================================*/\n\n";
return {warning + ir, warning + asmStack.print()};
}
@@ -134,6 +134,7 @@ string IRGenerator::generate(
Whiskers t(R"(
object "<CreationObject>" {
code {
<sourceLocationComment>
<memoryInitCreation>
<callValueCheck>
<?library>
@@ -146,6 +147,7 @@ string IRGenerator::generate(
}
object "<DeployedObject>" {
code {
<sourceLocationComment>
<memoryInitDeployed>
<?library>
let called_via_delegatecall := iszero(eq(loadimmutable("<library_address>"), address()))
@@ -164,6 +166,8 @@ string IRGenerator::generate(
for (VariableDeclaration const* var: ContractType(_contract).immutableVariables())
m_context.registerImmutableVariable(*var);
t("sourceLocationComment", sourceLocationComment(_contract, m_context));
t("CreationObject", IRNames::creationObject(_contract));
t("library", _contract.isLibrary());
@@ -186,7 +190,7 @@ string IRGenerator::generate(
t("deploy", deployCode(_contract));
generateConstructors(_contract);
set<FunctionDefinition const*> creationFunctionList = generateQueuedFunctions();
InternalDispatchMap internalDispatchMap = generateInternalDispatchFunctions();
InternalDispatchMap internalDispatchMap = generateInternalDispatchFunctions(_contract);
t("functions", m_context.functionCollector().requestedFunctions());
t("subObjects", subObjectSources(m_context.subObjectsCreated()));
@@ -207,7 +211,7 @@ string IRGenerator::generate(
t("library_address", IRNames::libraryAddressImmutable());
t("dispatch", dispatchRoutine(_contract));
set<FunctionDefinition const*> deployedFunctionList = generateQueuedFunctions();
generateInternalDispatchFunctions();
generateInternalDispatchFunctions(_contract);
t("deployedFunctions", m_context.functionCollector().requestedFunctions());
t("deployedSubObjects", subObjectSources(m_context.subObjectsCreated()));
t("metadataName", yul::Object::metadataName());
@@ -249,7 +253,7 @@ set<FunctionDefinition const*> IRGenerator::generateQueuedFunctions()
return functions;
}
InternalDispatchMap IRGenerator::generateInternalDispatchFunctions()
InternalDispatchMap IRGenerator::generateInternalDispatchFunctions(ContractDefinition const& _contract)
{
solAssert(
m_context.functionGenerationQueueEmpty(),
@@ -264,6 +268,7 @@ InternalDispatchMap IRGenerator::generateInternalDispatchFunctions()
m_context.functionCollector().createFunction(funName, [&]() {
Whiskers templ(R"(
function <functionName>(fun<?+in>, <in></+in>) <?+out>-> <out></+out> {
<sourceLocationComment>
switch fun
<#cases>
case <funID>
@@ -274,6 +279,7 @@ InternalDispatchMap IRGenerator::generateInternalDispatchFunctions()
default { <panic>() }
}
)");
templ("sourceLocationComment", sourceLocationComment(_contract, m_context));
templ("functionName", funName);
templ("panic", m_utils.panicFunction(PanicCode::InvalidInternalFunction));
templ("in", suffixedVariableNameList("in_", 0, arity.in));
@@ -319,10 +325,14 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function)
m_context.resetLocalVariables();
Whiskers t(R"(
function <functionName>(<params>)<?+retParams> -> <retParams></+retParams> {
<sourceLocationComment>
<retInit>
<body>
}
)");
t("sourceLocationComment", sourceLocationComment(_function, m_context));
t("functionName", functionName);
vector<string> params;
for (auto const& varDecl: _function.parameters())
@@ -377,6 +387,7 @@ string IRGenerator::generateModifier(
m_context.resetLocalVariables();
Whiskers t(R"(
function <functionName>(<params>)<?+retParams> -> <retParams></+retParams> {
<sourceLocationComment>
<assignRetParams>
<evalArgs>
<body>
@@ -404,6 +415,7 @@ string IRGenerator::generateModifier(
_modifierInvocation.name().annotation().referencedDeclaration
);
solAssert(modifier, "");
t("sourceLocationComment", sourceLocationComment(*modifier, m_context));
switch (*_modifierInvocation.name().annotation().requiredLookup)
{
case VirtualLookup::Virtual:
@@ -455,10 +467,12 @@ string IRGenerator::generateFunctionWithModifierInner(FunctionDefinition const&
m_context.resetLocalVariables();
Whiskers t(R"(
function <functionName>(<params>)<?+retParams> -> <retParams></+retParams> {
<sourceLocationComment>
<assignRetParams>
<body>
}
)");
t("sourceLocationComment", sourceLocationComment(_function, m_context));
t("functionName", functionName);
vector<string> retParams;
vector<string> retParamsIn;
@@ -497,9 +511,11 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
solUnimplementedAssert(type->sizeOnStack() == 1, "");
return Whiskers(R"(
function <functionName>() -> rval {
<sourceLocationComment>
rval := loadimmutable("<id>")
}
)")
("sourceLocationComment", sourceLocationComment(_varDecl, m_context))
("functionName", functionName)
("id", to_string(_varDecl.id()))
.render();
@@ -509,9 +525,11 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
solAssert(paramTypes.empty(), "");
return Whiskers(R"(
function <functionName>() -> <ret> {
<sourceLocationComment>
<ret> := <constantValueFunction>()
}
)")
("sourceLocationComment", sourceLocationComment(_varDecl, m_context))
("functionName", functionName)
("constantValueFunction", IRGeneratorForStatements(m_context, m_utils).constantValueFunction(_varDecl))
("ret", suffixedVariableNameList("ret_", 0, _varDecl.type()->sizeOnStack()))
@@ -624,6 +642,7 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
return Whiskers(R"(
function <functionName>(<params>) -> <retVariables> {
<sourceLocationComment>
<code>
}
)")
@@ -631,6 +650,7 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
("params", joinHumanReadable(parameters))
("retVariables", joinHumanReadable(returnVariables))
("code", std::move(code))
("sourceLocationComment", sourceLocationComment(_varDecl, m_context))
.render();
});
}
@@ -739,6 +759,7 @@ void IRGenerator::generateConstructors(ContractDefinition const& _contract)
Whiskers t(R"(
function <functionName>(<params><comma><baseParams>) {
<evalBaseArguments>
<sourceLocationComment>
<?hasNextConstructor> <nextConstructor>(<nextParams>) </hasNextConstructor>
<initStateVariables>
<userDefinedConstructorBody>
@@ -748,6 +769,14 @@ void IRGenerator::generateConstructors(ContractDefinition const& _contract)
if (contract->constructor())
for (ASTPointer<VariableDeclaration> const& varDecl: contract->constructor()->parameters())
params += m_context.addLocalVariable(*varDecl).stackSlots();
t("sourceLocationComment", sourceLocationComment(
contract->constructor() ?
contract->constructor()->location() :
contract->location(),
m_context
));
t("params", joinHumanReadable(params));
vector<string> baseParams = listAllParams(baseConstructorParams);
t("baseParams", joinHumanReadable(baseParams));
@@ -984,7 +1013,7 @@ void IRGenerator::resetContext(ContractDefinition const& _contract)
m_context.internalDispatchClean(),
"Reset internal dispatch map without consuming it."
);
m_context = IRGenerationContext(m_evmVersion, m_context.revertStrings(), m_optimiserSettings);
m_context = IRGenerationContext(m_evmVersion, m_context.revertStrings(), m_optimiserSettings, m_context.sourceIndices());
m_context.setMostDerivedContract(_contract);
for (auto const& var: ContractType(_contract).stateVariables())
+4 -3
View File
@@ -42,11 +42,12 @@ public:
IRGenerator(
langutil::EVMVersion _evmVersion,
RevertStrings _revertStrings,
OptimiserSettings _optimiserSettings
OptimiserSettings _optimiserSettings,
std::map<std::string, unsigned> _sourceIndices
):
m_evmVersion(_evmVersion),
m_optimiserSettings(_optimiserSettings),
m_context(_evmVersion, _revertStrings, std::move(_optimiserSettings)),
m_context(_evmVersion, _revertStrings, std::move(_optimiserSettings), std::move(_sourceIndices)),
m_utils(_evmVersion, m_context.revertStrings(), m_context.functionCollector())
{}
@@ -74,7 +75,7 @@ private:
/// possibly be called via a pointer.
/// @return The content of the dispatch for reuse in runtime code. Reuse is necessary because
/// pointers to functions can be passed from the creation code in storage variables.
InternalDispatchMap generateInternalDispatchFunctions();
InternalDispatchMap generateInternalDispatchFunctions(ContractDefinition const& _contract);
/// Generates code for and returns the name of the function.
std::string generateFunction(FunctionDefinition const& _function);
std::string generateModifier(
@@ -205,10 +205,34 @@ private:
}
string IRGeneratorForStatementsBase::code() const
{
return m_code.str();
}
std::ostringstream& IRGeneratorForStatementsBase::appendCode(bool _addLocationComment)
{
if (
_addLocationComment &&
m_currentLocation.isValid() &&
m_lastLocation != m_currentLocation
)
m_code << sourceLocationComment(m_currentLocation, m_context) << "\n";
m_lastLocation = m_currentLocation;
return m_code;
}
void IRGeneratorForStatementsBase::setLocation(ASTNode const& _node)
{
m_currentLocation = _node.location();
}
string IRGeneratorForStatements::code() const
{
solAssert(!m_currentLValue, "LValue not reset!");
return m_code.str();
return IRGeneratorForStatementsBase::code();
}
void IRGeneratorForStatements::generate(Block const& _block)
@@ -289,6 +313,8 @@ IRVariable IRGeneratorForStatements::evaluateExpression(Expression const& _expre
setLocation(_expression);
_expression.accept(*this);
setLocation(_expression);
IRVariable variable{m_context.newYulVariable(), _targetType};
define(variable, _expression);
return variable;
@@ -305,16 +331,16 @@ string IRGeneratorForStatements::constantValueFunction(VariableDeclaration const
{
try
{
setLocation(_constant);
string functionName = IRNames::constantValueFunction(_constant);
return m_context.functionCollector().createFunction(functionName, [&] {
Whiskers templ(R"(
<sourceLocationComment>
function <functionName>() -> <ret> {
<code>
<ret> := <value>
}
)");
templ("sourceLocationComment", sourceLocationComment(_constant, m_context));
templ("functionName", functionName);
IRGeneratorForStatements generator(m_context, m_utils);
solAssert(_constant.value(), "");
@@ -376,19 +402,19 @@ bool IRGeneratorForStatements::visit(Conditional const& _conditional)
string condition = expressionAsType(_conditional.condition(), *TypeProvider::boolean());
declare(_conditional);
m_code << "switch " << condition << "\n" "case 0 {\n";
appendCode() << "switch " << condition << "\n" "case 0 {\n";
_conditional.falseExpression().accept(*this);
setLocation(_conditional);
assign(_conditional, _conditional.falseExpression());
m_code << "}\n" "default {\n";
appendCode() << "}\n" "default {\n";
_conditional.trueExpression().accept(*this);
setLocation(_conditional);
assign(_conditional, _conditional.trueExpression());
m_code << "}\n";
appendCode() << "}\n";
return false;
}
@@ -472,7 +498,7 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple)
component.accept(*this);
setLocation(_tuple);
IRVariable converted = convert(component, baseType);
m_code <<
appendCode() <<
m_utils.writeToMemoryFunction(baseType) <<
"(" <<
("add(" + mpos + ", " + to_string(i * arrayType.memoryStride()) + ")") <<
@@ -553,24 +579,25 @@ bool IRGeneratorForStatements::visit(IfStatement const& _ifStatement)
if (_ifStatement.falseStatement())
{
m_code << "switch " << condition << "\n" "case 0 {\n";
appendCode() << "switch " << condition << "\n" "case 0 {\n";
_ifStatement.falseStatement()->accept(*this);
setLocation(_ifStatement);
m_code << "}\n" "default {\n";
appendCode() << "}\n" "default {\n";
}
else
m_code << "if " << condition << " {\n";
appendCode() << "if " << condition << " {\n";
_ifStatement.trueStatement().accept(*this);
setLocation(_ifStatement);
m_code << "}\n";
appendCode() << "}\n";
return false;
}
void IRGeneratorForStatements::endVisit(PlaceholderStatement const&)
void IRGeneratorForStatements::endVisit(PlaceholderStatement const& _placeholder)
{
solAssert(m_placeholderCallback, "");
m_code << m_placeholderCallback();
setLocation(_placeholder);
appendCode() << m_placeholderCallback();
}
bool IRGeneratorForStatements::visit(ForStatement const& _forStatement)
@@ -603,14 +630,14 @@ bool IRGeneratorForStatements::visit(WhileStatement const& _whileStatement)
bool IRGeneratorForStatements::visit(Continue const& _continue)
{
setLocation(_continue);
m_code << "continue\n";
appendCode() << "continue\n";
return false;
}
bool IRGeneratorForStatements::visit(Break const& _break)
{
setLocation(_break);
m_code << "break\n";
appendCode() << "break\n";
return false;
}
@@ -628,7 +655,7 @@ void IRGeneratorForStatements::endVisit(Return const& _return)
else if (returnParameters.size() == 1)
assign(m_context.localVariable(*returnParameters.front()), *value);
}
m_code << "leave\n";
appendCode() << "leave\n";
}
void IRGeneratorForStatements::endVisit(UnaryOperation const& _unaryOperation)
@@ -643,7 +670,7 @@ void IRGeneratorForStatements::endVisit(UnaryOperation const& _unaryOperation)
std::visit(
util::GenericVisitor{
[&](IRLValue::Storage const& _storage) {
m_code <<
appendCode() <<
m_utils.storageSetToZeroFunction(m_currentLValue->type) <<
"(" <<
_storage.slot <<
@@ -870,7 +897,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
for (size_t i = 0; i < arguments.size(); i++)
{
IRVariable converted = convert(*arguments[i], *parameterTypes[i]);
m_code <<
appendCode() <<
m_utils.writeToMemoryFunction(*functionType->parameterTypes()[i]) <<
"(add(" <<
IRVariable(_functionCall).part("mpos").name() <<
@@ -1001,7 +1028,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
templ("indexedArgs", joinHumanReadablePrefixed(indexedArgs | ranges::views::transform([&](auto const& _arg) {
return _arg.commaSeparatedList();
})));
m_code << templ.render();
appendCode() << templ.render();
break;
}
case FunctionType::Kind::Error:
@@ -1030,10 +1057,10 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
messageArgumentType
);
m_code << move(requireOrAssertFunction) << "(" << IRVariable(*arguments[0]).name();
appendCode() << move(requireOrAssertFunction) << "(" << IRVariable(*arguments[0]).name();
if (messageArgumentType && messageArgumentType->sizeOnStack() > 0)
m_code << ", " << IRVariable(*arguments[1]).commaSeparatedList();
m_code << ")\n";
appendCode() << ", " << IRVariable(*arguments[1]).commaSeparatedList();
appendCode() << ")\n";
break;
}
@@ -1075,7 +1102,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
// We might want to introduce a new set of memory handling functions here
// a la "setMemoryCheckPoint" and "freeUntilCheckPoint".
string freeMemoryPre = m_context.newYulVariable();
m_code << "let " << freeMemoryPre << " := " << m_utils.allocateUnboundedFunction() << "()\n";
appendCode() << "let " << freeMemoryPre << " := " << m_utils.allocateUnboundedFunction() << "()\n";
IRVariable array = convert(*arguments[0], *TypeProvider::bytesMemory());
IRVariable hashVariable(m_context.newYulVariable(), *TypeProvider::fixedBytes(32));
@@ -1092,7 +1119,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
IRVariable selectorVariable(m_context.newYulVariable(), *TypeProvider::fixedBytes(4));
define(selectorVariable, hashVariable);
selector = selectorVariable.name();
m_code << m_utils.finalizeAllocationFunction() << "(" << freeMemoryPre << ", 0)\n";
appendCode() << m_utils.finalizeAllocationFunction() << "(" << freeMemoryPre << ", 0)\n";
}
}
else if (functionType->kind() == FunctionType::Kind::ABIEncodeWithSelector)
@@ -1122,7 +1149,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
templ("arguments", joinHumanReadablePrefixed(argumentVars));
templ("finalizeAllocation", m_utils.finalizeAllocationFunction());
m_code << templ.render();
appendCode() << templ.render();
break;
}
case FunctionType::Kind::ABIDecode:
@@ -1161,7 +1188,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
}
templ("retVars", IRVariable(_functionCall).commaSeparatedList());
m_code << templ.render();
appendCode() << templ.render();
break;
}
case FunctionType::Kind::Revert:
@@ -1173,7 +1200,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
arguments.front()->annotation().type->isImplicitlyConvertibleTo(*TypeProvider::stringMemory()),
"");
if (m_context.revertStrings() == RevertStrings::Strip || arguments.empty())
m_code << "revert(0, 0)\n";
appendCode() << "revert(0, 0)\n";
else
revertWithError(
"Error(string)",
@@ -1248,7 +1275,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
{
auto slotName = m_context.newYulVariable();
auto offsetName = m_context.newYulVariable();
m_code << "let " << slotName << ", " << offsetName << " := " <<
appendCode() << "let " << slotName << ", " << offsetName << " := " <<
m_utils.storageArrayPushZeroFunction(*arrayType) <<
"(" << IRVariable(_functionCall.expression()).commaSeparatedList() << ")\n";
setLValue(_functionCall, IRLValue{
@@ -1266,7 +1293,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
convert(*arguments.front(), *arrayType->baseType()) :
*arguments.front();
m_code <<
appendCode() <<
m_utils.storageArrayPushFunction(*arrayType, &argument.type()) <<
"(" <<
IRVariable(_functionCall.expression()).commaSeparatedList() <<
@@ -1311,7 +1338,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
Whiskers templ("if iszero(<modulus>) { <panic>() }\n");
templ("modulus", modulus.name());
templ("panic", m_utils.panicFunction(PanicCode::DivisionByZero));
m_code << templ.render();
appendCode() << templ.render();
string args;
for (size_t i = 0; i < 2; ++i)
@@ -1357,8 +1384,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
&dynamic_cast<ContractType const&>(*functionType->returnParameterTypes().front()).contractDefinition();
m_context.subObjectsCreated().insert(contract);
Whiskers t(R"(
let <memPos> := <allocateUnbounded>()
Whiskers t(R"(let <memPos> := <allocateUnbounded>()
let <memEnd> := add(<memPos>, datasize("<object>"))
if or(gt(<memEnd>, 0xffffffffffffffff), lt(<memEnd>, <memPos>)) { <panic>() }
datacopy(<memPos>, dataoffset("<object>"), datasize("<object>"))
@@ -1394,7 +1420,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
t("success", IRNames::trySuccessConditionVariable(_functionCall));
else
t("forwardingRevert", m_utils.forwardingRevertFunction());
m_code << t.render();
appendCode() << t.render();
break;
}
@@ -1422,7 +1448,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
templ("success", IRVariable(_functionCall).commaSeparatedList());
templ("isTransfer", functionType->kind() == FunctionType::Kind::Transfer);
templ("forwardingRevert", m_utils.forwardingRevertFunction());
m_code << templ.render();
appendCode() << templ.render();
break;
}
@@ -1485,7 +1511,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")");
}
m_code << templ.render();
appendCode() << templ.render();
break;
}
@@ -1722,7 +1748,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
solAssert(!contractType.isSuper(), "");
ContractDefinition const& contract = contractType.contractDefinition();
m_context.subObjectsCreated().insert(&contract);
m_code << Whiskers(R"(
appendCode() << Whiskers(R"(
let <size> := datasize("<objectName>")
let <result> := <allocationFunction>(add(<size>, 32))
mstore(<result>, <size>)
@@ -1775,7 +1801,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
{
pair<u256, unsigned> const& offsets = structType.storageOffsetsOfMember(member);
string slot = m_context.newYulVariable();
m_code << "let " << slot << " := " <<
appendCode() << "let " << slot << " := " <<
("add(" + expression.part("slot").name() + ", " + offsets.first.str() + ")\n");
setLValue(_memberAccess, IRLValue{
type(_memberAccess),
@@ -1786,7 +1812,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
case DataLocation::Memory:
{
string pos = m_context.newYulVariable();
m_code << "let " << pos << " := " <<
appendCode() << "let " << pos << " := " <<
("add(" + expression.part("mpos").name() + ", " + structType.memoryOffsetOfMember(member).str() + ")\n");
setLValue(_memberAccess, IRLValue{
type(_memberAccess),
@@ -1798,7 +1824,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
{
string baseRef = expression.part("offset").name();
string offset = m_context.newYulVariable();
m_code << "let " << offset << " := " << "add(" << baseRef << ", " << to_string(structType.calldataOffsetOfMember(member)) << ")\n";
appendCode() << "let " << offset << " := " << "add(" << baseRef << ", " << to_string(structType.calldataOffsetOfMember(member)) << ")\n";
if (_memberAccess.annotation().type->isDynamicallyEncoded())
define(_memberAccess) <<
m_utils.accessCalldataTailFunction(*_memberAccess.annotation().type) <<
@@ -2023,7 +2049,7 @@ bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm)
solAssert(holds_alternative<yul::Block>(modified), "");
// Do not provide dialect so that we get the full type information.
m_code << yul::AsmPrinter()(std::get<yul::Block>(modified)) << "\n";
appendCode() << yul::AsmPrinter()(std::get<yul::Block>(modified)) << "\n";
return false;
}
@@ -2046,7 +2072,7 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
templ("indexAccess", m_utils.mappingIndexAccessFunction(mappingType, keyType));
templ("base", IRVariable(_indexAccess.baseExpression()).commaSeparatedList());
templ("key", IRVariable(*_indexAccess.indexExpression()).commaSeparatedList());
m_code << templ.render();
appendCode() << templ.render();
setLValue(_indexAccess, IRLValue{
*_indexAccess.annotation().type,
IRLValue::Storage{
@@ -2074,7 +2100,7 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
string slot = m_context.newYulVariable();
string offset = m_context.newYulVariable();
m_code << Whiskers(R"(
appendCode() << Whiskers(R"(
let <slot>, <offset> := <indexFunc>(<array>, <index>)
)")
("slot", slot)
@@ -2141,7 +2167,7 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
IRVariable index{m_context.newYulVariable(), *TypeProvider::uint256()};
define(index, *_indexAccess.indexExpression());
m_code << Whiskers(R"(
appendCode() << Whiskers(R"(
if iszero(lt(<index>, <length>)) { <panic>() }
let <result> := <shl248>(byte(<index>, <array>))
)")
@@ -2307,7 +2333,6 @@ void IRGeneratorForStatements::handleVariableReference(
Expression const& _referencingExpression
)
{
setLocation(_referencingExpression);
if ((_variable.isStateVariable() || _variable.isFileLevelVariable()) && _variable.isConstant())
define(_referencingExpression) << constantValueFunction(_variable) << "()\n";
else if (_variable.isStateVariable() && _variable.immutable())
@@ -2377,11 +2402,10 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
// We could also just use MLOAD; POP right before the gas calculation, but the optimizer
// would remove that, so we use MSTORE here.
if (!funType.gasSet() && returnInfo.estimatedReturnSize > 0)
m_code << "mstore(add(" << m_utils.allocateUnboundedFunction() << "() , " << to_string(returnInfo.estimatedReturnSize) << "), 0)\n";
appendCode() << "mstore(add(" << m_utils.allocateUnboundedFunction() << "() , " << to_string(returnInfo.estimatedReturnSize) << "), 0)\n";
}
Whiskers templ(R"(
if iszero(extcodesize(<address>)) { <revertNoCode>() }
Whiskers templ(R"(if iszero(extcodesize(<address>)) { <revertNoCode>() }
// storage for arguments and returned data
let <pos> := <allocateUnbounded>()
@@ -2474,7 +2498,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
templ("forwardingRevert", m_utils.forwardingRevertFunction());
m_code << templ.render();
appendCode() << templ.render();
}
void IRGeneratorForStatements::appendBareCall(
@@ -2565,7 +2589,7 @@ void IRGeneratorForStatements::appendBareCall(
templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")");
}
m_code << templ.render();
appendCode() << templ.render();
}
IRVariable IRGeneratorForStatements::convert(IRVariable const& _from, Type const& _to)
@@ -2597,14 +2621,14 @@ std::string IRGeneratorForStatements::expressionAsType(Expression const& _expres
std::ostream& IRGeneratorForStatements::define(IRVariable const& _var)
{
if (_var.type().sizeOnStack() > 0)
m_code << "let " << _var.commaSeparatedList() << " := ";
return m_code;
appendCode() << "let " << _var.commaSeparatedList() << " := ";
return appendCode(false);
}
void IRGeneratorForStatements::declare(IRVariable const& _var)
{
if (_var.type().sizeOnStack() > 0)
m_code << "let " << _var.commaSeparatedList() << "\n";
appendCode() << "let " << _var.commaSeparatedList() << "\n";
}
void IRGeneratorForStatements::declareAssign(IRVariable const& _lhs, IRVariable const& _rhs, bool _declare)
@@ -2615,15 +2639,15 @@ void IRGeneratorForStatements::declareAssign(IRVariable const& _lhs, IRVariable
if (stackItemType)
declareAssign(_lhs.part(stackItemName), _rhs.part(stackItemName), _declare);
else
m_code << (_declare ? "let ": "") << _lhs.part(stackItemName).name() << " := " << _rhs.part(stackItemName).name() << "\n";
appendCode() << (_declare ? "let ": "") << _lhs.part(stackItemName).name() << " := " << _rhs.part(stackItemName).name() << "\n";
else
{
if (_lhs.type().sizeOnStack() > 0)
m_code <<
appendCode() <<
(_declare ? "let ": "") <<
_lhs.commaSeparatedList() <<
" := ";
m_code << m_context.utils().conversionFunction(_rhs.type(), _lhs.type()) <<
appendCode() << m_context.utils().conversionFunction(_rhs.type(), _lhs.type()) <<
"(" <<
_rhs.commaSeparatedList() <<
")\n";
@@ -2761,13 +2785,13 @@ void IRGeneratorForStatements::appendAndOrOperatorCode(BinaryOperation const& _b
IRVariable value(_binOp);
define(value, _binOp.leftExpression());
if (op == Token::Or)
m_code << "if iszero(" << value.name() << ") {\n";
appendCode() << "if iszero(" << value.name() << ") {\n";
else
m_code << "if " << value.name() << " {\n";
appendCode() << "if " << value.name() << " {\n";
_binOp.rightExpression().accept(*this);
setLocation(_binOp);
assign(value, _binOp.rightExpression());
m_code << "}\n";
appendCode() << "}\n";
}
void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable const& _value)
@@ -2783,7 +2807,7 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable
[&](string const& _offset) { offsetArgument = ", " + _offset; }
}, _storage.offset);
m_code <<
appendCode() <<
m_utils.updateStorageValueFunction(_value.type(), _lvalue.type, offsetStatic) <<
"(" <<
_storage.slot <<
@@ -2801,10 +2825,10 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable
if (_memory.byteArrayElement)
{
solAssert(_lvalue.type == *TypeProvider::byte(), "");
m_code << "mstore8(" + _memory.address + ", byte(0, " + prepared.commaSeparatedList() + "))\n";
appendCode() << "mstore8(" + _memory.address + ", byte(0, " + prepared.commaSeparatedList() + "))\n";
}
else
m_code << m_utils.writeToMemoryFunction(_lvalue.type) <<
appendCode() << m_utils.writeToMemoryFunction(_lvalue.type) <<
"(" <<
_memory.address <<
", " <<
@@ -2812,7 +2836,7 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable
")\n";
}
else if (auto const* literalType = dynamic_cast<StringLiteralType const*>(&_value.type()))
m_code <<
appendCode() <<
m_utils.writeToMemoryFunction(*TypeProvider::uint256()) <<
"(" <<
_memory.address <<
@@ -2824,7 +2848,7 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable
solAssert(_lvalue.type.sizeOnStack() == 1, "");
auto const* valueReferenceType = dynamic_cast<ReferenceType const*>(&_value.type());
solAssert(valueReferenceType && valueReferenceType->dataStoredIn(DataLocation::Memory), "");
m_code << "mstore(" + _memory.address + ", " + _value.part("mpos").name() + ")\n";
appendCode() << "mstore(" + _memory.address + ", " + _value.part("mpos").name() + ")\n";
}
},
[&](IRLValue::Stack const& _stack) { assign(_stack.variable, _value); },
@@ -2838,7 +2862,7 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable
IRVariable prepared(m_context.newYulVariable(), _lvalue.type);
define(prepared, _value);
m_code << "mstore(" << to_string(memOffset) << ", " << prepared.commaSeparatedList() << ")\n";
appendCode() << "mstore(" << to_string(memOffset) << ", " << prepared.commaSeparatedList() << ")\n";
},
[&](IRLValue::Tuple const& _tuple) {
auto components = std::move(_tuple.components);
@@ -2931,36 +2955,36 @@ void IRGeneratorForStatements::generateLoop(
{
solAssert(_conditionExpression, "Expected condition for doWhile");
firstRun = m_context.newYulVariable();
m_code << "let " << firstRun << " := 1\n";
appendCode() << "let " << firstRun << " := 1\n";
}
m_code << "for {\n";
appendCode() << "for {\n";
if (_initExpression)
_initExpression->accept(*this);
m_code << "} 1 {\n";
appendCode() << "} 1 {\n";
if (_loopExpression)
_loopExpression->accept(*this);
m_code << "}\n";
m_code << "{\n";
appendCode() << "}\n";
appendCode() << "{\n";
if (_conditionExpression)
{
if (_isDoWhile)
m_code << "if iszero(" << firstRun << ") {\n";
appendCode() << "if iszero(" << firstRun << ") {\n";
_conditionExpression->accept(*this);
m_code <<
appendCode() <<
"if iszero(" <<
expressionAsType(*_conditionExpression, *TypeProvider::boolean()) <<
") { break }\n";
if (_isDoWhile)
m_code << "}\n" << firstRun << " := 0\n";
appendCode() << "}\n" << firstRun << " := 0\n";
}
_body.accept(*this);
m_code << "}\n";
appendCode() << "}\n";
}
Type const& IRGeneratorForStatements::type(Expression const& _expression)
@@ -2975,9 +2999,9 @@ bool IRGeneratorForStatements::visit(TryStatement const& _tryStatement)
externalCall.accept(*this);
setLocation(_tryStatement);
m_code << "switch iszero(" << IRNames::trySuccessConditionVariable(externalCall) << ")\n";
appendCode() << "switch iszero(" << IRNames::trySuccessConditionVariable(externalCall) << ")\n";
m_code << "case 0 { // success case\n";
appendCode() << "case 0 { // success case\n";
TryCatchClause const& successClause = *_tryStatement.clauses().front();
if (successClause.parameters())
{
@@ -2995,32 +3019,34 @@ bool IRGeneratorForStatements::visit(TryStatement const& _tryStatement)
successClause.block().accept(*this);
setLocation(_tryStatement);
m_code << "}\n";
appendCode() << "}\n";
m_code << "default { // failure case\n";
appendCode() << "default { // failure case\n";
handleCatch(_tryStatement);
m_code << "}\n";
appendCode() << "}\n";
return false;
}
void IRGeneratorForStatements::handleCatch(TryStatement const& _tryStatement)
{
setLocation(_tryStatement);
string const runFallback = m_context.newYulVariable();
m_code << "let " << runFallback << " := 1\n";
appendCode() << "let " << runFallback << " := 1\n";
// This function returns zero on "short returndata". We have to add a success flag
// once we implement custom error codes.
if (_tryStatement.errorClause() || _tryStatement.panicClause())
m_code << "switch " << m_utils.returnDataSelectorFunction() << "()\n";
appendCode() << "switch " << m_utils.returnDataSelectorFunction() << "()\n";
if (TryCatchClause const* errorClause = _tryStatement.errorClause())
{
m_code << "case " << selectorFromSignature32("Error(string)") << " {\n";
appendCode() << "case " << selectorFromSignature32("Error(string)") << " {\n";
setLocation(*errorClause);
string const dataVariable = m_context.newYulVariable();
m_code << "let " << dataVariable << " := " << m_utils.tryDecodeErrorMessageFunction() << "()\n";
m_code << "if " << dataVariable << " {\n";
m_code << runFallback << " := 0\n";
appendCode() << "let " << dataVariable << " := " << m_utils.tryDecodeErrorMessageFunction() << "()\n";
appendCode() << "if " << dataVariable << " {\n";
appendCode() << runFallback << " := 0\n";
if (errorClause->parameters())
{
solAssert(errorClause->parameters()->parameters().size() == 1, "");
@@ -3028,17 +3054,20 @@ void IRGeneratorForStatements::handleCatch(TryStatement const& _tryStatement)
define(var) << dataVariable << "\n";
}
errorClause->accept(*this);
m_code << "}\n";
m_code << "}\n";
setLocation(*errorClause);
appendCode() << "}\n";
setLocation(_tryStatement);
appendCode() << "}\n";
}
if (TryCatchClause const* panicClause = _tryStatement.panicClause())
{
m_code << "case " << selectorFromSignature32("Panic(uint256)") << " {\n";
appendCode() << "case " << selectorFromSignature32("Panic(uint256)") << " {\n";
setLocation(*panicClause);
string const success = m_context.newYulVariable();
string const code = m_context.newYulVariable();
m_code << "let " << success << ", " << code << " := " << m_utils.tryDecodePanicDataFunction() << "()\n";
m_code << "if " << success << " {\n";
m_code << runFallback << " := 0\n";
appendCode() << "let " << success << ", " << code << " := " << m_utils.tryDecodePanicDataFunction() << "()\n";
appendCode() << "if " << success << " {\n";
appendCode() << runFallback << " := 0\n";
if (panicClause->parameters())
{
solAssert(panicClause->parameters()->parameters().size() == 1, "");
@@ -3046,20 +3075,25 @@ void IRGeneratorForStatements::handleCatch(TryStatement const& _tryStatement)
define(var) << code << "\n";
}
panicClause->accept(*this);
m_code << "}\n";
m_code << "}\n";
setLocation(*panicClause);
appendCode() << "}\n";
setLocation(_tryStatement);
appendCode() << "}\n";
}
m_code << "if " << runFallback << " {\n";
setLocation(_tryStatement);
appendCode() << "if " << runFallback << " {\n";
if (_tryStatement.fallbackClause())
handleCatchFallback(*_tryStatement.fallbackClause());
else
m_code << m_utils.forwardingRevertFunction() << "()\n";
m_code << "}\n";
appendCode() << m_utils.forwardingRevertFunction() << "()\n";
setLocation(_tryStatement);
appendCode() << "}\n";
}
void IRGeneratorForStatements::handleCatchFallback(TryCatchClause const& _fallback)
{
setLocation(_fallback);
if (_fallback.parameters())
{
solAssert(m_context.evmVersion().supportsReturndata(), "");
@@ -3104,7 +3138,7 @@ void IRGeneratorForStatements::revertWithError(
templ("argumentVars", joinHumanReadablePrefixed(errorArgumentVars));
templ("encode", m_context.abiFunctions().tupleEncoder(errorArgumentTypes, _parameterTypes));
m_code << templ.render();
appendCode() << templ.render();
}
@@ -3114,11 +3148,6 @@ bool IRGeneratorForStatements::visit(TryCatchClause const& _clause)
return false;
}
void IRGeneratorForStatements::setLocation(ASTNode const& _node)
{
m_currentLocation = _node.location();
}
string IRGeneratorForStatements::linkerSymbol(ContractDefinition const& _library) const
{
solAssert(_library.isLibrary(), "");
@@ -33,11 +33,33 @@ namespace solidity::frontend
class IRGenerationContext;
class YulUtilFunctions;
/**
* Base class for the statement generator.
* Encapsulates access to the yul code stream and handles source code locations.
*/
class IRGeneratorForStatementsBase: public ASTConstVisitor
{
public:
IRGeneratorForStatementsBase(IRGenerationContext& _context):
m_context(_context)
{}
virtual std::string code() const;
std::ostringstream& appendCode(bool _addLocationComment = true);
protected:
void setLocation(ASTNode const& _node);
langutil::SourceLocation m_currentLocation = {};
langutil::SourceLocation m_lastLocation = {};
IRGenerationContext& m_context;
private:
std::ostringstream m_code;
};
/**
* Component that translates Solidity's AST into Yul at statement level and below.
* It is an AST visitor that appends to an internal string buffer.
*/
class IRGeneratorForStatements: public ASTConstVisitor
class IRGeneratorForStatements: public IRGeneratorForStatementsBase
{
public:
IRGeneratorForStatements(
@@ -45,12 +67,12 @@ public:
YulUtilFunctions& _utils,
std::function<std::string()> _placeholderCallback = {}
):
m_context(_context),
IRGeneratorForStatementsBase(_context),
m_placeholderCallback(std::move(_placeholderCallback)),
m_utils(_utils)
{}
std::string code() const;
std::string code() const override;
/// Generate the code for the statements in the block;
void generate(Block const& _block);
@@ -190,16 +212,11 @@ private:
static Type const& type(Expression const& _expression);
void setLocation(ASTNode const& _node);
std::string linkerSymbol(ContractDefinition const& _library) const;
std::ostringstream m_code;
IRGenerationContext& m_context;
std::function<std::string()> m_placeholderCallback;
YulUtilFunctions& m_utils;
std::optional<IRLValue> m_currentLValue;
langutil::SourceLocation m_currentLocation;
};
}