| 115 | } |
| 116 | |
| 117 | void StackToMemoryMover::operator()(FunctionDefinition& _functionDefinition) |
| 118 | { |
| 119 | // It is important to first visit the function body, so that it doesn't replace the memory inits for |
| 120 | // variable arguments we might generate below. |
| 121 | ASTModifier::operator()(_functionDefinition); |
| 122 | |
| 123 | std::vector<Statement> memoryVariableInits; |
| 124 | |
| 125 | // All function parameters with a memory slot are moved at the beginning of the function body. |
| 126 | for (NameWithDebugData const& param: _functionDefinition.parameters) |
| 127 | if (auto slot = m_memoryOffsetTracker(param.name)) |
| 128 | memoryVariableInits += generateMemoryStore( |
| 129 | m_context.dialect, |
| 130 | param.debugData, |
| 131 | *slot, |
| 132 | Identifier{param.debugData, param.name} |
| 133 | ); |
| 134 | |
| 135 | // All memory return variables have to be initialized to zero in memory. |
| 136 | for (NameWithDebugData const& returnVariable: _functionDefinition.returnVariables) |
| 137 | if (auto slot = m_memoryOffsetTracker(returnVariable.name)) |
| 138 | memoryVariableInits += generateMemoryStore( |
| 139 | m_context.dialect, |
| 140 | returnVariable.debugData, |
| 141 | *slot, |
| 142 | Literal{returnVariable.debugData, LiteralKind::Number, LiteralValue(u256{0})} |
| 143 | ); |
| 144 | |
| 145 | // Special case of a function with a single return argument that needs to move to memory. |
| 146 | if (_functionDefinition.returnVariables.size() == 1 && m_memoryOffsetTracker(_functionDefinition.returnVariables.front().name)) |
| 147 | { |
| 148 | NameWithDebugDataList stackParameters = _functionDefinition.parameters | ranges::views::filter( |
| 149 | std::not_fn(m_memoryOffsetTracker) |
| 150 | ) | ranges::to<NameWithDebugDataList>; |
| 151 | // Generate new function without return variable and with only the non-moved parameters. |
| 152 | YulName newFunctionName = m_context.dispenser.newName(_functionDefinition.name); |
| 153 | m_newFunctionDefinitions.emplace_back(FunctionDefinition{ |
| 154 | _functionDefinition.debugData, |
| 155 | newFunctionName, |
| 156 | stackParameters, |
| 157 | {}, |
| 158 | std::move(_functionDefinition.body) |
| 159 | }); |
| 160 | // Generate new names for the arguments to maintain disambiguation. |
| 161 | std::map<YulName, YulName> newArgumentNames; |
| 162 | for (NameWithDebugData const& _var: stackParameters) |
| 163 | newArgumentNames[_var.name] = m_context.dispenser.newName(_var.name); |
| 164 | for (auto& parameter: _functionDefinition.parameters) |
| 165 | parameter.name = util::valueOrDefault(newArgumentNames, parameter.name, parameter.name); |
| 166 | // Replace original function by a call to the new function and an assignment to the return variable from memory. |
| 167 | _functionDefinition.body = Block{_functionDefinition.debugData, std::move(memoryVariableInits)}; |
| 168 | _functionDefinition.body.statements.emplace_back(ExpressionStatement{ |
| 169 | _functionDefinition.debugData, |
| 170 | FunctionCall{ |
| 171 | _functionDefinition.debugData, |
| 172 | Identifier{_functionDefinition.debugData, newFunctionName}, |
| 173 | stackParameters | ranges::views::transform([&](NameWithDebugData const& _arg) { |
| 174 | return Expression{Identifier{_arg.debugData, newArgumentNames.at(_arg.name)}}; |
nothing calls this directly
no test coverage detected