mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #8626 from ethereum/immutable-functioncallgraph
Prepare literalArguments for immutable builtin functions
This commit is contained in:
commit
a7a1feb1b8
@ -255,14 +255,14 @@ vector<YulString> AsmAnalyzer::operator()(FunctionCall const& _funCall)
|
|||||||
yulAssert(!_funCall.functionName.name.empty(), "");
|
yulAssert(!_funCall.functionName.name.empty(), "");
|
||||||
vector<YulString> const* parameterTypes = nullptr;
|
vector<YulString> const* parameterTypes = nullptr;
|
||||||
vector<YulString> const* returnTypes = nullptr;
|
vector<YulString> const* returnTypes = nullptr;
|
||||||
bool needsLiteralArguments = false;
|
vector<bool> const* needsLiteralArguments = nullptr;
|
||||||
|
|
||||||
if (BuiltinFunction const* f = m_dialect.builtin(_funCall.functionName.name))
|
if (BuiltinFunction const* f = m_dialect.builtin(_funCall.functionName.name))
|
||||||
{
|
{
|
||||||
parameterTypes = &f->parameters;
|
parameterTypes = &f->parameters;
|
||||||
returnTypes = &f->returns;
|
returnTypes = &f->returns;
|
||||||
if (f->literalArguments)
|
if (f->literalArguments)
|
||||||
needsLiteralArguments = true;
|
needsLiteralArguments = &f->literalArguments.value();
|
||||||
}
|
}
|
||||||
else if (!m_currentScope->lookup(_funCall.functionName.name, GenericVisitor{
|
else if (!m_currentScope->lookup(_funCall.functionName.name, GenericVisitor{
|
||||||
[&](Scope::Variable const&)
|
[&](Scope::Variable const&)
|
||||||
@ -293,11 +293,13 @@ vector<YulString> AsmAnalyzer::operator()(FunctionCall const& _funCall)
|
|||||||
);
|
);
|
||||||
|
|
||||||
vector<YulString> argTypes;
|
vector<YulString> argTypes;
|
||||||
for (auto const& arg: _funCall.arguments | boost::adaptors::reversed)
|
for (size_t i = _funCall.arguments.size(); i > 0; i--)
|
||||||
{
|
{
|
||||||
|
Expression const& arg = _funCall.arguments[i - 1];
|
||||||
|
|
||||||
argTypes.emplace_back(expectExpression(arg));
|
argTypes.emplace_back(expectExpression(arg));
|
||||||
|
|
||||||
if (needsLiteralArguments)
|
if (needsLiteralArguments && (*needsLiteralArguments)[i - 1])
|
||||||
{
|
{
|
||||||
if (!holds_alternative<Literal>(arg))
|
if (!holds_alternative<Literal>(arg))
|
||||||
typeError(
|
typeError(
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace solidity::yul
|
namespace solidity::yul
|
||||||
{
|
{
|
||||||
@ -46,8 +47,8 @@ struct BuiltinFunction
|
|||||||
ControlFlowSideEffects controlFlowSideEffects;
|
ControlFlowSideEffects controlFlowSideEffects;
|
||||||
/// If true, this is the msize instruction.
|
/// If true, this is the msize instruction.
|
||||||
bool isMSize = false;
|
bool isMSize = false;
|
||||||
/// If true, can only accept literals as arguments and they cannot be moved to variables.
|
/// If set, same length as the arguments, if true at index i, the i'th argument has to be a literal which means it can't be moved to variables.
|
||||||
bool literalArguments = false;
|
std::optional<std::vector<bool>> literalArguments;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Dialect: boost::noncopyable
|
struct Dialect: boost::noncopyable
|
||||||
|
@ -55,7 +55,7 @@ pair<YulString, BuiltinFunctionForEVM> createEVMFunction(
|
|||||||
f.controlFlowSideEffects.terminates = evmasm::SemanticInformation::terminatesControlFlow(_instruction);
|
f.controlFlowSideEffects.terminates = evmasm::SemanticInformation::terminatesControlFlow(_instruction);
|
||||||
f.controlFlowSideEffects.reverts = evmasm::SemanticInformation::reverts(_instruction);
|
f.controlFlowSideEffects.reverts = evmasm::SemanticInformation::reverts(_instruction);
|
||||||
f.isMSize = _instruction == evmasm::Instruction::MSIZE;
|
f.isMSize = _instruction == evmasm::Instruction::MSIZE;
|
||||||
f.literalArguments = false;
|
f.literalArguments.reset();
|
||||||
f.instruction = _instruction;
|
f.instruction = _instruction;
|
||||||
f.generateCode = [_instruction](
|
f.generateCode = [_instruction](
|
||||||
FunctionCall const&,
|
FunctionCall const&,
|
||||||
@ -75,17 +75,22 @@ pair<YulString, BuiltinFunctionForEVM> createFunction(
|
|||||||
size_t _params,
|
size_t _params,
|
||||||
size_t _returns,
|
size_t _returns,
|
||||||
SideEffects _sideEffects,
|
SideEffects _sideEffects,
|
||||||
bool _literalArguments,
|
vector<bool> _literalArguments,
|
||||||
std::function<void(FunctionCall const&, AbstractAssembly&, BuiltinContext&, std::function<void()>)> _generateCode
|
std::function<void(FunctionCall const&, AbstractAssembly&, BuiltinContext&, std::function<void()>)> _generateCode
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
solAssert(_literalArguments.size() == _params || _literalArguments.empty(), "");
|
||||||
|
|
||||||
YulString name{std::move(_name)};
|
YulString name{std::move(_name)};
|
||||||
BuiltinFunctionForEVM f;
|
BuiltinFunctionForEVM f;
|
||||||
f.name = name;
|
f.name = name;
|
||||||
f.parameters.resize(_params);
|
f.parameters.resize(_params);
|
||||||
f.returns.resize(_returns);
|
f.returns.resize(_returns);
|
||||||
f.sideEffects = std::move(_sideEffects);
|
f.sideEffects = std::move(_sideEffects);
|
||||||
f.literalArguments = _literalArguments;
|
if (!_literalArguments.empty())
|
||||||
|
f.literalArguments = std::move(_literalArguments);
|
||||||
|
else
|
||||||
|
f.literalArguments.reset();
|
||||||
f.isMSize = false;
|
f.isMSize = false;
|
||||||
f.instruction = {};
|
f.instruction = {};
|
||||||
f.generateCode = std::move(_generateCode);
|
f.generateCode = std::move(_generateCode);
|
||||||
@ -107,7 +112,7 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
|
|||||||
|
|
||||||
if (_objectAccess)
|
if (_objectAccess)
|
||||||
{
|
{
|
||||||
builtins.emplace(createFunction("datasize", 1, 1, SideEffects{}, true, [](
|
builtins.emplace(createFunction("datasize", 1, 1, SideEffects{}, {true}, [](
|
||||||
FunctionCall const& _call,
|
FunctionCall const& _call,
|
||||||
AbstractAssembly& _assembly,
|
AbstractAssembly& _assembly,
|
||||||
BuiltinContext& _context,
|
BuiltinContext& _context,
|
||||||
@ -128,7 +133,7 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
|
|||||||
_assembly.appendDataSize(_context.subIDs.at(dataName));
|
_assembly.appendDataSize(_context.subIDs.at(dataName));
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
builtins.emplace(createFunction("dataoffset", 1, 1, SideEffects{}, true, [](
|
builtins.emplace(createFunction("dataoffset", 1, 1, SideEffects{}, {true}, [](
|
||||||
FunctionCall const& _call,
|
FunctionCall const& _call,
|
||||||
AbstractAssembly& _assembly,
|
AbstractAssembly& _assembly,
|
||||||
BuiltinContext& _context,
|
BuiltinContext& _context,
|
||||||
@ -154,7 +159,7 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
|
|||||||
3,
|
3,
|
||||||
0,
|
0,
|
||||||
SideEffects{false, false, false, false, true},
|
SideEffects{false, false, false, false, true},
|
||||||
false,
|
{},
|
||||||
[](
|
[](
|
||||||
FunctionCall const&,
|
FunctionCall const&,
|
||||||
AbstractAssembly& _assembly,
|
AbstractAssembly& _assembly,
|
||||||
@ -262,7 +267,7 @@ EVMDialectTyped::EVMDialectTyped(langutil::EVMVersion _evmVersion, bool _objectA
|
|||||||
m_functions["popbool"_yulstring] = m_functions["pop"_yulstring];
|
m_functions["popbool"_yulstring] = m_functions["pop"_yulstring];
|
||||||
m_functions["popbool"_yulstring].name = "popbool"_yulstring;
|
m_functions["popbool"_yulstring].name = "popbool"_yulstring;
|
||||||
m_functions["popbool"_yulstring].parameters = {"bool"_yulstring};
|
m_functions["popbool"_yulstring].parameters = {"bool"_yulstring};
|
||||||
m_functions.insert(createFunction("bool_to_u256", 1, 1, {}, false, [](
|
m_functions.insert(createFunction("bool_to_u256", 1, 1, {}, {}, [](
|
||||||
FunctionCall const&,
|
FunctionCall const&,
|
||||||
AbstractAssembly&,
|
AbstractAssembly&,
|
||||||
BuiltinContext&,
|
BuiltinContext&,
|
||||||
@ -272,7 +277,7 @@ EVMDialectTyped::EVMDialectTyped(langutil::EVMVersion _evmVersion, bool _objectA
|
|||||||
}));
|
}));
|
||||||
m_functions["bool_to_u256"_yulstring].parameters = {"bool"_yulstring};
|
m_functions["bool_to_u256"_yulstring].parameters = {"bool"_yulstring};
|
||||||
m_functions["bool_to_u256"_yulstring].returns = {"u256"_yulstring};
|
m_functions["bool_to_u256"_yulstring].returns = {"u256"_yulstring};
|
||||||
m_functions.insert(createFunction("u256_to_bool", 1, 1, {}, false, [](
|
m_functions.insert(createFunction("u256_to_bool", 1, 1, {}, {}, [](
|
||||||
FunctionCall const&,
|
FunctionCall const&,
|
||||||
AbstractAssembly& _assembly,
|
AbstractAssembly& _assembly,
|
||||||
BuiltinContext&,
|
BuiltinContext&,
|
||||||
|
@ -45,7 +45,7 @@ struct BuiltinContext
|
|||||||
std::map<YulString, AbstractAssembly::SubID> subIDs;
|
std::map<YulString, AbstractAssembly::SubID> subIDs;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BuiltinFunctionForEVM: BuiltinFunction
|
struct BuiltinFunctionForEVM: public BuiltinFunction
|
||||||
{
|
{
|
||||||
std::optional<evmasm::Instruction> instruction;
|
std::optional<evmasm::Instruction> instruction;
|
||||||
/// Function to generate code for the given function call and append it to the abstract
|
/// Function to generate code for the given function call and append it to the abstract
|
||||||
|
@ -136,11 +136,15 @@ wasm::Expression WasmCodeTransform::operator()(FunctionCall const& _call)
|
|||||||
}
|
}
|
||||||
typeConversionNeeded = true;
|
typeConversionNeeded = true;
|
||||||
}
|
}
|
||||||
else if (builtin->literalArguments)
|
else if (builtin->literalArguments && contains(builtin->literalArguments.value(), true))
|
||||||
{
|
{
|
||||||
vector<wasm::Expression> literals;
|
vector<wasm::Expression> literals;
|
||||||
for (auto const& arg: _call.arguments)
|
for (size_t i = 0; i < _call.arguments.size(); i++)
|
||||||
literals.emplace_back(wasm::StringLiteral{std::get<Literal>(arg).value.str()});
|
if (builtin->literalArguments.value()[i])
|
||||||
|
literals.emplace_back(wasm::StringLiteral{std::get<Literal>(_call.arguments[i]).value.str()});
|
||||||
|
else
|
||||||
|
literals.emplace_back(visitReturnByValue(_call.arguments[i]));
|
||||||
|
|
||||||
return wasm::BuiltinCall{_call.functionName.name.str(), std::move(literals)};
|
return wasm::BuiltinCall{_call.functionName.name.str(), std::move(literals)};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -102,8 +102,8 @@ WasmDialect::WasmDialect()
|
|||||||
m_functions["unreachable"_yulstring].controlFlowSideEffects.terminates = true;
|
m_functions["unreachable"_yulstring].controlFlowSideEffects.terminates = true;
|
||||||
m_functions["unreachable"_yulstring].controlFlowSideEffects.reverts = true;
|
m_functions["unreachable"_yulstring].controlFlowSideEffects.reverts = true;
|
||||||
|
|
||||||
addFunction("datasize", {i64}, {i64}, true, true);
|
addFunction("datasize", {i64}, {i64}, true, {true});
|
||||||
addFunction("dataoffset", {i64}, {i64}, true, true);
|
addFunction("dataoffset", {i64}, {i64}, true, {true});
|
||||||
|
|
||||||
addEthereumExternals();
|
addEthereumExternals();
|
||||||
}
|
}
|
||||||
@ -204,7 +204,7 @@ void WasmDialect::addEthereumExternals()
|
|||||||
f.controlFlowSideEffects = ext.controlFlowSideEffects;
|
f.controlFlowSideEffects = ext.controlFlowSideEffects;
|
||||||
f.isMSize = false;
|
f.isMSize = false;
|
||||||
f.sideEffects.invalidatesStorage = (ext.name == "storageStore");
|
f.sideEffects.invalidatesStorage = (ext.name == "storageStore");
|
||||||
f.literalArguments = false;
|
f.literalArguments.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,7 +213,7 @@ void WasmDialect::addFunction(
|
|||||||
vector<YulString> _params,
|
vector<YulString> _params,
|
||||||
vector<YulString> _returns,
|
vector<YulString> _returns,
|
||||||
bool _movable,
|
bool _movable,
|
||||||
bool _literalArguments
|
std::vector<bool> _literalArguments
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
YulString name{move(_name)};
|
YulString name{move(_name)};
|
||||||
@ -224,5 +224,8 @@ void WasmDialect::addFunction(
|
|||||||
f.returns = std::move(_returns);
|
f.returns = std::move(_returns);
|
||||||
f.sideEffects = _movable ? SideEffects{} : SideEffects::worst();
|
f.sideEffects = _movable ? SideEffects{} : SideEffects::worst();
|
||||||
f.isMSize = false;
|
f.isMSize = false;
|
||||||
f.literalArguments = _literalArguments;
|
if (!_literalArguments.empty())
|
||||||
|
f.literalArguments = std::move(_literalArguments);
|
||||||
|
else
|
||||||
|
f.literalArguments.reset();
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ private:
|
|||||||
std::vector<YulString> _params,
|
std::vector<YulString> _params,
|
||||||
std::vector<YulString> _returns,
|
std::vector<YulString> _returns,
|
||||||
bool _movable = true,
|
bool _movable = true,
|
||||||
bool _literalArguments = false
|
std::vector<bool> _literalArguments = std::vector<bool>{}
|
||||||
);
|
);
|
||||||
|
|
||||||
std::map<YulString, BuiltinFunction> m_functions;
|
std::map<YulString, BuiltinFunction> m_functions;
|
||||||
|
@ -41,15 +41,24 @@ void WordSizeTransform::operator()(FunctionDefinition& _fd)
|
|||||||
|
|
||||||
void WordSizeTransform::operator()(FunctionCall& _fc)
|
void WordSizeTransform::operator()(FunctionCall& _fc)
|
||||||
{
|
{
|
||||||
|
vector<bool> const* literalArguments = nullptr;
|
||||||
|
|
||||||
if (BuiltinFunction const* fun = m_inputDialect.builtin(_fc.functionName.name))
|
if (BuiltinFunction const* fun = m_inputDialect.builtin(_fc.functionName.name))
|
||||||
if (fun->literalArguments)
|
if (fun->literalArguments)
|
||||||
|
literalArguments = &fun->literalArguments.value();
|
||||||
|
|
||||||
|
vector<Expression> newArgs;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < _fc.arguments.size(); i++)
|
||||||
|
if (!literalArguments || !(*literalArguments)[i])
|
||||||
|
newArgs += expandValueToVector(_fc.arguments[i]);
|
||||||
|
else
|
||||||
{
|
{
|
||||||
for (Expression& arg: _fc.arguments)
|
get<Literal>(_fc.arguments[i]).type = m_targetDialect.defaultType;
|
||||||
get<Literal>(arg).type = m_targetDialect.defaultType;
|
newArgs.emplace_back(std::move(_fc.arguments[i]));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rewriteFunctionCallArguments(_fc.arguments);
|
_fc.arguments = std::move(newArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WordSizeTransform::operator()(If& _if)
|
void WordSizeTransform::operator()(If& _if)
|
||||||
@ -97,9 +106,9 @@ void WordSizeTransform::operator()(Block& _block)
|
|||||||
|
|
||||||
// Special handling for datasize and dataoffset - they will only need one variable.
|
// Special handling for datasize and dataoffset - they will only need one variable.
|
||||||
if (BuiltinFunction const* f = m_inputDialect.builtin(std::get<FunctionCall>(*varDecl.value).functionName.name))
|
if (BuiltinFunction const* f = m_inputDialect.builtin(std::get<FunctionCall>(*varDecl.value).functionName.name))
|
||||||
if (f->literalArguments)
|
if (f->name == "datasize"_yulstring || f->name == "dataoffset"_yulstring)
|
||||||
{
|
{
|
||||||
yulAssert(f->name == "datasize"_yulstring || f->name == "dataoffset"_yulstring, "");
|
yulAssert(f->literalArguments && f->literalArguments.value()[0], "");
|
||||||
yulAssert(varDecl.variables.size() == 1, "");
|
yulAssert(varDecl.variables.size() == 1, "");
|
||||||
auto newLhs = generateU64IdentifierNames(varDecl.variables[0].name);
|
auto newLhs = generateU64IdentifierNames(varDecl.variables[0].name);
|
||||||
vector<Statement> ret;
|
vector<Statement> ret;
|
||||||
@ -157,9 +166,9 @@ void WordSizeTransform::operator()(Block& _block)
|
|||||||
|
|
||||||
// Special handling for datasize and dataoffset - they will only need one variable.
|
// Special handling for datasize and dataoffset - they will only need one variable.
|
||||||
if (BuiltinFunction const* f = m_inputDialect.builtin(std::get<FunctionCall>(*assignment.value).functionName.name))
|
if (BuiltinFunction const* f = m_inputDialect.builtin(std::get<FunctionCall>(*assignment.value).functionName.name))
|
||||||
if (f->literalArguments)
|
if (f->name == "datasize"_yulstring || f->name == "dataoffset"_yulstring)
|
||||||
{
|
{
|
||||||
yulAssert(f->name == "datasize"_yulstring || f->name == "dataoffset"_yulstring, "");
|
yulAssert(f->literalArguments && f->literalArguments.value()[0], "");
|
||||||
yulAssert(assignment.variableNames.size() == 1, "");
|
yulAssert(assignment.variableNames.size() == 1, "");
|
||||||
auto newLhs = generateU64IdentifierNames(assignment.variableNames[0].name);
|
auto newLhs = generateU64IdentifierNames(assignment.variableNames[0].name);
|
||||||
vector<Statement> ret;
|
vector<Statement> ret;
|
||||||
@ -266,17 +275,6 @@ void WordSizeTransform::rewriteIdentifierList(vector<Identifier>& _ids)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WordSizeTransform::rewriteFunctionCallArguments(vector<Expression>& _args)
|
|
||||||
{
|
|
||||||
iterateReplacing(
|
|
||||||
_args,
|
|
||||||
[&](Expression& _e) -> std::optional<vector<Expression>>
|
|
||||||
{
|
|
||||||
return expandValueToVector(_e);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
vector<Statement> WordSizeTransform::handleSwitchInternal(
|
vector<Statement> WordSizeTransform::handleSwitchInternal(
|
||||||
langutil::SourceLocation const& _location,
|
langutil::SourceLocation const& _location,
|
||||||
vector<YulString> const& _splitExpressions,
|
vector<YulString> const& _splitExpressions,
|
||||||
|
@ -83,7 +83,6 @@ private:
|
|||||||
|
|
||||||
void rewriteVarDeclList(std::vector<TypedName>&);
|
void rewriteVarDeclList(std::vector<TypedName>&);
|
||||||
void rewriteIdentifierList(std::vector<Identifier>&);
|
void rewriteIdentifierList(std::vector<Identifier>&);
|
||||||
void rewriteFunctionCallArguments(std::vector<Expression>&);
|
|
||||||
|
|
||||||
std::vector<Statement> handleSwitch(Switch& _switch);
|
std::vector<Statement> handleSwitch(Switch& _switch);
|
||||||
std::vector<Statement> handleSwitchInternal(
|
std::vector<Statement> handleSwitchInternal(
|
||||||
|
@ -58,12 +58,21 @@ void CommonSubexpressionEliminator::visit(Expression& _e)
|
|||||||
// If this is a function call to a function that requires literal arguments,
|
// If this is a function call to a function that requires literal arguments,
|
||||||
// do not try to simplify there.
|
// do not try to simplify there.
|
||||||
if (holds_alternative<FunctionCall>(_e))
|
if (holds_alternative<FunctionCall>(_e))
|
||||||
if (BuiltinFunction const* builtin = m_dialect.builtin(std::get<FunctionCall>(_e).functionName.name))
|
{
|
||||||
if (builtin->literalArguments)
|
FunctionCall& funCall = std::get<FunctionCall>(_e);
|
||||||
|
|
||||||
|
if (BuiltinFunction const* builtin = m_dialect.builtin(funCall.functionName.name))
|
||||||
|
{
|
||||||
|
for (size_t i = funCall.arguments.size(); i > 0; i--)
|
||||||
// We should not modify function arguments that have to be literals
|
// We should not modify function arguments that have to be literals
|
||||||
// Note that replacing the function call entirely is fine,
|
// Note that replacing the function call entirely is fine,
|
||||||
// if the function call is movable.
|
// if the function call is movable.
|
||||||
|
if (!builtin->literalArguments || !builtin->literalArguments.value()[i - 1])
|
||||||
|
visit(funCall.arguments[i - 1]);
|
||||||
|
|
||||||
descend = false;
|
descend = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We visit the inner expression first to first simplify inner expressions,
|
// We visit the inner expression first to first simplify inner expressions,
|
||||||
// which hopefully allows more matches.
|
// which hopefully allows more matches.
|
||||||
|
@ -47,13 +47,15 @@ void ExpressionSplitter::run(OptimiserStepContext& _context, Block& _ast)
|
|||||||
|
|
||||||
void ExpressionSplitter::operator()(FunctionCall& _funCall)
|
void ExpressionSplitter::operator()(FunctionCall& _funCall)
|
||||||
{
|
{
|
||||||
|
vector<bool> const* literalArgs = nullptr;
|
||||||
|
|
||||||
if (BuiltinFunction const* builtin = m_dialect.builtin(_funCall.functionName.name))
|
if (BuiltinFunction const* builtin = m_dialect.builtin(_funCall.functionName.name))
|
||||||
if (builtin->literalArguments)
|
if (builtin->literalArguments)
|
||||||
// We cannot outline function arguments that have to be literals
|
literalArgs = &builtin->literalArguments.value();
|
||||||
return;
|
|
||||||
|
|
||||||
for (auto& arg: _funCall.arguments | boost::adaptors::reversed)
|
for (size_t i = _funCall.arguments.size(); i > 0; i--)
|
||||||
outlineExpression(arg);
|
if (!literalArgs || !(*literalArgs)[i - 1])
|
||||||
|
outlineExpression(_funCall.arguments[i - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExpressionSplitter::operator()(If& _if)
|
void ExpressionSplitter::operator()(If& _if)
|
||||||
|
@ -539,7 +539,7 @@ BOOST_AUTO_TEST_CASE(builtins_analysis)
|
|||||||
{
|
{
|
||||||
return _name == "builtin"_yulstring ? &f : nullptr;
|
return _name == "builtin"_yulstring ? &f : nullptr;
|
||||||
}
|
}
|
||||||
BuiltinFunction f{"builtin"_yulstring, vector<Type>(2), vector<Type>(3), {}, {}};
|
BuiltinFunction f{"builtin"_yulstring, vector<Type>(2), vector<Type>(3), {}, {}, false, {}};
|
||||||
};
|
};
|
||||||
|
|
||||||
SimpleDialect dialect;
|
SimpleDialect dialect;
|
||||||
|
Loading…
Reference in New Issue
Block a user