souffle  2.0.2-371-g6315b36
HoistAggregate.cpp
Go to the documentation of this file.
1 /*
2  * Souffle - A Datalog Compiler
3  * Copyright (c) 2018, The Souffle Developers. All rights reserved
4  * Licensed under the Universal Permissive License v 1.0 as shown at:
5  * - https://opensource.org/licenses/UPL
6  * - <souffle root>/licenses/SOUFFLE-UPL.txt
7  */
8 
9 /************************************************************************
10  *
11  * @file HoistAggregate.cpp
12  *
13  ***********************************************************************/
14 
16 #include "ram/Node.h"
17 #include "ram/Operation.h"
18 #include "ram/Program.h"
19 #include "ram/Statement.h"
20 #include "ram/utility/Visitor.h"
22 #include <cassert>
23 #include <functional>
24 #include <memory>
25 #include <utility>
26 #include <vector>
27 
28 namespace souffle::ram::transform {
29 
30 bool HoistAggregateTransformer::hoistAggregate(Program& program) {
31  bool changed = false;
32 
33  // There are two cases: aggregates that have no data-dependencies on
34  // other RAM operations, and aggregates that have data-dependencies.
35  // A rewriter has two tasks: (1) identify a single aggregate that
36  // can be hoisted and (2) insert it at the outermost level.
37  // We assume all Operations are renumbered for this transformation.
38 
39  // Hoist a single aggregate to an outer scope that is data-independent.
40  visitDepthFirst(program, [&](const Query& query) {
41  Own<NestedOperation> newAgg;
42  bool priorTupleOp = false;
43  std::function<Own<Node>(Own<Node>)> aggRewriter = [&](Own<Node> node) -> Own<Node> {
44  if (isA<Aggregate>(node.get())) {
45  auto* tupleOp = dynamic_cast<TupleOperation*>(node.get());
46  assert(tupleOp != nullptr && "aggregate conversion to tuple operation failed");
47  if (rla->getLevel(tupleOp) == -1 && !priorTupleOp) {
48  changed = true;
49  newAgg = souffle::clone(tupleOp);
50  assert(newAgg != nullptr && "failed to make a clone");
51  return souffle::clone(&tupleOp->getOperation());
52  }
53  } else if (isA<TupleOperation>(node.get())) {
54  // tuple operation that is a non-aggregate
55  priorTupleOp = true;
56  }
57  node->apply(makeLambdaRamMapper(aggRewriter));
58  return node;
59  };
60  const_cast<Query*>(&query)->apply(makeLambdaRamMapper(aggRewriter));
61  if (newAgg != nullptr) {
62  newAgg->rewrite(&newAgg->getOperation(), souffle::clone(&query.getOperation()));
63  const_cast<Query*>(&query)->rewrite(&query.getOperation(), std::move(newAgg));
64  }
65  });
66 
67  // hoist a single aggregate to an outer scope that is data-dependent on a prior operation.
68  visitDepthFirst(program, [&](const Query& query) {
69  int newLevel = -1;
70  Own<NestedOperation> newAgg;
71  int priorOpLevel = -1;
72 
73  std::function<Own<Node>(Own<Node>)> aggRewriter = [&](Own<Node> node) -> Own<Node> {
74  if (isA<AbstractAggregate>(node.get())) {
75  auto* tupleOp = dynamic_cast<TupleOperation*>(node.get());
76  assert(tupleOp != nullptr && "aggregate conversion to nested operation failed");
77  int dataDepLevel = rla->getLevel(tupleOp);
78  if (dataDepLevel != -1 && dataDepLevel < tupleOp->getTupleId() - 1) {
79  // If all tuple ops between the data-dependence level and agg
80  // are aggregates, then we do not hoist, i.e., we would
81  // continuously swap their positions.
82  if (dataDepLevel != priorOpLevel) {
83  changed = true;
84  newLevel = dataDepLevel;
85  newAgg = souffle::clone(tupleOp);
86  assert(newAgg != nullptr && "failed to make a clone");
87  return souffle::clone(&tupleOp->getOperation());
88  }
89  }
90  } else if (const TupleOperation* tupleOp = dynamic_cast<TupleOperation*>(node.get())) {
91  priorOpLevel = tupleOp->getTupleId();
92  }
93  node->apply(makeLambdaRamMapper(aggRewriter));
94  if (auto* search = dynamic_cast<TupleOperation*>(node.get())) {
95  if (newAgg != nullptr && search->getTupleId() == newLevel) {
96  newAgg->rewrite(&newAgg->getOperation(), souffle::clone(&search->getOperation()));
97  search->rewrite(&search->getOperation(), std::move(newAgg));
98  }
99  }
100  return node;
101  };
102  const_cast<Query*>(&query)->apply(makeLambdaRamMapper(aggRewriter));
103  });
104  return changed;
105 } // namespace souffle
106 
107 } // namespace souffle::ram::transform
souffle::ram::transform
Definition: ChoiceConversion.cpp:30
souffle::ram::analysis::LevelAnalysis::getLevel
int getLevel(const Node *value) const
Get level of a RAM expression/condition.
Definition: Level.cpp:64
souffle::ram::Query
A relational algebra query.
Definition: Query.h:50
souffle::ram::transform::Transformer::apply
bool apply(TranslationUnit &translationUnit)
@Brief apply the transformer to a translation unit @Param translationUnit that will be transformed.
Definition: Transformer.cpp:39
HoistAggregate.h
souffle::ram::Query::getOperation
const Operation & getOperation() const
Get RAM operation.
Definition: Query.h:57
souffle::Own
std::unique_ptr< A > Own
Definition: ContainerUtil.h:42
MiscUtil.h
Program.h
Visitor.h
souffle::clone
auto clone(const std::vector< A * > &xs)
Definition: ContainerUtil.h:172
souffle::ram::transform::HoistAggregateTransformer::rla
analysis::LevelAnalysis * rla
Definition: HoistAggregate.h:54
souffle::ram::TupleOperation
Abstract class for relation searches and lookups.
Definition: TupleOperation.h:35
Node.h
Operation.h
souffle::ram::transform::HoistAggregateTransformer::hoistAggregate
bool hoistAggregate(Program &program)
Apply hoistAggregate to the whole program.
Definition: HoistAggregate.cpp:34
Statement.h
souffle::ram::visitDepthFirst
void visitDepthFirst(const Node &root, Visitor< R, Ps... > &visitor, Args &... args)
A utility function visiting all nodes within the RAM fragments rooted by the given node recursively i...
Definition: Visitor.h:357
souffle::ram::makeLambdaRamMapper
LambdaNodeMapper< Lambda > makeLambdaRamMapper(const Lambda &lambda)
Creates a node mapper based on a corresponding lambda expression.
Definition: LambdaNodeMapper.h:67