1 /*
2 	This file is part of solidity.
3 
4 	solidity is free software: you can redistribute it and/or modify
5 	it under the terms of the GNU General Public License as published by
6 	the Free Software Foundation, either version 3 of the License, or
7 	(at your option) any later version.
8 
9 	solidity is distributed in the hope that it will be useful,
10 	but WITHOUT ANY WARRANTY; without even the implied warranty of
11 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 	GNU General Public License for more details.
13 
14 	You should have received a copy of the GNU General Public License
15 	along with solidity.  If not, see <http://www.gnu.org/licenses/>.
16 */
17 // SPDX-License-Identifier: GPL-3.0
18 #include <libyul/backends/evm/OptimizedEVMCodeTransform.h>
19 
20 #include <libyul/backends/evm/ControlFlowGraphBuilder.h>
21 #include <libyul/backends/evm/StackHelpers.h>
22 #include <libyul/backends/evm/StackLayoutGenerator.h>
23 
24 #include <libyul/Utilities.h>
25 
26 #include <libsolutil/Visitor.h>
27 #include <libsolutil/cxx20.h>
28 
29 #include <range/v3/view/drop.hpp>
30 #include <range/v3/view/enumerate.hpp>
31 #include <range/v3/view/filter.hpp>
32 #include <range/v3/view/iota.hpp>
33 #include <range/v3/view/map.hpp>
34 #include <range/v3/view/reverse.hpp>
35 #include <range/v3/view/take_last.hpp>
36 
37 using namespace solidity;
38 using namespace solidity::yul;
39 using namespace std;
40 
run(AbstractAssembly & _assembly,AsmAnalysisInfo & _analysisInfo,Block const & _block,EVMDialect const & _dialect,BuiltinContext & _builtinContext,UseNamedLabels _useNamedLabelsForFunctions)41 vector<StackTooDeepError> OptimizedEVMCodeTransform::run(
42 	AbstractAssembly& _assembly,
43 	AsmAnalysisInfo& _analysisInfo,
44 	Block const& _block,
45 	EVMDialect const& _dialect,
46 	BuiltinContext& _builtinContext,
47 	UseNamedLabels _useNamedLabelsForFunctions
48 )
49 {
50 	std::unique_ptr<CFG> dfg = ControlFlowGraphBuilder::build(_analysisInfo, _dialect, _block);
51 	StackLayout stackLayout = StackLayoutGenerator::run(*dfg);
52 	OptimizedEVMCodeTransform optimizedCodeTransform(
53 		_assembly,
54 		_builtinContext,
55 		_useNamedLabelsForFunctions,
56 		*dfg,
57 		stackLayout
58 	);
59 	// Create initial entry layout.
60 	optimizedCodeTransform.createStackLayout(debugDataOf(*dfg->entry), stackLayout.blockInfos.at(dfg->entry).entryLayout);
61 	optimizedCodeTransform(*dfg->entry);
62 	for (Scope::Function const* function: dfg->functions)
63 		optimizedCodeTransform(dfg->functionInfo.at(function));
64 	return move(optimizedCodeTransform.m_stackErrors);
65 }
66 
operator ()(CFG::FunctionCall const & _call)67 void OptimizedEVMCodeTransform::operator()(CFG::FunctionCall const& _call)
68 {
69 	// Validate stack.
70 	{
71 		yulAssert(m_assembly.stackHeight() == static_cast<int>(m_stack.size()), "");
72 		yulAssert(m_stack.size() >= _call.function.get().arguments.size() + 1, "");
73 		// Assert that we got the correct arguments on stack for the call.
74 		for (auto&& [arg, slot]: ranges::zip_view(
75 			_call.functionCall.get().arguments | ranges::views::reverse,
76 			m_stack | ranges::views::take_last(_call.functionCall.get().arguments.size())
77 		))
78 			validateSlot(slot, arg);
79 		// Assert that we got the correct return label on stack.
80 		auto const* returnLabelSlot = get_if<FunctionCallReturnLabelSlot>(
81 			&m_stack.at(m_stack.size() - _call.functionCall.get().arguments.size() - 1)
82 		);
83 		yulAssert(returnLabelSlot && &returnLabelSlot->call.get() == &_call.functionCall.get(), "");
84 	}
85 
86 	// Emit code.
87 	{
88 		m_assembly.setSourceLocation(originLocationOf(_call));
89 		m_assembly.appendJumpTo(
90 			getFunctionLabel(_call.function),
91 			static_cast<int>(_call.function.get().returns.size() - _call.function.get().arguments.size()) - 1,
92 			AbstractAssembly::JumpType::IntoFunction
93 		);
94 		m_assembly.appendLabel(m_returnLabels.at(&_call.functionCall.get()));
95 	}
96 
97 	// Update stack.
98 	{
99 		// Remove arguments and return label from m_stack.
100 		for (size_t i = 0; i < _call.function.get().arguments.size() + 1; ++i)
101 			m_stack.pop_back();
102 		// Push return values to m_stack.
103 		for (size_t index: ranges::views::iota(0u, _call.function.get().returns.size()))
104 			m_stack.emplace_back(TemporarySlot{_call.functionCall, index});
105 		yulAssert(m_assembly.stackHeight() == static_cast<int>(m_stack.size()), "");
106 	}
107 }
108 
operator ()(CFG::BuiltinCall const & _call)109 void OptimizedEVMCodeTransform::operator()(CFG::BuiltinCall const& _call)
110 {
111 	// Validate stack.
112 	{
113 		yulAssert(m_assembly.stackHeight() == static_cast<int>(m_stack.size()), "");
114 		yulAssert(m_stack.size() >= _call.arguments, "");
115 		// Assert that we got a correct stack for the call.
116 		for (auto&& [arg, slot]: ranges::zip_view(
117 			_call.functionCall.get().arguments |
118 			ranges::views::enumerate |
119 			ranges::views::filter(util::mapTuple([&](size_t idx, auto&) -> bool {
120 				return !_call.builtin.get().literalArgument(idx);
121 			})) |
122 			ranges::views::reverse |
123 			ranges::views::values,
124 			m_stack | ranges::views::take_last(_call.arguments)
125 		))
126 			validateSlot(slot, arg);
127 	}
128 
129 	// Emit code.
130 	{
131 		m_assembly.setSourceLocation(originLocationOf(_call));
132 		static_cast<BuiltinFunctionForEVM const&>(_call.builtin.get()).generateCode(
133 			_call.functionCall,
134 			m_assembly,
135 			m_builtinContext
136 		);
137 	}
138 
139 	// Update stack.
140 	{
141 		// Remove arguments from m_stack.
142 		for (size_t i = 0; i < _call.arguments; ++i)
143 			m_stack.pop_back();
144 		// Push return values to m_stack.
145 		for (size_t index: ranges::views::iota(0u, _call.builtin.get().returns.size()))
146 			m_stack.emplace_back(TemporarySlot{_call.functionCall, index});
147 		yulAssert(m_assembly.stackHeight() == static_cast<int>(m_stack.size()), "");
148 	}
149 }
150 
operator ()(CFG::Assignment const & _assignment)151 void OptimizedEVMCodeTransform::operator()(CFG::Assignment const& _assignment)
152 {
153 	yulAssert(m_assembly.stackHeight() == static_cast<int>(m_stack.size()), "");
154 
155 	// Invalidate occurrences of the assigned variables.
156 	for (auto& currentSlot: m_stack)
157 		if (VariableSlot const* varSlot = get_if<VariableSlot>(&currentSlot))
158 			if (util::contains(_assignment.variables, *varSlot))
159 				currentSlot = JunkSlot{};
160 
161 	// Assign variables to current stack top.
162 	yulAssert(m_stack.size() >= _assignment.variables.size(), "");
163 	for (auto&& [currentSlot, varSlot]: ranges::zip_view(
164 		m_stack | ranges::views::take_last(_assignment.variables.size()),
165 		_assignment.variables
166 	))
167 		currentSlot = varSlot;
168 }
169 
OptimizedEVMCodeTransform(AbstractAssembly & _assembly,BuiltinContext & _builtinContext,UseNamedLabels _useNamedLabelsForFunctions,CFG const & _dfg,StackLayout const & _stackLayout)170 OptimizedEVMCodeTransform::OptimizedEVMCodeTransform(
171 	AbstractAssembly& _assembly,
172 	BuiltinContext& _builtinContext,
173 	UseNamedLabels _useNamedLabelsForFunctions,
174 	CFG const& _dfg,
175 	StackLayout const& _stackLayout
176 ):
177 	m_assembly(_assembly),
178 	m_builtinContext(_builtinContext),
179 	m_dfg(_dfg),
180 	m_stackLayout(_stackLayout),
181 	m_functionLabels([&](){
182 		map<CFG::FunctionInfo const*, AbstractAssembly::LabelID> functionLabels;
183 		set<YulString> assignedFunctionNames;
184 		for (Scope::Function const* function: m_dfg.functions)
185 		{
186 			CFG::FunctionInfo const& functionInfo = m_dfg.functionInfo.at(function);
187 			bool nameAlreadySeen = !assignedFunctionNames.insert(function->name).second;
188 			if (_useNamedLabelsForFunctions == UseNamedLabels::YesAndForceUnique)
189 				yulAssert(!nameAlreadySeen);
190 			bool useNamedLabel = _useNamedLabelsForFunctions != UseNamedLabels::Never && !nameAlreadySeen;
191 			functionLabels[&functionInfo] = useNamedLabel ?
192 				m_assembly.namedLabel(
193 					function->name.str(),
194 					function->arguments.size(),
195 					function->returns.size(),
196 					functionInfo.debugData ? functionInfo.debugData->astID : nullopt
197 				) :
198 				m_assembly.newLabelId();
199 		}
200 		return functionLabels;
201 	}())
202 {
203 }
204 
assertLayoutCompatibility(Stack const & _currentStack,Stack const & _desiredStack)205 void OptimizedEVMCodeTransform::assertLayoutCompatibility(Stack const& _currentStack, Stack const& _desiredStack)
206 {
207 	yulAssert(_currentStack.size() == _desiredStack.size(), "");
208 	for (auto&& [currentSlot, desiredSlot]: ranges::zip_view(_currentStack, _desiredStack))
209 		yulAssert(holds_alternative<JunkSlot>(desiredSlot) || currentSlot == desiredSlot, "");
210 }
211 
getFunctionLabel(Scope::Function const & _function)212 AbstractAssembly::LabelID OptimizedEVMCodeTransform::getFunctionLabel(Scope::Function const& _function)
213 {
214 	return m_functionLabels.at(&m_dfg.functionInfo.at(&_function));
215 }
216 
validateSlot(StackSlot const & _slot,Expression const & _expression)217 void OptimizedEVMCodeTransform::validateSlot(StackSlot const& _slot, Expression const& _expression)
218 {
219 	std::visit(util::GenericVisitor{
220 		[&](yul::Literal const& _literal) {
221 			auto* literalSlot = get_if<LiteralSlot>(&_slot);
222 			yulAssert(literalSlot && valueOfLiteral(_literal) == literalSlot->value, "");
223 		},
224 		[&](yul::Identifier const& _identifier) {
225 			auto* variableSlot = get_if<VariableSlot>(&_slot);
226 			yulAssert(variableSlot && variableSlot->variable.get().name == _identifier.name, "");
227 		},
228 		[&](yul::FunctionCall const& _call) {
229 			auto* temporarySlot = get_if<TemporarySlot>(&_slot);
230 			yulAssert(temporarySlot && &temporarySlot->call.get() == &_call && temporarySlot->index == 0, "");
231 		}
232 	}, _expression);
233 }
234 
createStackLayout(std::shared_ptr<DebugData const> _debugData,Stack _targetStack)235 void OptimizedEVMCodeTransform::createStackLayout(std::shared_ptr<DebugData const> _debugData, Stack _targetStack)
236 {
237 	static constexpr auto slotVariableName = [](StackSlot const& _slot) {
238 		return std::visit(util::GenericVisitor{
239 			[](VariableSlot const& _var) { return _var.variable.get().name; },
240 			[](auto const&) { return YulString{}; }
241 		}, _slot);
242 	};
243 
244 	yulAssert(m_assembly.stackHeight() == static_cast<int>(m_stack.size()), "");
245 	// ::createStackLayout asserts that it has successfully achieved the target layout.
246 	langutil::SourceLocation sourceLocation = _debugData ? _debugData->originLocation : langutil::SourceLocation{};
247 	m_assembly.setSourceLocation(sourceLocation);
248 	::createStackLayout(
249 		m_stack,
250 		_targetStack | ranges::to<Stack>,
251 		// Swap callback.
252 		[&](unsigned _i)
253 		{
254 			yulAssert(static_cast<int>(m_stack.size()) == m_assembly.stackHeight(), "");
255 			yulAssert(_i > 0 && _i < m_stack.size(), "");
256 			if (_i <= 16)
257 				m_assembly.appendInstruction(evmasm::swapInstruction(_i));
258 			else
259 			{
260 				int deficit = static_cast<int>(_i) - 16;
261 				StackSlot const& deepSlot = m_stack.at(m_stack.size() - _i - 1);
262 				YulString varNameDeep = slotVariableName(deepSlot);
263 				YulString varNameTop = slotVariableName(m_stack.back());
264 				string msg =
265 					"Cannot swap " + (varNameDeep.empty() ? "Slot " + stackSlotToString(deepSlot) : "Variable " + varNameDeep.str()) +
266 					" with " + (varNameTop.empty() ? "Slot " + stackSlotToString(m_stack.back()) : "Variable " + varNameTop.str()) +
267 					": too deep in the stack by " + to_string(deficit) + " slots in " + stackToString(m_stack);
268 				m_stackErrors.emplace_back(StackTooDeepError(
269 					m_currentFunctionInfo ? m_currentFunctionInfo->function.name : YulString{},
270 					varNameDeep.empty() ? varNameTop : varNameDeep,
271 					deficit,
272 					msg
273 				));
274 				m_assembly.markAsInvalid();
275 			}
276 		},
277 		// Push or dup callback.
278 		[&](StackSlot const& _slot)
279 		{
280 			yulAssert(static_cast<int>(m_stack.size()) == m_assembly.stackHeight(), "");
281 
282 			// Dup the slot, if already on stack and reachable.
283 			if (auto depth = util::findOffset(m_stack | ranges::views::reverse, _slot))
284 			{
285 				if (*depth < 16)
286 				{
287 					m_assembly.appendInstruction(evmasm::dupInstruction(static_cast<unsigned>(*depth + 1)));
288 					return;
289 				}
290 				else if (!canBeFreelyGenerated(_slot))
291 				{
292 					int deficit = static_cast<int>(*depth - 15);
293 					YulString varName = slotVariableName(_slot);
294 					string msg =
295 						(varName.empty() ? "Slot " + stackSlotToString(_slot) : "Variable " + varName.str())
296 						+ " is " + to_string(*depth - 15) + " too deep in the stack " + stackToString(m_stack);
297 					m_stackErrors.emplace_back(StackTooDeepError(
298 						m_currentFunctionInfo ? m_currentFunctionInfo->function.name : YulString{},
299 						varName,
300 						deficit,
301 						msg
302 					));
303 					m_assembly.markAsInvalid();
304 					m_assembly.appendConstant(u256(0xCAFFEE));
305 					return;
306 				}
307 				// else: the slot is too deep in stack, but can be freely generated, we fall through to push it again.
308 			}
309 
310 			// The slot can be freely generated or is an unassigned return variable. Push it.
311 			std::visit(util::GenericVisitor{
312 				[&](LiteralSlot const& _literal)
313 				{
314 					m_assembly.setSourceLocation(originLocationOf(_literal));
315 					m_assembly.appendConstant(_literal.value);
316 					m_assembly.setSourceLocation(sourceLocation);
317 				},
318 				[&](FunctionReturnLabelSlot const&)
319 				{
320 					yulAssert(false, "Cannot produce function return label.");
321 				},
322 				[&](FunctionCallReturnLabelSlot const& _returnLabel)
323 				{
324 					if (!m_returnLabels.count(&_returnLabel.call.get()))
325 						m_returnLabels[&_returnLabel.call.get()] = m_assembly.newLabelId();
326 					m_assembly.setSourceLocation(originLocationOf(_returnLabel.call.get()));
327 					m_assembly.appendLabelReference(m_returnLabels.at(&_returnLabel.call.get()));
328 					m_assembly.setSourceLocation(sourceLocation);
329 				},
330 				[&](VariableSlot const& _variable)
331 				{
332 					if (m_currentFunctionInfo && util::contains(m_currentFunctionInfo->returnVariables, _variable))
333 					{
334 						m_assembly.setSourceLocation(originLocationOf(_variable));
335 						m_assembly.appendConstant(0);
336 						m_assembly.setSourceLocation(sourceLocation);
337 						return;
338 					}
339 					yulAssert(false, "Variable not found on stack.");
340 				},
341 				[&](TemporarySlot const&)
342 				{
343 					yulAssert(false, "Function call result requested, but not found on stack.");
344 				},
345 				[&](JunkSlot const&)
346 				{
347 					// Note: this will always be popped, so we can push anything.
348 					m_assembly.appendInstruction(evmasm::Instruction::CODESIZE);
349 				}
350 			}, _slot);
351 		},
352 		// Pop callback.
353 		[&]()
354 		{
355 			m_assembly.appendInstruction(evmasm::Instruction::POP);
356 		}
357 	);
358 	yulAssert(m_assembly.stackHeight() == static_cast<int>(m_stack.size()), "");
359 }
360 
operator ()(CFG::BasicBlock const & _block)361 void OptimizedEVMCodeTransform::operator()(CFG::BasicBlock const& _block)
362 {
363 	// Assert that this is the first visit of the block and mark as generated.
364 	yulAssert(m_generated.insert(&_block).second, "");
365 
366 	m_assembly.setSourceLocation(originLocationOf(_block));
367 	auto const& blockInfo = m_stackLayout.blockInfos.at(&_block);
368 
369 	// Assert that the stack is valid for entering the block.
370 	assertLayoutCompatibility(m_stack, blockInfo.entryLayout);
371 	m_stack = blockInfo.entryLayout; // Might set some slots to junk, if not required by the block.
372 	yulAssert(static_cast<int>(m_stack.size()) == m_assembly.stackHeight(), "");
373 
374 	// Emit jump label, if required.
375 	if (auto label = util::valueOrNullptr(m_blockLabels, &_block))
376 		m_assembly.appendLabel(*label);
377 
378 	for (auto const& operation: _block.operations)
379 	{
380 		// Create required layout for entering the operation.
381 		createStackLayout(debugDataOf(operation.operation), m_stackLayout.operationEntryLayout.at(&operation));
382 
383 		// Assert that we have the inputs of the operation on stack top.
384 		yulAssert(static_cast<int>(m_stack.size()) == m_assembly.stackHeight(), "");
385 		yulAssert(m_stack.size() >= operation.input.size(), "");
386 		size_t baseHeight = m_stack.size() - operation.input.size();
387 		assertLayoutCompatibility(
388 			m_stack | ranges::views::take_last(operation.input.size()) | ranges::to<Stack>,
389 			operation.input
390 		);
391 
392 		// Perform the operation.
393 		std::visit(*this, operation.operation);
394 
395 		// Assert that the operation produced its proclaimed output.
396 		yulAssert(static_cast<int>(m_stack.size()) == m_assembly.stackHeight(), "");
397 		yulAssert(m_stack.size() == baseHeight + operation.output.size(), "");
398 		yulAssert(m_stack.size() >= operation.output.size(), "");
399 		assertLayoutCompatibility(
400 			m_stack | ranges::views::take_last(operation.output.size()) | ranges::to<Stack>,
401 			operation.output
402 		);
403 	}
404 
405 	// Exit the block.
406 	m_assembly.setSourceLocation(originLocationOf(_block));
407 	std::visit(util::GenericVisitor{
408 		[&](CFG::BasicBlock::MainExit const&)
409 		{
410 			m_assembly.appendInstruction(evmasm::Instruction::STOP);
411 		},
412 		[&](CFG::BasicBlock::Jump const& _jump)
413 		{
414 			// Create the stack expected at the jump target.
415 			createStackLayout(debugDataOf(_jump), m_stackLayout.blockInfos.at(_jump.target).entryLayout);
416 
417 			// If this is the only jump to the block, we do not need a label and can directly continue with the target block.
418 			if (!m_blockLabels.count(_jump.target) && _jump.target->entries.size() == 1)
419 			{
420 				yulAssert(!_jump.backwards, "");
421 				(*this)(*_jump.target);
422 			}
423 			else
424 			{
425 				// Generate a jump label for the target, if not already present.
426 				if (!m_blockLabels.count(_jump.target))
427 					m_blockLabels[_jump.target] = m_assembly.newLabelId();
428 
429 				// If we already have generated the target block, jump to it, otherwise generate it in place.
430 				if (m_generated.count(_jump.target))
431 					m_assembly.appendJumpTo(m_blockLabels[_jump.target]);
432 				else
433 					(*this)(*_jump.target);
434 			}
435 		},
436 		[&](CFG::BasicBlock::ConditionalJump const& _conditionalJump)
437 		{
438 			// Create the shared entry layout of the jump targets, which is stored as exit layout of the current block.
439 			createStackLayout(debugDataOf(_conditionalJump), blockInfo.exitLayout);
440 
441 			// Create labels for the targets, if not already present.
442 			if (!m_blockLabels.count(_conditionalJump.nonZero))
443 				m_blockLabels[_conditionalJump.nonZero] = m_assembly.newLabelId();
444 			if (!m_blockLabels.count(_conditionalJump.zero))
445 				m_blockLabels[_conditionalJump.zero] = m_assembly.newLabelId();
446 
447 			// Assert that we have the correct condition on stack.
448 			yulAssert(!m_stack.empty(), "");
449 			yulAssert(m_stack.back() == _conditionalJump.condition, "");
450 
451 			// Emit the conditional jump to the non-zero label and update the stored stack.
452 			m_assembly.appendJumpToIf(m_blockLabels[_conditionalJump.nonZero]);
453 			m_stack.pop_back();
454 
455 			// Assert that we have a valid stack for both jump targets.
456 			assertLayoutCompatibility(m_stack, m_stackLayout.blockInfos.at(_conditionalJump.nonZero).entryLayout);
457 			assertLayoutCompatibility(m_stack, m_stackLayout.blockInfos.at(_conditionalJump.zero).entryLayout);
458 
459 			{
460 				// Restore the stack afterwards for the non-zero case below.
461 				ScopeGuard stackRestore([storedStack = m_stack, this]() {
462 					m_stack = move(storedStack);
463 					m_assembly.setStackHeight(static_cast<int>(m_stack.size()));
464 				});
465 
466 				// If we have already generated the zero case, jump to it, otherwise generate it in place.
467 				if (m_generated.count(_conditionalJump.zero))
468 					m_assembly.appendJumpTo(m_blockLabels[_conditionalJump.zero]);
469 				else
470 					(*this)(*_conditionalJump.zero);
471 			}
472 			// Note that each block visit terminates control flow, so we cannot fall through from the zero case.
473 
474 			// Generate the non-zero block, if not done already.
475 			if (!m_generated.count(_conditionalJump.nonZero))
476 				(*this)(*_conditionalJump.nonZero);
477 		},
478 		[&](CFG::BasicBlock::FunctionReturn const& _functionReturn)
479 		{
480 			yulAssert(m_currentFunctionInfo, "");
481 			yulAssert(m_currentFunctionInfo == _functionReturn.info, "");
482 
483 			// Construct the function return layout, which is fully determined by the function signature.
484 			Stack exitStack = m_currentFunctionInfo->returnVariables | ranges::views::transform([](auto const& _varSlot){
485 				return StackSlot{_varSlot};
486 			}) | ranges::to<Stack>;
487 			exitStack.emplace_back(FunctionReturnLabelSlot{_functionReturn.info->function});
488 
489 			// Create the function return layout and jump.
490 			createStackLayout(debugDataOf(_functionReturn), exitStack);
491 			m_assembly.appendJump(0, AbstractAssembly::JumpType::OutOfFunction);
492 		},
493 		[&](CFG::BasicBlock::Terminated const&)
494 		{
495 			// Assert that the last builtin call was in fact terminating.
496 			yulAssert(!_block.operations.empty(), "");
497 			CFG::BuiltinCall const* builtinCall = get_if<CFG::BuiltinCall>(&_block.operations.back().operation);
498 			yulAssert(builtinCall, "");
499 			yulAssert(builtinCall->builtin.get().controlFlowSideEffects.terminatesOrReverts(), "");
500 		}
501 	}, _block.exit);
502 	// TODO: We could assert that the last emitted assembly item terminated or was an (unconditional) jump.
503 	//       But currently AbstractAssembly does not allow peeking at the last emitted assembly item.
504 	m_stack.clear();
505 	m_assembly.setStackHeight(0);
506 }
507 
operator ()(CFG::FunctionInfo const & _functionInfo)508 void OptimizedEVMCodeTransform::operator()(CFG::FunctionInfo const& _functionInfo)
509 {
510 	yulAssert(!m_currentFunctionInfo, "");
511 	ScopedSaveAndRestore currentFunctionInfoRestore(m_currentFunctionInfo, &_functionInfo);
512 
513 	yulAssert(m_stack.empty() && m_assembly.stackHeight() == 0, "");
514 
515 	// Create function entry layout in m_stack.
516 	m_stack.emplace_back(FunctionReturnLabelSlot{_functionInfo.function});
517 	for (auto const& param: _functionInfo.parameters | ranges::views::reverse)
518 		m_stack.emplace_back(param);
519 	m_assembly.setStackHeight(static_cast<int>(m_stack.size()));
520 
521 	m_assembly.setSourceLocation(originLocationOf(_functionInfo));
522 	m_assembly.appendLabel(getFunctionLabel(_functionInfo.function));
523 
524 	// Create the entry layout of the function body block and visit.
525 	createStackLayout(debugDataOf(_functionInfo), m_stackLayout.blockInfos.at(_functionInfo.entry).entryLayout);
526 	(*this)(*_functionInfo.entry);
527 
528 	m_stack.clear();
529 	m_assembly.setStackHeight(0);
530 }
531