source: icGREP/icgrep-devel/icgrep/pablo/passes/flattenassociativedfg.cpp @ 4899

Last change on this file since 4899 was 4899, checked in by nmedfort, 3 years ago

Work on lowering + minor bug fixes.

File size: 25.5 KB
Line 
1#include "flattenassociativedfg.h"
2#include <pablo/codegenstate.h>
3#include <pablo/optimizers/pablo_simplifier.hpp>
4#include <pablo/analysis/pabloverifier.hpp>
5#include <boost/container/flat_set.hpp>
6#include <boost/container/flat_map.hpp>
7#include <boost/graph/adjacency_list.hpp>
8#include <queue>
9
10using namespace boost;
11using namespace boost::container;
12
13namespace pablo {
14
15using TypeId = PabloAST::ClassTypeId;
16
17/** ------------------------------------------------------------------------------------------------------------- *
18 * @brief coalesce
19 ** ------------------------------------------------------------------------------------------------------------- */
20inline void FlattenAssociativeDFG::coalesce(Variadic * const var) {
21    const TypeId typeId = var->getClassTypeId();
22    for (unsigned i = 0; i < var->getNumOperands(); ) {
23        PabloAST * const op = var->getOperand(i);
24        if (op->getClassTypeId() == typeId) {
25            Variadic * removedVar = cast<Variadic>(var->removeOperand(i));
26            for (unsigned j = 0; j != cast<Variadic>(op)->getNumOperands(); ++j) {
27                var->addOperand(cast<Variadic>(op)->getOperand(j));
28            }
29            if (removedVar->getNumOperands() == 1) {
30                removedVar->replaceWith(removedVar->getOperand(0));
31            } else if (removedVar->getNumUses() == 0) {
32                removedVar->eraseFromParent(true);
33            }
34            continue;
35        }
36        ++i;
37    }
38}
39
40/** ------------------------------------------------------------------------------------------------------------- *
41 * @brief coalesce
42 ** ------------------------------------------------------------------------------------------------------------- */
43void FlattenAssociativeDFG::coalesce(PabloBlock * const block, const bool traverse) {
44    Statement * stmt = block->front();
45    while (stmt) {
46        Statement * next = stmt->getNextNode();
47        if (LLVM_UNLIKELY((isa<If>(stmt) || isa<While>(stmt)) && traverse)) {
48            coalesce(isa<If>(stmt) ? cast<If>(stmt)->getBody() : cast<While>(stmt)->getBody(), true);
49        } else if (isa<And>(stmt) || isa<Or>(stmt) || isa<Xor>(stmt)) {
50            coalesce(cast<Variadic>(stmt));
51        } else if (isa<Not>(stmt)) {
52            deMorgansExpansion(cast<Not>(stmt), block);
53        }
54        stmt = next;
55    }
56}
57
58/** ------------------------------------------------------------------------------------------------------------- *
59 * @brief deMorgansExpansion
60 *
61 * Apply the De Morgans' law to any negated And or Or statement with the intent of further coalescing its operands
62 * thereby allowing the Simplifier to check for tautologies and contradictions.
63 ** ------------------------------------------------------------------------------------------------------------- */
64inline void FlattenAssociativeDFG::deMorgansExpansion(Not * const var, PabloBlock * const block) {
65    PabloAST * const negatedVar = var->getOperand(0);
66    if (isa<And>(negatedVar) || isa<Or>(negatedVar)) {
67        Variadic * src = cast<Variadic>(negatedVar);
68        const unsigned operands = src->getNumOperands();
69        Variadic * replacement = nullptr;
70        block->setInsertPoint(var->getPrevNode());
71        if (isa<And>(negatedVar)) {
72            replacement = block->createOr(operands);
73        } else {
74            replacement = block->createAnd(operands);
75        }
76        block->setInsertPoint(replacement->getPrevNode());
77        for (unsigned i = 0; i != operands; ++i) {
78            replacement->addOperand(block->createNot(src->getOperand(i)));
79        }
80        coalesce(replacement);
81        var->replaceWith(replacement, true, true);
82    }
83}
84
85/** ------------------------------------------------------------------------------------------------------------- *
86 * @brief deMorgansReduction
87 ** ------------------------------------------------------------------------------------------------------------- */
88inline void FlattenAssociativeDFG::deMorgansReduction(Variadic * const var, PabloBlock * const block) {
89    unsigned negations = 0;
90    for (unsigned i = 0; i < var->getNumOperands(); ++i) {
91        if (isa<Not>(var->getOperand(i))) {
92            ++negations;
93        }
94    }
95    if (negations > 1) {
96        PabloAST * negated[negations];
97        for (unsigned i = var->getNumOperands(), j = negations; i && j; ) {
98            if (isa<Not>(var->getOperand(--i))) {
99                negated[--j] = cast<Not>(var->removeOperand(i))->getOperand(0);
100            }
101        }
102        block->setInsertPoint(var->getPrevNode());
103        Variadic * extractedVar = nullptr;
104        if (isa<And>(var)) {
105            extractedVar = block->createOr(negations);
106        } else { // if (isa<Or>(var)) {
107            extractedVar = block->createAnd(negations);
108        }
109        for (unsigned i = 0; i != negations; ++i) {
110            extractedVar->addOperand(negated[i]);
111        }
112        var->addOperand(block->createNot(extractedVar));
113    }
114}
115
116/** ------------------------------------------------------------------------------------------------------------- *
117 * @brief deMorgansReduction
118 ** ------------------------------------------------------------------------------------------------------------- */
119void FlattenAssociativeDFG::deMorgansReduction(PabloBlock * const block, const bool traverse) {
120    for (Statement * stmt : *block) {
121        if (LLVM_UNLIKELY((isa<If>(stmt) || isa<While>(stmt)) && traverse)) {
122            deMorgansReduction(isa<If>(stmt) ? cast<If>(stmt)->getBody() : cast<While>(stmt)->getBody(), true);
123        } else if (isa<And>(stmt) || isa<Or>(stmt)) {
124            deMorgansReduction(cast<Variadic>(stmt), block);
125        }
126    }
127}
128
129union VertexData {
130    Assign * def;
131    TypeId   typeId;
132    explicit VertexData() : def(nullptr) { }
133    explicit VertexData(Assign * def) : def(def) { }
134    explicit VertexData(TypeId typeId) : typeId(typeId) { }
135};
136using Graph = adjacency_list<vecS, vecS, bidirectionalS, VertexData, Variadic *>;
137using Vertex = Graph::vertex_descriptor;
138using SourceMap = flat_map<Assign *, Vertex>;
139using SinkMap = flat_map<TypeId, Vertex>;
140using VertexSet = std::vector<Vertex>;
141using Biclique = std::pair<VertexSet, VertexSet>;
142using BicliqueSet = std::vector<Biclique>;
143
144/** ------------------------------------------------------------------------------------------------------------- *
145 * @brief addToVariadicGraph
146 ** ------------------------------------------------------------------------------------------------------------- */
147static bool addToVariadicGraph(Assign * const def, Graph & G, SourceMap & A, SinkMap & B) {
148
149    if (LLVM_LIKELY(A.count(def) == 0)) {
150        // Test if its valid to transform this statement
151        for (PabloAST * user : def->users()) {
152            if (isa<Variadic>(user) == 0) {
153                if (isa<If>(user)) {
154                    if (LLVM_LIKELY(cast<If>(user)->getCondition() != def)) {
155                        continue;
156                    }
157                }
158                return false;
159            }
160        }
161
162        // Add the statement and its users to G
163        const Vertex u = add_vertex(VertexData(def), G);
164        A.emplace(def, u);
165        for (PabloAST * user : def->users()) {
166            if (isa<Variadic>(user)) {
167                auto f = B.find(user->getClassTypeId());
168                if (f == B.end()) {
169                    f = B.emplace(user->getClassTypeId(), add_vertex(VertexData(user->getClassTypeId()), G)).first;
170                }
171                assert (f != B.end());
172                G[add_edge(u, f->second, G).first] = cast<Variadic>(user);
173            }
174        }
175    }
176    return true;
177}
178
179/** ------------------------------------------------------------------------------------------------------------- *
180 * @brief matches
181 ** ------------------------------------------------------------------------------------------------------------- */
182inline static bool matches(const PabloAST * const a, const PabloAST * const b) {
183    return (isa<Assign>(b)) && (cast<Assign>(a)->getParent() == cast<Assign>(b)->getParent());
184}
185
186/** ------------------------------------------------------------------------------------------------------------- *
187 * @brief enumerateBicliques
188 *
189 * Adaptation of the MICA algorithm as described in "Consensus algorithms for the generation of all maximal
190 * bicliques" by Alexe et. al. (2003). Note: this implementation considers all verticies in set A to be in
191 * bipartition A and their adjacencies to be in B.
192  ** ------------------------------------------------------------------------------------------------------------- */
193static BicliqueSet enumerateBicliques(const Graph & G, const VertexSet & A) {
194    using IntersectionSets = std::set<VertexSet>;
195
196    IntersectionSets B1;
197    for (auto u : A) {
198        flat_set<Vertex> adj;
199        adj.reserve(out_degree(u, G));
200        for (auto e : make_iterator_range(out_edges(u, G))) {
201            adj.insert(target(e, G));
202        }
203        B1.emplace(adj.begin(), adj.end());
204    }
205
206    IntersectionSets B(B1);
207
208    IntersectionSets Bi;
209
210    VertexSet clique;
211    for (auto i = B1.begin(); i != B1.end(); ++i) {
212        for (auto j = i; ++j != B1.end(); ) {
213            std::set_intersection(i->begin(), i->end(), j->begin(), j->end(), std::back_inserter(clique));
214            if (clique.size() > 0) {
215                if (B.count(clique) == 0) {
216                    Bi.insert(clique);
217                }
218                clique.clear();
219            }
220        }
221    }
222
223    for (;;) {
224        if (Bi.empty()) {
225            break;
226        }
227        B.insert(Bi.begin(), Bi.end());
228        IntersectionSets Bk;
229        for (auto i = B1.begin(); i != B1.end(); ++i) {
230            for (auto j = Bi.begin(); j != Bi.end(); ++j) {
231                std::set_intersection(i->begin(), i->end(), j->begin(), j->end(), std::back_inserter(clique));
232                if (clique.size() > 0) {
233                    if (B.count(clique) == 0) {
234                        Bk.insert(clique);
235                    }
236                    clique.clear();
237                }
238            }
239        }
240        Bi.swap(Bk);
241    }
242
243    BicliqueSet S;
244    for (auto Bi = B.begin(); Bi != B.end(); ++Bi) {
245        VertexSet Ai(A);
246        for (const Vertex u : *Bi) {
247            VertexSet Aj;
248            Aj.reserve(in_degree(u, G));
249            for (auto e : make_iterator_range(in_edges(u, G))) {
250                Aj.push_back(source(e, G));
251            }
252            std::sort(Aj.begin(), Aj.end());
253            VertexSet Ak;
254            Ak.reserve(std::min(Ai.size(), Aj.size()));
255            std::set_intersection(Ai.begin(), Ai.end(), Aj.begin(), Aj.end(), std::back_inserter(Ak));
256            Ai.swap(Ak);
257        }
258        assert (Ai.size() > 0); // cannot happen if this algorithm is working correctly
259        // If |Ai| > |Bi|, removing Ai from of the Variadic and sinking it into the nested scope will
260        // reduce the number of values stored.
261        if (Ai.size() > Bi->size()) {
262            S.emplace_back(std::move(Ai), std::move(*Bi));
263        }
264    }
265    return S;
266}
267
268/** ------------------------------------------------------------------------------------------------------------- *
269 * @brief intersects
270 ** ------------------------------------------------------------------------------------------------------------- */
271template <class Type>
272inline bool intersects(const Type & A, const Type & B) {
273    auto first1 = A.begin(), last1 = A.end();
274    auto first2 = B.begin(), last2 = B.end();
275    while (first1 != last1 && first2 != last2) {
276        if (*first1 < *first2) {
277            ++first1;
278        } else if (*first2 < *first1) {
279            ++first2;
280        } else {
281            return true;
282        }
283    }
284    return false;
285}
286
287/** ------------------------------------------------------------------------------------------------------------- *
288 * @brief independentCliqueSets
289 ** ------------------------------------------------------------------------------------------------------------- */
290template <unsigned side>
291inline static BicliqueSet independentCliqueSets(BicliqueSet && cliques, const unsigned minimum) {
292    using IndependentSetGraph = adjacency_list<hash_setS, vecS, undirectedS, unsigned>;
293
294    const auto l = cliques.size();
295    IndependentSetGraph I(l);
296
297    // Initialize our weights
298    for (unsigned i = 0; i != l; ++i) {
299        I[i] = std::pow(std::get<side>(cliques[i]).size(), 2);
300    }
301
302    // Determine our constraints
303    for (unsigned i = 0; i != l; ++i) {
304        for (unsigned j = i + 1; j != l; ++j) {
305            if (intersects(std::get<side>(cliques[i]), std::get<side>(cliques[j]))) {
306                add_edge(i, j, I);
307            }
308        }
309    }
310
311    // Use the greedy algorithm to choose our independent set
312    VertexSet selected;
313    for (;;) {
314        unsigned w = 0;
315        Vertex u = 0;
316        for (unsigned i = 0; i != l; ++i) {
317            if (I[i] > w) {
318                w = I[i];
319                u = i;
320            }
321        }
322        if (w < minimum) break;
323        selected.push_back(u);
324        I[u] = 0;
325        for (auto v : make_iterator_range(adjacent_vertices(u, I))) {
326            I[v] = 0;
327        }
328    }
329
330    // Sort the selected list and then remove the unselected cliques
331    std::sort(selected.begin(), selected.end(), std::greater<Vertex>());
332    auto end = cliques.end();
333    for (const unsigned offset : selected) {
334        end = cliques.erase(cliques.begin() + offset + 1, end) - 1;
335    }
336    cliques.erase(cliques.begin(), end);
337
338    return std::move(cliques);
339}
340
341/** ------------------------------------------------------------------------------------------------------------- *
342 * @brief tryToPartiallyExtractVariadic
343 ** ------------------------------------------------------------------------------------------------------------- */
344inline void FlattenAssociativeDFG::tryToPartiallyExtractVariadic(Variadic * const var) {
345
346    for (unsigned i = 0; i < var->getNumOperands(); ++i) {
347        PabloAST * const op = var->getOperand(i);
348        if (isa<Assign>(op)) {
349
350            // Have we found a variadic operation that can sunk into a nested scope?
351            for (unsigned j = i + 1; j != var->getNumOperands(); ++j) {
352                bool invalid = false;
353                if (LLVM_UNLIKELY(matches(op, var->getOperand(j)))) {
354                    Graph G;
355                    SourceMap A;
356                    SinkMap B;
357                    if (addToVariadicGraph(cast<Assign>(op), G, A, B) == 0) {
358                        invalid = true;
359                        break;
360                    }
361                    addToVariadicGraph(cast<Assign>(var->getOperand(j)), G, A, B);
362                    for (++j; j != var->getNumOperands(); ++j) {
363                        if (LLVM_UNLIKELY(matches(op, var->getOperand(j)))) {
364                            addToVariadicGraph(cast<Assign>(var->getOperand(j)), G, A, B);
365                        }
366                    }
367
368                    if (A.size() > 1) {
369
370                        VertexSet H;
371                        H.reserve(A.size());
372                        for (auto a : A) {
373                            H.push_back(a.second);
374                        }
375
376                        const auto S = independentCliqueSets<0>(std::move(enumerateBicliques(G, H)), 2);
377                        assert (S.size() > 0);
378                        for (const Biclique & C : S) {
379                            const VertexSet & sources = std::get<0>(C);
380                            const VertexSet & variadics = std::get<1>(C);
381                            assert (variadics.size() > 0);
382                            assert (sources.size() > variadics.size());
383                            PabloBlock * const block = cast<Assign>(op)->getParent();
384                            block->setInsertPoint(block->back());
385                            for (const auto v : variadics) {
386                                Variadic * joiner = nullptr;
387                                switch (G[v].typeId) {
388                                    case TypeId::And:
389                                        joiner = block->createAnd(sources.size());
390                                        break;
391                                    case TypeId::Or:
392                                        joiner = block->createOr(sources.size());
393                                        break;
394                                    case TypeId::Xor:
395                                        joiner = block->createXor(sources.size());
396                                        break;
397                                    default: llvm_unreachable("Unexpected!");
398                                }
399                                assert (joiner);
400                                flat_set<Assign *> defs;
401                                for (const auto u : sources) {
402                                    defs.insert(G[u].def);
403                                }
404                                for (Assign * def : defs) {
405                                    joiner->addOperand(def->getOperand(0));                                   
406                                }
407
408                                coalesce(joiner);
409                                Assign * const joined = block->createAssign("m", joiner);
410
411                                for (Assign * def : defs) {
412                                    def->replaceWith(joined);
413                                    assert (def->getNumUses() == 0);
414                                }
415                            }
416                        }
417                        --i;
418                    }
419                    break;
420                }
421                if (invalid) {
422                    break;
423                }
424            }
425        }
426    }
427}
428
429/** ------------------------------------------------------------------------------------------------------------- *
430 * @brief tryToPartiallyExtractVariadic
431 ** ------------------------------------------------------------------------------------------------------------- */
432void FlattenAssociativeDFG::tryToPartiallyExtractVariadic(PabloBlock * const block) {
433    for (Statement * stmt = block->back(); stmt; stmt = stmt->getPrevNode()) {
434        if (LLVM_UNLIKELY(isa<If>(stmt) || isa<While>(stmt))) {
435            tryToPartiallyExtractVariadic(isa<If>(stmt) ? cast<If>(stmt)->getBody() : cast<While>(stmt)->getBody());
436        } else if (isa<Variadic>(stmt)) {
437            tryToPartiallyExtractVariadic(cast<Variadic>(stmt));
438        }
439    }
440}
441
442using ScopeDependencyGraph = adjacency_list<hash_setS, vecS, bidirectionalS, PabloAST *>;
443using ScopeDependencyMap = flat_map<PabloAST *, ScopeDependencyGraph::vertex_descriptor>;
444
445/** ------------------------------------------------------------------------------------------------------------- *
446 * @brief find
447 ** ------------------------------------------------------------------------------------------------------------- */
448inline ScopeDependencyGraph::vertex_descriptor find(PabloAST * expr, ScopeDependencyGraph & G, ScopeDependencyMap & M) {
449    auto f = M.find(expr);
450    if (f == M.end()) {
451        f = M.emplace(expr, add_vertex(expr, G)).first;
452    }
453    return f->second;
454}
455
456/** ------------------------------------------------------------------------------------------------------------- *
457 * @brief buildScopeDependencyGraph
458 ** ------------------------------------------------------------------------------------------------------------- */
459ScopeDependencyGraph::vertex_descriptor buildScopeDependencyGraph(Variadic * const var, ScopeDependencyGraph & G, ScopeDependencyMap & M) {
460    auto f = M.find(var);
461    if (f != M.end()) {
462        return f->second;
463    }
464    auto u = add_vertex(var, G);
465    M.emplace(var, u);
466    for (unsigned i = 0; i != var->getNumOperands(); ++i) {
467        PabloAST * expr = var->getOperand(i);
468        PabloAST * value = var;
469        while (isa<Assign>(expr)) {           
470            value = expr;
471            expr = cast<Assign>(expr)->getExpression();
472        }
473        if ((expr->getClassTypeId() == var->getClassTypeId()) && (expr->getNumUses() == 1)) {
474            const auto v = find(value, G, M);
475            add_edge(v, u, G);
476            add_edge(buildScopeDependencyGraph(cast<Variadic>(expr), G, M), v, G);
477        }
478    }
479    return u;
480}
481
482/** ------------------------------------------------------------------------------------------------------------- *
483 * @brief analyzeScopeDependencies
484 ** ------------------------------------------------------------------------------------------------------------- */
485inline void analyzeScopeDependencies(Assign * const def, ScopeDependencyGraph & G, ScopeDependencyMap & M) {
486    if (LLVM_LIKELY(isa<Variadic>(def->getExpression()))) {
487        buildScopeDependencyGraph(cast<Variadic>(def->getExpression()), G, M);
488    }
489}
490
491/** ------------------------------------------------------------------------------------------------------------- *
492 * @brief analyzeScopeDependencies
493 ** ------------------------------------------------------------------------------------------------------------- */
494void analyzeScopeDependencies(PabloBlock * const block, ScopeDependencyGraph & G, ScopeDependencyMap & M) {
495    for (Statement * stmt : *block) {
496        if (LLVM_UNLIKELY(isa<If>(stmt) || isa<While>(stmt))) {
497            analyzeScopeDependencies(isa<If>(stmt) ? cast<If>(stmt)->getBody() : cast<While>(stmt)->getBody(), G, M);
498        } else if (LLVM_UNLIKELY(isa<Assign>(stmt))) {
499            analyzeScopeDependencies(cast<Assign>(stmt), G, M);
500        }
501    }
502}
503
504/** ------------------------------------------------------------------------------------------------------------- *
505 * @brief removeDependenciesWithUnresolvedUses
506 ** ------------------------------------------------------------------------------------------------------------- */
507void removeDependenciesWithUnresolvedUses(ScopeDependencyGraph & G) {
508    for (auto u : make_iterator_range(vertices(G))) {
509        const PabloAST * const expr = G[u];
510        unsigned uses = 0;
511        if (isa<Assign>(expr)) {
512            for (const PabloAST * user : cast<Assign>(expr)->users()) {
513                if (!isa<If>(user) || cast<If>(user)->getCondition() == expr) {
514                    ++uses;
515                }
516            }
517        } else {
518            uses = expr->getNumUses();
519        }
520        if (uses != out_degree(u, G)) {
521            clear_out_edges(u, G);
522        }
523    }
524}
525
526/** ------------------------------------------------------------------------------------------------------------- *
527 * @brief eliminateUnecessaryDependencies
528 ** ------------------------------------------------------------------------------------------------------------- */
529void eliminateUnecessaryDependencies(ScopeDependencyGraph & G) {
530    using Vertex = ScopeDependencyGraph::vertex_descriptor;
531    std::vector<bool> visited(num_vertices(G), false);
532    std::queue<Vertex> Q;
533    for (auto u : make_iterator_range(vertices(G))) {
534        if (out_degree(u, G) == 0 && in_degree(u, G) != 0) {
535            Q.push(u);
536        }
537    }
538    while (Q.size() > 0) {
539        const auto u = Q.front(); Q.pop();
540        visited[u] = true;
541        for (auto e : make_iterator_range(in_edges(u, G))) {
542            bool usersHaveBeenVisited = true;
543            const auto v = source(e, G);
544            for (auto e : make_iterator_range(out_edges(v, G))) {
545                if (visited[target(e, G)] == 0) {
546                    usersHaveBeenVisited = false;
547                    break;
548                }
549            }
550            if (usersHaveBeenVisited) {
551                Q.push(v);
552                for (auto e : make_iterator_range(in_edges(u, G))) {
553                    const auto w = source(e, G);
554                    if (w != v) {
555                        auto f = add_edge(w, v, G);
556                        if (f.second == 0) {
557                            cast<Variadic>(G[v])->deleteOperand(G[w]);
558                        }
559                    }
560                }
561            }
562        }
563    }
564}
565
566/** ------------------------------------------------------------------------------------------------------------- *
567 * @brief removeFalseScopeDependencies
568 *
569 * After coalescing the AST, we may find that a result of some If statement is added to a result of a subsequent
570 * If statement. Unless necessary for correctness, eliminate it as we can potentially schedule the If nodes
571 * better without the sequential dependency.
572 *
573 * TODO: make this only iterate over the scope blocks and test the scope branch.
574 ** ------------------------------------------------------------------------------------------------------------- */
575inline void FlattenAssociativeDFG::removeFalseScopeDependencies(PabloFunction & function) {
576    ScopeDependencyGraph G;
577    {
578        ScopeDependencyMap M;
579        analyzeScopeDependencies(function.getEntryBlock(), G, M);
580    }
581    removeDependenciesWithUnresolvedUses(G);
582    eliminateUnecessaryDependencies(G);
583}
584
585/** ------------------------------------------------------------------------------------------------------------- *
586 * @brief transform
587 ** ------------------------------------------------------------------------------------------------------------- */
588void FlattenAssociativeDFG::transform(PabloFunction & function) {
589
590    FlattenAssociativeDFG::coalesce(function.getEntryBlock(), true);
591    #ifndef NDEBUG
592    PabloVerifier::verify(function, "post-coalescence");
593    #endif
594
595    Simplifier::optimize(function);
596
597    FlattenAssociativeDFG::deMorgansReduction(function.getEntryBlock(), true);
598    #ifndef NDEBUG
599    PabloVerifier::verify(function, "post-demorgans-reduction");
600    #endif
601
602    Simplifier::optimize(function);
603
604    FlattenAssociativeDFG::removeFalseScopeDependencies(function);
605    #ifndef NDEBUG
606    PabloVerifier::verify(function, "post-remove-false-scope-dependencies");
607    #endif
608
609    FlattenAssociativeDFG::tryToPartiallyExtractVariadic(function.getEntryBlock());
610    #ifndef NDEBUG
611    PabloVerifier::verify(function, "post-partial-variadic-extraction");
612    #endif
613
614    Simplifier::optimize(function);
615
616
617}
618
619}
Note: See TracBrowser for help on using the repository browser.