| //===-- ExpandBigSwitches.cpp - Alloca optimization ---------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===-----------------------------------------------------------------------===// |
| // |
| // Very large switches can be a problem for JS engines. We split them up here. |
| // |
| //===-----------------------------------------------------------------------===// |
| |
| #include "OptPasses.h" |
| |
| #include "llvm/IR/Instruction.h" |
| #include "llvm/IR/Instructions.h" |
| #include "llvm/IR/Function.h" |
| #include "llvm/IR/Constants.h" |
| #include "llvm/Support/raw_ostream.h" |
| |
| #include <vector> |
| #include <algorithm> |
| |
| namespace llvm { |
| |
| struct ExpandBigSwitches : public FunctionPass { |
| static char ID; // Pass identification, replacement for typeid |
| ExpandBigSwitches() : FunctionPass(ID) {} |
| // XXX initialize..(*PassRegistry::getPassRegistry()); } |
| |
| bool runOnFunction(Function &Func) override; |
| |
| StringRef getPassName() const override { return "ExpandBigSwitches"; } |
| }; |
| |
| char ExpandBigSwitches::ID = 0; |
| |
| // Check if we need to split a switch. If so, return the median, on which we will do so |
| static bool ConsiderSplit(const SwitchInst *SI, int64_t& Median) { |
| int64_t Minn = INT64_MAX, Maxx = INT64_MIN; |
| std::vector<int64_t> Values; |
| for (SwitchInst::ConstCaseIt i = SI->case_begin(), e = SI->case_end(); i != e; ++i) { |
| int64_t Curr = i.getCaseValue()->getSExtValue(); |
| if (Curr < Minn) Minn = Curr; |
| if (Curr > Maxx) Maxx = Curr; |
| Values.push_back(Curr); |
| } |
| int64_t Range = Maxx - Minn; |
| int Num = SI->getNumCases(); |
| if (Num < 1024 && Range <= 10*1024 && (Range/Num) <= 1024) return false; |
| // this is either too big, or too rangey |
| std::sort(Values.begin(), Values.end()); |
| Median = Values[Values.size()/2]; |
| return true; |
| } |
| |
| static void DoSplit(SwitchInst *SI, int64_t Median) { |
| // switch (x) { ..very many.. } |
| // |
| // ==> |
| // |
| // if (x < median) { |
| // switch (x) { ..first half.. } |
| // } else { |
| // switch (x) { ..second half.. } |
| // } |
| |
| BasicBlock *SwitchBB = SI->getParent(); |
| Function *F = SwitchBB->getParent(); |
| Value *Condition = SI->getOperand(0); |
| BasicBlock *DD = SI->getDefaultDest(); |
| unsigned NumItems = SI->getNumCases(); |
| Type *T = Condition->getType(); |
| |
| Instruction *Check = new ICmpInst(SI, ICmpInst::ICMP_SLT, Condition, ConstantInt::get(T, Median), "switch-split"); |
| BasicBlock *LowBB = BasicBlock::Create(SI->getContext(), "switchsplit_low", F); |
| BasicBlock *HighBB = BasicBlock::Create(SI->getContext(), "switchsplit_high", F); |
| BranchInst *Br = BranchInst::Create(LowBB, HighBB, Check, SwitchBB); |
| |
| SwitchInst *LowSI = SwitchInst::Create(Condition, DD, NumItems/2, LowBB); |
| SwitchInst *HighSI = SwitchInst::Create(Condition, DD, NumItems/2, HighBB); |
| |
| for (SwitchInst::CaseIt i = SI->case_begin(), e = SI->case_end(); i != e; ++i) { |
| BasicBlock *BB = i.getCaseSuccessor(); |
| auto Value = i.getCaseValue(); |
| SwitchInst *NewSI = Value->getSExtValue() < Median ? LowSI : HighSI; |
| NewSI->addCase(Value, BB); |
| // update phis |
| BasicBlock *NewBB = NewSI->getParent(); |
| for (BasicBlock::iterator I = BB->begin(); I != BB->end(); ++I) { |
| PHINode *Phi = dyn_cast<PHINode>(I); |
| if (!Phi) break; |
| int Index = Phi->getBasicBlockIndex(SwitchBB); |
| if (Index < 0) continue; |
| Phi->addIncoming(Phi->getIncomingValue(Index), NewBB); |
| Phi->removeIncomingValue(Index); |
| } |
| } |
| |
| // fix default dest |
| for (BasicBlock::iterator I = DD->begin(); I != DD->end(); ++I) { |
| PHINode *Phi = dyn_cast<PHINode>(I); |
| if (!Phi) break; |
| int Index = Phi->getBasicBlockIndex(SwitchBB); |
| if (Index < 0) continue; |
| Phi->addIncoming(Phi->getIncomingValue(Index), LowBB); |
| Phi->addIncoming(Phi->getIncomingValue(Index), HighBB); |
| Phi->removeIncomingValue(Index); |
| } |
| |
| // finish up |
| SI->eraseFromParent(); |
| assert(SwitchBB->getTerminator() == Br); |
| assert(LowSI->getNumCases() + HighSI->getNumCases() == NumItems); |
| assert(LowSI->getNumCases() < HighSI->getNumCases() + 2); |
| assert(HighSI->getNumCases() < LowSI->getNumCases() + 2); |
| } |
| |
| bool ExpandBigSwitches::runOnFunction(Function &Func) { |
| bool Changed = false; |
| |
| struct SplitInfo { |
| SwitchInst *SI; |
| int64_t Median; |
| }; |
| |
| while (1) { // repetively split in 2 |
| std::vector<SplitInfo> ToSplit; |
| // find switches we need to split |
| for (Function::iterator B = Func.begin(), E = Func.end(); B != E; ++B) { |
| Instruction *I = B->getTerminator(); |
| SwitchInst *SI = dyn_cast<SwitchInst>(I); |
| if (!SI) continue; |
| SplitInfo Curr; |
| if (!ConsiderSplit(SI, Curr.Median)) continue; |
| Curr.SI = SI; |
| Changed = true; |
| ToSplit.push_back(Curr); |
| } |
| if (ToSplit.size() == 0) break; |
| // split them |
| for (auto& Curr : ToSplit) { |
| DoSplit(Curr.SI, Curr.Median); |
| } |
| } |
| |
| return Changed; |
| } |
| |
| // |
| |
| extern FunctionPass *createEmscriptenExpandBigSwitchesPass() { |
| return new ExpandBigSwitches(); |
| } |
| |
| } // End llvm namespace |