2020 */
2121
2222#include < libyul/backends/evm/SSAControlFlowGraphBuilder.h>
23- #include < libyul/AST.h>
24- #include < libyul/Exceptions.h>
23+
2524#include < libyul/backends/evm/ControlFlow.h>
25+ #include < libyul/AST.h>
2626#include < libyul/ControlFlowSideEffectsCollector.h>
27+ #include < libyul/Exceptions.h>
2728#include < libyul/Utilities.h>
2829
2930#include < libsolutil/Algorithms.h>
3031#include < libsolutil/StringUtils.h>
3132#include < libsolutil/Visitor.h>
3233
34+ #include < range/v3/algorithm/replace.hpp>
3335#include < range/v3/range/conversion.hpp>
3436#include < range/v3/view/drop_last.hpp>
3537#include < range/v3/view/enumerate.hpp>
@@ -49,26 +51,29 @@ SSAControlFlowGraphBuilder::SSAControlFlowGraphBuilder(
4951 SSACFG& _graph,
5052 AsmAnalysisInfo const & _analysisInfo,
5153 ControlFlowSideEffectsCollector const & _sideEffects,
52- Dialect const & _dialect
54+ Dialect const & _dialect,
55+ bool _keepLiteralAssignments
5356):
5457 m_controlFlow (_controlFlow),
5558 m_graph (_graph),
5659 m_info (_analysisInfo),
5760 m_sideEffects (_sideEffects),
58- m_dialect (_dialect)
61+ m_dialect (_dialect),
62+ m_keepLiteralAssignments (_keepLiteralAssignments)
5963{
6064}
6165
6266std::unique_ptr<ControlFlow> SSAControlFlowGraphBuilder::build (
6367 AsmAnalysisInfo const & _analysisInfo,
6468 Dialect const & _dialect,
65- Block const & _block
69+ Block const & _block,
70+ bool _keepLiteralAssignments
6671)
6772{
6873 ControlFlowSideEffectsCollector sideEffects (_dialect, _block);
6974
7075 auto controlFlow = std::make_unique<ControlFlow>();
71- SSAControlFlowGraphBuilder builder (*controlFlow, *controlFlow->mainGraph , _analysisInfo, sideEffects, _dialect);
76+ SSAControlFlowGraphBuilder builder (*controlFlow, *controlFlow->mainGraph , _analysisInfo, sideEffects, _dialect, _keepLiteralAssignments );
7277 builder.m_currentBlock = controlFlow->mainGraph ->makeBlock (debugDataOf (_block));
7378 builder.sealBlock (builder.m_currentBlock );
7479 builder (_block);
@@ -147,8 +152,8 @@ SSACFG::ValueId SSAControlFlowGraphBuilder::tryRemoveTrivialPhi(SSACFG::ValueId
147152 [](SSACFG::BasicBlock::Terminated&) {}
148153 }, block.exit );
149154 }
150- for (auto & [_, currentVariableDefs] : m_currentDef)
151- std ::replace (currentVariableDefs. begin (), currentVariableDefs. end () , _phi, same);
155+ for (auto & currentVariableDefs: m_currentDef | ranges::views::values )
156+ ranges ::replace (currentVariableDefs, _phi, same);
152157
153158 for (auto phiUse: phiUses)
154159 tryRemoveTrivialPhi (phiUse);
@@ -178,11 +183,6 @@ void SSAControlFlowGraphBuilder::cleanUnreachable()
178183 }, block.exit );
179184 });
180185
181- auto isUnreachableValue = [&](SSACFG::ValueId const & _value) -> bool {
182- auto * valueInfo = std::get_if<SSACFG::UnreachableValue>(&m_graph.valueInfo (_value));
183- return (valueInfo) ? true : false ;
184- };
185-
186186 // Remove all entries from unreachable nodes from the graph.
187187 for (SSACFG::BlockId blockId: reachabilityCheck.visited )
188188 {
@@ -197,7 +197,7 @@ void SSAControlFlowGraphBuilder::cleanUnreachable()
197197 for (auto phi: block.phis )
198198 if (auto * phiInfo = std::get_if<SSACFG::PhiValue>(&m_graph.valueInfo (phi)))
199199 std::erase_if (phiInfo->arguments , [&](SSACFG::ValueId _arg) {
200- if (isUnreachableValue ( _arg))
200+ if (std::holds_alternative<SSACFG::UnreachableValue>(m_graph. valueInfo ( _arg) ))
201201 {
202202 maybeTrivialPhi.insert (phi);
203203 return true ;
@@ -240,7 +240,7 @@ void SSAControlFlowGraphBuilder::buildFunctionGraph(
240240 cfg.arguments = arguments;
241241 cfg.returns = returns;
242242
243- SSAControlFlowGraphBuilder builder (m_controlFlow, cfg, m_info, m_sideEffects, m_dialect);
243+ SSAControlFlowGraphBuilder builder (m_controlFlow, cfg, m_info, m_sideEffects, m_dialect, m_keepLiteralAssignments );
244244 builder.m_currentBlock = cfg.entry ;
245245 builder.m_functionDefinitions = m_functionDefinitions;
246246 for (auto && [var, varId]: cfg.arguments )
@@ -273,6 +273,11 @@ void SSAControlFlowGraphBuilder::operator()(Assignment const& _assignment)
273273
274274void SSAControlFlowGraphBuilder::operator ()(VariableDeclaration const & _variableDeclaration)
275275{
276+ // if we have no value (like in `let a` without right-hand side), we can just skip this. the variable(s) will be
277+ // added when first needed
278+ if (!_variableDeclaration.value )
279+ return ;
280+
276281 assign (
277282 _variableDeclaration.variables | ranges::views::transform ([&](auto & _var) { return std::ref (lookupVariable (_var.name )); }) | ranges::to<std::vector>,
278283 _variableDeclaration.value .get ()
@@ -533,15 +538,27 @@ void SSAControlFlowGraphBuilder::assign(std::vector<std::reference_wrapper<Scope
533538 auto rhs = [&]() -> std::vector<SSACFG::ValueId> {
534539 if (auto const * functionCall = std::get_if<FunctionCall>(_expression))
535540 return visitFunctionCall (*functionCall);
536- else if (_expression)
541+ if (_expression)
537542 return {std::visit (*this , *_expression)};
538- else
539- return {_variables.size (), zero ()};
543+ return {_variables.size (), zero ()};
540544 }();
541545 yulAssert (rhs.size () == _variables.size ());
542546
543547 for (auto const & [var, value]: ranges::zip_view (_variables, rhs))
544- writeVariable (var, m_currentBlock, value);
548+ {
549+ if (m_keepLiteralAssignments && m_graph.isLiteralValue (value))
550+ {
551+ SSACFG::Operation assignment{
552+ .outputs = {m_graph.newVariable (m_currentBlock)},
553+ .kind = SSACFG::LiteralAssignment{},
554+ .inputs = {value}
555+ };
556+ currentBlock ().operations .emplace_back (assignment);
557+ writeVariable (var, m_currentBlock, assignment.outputs .back ());
558+ }
559+ else
560+ writeVariable (var, m_currentBlock, value);
561+ }
545562
546563}
547564
@@ -610,7 +627,7 @@ SSACFG::ValueId SSAControlFlowGraphBuilder::readVariableRecursive(Scope::Variabl
610627 // incomplete block
611628 val = m_graph.newPhi (_block);
612629 block.phis .insert (val);
613- info.incompletePhis .emplace_back (val, std::ref ( _variable) );
630+ info.incompletePhis .emplace_back (val, _variable);
614631 }
615632 else if (block.entries .size () == 1 )
616633 // one predecessor: no phi needed
@@ -633,7 +650,7 @@ SSACFG::ValueId SSAControlFlowGraphBuilder::addPhiOperands(Scope::Variable const
633650{
634651 yulAssert (std::holds_alternative<SSACFG::PhiValue>(m_graph.valueInfo (_phi)));
635652 auto & phi = std::get<SSACFG::PhiValue>(m_graph.valueInfo (_phi));
636- for (auto pred: m_graph.block (phi.block ).entries )
653+ for (auto const & pred: m_graph.block (phi.block ).entries )
637654 phi.arguments .emplace_back (readVariable (_variable, pred));
638655 // we call tryRemoveTrivialPhi explicitly to avoid removing trivial phis in unsealed blocks
639656 return _phi;
@@ -660,14 +677,14 @@ Scope::Variable const& SSAControlFlowGraphBuilder::lookupVariable(YulName _name)
660677 yulAssert (m_scope, " " );
661678 Scope::Variable const * var = nullptr ;
662679 if (m_scope->lookup (_name, util::GenericVisitor{
663- [&](Scope::Variable& _var) { var = &_var; },
664- [](Scope::Function&)
680+ [&](Scope::Variable const & _var) { var = &_var; },
681+ [](Scope::Function const &)
665682 {
666683 yulAssert (false , " Function not removed during desugaring." );
667684 }
668685 }))
669686 {
670- yulAssert (var, " " );
687+ yulAssert (var);
671688 return *var;
672689 };
673690 yulAssert (false , " External identifier access unimplemented." );
0 commit comments