Index: src/IceCfg.cpp |
diff --git a/src/IceCfg.cpp b/src/IceCfg.cpp |
index f71737994aba701b4a75e6dcd296b11a5f66fed9..8f6a2fffaca0ecbf8d040b895458c0b33bb69895 100644 |
--- a/src/IceCfg.cpp |
+++ b/src/IceCfg.cpp |
@@ -507,6 +507,132 @@ void Cfg::shuffleNodes() { |
dump("After basic block shuffling"); |
} |
+void Cfg::localCSE() { |
+ // Performs basic-block local common-subexpression elimination |
+ // If we have |
+ // t1 = op b c |
+ // t2 = op b c |
+ // This pass will replace future references to t2 in a basic block by t1 |
+ // Points to note: |
+ // 1. Does not assume SSA, but not tested on non-SSA input yet as it is run |
+ // at the beginning. |
+ // 2. Leaves removal of instructions to DCE. |
+ // 3. Only enabled on arithmetic instructions. pnacl-clang (-O2) is expected |
+ // to take care of cases not arising from GEP simplification. |
+ // 4. By default, two passes are made over each basic block. Control this |
+ // with -lcse-max-iters=N |
+ |
+ TimerMarker T(TimerStack::TT_localCse, this); |
+ struct VariableHash { |
+ size_t operator()(const Variable *Var) const { return Var->hashValue(); } |
+ }; |
+ |
+ struct InstHash { |
+ size_t operator()(const Inst *Instr) const { |
+ auto Kind = Instr->getKind(); |
+ auto Result = |
+ std::hash<typename std::underlying_type<Inst::InstKind>::type>()( |
+ Kind); |
+ for (SizeT i = 0; i < Instr->getSrcSize(); ++i) { |
+ Result ^= Instr->getSrc(i)->hashValue(); |
+ } |
+ return Result; |
+ } |
+ }; |
+ struct InstEq { |
+ bool srcEq(const Operand *A, const Operand *B) const { |
+ if (llvm::isa<Variable>(A) || llvm::isa<Constant>(A)) |
+ return (A == B); |
+ return false; |
+ } |
+ bool operator()(const Inst *InstrA, const Inst *InstrB) const { |
+ if ((InstrA->getKind() != InstrB->getKind()) || |
+ (InstrA->getSrcSize() != InstrB->getSrcSize())) |
+ return false; |
+ |
+ if (auto *A = llvm::dyn_cast<InstArithmetic>(InstrA)) { |
+ auto *B = llvm::cast<InstArithmetic>(InstrB); |
+ // A, B are guaranteed to be of the same 'kind' at this point |
+ // So, dyn_cast is not needed |
+ if (A->getOp() != B->getOp()) |
+ return false; |
+ } |
+ // Does not enter loop if different kind or number of operands |
+ for (SizeT i = 0; i < InstrA->getSrcSize(); ++i) { |
+ if (!srcEq(InstrA->getSrc(i), InstrB->getSrc(i))) |
+ return false; |
+ } |
+ return true; |
+ } |
+ }; |
+ |
+ for (CfgNode *Node : getNodes()) { |
+ CfgUnorderedSet<Inst *, InstHash, InstEq> Seen; |
+ |
+ CfgUnorderedMap<Variable *, Variable *, VariableHash> Replacements; |
+ // Combining the above two into a single data structure might consume less |
+ // memory but will be slower i.e map of Instruction -> Set of Variables |
+ |
+ CfgUnorderedMap<Variable *, std::vector<Inst *>, VariableHash> Dependency; |
+ // Not necessary for SSA, still keeping it in case this pass is not run at |
+ // the beginning. Remove to improve performace. |
+ |
+ int IterCount = getFlags().getLocalCseMaxIterations(); |
+ |
+ while (IterCount--) { |
+ // TODO : Stats on IterCount -> performance |
+ for (Inst &Instr : Node->getInsts()) { |
+ if (Instr.isDeleted() || !llvm::isa<InstArithmetic>(&Instr)) |
+ continue; |
+ |
+ // Invalidate replacements |
+ auto Iter = Replacements.find(Instr.getDest()); |
+ if (Iter != Replacements.end()) { |
+ Replacements.erase(Iter); |
+ } |
+ |
+ // Invalidate 'seen' instructions whose operands were just updated. |
+ auto DepIter = Dependency.find(Instr.getDest()); |
+ if (DepIter != Dependency.end()) { |
+ for (auto DepInst : DepIter->second) { |
+ Seen.erase(DepInst); |
+ } |
+ } |
+ // The above two can be removed if SSA is assumed. |
+ |
+ // Replace - doing this before checking for repetitions might enable |
+ // more |
+ // optimizations |
+ for (SizeT i = 0; i < Instr.getSrcSize(); ++i) { |
+ auto *Opnd = Instr.getSrc(i); |
+ if (auto *Var = llvm::dyn_cast<Variable>(Opnd)) { |
+ if (Replacements.find(Var) != Replacements.end()) { |
+ Instr.replaceSource(i, Replacements[Var]); |
+ } |
+ } |
+ } |
+ |
+ // Check for repetitions |
+ auto SeenIter = Seen.find(&Instr); |
+ if (SeenIter != Seen.end()) { // seen before |
+ const Inst *Found = *SeenIter; |
+ Replacements[Instr.getDest()] = Found->getDest(); |
+ } else { // new |
+ Seen.insert(&Instr); |
+ |
+ // Update dependencies |
+ for (SizeT i = 0; i < Instr.getSrcSize(); ++i) { |
+ auto *Opnd = Instr.getSrc(i); |
+ if (auto *Var = llvm::dyn_cast<Variable>(Opnd)) { |
+ Dependency[Var].push_back(&Instr); |
+ } |
+ } |
+ } |
+ } |
+ } |
+ } |
+} |
+ |
void Cfg::doArgLowering() { |
TimerMarker T(TimerStack::TT_doArgLowering, this); |
getTarget()->lowerArguments(); |