//
// SROA - Replace aggregates or pieces of aggregates with scalar SSA values.
//
-FunctionPass *createSROAPass();
+FunctionPass *createSROAPass(bool PreserveCFG = true);
//===----------------------------------------------------------------------===//
//
-//===- SROA.h - Scalar Replacement Of Aggregates ----------------*- C++ -*-===//
+//===- SROA.h - Scalar Replacement Of Aggregates ----------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
#ifndef LLVM_TRANSFORMS_SCALAR_SROA_H
#define LLVM_TRANSFORMS_SCALAR_SROA_H
+#include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/IR/PassManager.h"
namespace llvm {
class AllocaInst;
+class LoadInst;
class AssumptionCache;
class DominatorTree;
+class DomTreeUpdater;
class Function;
class LLVMContext;
class PHINode;
class Partition;
class SROALegacyPass;
+class SelectHandSpeculativity {
+ unsigned char Storage = 0;
+ using TrueVal = Bitfield::Element<bool, 0, 1>; // Low 0'th bit.
+ using FalseVal = Bitfield::Element<bool, 1, 1>; // Low 1'th bit.
+public:
+ SelectHandSpeculativity() = default;
+ SelectHandSpeculativity &setAsSpeculatable(bool isTrueVal);
+ bool isSpeculatable(bool isTrueVal) const;
+ bool areAllSpeculatable() const;
+ bool areAnySpeculatable() const;
+ bool areNoneSpeculatable() const;
+ // For interop as int half of PointerIntPair.
+ explicit operator intptr_t() const { return static_cast<intptr_t>(Storage); }
+ explicit SelectHandSpeculativity(intptr_t Storage_) : Storage(Storage_) {}
+};
+static_assert(sizeof(SelectHandSpeculativity) == sizeof(unsigned char));
+
+using PossiblySpeculatableLoad =
+ PointerIntPair<LoadInst *, 2, sroa::SelectHandSpeculativity>;
+using PossiblySpeculatableLoads = SmallVector<PossiblySpeculatableLoad, 2>;
+
} // end namespace sroa
+enum class SROAOptions : bool { ModifyCFG, PreserveCFG };
+
/// An optimization pass providing Scalar Replacement of Aggregates.
///
/// This pass takes allocations which can be completely analyzed (that is, they
/// SSA vector values.
class SROAPass : public PassInfoMixin<SROAPass> {
LLVMContext *C = nullptr;
- DominatorTree *DT = nullptr;
+ DomTreeUpdater *DTU = nullptr;
AssumptionCache *AC = nullptr;
+ const bool PreserveCFG;
/// Worklist of alloca instructions to simplify.
///
/// All of these PHIs have been checked for the safety of speculation and by
/// being speculated will allow promoting allocas currently in the promotable
/// queue.
- SetVector<PHINode *, SmallVector<PHINode *, 2>> SpeculatablePHIs;
+ SetVector<PHINode *, SmallVector<PHINode *, 8>> SpeculatablePHIs;
- /// A worklist of select instructions to speculate prior to promoting
+ /// A worklist of select instructions to rewrite prior to promoting
/// allocas.
+ SmallMapVector<SelectInst *, sroa::PossiblySpeculatableLoads, 8>
+ SelectsToRewrite;
+
+ /// Select instructions that use an alloca and are subsequently loaded can be
+ /// rewritten to load both input pointers and then select between the result,
+ /// allowing the load of the alloca to be promoted.
+ /// From this:
+ /// %P2 = select i1 %cond, ptr %Alloca, ptr %Other
+ /// %V = load <type>, ptr %P2
+ /// to:
+ /// %V1 = load <type>, ptr %Alloca -> will be mem2reg'd
+ /// %V2 = load <type>, ptr %Other
+ /// %V = select i1 %cond, <type> %V1, <type> %V2
///
- /// All of these select instructions have been checked for the safety of
- /// speculation and by being speculated will allow promoting allocas
- /// currently in the promotable queue.
- SetVector<SelectInst *, SmallVector<SelectInst *, 2>> SpeculatableSelects;
+ /// We can do this to a select if its only uses are loads
+ /// and if either the operand to the select can be loaded unconditionally,
+ /// or if we are allowed to perform CFG modifications.
+ /// If found an intervening bitcast with a single use of the load,
+ /// allow the promotion.
+ static std::optional<sroa::PossiblySpeculatableLoads>
+ isSafeSelectToSpeculate(SelectInst &SI, bool PreserveCFG);
public:
- SROAPass() = default;
+ /// If \p PreserveCFG is set, then the pass is not allowed to modify CFG
+ /// in any way, even if it would update CFG analyses.
+ SROAPass(SROAOptions PreserveCFG);
/// Run the pass over the function.
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
+ void printPipeline(raw_ostream &OS,
+ function_ref<StringRef(StringRef)> MapClassName2PassName);
+
private:
friend class sroa::AllocaSliceRewriter;
friend class sroa::SROALegacyPass;
/// Helper used by both the public run method and by the legacy pass.
+ PreservedAnalyses runImpl(Function &F, DomTreeUpdater &RunDTU,
+ AssumptionCache &RunAC);
PreservedAnalyses runImpl(Function &F, DominatorTree &RunDT,
AssumptionCache &RunAC);
AllocaInst *rewritePartition(AllocaInst &AI, sroa::AllocaSlices &AS,
sroa::Partition &P);
bool splitAlloca(AllocaInst &AI, sroa::AllocaSlices &AS);
- bool runOnAlloca(AllocaInst &AI);
+ std::pair<bool /*Changed*/, bool /*CFGChanged*/> runOnAlloca(AllocaInst &AI);
void clobberUse(Use &U);
bool deleteDeadInstructions(SmallPtrSetImpl<AllocaInst *> &DeletedAllocas);
bool promoteAllocas(Function &F);
/// ElseBlock
/// SplitBefore
/// Tail
+///
+/// Updates DT if given.
void SplitBlockAndInsertIfThenElse(Value *Cond, Instruction *SplitBefore,
Instruction **ThenTerm,
Instruction **ElseTerm,
- MDNode *BranchWeights = nullptr);
+ MDNode *BranchWeights = nullptr,
+ DomTreeUpdater *DTU = nullptr);
/// Check whether BB is the merge point of a if-region.
/// If so, return the branch instruction that determines which entry into
return Result;
}
+Expected<SROAOptions> parseSROAOptions(StringRef Params) {
+ if (Params.empty() || Params == "modify-cfg")
+ return SROAOptions::ModifyCFG;
+ if (Params == "preserve-cfg")
+ return SROAOptions::PreserveCFG;
+ return make_error<StringError>(
+ formatv("invalid SROA pass parameter '{0}' (either preserve-cfg or "
+ "modify-cfg can be specified)",
+ Params)
+ .str(),
+ inconvertibleErrorCode());
+}
+
Expected<StackLifetime::LivenessType>
parseStackLifetimeOptions(StringRef Params) {
StackLifetime::LivenessType Result = StackLifetime::LivenessType::May;
// Form SSA out of local memory accesses after breaking apart aggregates into
// scalars.
- FPM.addPass(SROAPass());
+ FPM.addPass(SROAPass(SROAOptions::ModifyCFG));
// Catch trivial redundancies
FPM.addPass(EarlyCSEPass(true /* Enable mem-ssa. */));
/*UseBlockFrequencyInfo=*/false));
// Delete small array after loop unroll.
- FPM.addPass(SROAPass());
+ FPM.addPass(SROAPass(SROAOptions::ModifyCFG));
// Specially optimize memory movement as it doesn't look like dataflow in SSA.
FPM.addPass(MemCpyOptPass());
// Form SSA out of local memory accesses after breaking apart aggregates into
// scalars.
- FPM.addPass(SROAPass());
+ FPM.addPass(SROAPass(SROAOptions::ModifyCFG));
// Catch trivial redundancies
FPM.addPass(EarlyCSEPass(true /* Enable mem-ssa. */));
/*UseBlockFrequencyInfo=*/false));
// Delete small array after loop unroll.
- FPM.addPass(SROAPass());
+ FPM.addPass(SROAPass(SROAOptions::ModifyCFG));
// Try vectorization/scalarization transforms that are both improvements
// themselves and can allow further folds with GVN and InstCombine.
CGSCCPassManager &CGPipeline = MIWP.getPM();
FunctionPassManager FPM;
- FPM.addPass(SROAPass());
+ FPM.addPass(SROAPass(SROAOptions::ModifyCFG));
FPM.addPass(EarlyCSEPass()); // Catch trivial redundancies.
FPM.addPass(SimplifyCFGPass(SimplifyCFGOptions().convertSwitchRangeToICmp(
true))); // Merge & remove basic blocks.
// Compare/branch metadata may alter the behavior of passes like SimplifyCFG.
EarlyFPM.addPass(LowerExpectIntrinsicPass());
EarlyFPM.addPass(SimplifyCFGPass());
- EarlyFPM.addPass(SROAPass());
+ EarlyFPM.addPass(SROAPass(SROAOptions::ModifyCFG));
EarlyFPM.addPass(EarlyCSEPass());
if (Level == OptimizationLevel::O3)
EarlyFPM.addPass(CallSiteSplittingPass());
// Now that we are done with loop unrolling, be it either by LoopVectorizer,
// or LoopUnroll passes, some variable-offset GEP's into alloca's could have
// become constant-offset, thus enabling SROA and alloca promotion. Do so.
- FPM.addPass(SROAPass());
+ // NOTE: we are very late in the pipeline, and we don't have any LICM
+ // or SimplifyCFG passes scheduled after us, that would cleanup
+ // the CFG mess this may created if allowed to modify CFG, so forbid that.
+ FPM.addPass(SROAPass(SROAOptions::PreserveCFG));
}
if (!IsFullLTO) {
// Now that we are done with loop unrolling, be it either by LoopVectorizer,
// or LoopUnroll passes, some variable-offset GEP's into alloca's could have
// become constant-offset, thus enabling SROA and alloca promotion. Do so.
- FPM.addPass(SROAPass());
+ // NOTE: we are very late in the pipeline, and we don't have any LICM
+ // or SimplifyCFG passes scheduled after us, that would cleanup
+ // the CFG mess this may created if allowed to modify CFG, so forbid that.
+ FPM.addPass(SROAPass(SROAOptions::PreserveCFG));
FPM.addPass(InstCombinePass());
FPM.addPass(
RequireAnalysisPass<OptimizationRemarkEmitterAnalysis, Function>());
}
// Break up allocas
- FPM.addPass(SROAPass());
+ FPM.addPass(SROAPass(SROAOptions::ModifyCFG));
// LTO provides additional opportunities for tailcall elimination due to
// link-time inlining, and visibility of nocapture attribute.
FUNCTION_PASS("slp-vectorizer", SLPVectorizerPass())
FUNCTION_PASS("slsr", StraightLineStrengthReducePass())
FUNCTION_PASS("speculative-execution", SpeculativeExecutionPass())
-FUNCTION_PASS("sroa", SROAPass())
FUNCTION_PASS("strip-gc-relocates", StripGCRelocates())
FUNCTION_PASS("structurizecfg", StructurizeCFGPass())
FUNCTION_PASS("tailcallelim", TailCallElimPass())
"no-load-pre;load-pre;"
"no-split-backedge-load-pre;split-backedge-load-pre;"
"no-memdep;memdep")
+FUNCTION_PASS_WITH_PARAMS("sroa",
+ "SROAPass",
+ [](SROAOptions PreserveCFG) {
+ return SROAPass(PreserveCFG);
+ },
+ parseSROAOptions,
+ "preserve-cfg;modify-cfg")
FUNCTION_PASS_WITH_PARAMS("print<stack-lifetime>",
"StackLifetimePrinterPass",
[](StackLifetime::LivenessType Type) {
#include "llvm/ADT/iterator.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Analysis/AssumptionCache.h"
+#include "llvm/Analysis/DomTreeUpdater.h"
#include "llvm/Analysis/GlobalsModRef.h"
#include "llvm/Analysis/Loads.h"
#include "llvm/Analysis/PtrUseVisitor.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/Scalar.h"
+#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/Local.h"
#include "llvm/Transforms/Utils/PromoteMemToReg.h"
#include <algorithm>
STATISTIC(NumNewAllocas, "Number of new, smaller allocas introduced");
STATISTIC(NumPromoted, "Number of allocas promoted to SSA values");
STATISTIC(NumLoadsSpeculated, "Number of loads speculated to allow promotion");
+STATISTIC(NumLoadsPredicated,
+ "Number of loads rewritten into predicated loads to allow promotion");
STATISTIC(NumDeleted, "Number of instructions deleted");
STATISTIC(NumVectorized, "Number of vectorized aggregates");
/// GEPs.
static cl::opt<bool> SROAStrictInbounds("sroa-strict-inbounds", cl::init(false),
cl::Hidden);
-
namespace {
/// A custom IRBuilder inserter which prefixes all names, but only in
PN.eraseFromParent();
}
-/// Select instructions that use an alloca and are subsequently loaded can be
-/// rewritten to load both input pointers and then select between the result,
-/// allowing the load of the alloca to be promoted.
-/// From this:
-/// %P2 = select i1 %cond, i32* %Alloca, i32* %Other
-/// %V = load i32* %P2
-/// to:
-/// %V1 = load i32* %Alloca -> will be mem2reg'd
-/// %V2 = load i32* %Other
-/// %V = select i1 %cond, i32 %V1, i32 %V2
-///
-/// We can do this to a select if its only uses are loads and if the operand
-/// to the select can be loaded unconditionally. If found an intervening bitcast
-/// with a single use of the load, allow the promotion.
-static bool isSafeSelectToSpeculate(SelectInst &SI) {
- Value *TValue = SI.getTrueValue();
- Value *FValue = SI.getFalseValue();
+sroa::SelectHandSpeculativity &
+sroa::SelectHandSpeculativity::setAsSpeculatable(bool isTrueVal) {
+ if (isTrueVal)
+ Bitfield::set<sroa::SelectHandSpeculativity::TrueVal>(Storage, true);
+ else
+ Bitfield::set<sroa::SelectHandSpeculativity::FalseVal>(Storage, true);
+ return *this;
+}
+
+bool sroa::SelectHandSpeculativity::isSpeculatable(bool isTrueVal) const {
+ return isTrueVal
+ ? Bitfield::get<sroa::SelectHandSpeculativity::TrueVal>(Storage)
+ : Bitfield::get<sroa::SelectHandSpeculativity::FalseVal>(Storage);
+}
+
+bool sroa::SelectHandSpeculativity::areAllSpeculatable() const {
+ return isSpeculatable(/*isTrueVal=*/true) &&
+ isSpeculatable(/*isTrueVal=*/false);
+}
+
+bool sroa::SelectHandSpeculativity::areAnySpeculatable() const {
+ return isSpeculatable(/*isTrueVal=*/true) ||
+ isSpeculatable(/*isTrueVal=*/false);
+}
+bool sroa::SelectHandSpeculativity::areNoneSpeculatable() const {
+ return !areAnySpeculatable();
+}
+
+static sroa::SelectHandSpeculativity
+isSafeLoadOfSelectToSpeculate(LoadInst &LI, SelectInst &SI, bool PreserveCFG) {
+ assert(LI.isSimple() && "Only for simple loads");
+ sroa::SelectHandSpeculativity Spec;
+
const DataLayout &DL = SI.getModule()->getDataLayout();
+ for (Value *Value : {SI.getTrueValue(), SI.getFalseValue()})
+ if (isSafeToLoadUnconditionally(Value, LI.getType(), LI.getAlign(), DL,
+ &LI))
+ Spec.setAsSpeculatable(/*isTrueVal=*/Value == SI.getTrueValue());
+ else if (PreserveCFG)
+ return Spec;
+
+ return Spec;
+}
+
+std::optional<sroa::PossiblySpeculatableLoads>
+SROAPass::isSafeSelectToSpeculate(SelectInst &SI, bool PreserveCFG) {
+ PossiblySpeculatableLoads Loads;
for (User *U : SI.users()) {
LoadInst *LI;
else
LI = dyn_cast<LoadInst>(U);
- if (!LI || !LI->isSimple())
- return false;
+ // Note that atomic loads can be transformed;
+ // atomic semantics do not have any meaning for a local alloca.
+ if (!LI || LI->isVolatile())
+ return {}; // Give up on this `select`.
- // Both operands to the select need to be dereferenceable, either
- // absolutely (e.g. allocas) or at this point because we can see other
- // accesses to it.
- if (!isSafeToLoadUnconditionally(TValue, LI->getType(),
- LI->getAlign(), DL, LI))
- return false;
- if (!isSafeToLoadUnconditionally(FValue, LI->getType(),
- LI->getAlign(), DL, LI))
- return false;
+ PossiblySpeculatableLoad Load(LI);
+
+ if (!LI->isSimple()) {
+ // If the `load` is not simple, we can't speculatively execute it,
+ // but we could handle this via a CFG modification. But can we?
+ if (PreserveCFG)
+ return {}; // Give up on this `select`.
+ Loads.emplace_back(Load);
+ continue;
+ }
+
+ sroa::SelectHandSpeculativity Spec =
+ isSafeLoadOfSelectToSpeculate(*LI, SI, PreserveCFG);
+ if (PreserveCFG && !Spec.areAllSpeculatable())
+ return {}; // Give up on this `select`.
+
+ Load.setInt(Spec);
+ Loads.emplace_back(Load);
}
- return true;
+ return Loads;
}
-static void speculateSelectInstLoads(IRBuilderTy &IRB, SelectInst &SI) {
- LLVM_DEBUG(dbgs() << " original: " << SI << "\n");
+static void speculateSelectInstLoads(SelectInst &SI, LoadInst &LI,
+ IRBuilderTy &IRB) {
+ LLVM_DEBUG(dbgs() << " original load: " << SI << "\n");
IRB.SetInsertPoint(&SI);
Value *TV = SI.getTrueValue();
Value *FV = SI.getFalseValue();
- // Replace the loads of the select with a select of two loads.
- while (!SI.use_empty()) {
- LoadInst *LI;
- BitCastInst *BC = dyn_cast<BitCastInst>(SI.user_back());
- if (BC) {
- assert(BC->hasOneUse() && "Bitcast should have a single use.");
- LI = cast<LoadInst>(BC->user_back());
- } else {
- LI = cast<LoadInst>(SI.user_back());
- }
+ // Replace the given load of the select with a select of two loads.
+
+ assert(LI.isSimple() && "We only speculate simple loads");
+
+ IRB.SetInsertPoint(&LI);
+ LoadInst *TL =
+ IRB.CreateAlignedLoad(LI.getType(), TV, LI.getAlign(),
+ LI.getName() + ".sroa.speculate.load.true");
+ LoadInst *FL =
+ IRB.CreateAlignedLoad(LI.getType(), FV, LI.getAlign(),
+ LI.getName() + ".sroa.speculate.load.false");
+ NumLoadsSpeculated += 2;
+
+ // Transfer alignment and AA info if present.
+ TL->setAlignment(LI.getAlign());
+ FL->setAlignment(LI.getAlign());
+
+ AAMDNodes Tags = LI.getAAMetadata();
+ if (Tags) {
+ TL->setAAMetadata(Tags);
+ FL->setAAMetadata(Tags);
+ }
- assert(LI->isSimple() && "We only speculate simple loads");
+ Value *V = IRB.CreateSelect(SI.getCondition(), TL, FL,
+ LI.getName() + ".sroa.speculated");
- IRB.SetInsertPoint(LI);
- Value *NewTV =
- BC ? IRB.CreateBitCast(TV, BC->getType(), TV->getName() + ".sroa.cast")
- : TV;
- Value *NewFV =
- BC ? IRB.CreateBitCast(FV, BC->getType(), FV->getName() + ".sroa.cast")
- : FV;
- LoadInst *TL = IRB.CreateLoad(LI->getType(), NewTV,
- LI->getName() + ".sroa.speculate.load.true");
- LoadInst *FL = IRB.CreateLoad(LI->getType(), NewFV,
- LI->getName() + ".sroa.speculate.load.false");
- NumLoadsSpeculated += 2;
-
- // Transfer alignment and AA info if present.
- TL->setAlignment(LI->getAlign());
- FL->setAlignment(LI->getAlign());
-
- AAMDNodes Tags = LI->getAAMetadata();
- if (Tags) {
- TL->setAAMetadata(Tags);
- FL->setAAMetadata(Tags);
- }
+ LLVM_DEBUG(dbgs() << " speculated to: " << *V << "\n");
+ LI.replaceAllUsesWith(V);
+}
- Value *V = IRB.CreateSelect(SI.getCondition(), TL, FL,
- LI->getName() + ".sroa.speculated");
+static void rewriteLoadOfSelect(SelectInst &SI, LoadInst &LI,
+ sroa::SelectHandSpeculativity Spec,
+ DomTreeUpdater &DTU) {
+ LLVM_DEBUG(dbgs() << " original load: " << SI << "\n");
+ BasicBlock *Head = LI.getParent();
+ Instruction *ThenTerm = nullptr;
+ Instruction *ElseTerm = nullptr;
+ if (Spec.areNoneSpeculatable())
+ SplitBlockAndInsertIfThenElse(SI.getCondition(), &LI, &ThenTerm, &ElseTerm,
+ SI.getMetadata(LLVMContext::MD_prof), &DTU);
+ else {
+ SplitBlockAndInsertIfThen(SI.getCondition(), &LI, /*Unreachable=*/false,
+ SI.getMetadata(LLVMContext::MD_prof), &DTU,
+ /*LI=*/nullptr, /*ThenBlock=*/nullptr);
+ if (Spec.isSpeculatable(/*isTrueVal=*/true))
+ cast<BranchInst>(Head->getTerminator())->swapSuccessors();
+ }
+ auto *HeadBI = cast<BranchInst>(Head->getTerminator());
+ Spec = {}; // Do not use `Spec` beyond this point.
+ BasicBlock *Tail = LI.getParent();
+ Tail->setName(Head->getName() + ".cont");
+ auto *PN = PHINode::Create(LI.getType(), 2, "", &LI);
+ for (BasicBlock *SuccBB : successors(Head)) {
+ bool IsThen = SuccBB == HeadBI->getSuccessor(0);
+ int SuccIdx = IsThen ? 0 : 1;
+ auto *NewLoadBB = SuccBB == Tail ? Head : SuccBB;
+ if (NewLoadBB != Head) {
+ NewLoadBB->setName(Head->getName() + (IsThen ? ".then" : ".else"));
+ ++NumLoadsPredicated;
+ } else
+ ++NumLoadsSpeculated;
+ auto *CondLoad = cast<LoadInst>(LI.clone());
+ CondLoad->insertBefore(NewLoadBB->getTerminator());
+ CondLoad->setOperand(0, SI.getOperand(1 + SuccIdx));
+ CondLoad->setName(LI.getName() + (IsThen ? ".then" : ".else") + ".val");
+ PN->addIncoming(CondLoad, NewLoadBB);
+ }
+ PN->takeName(&LI);
+ LLVM_DEBUG(dbgs() << " to: " << *PN << "\n");
+ LI.replaceAllUsesWith(PN);
+}
- LLVM_DEBUG(dbgs() << " speculated to: " << *V << "\n");
- LI->replaceAllUsesWith(V);
+static bool rewriteSelectInstLoads(SelectInst &SI,
+ const sroa::PossiblySpeculatableLoads &Loads,
+ IRBuilderTy &IRB, DomTreeUpdater *DTU) {
+ bool CFGChanged = false;
+ LLVM_DEBUG(dbgs() << " original select: " << SI << "\n");
+
+ for (const PossiblySpeculatableLoad &Load : Loads) {
+ LoadInst *LI = Load.getPointer();
+ sroa::SelectHandSpeculativity Spec = Load.getInt();
+ if (Spec.areAllSpeculatable()) {
+ speculateSelectInstLoads(SI, *LI, IRB);
+ } else {
+ assert(DTU && "Should not get here when not allowed to modify the CFG!");
+ rewriteLoadOfSelect(SI, *LI, Spec, *DTU);
+ CFGChanged = true;
+ }
LI->eraseFromParent();
- if (BC)
- BC->eraseFromParent();
}
+
+ for (User *U : make_early_inc_range(SI.users()))
+ cast<BitCastInst>(U)->eraseFromParent();
SI.eraseFromParent();
+ return CFGChanged;
}
/// Build a GEP out of a base pointer and indices.
break;
}
- for (SelectInst *Sel : SelectUsers)
- if (!isSafeSelectToSpeculate(*Sel)) {
+ SmallVector<std::pair<SelectInst *, PossiblySpeculatableLoads>, 2>
+ NewSelectsToRewrite;
+ NewSelectsToRewrite.reserve(SelectUsers.size());
+ for (SelectInst *Sel : SelectUsers) {
+ std::optional<PossiblySpeculatableLoads> Loads =
+ isSafeSelectToSpeculate(*Sel, PreserveCFG);
+ if (!Loads) {
Promotable = false;
PHIUsers.clear();
SelectUsers.clear();
+ NewSelectsToRewrite.clear();
break;
}
+ NewSelectsToRewrite.emplace_back(std::make_pair(Sel, *Loads));
+ }
if (Promotable) {
for (Use *U : AS.getDeadUsesIfPromotable()) {
// next iteration.
for (PHINode *PHIUser : PHIUsers)
SpeculatablePHIs.insert(PHIUser);
- for (SelectInst *SelectUser : SelectUsers)
- SpeculatableSelects.insert(SelectUser);
+ SelectsToRewrite.reserve(SelectsToRewrite.size() +
+ NewSelectsToRewrite.size());
+ for (auto &&KV : llvm::make_range(
+ std::make_move_iterator(NewSelectsToRewrite.begin()),
+ std::make_move_iterator(NewSelectsToRewrite.end())))
+ SelectsToRewrite.insert(std::move(KV));
Worklist.insert(NewAI);
}
} else {
/// This analyzes the alloca to ensure we can reason about it, builds
/// the slices of the alloca, and then hands it off to be split and
/// rewritten as needed.
-bool SROAPass::runOnAlloca(AllocaInst &AI) {
+std::pair<bool /*Changed*/, bool /*CFGChanged*/>
+SROAPass::runOnAlloca(AllocaInst &AI) {
+ bool Changed = false;
+ bool CFGChanged = false;
+
LLVM_DEBUG(dbgs() << "SROA alloca: " << AI << "\n");
++NumAllocasAnalyzed;
// Special case dead allocas, as they're trivial.
if (AI.use_empty()) {
AI.eraseFromParent();
- return true;
+ Changed = true;
+ return {Changed, CFGChanged};
}
const DataLayout &DL = AI.getModule()->getDataLayout();
auto *AT = AI.getAllocatedType();
if (AI.isArrayAllocation() || !AT->isSized() || isa<ScalableVectorType>(AT) ||
DL.getTypeAllocSize(AT).getFixedSize() == 0)
- return false;
-
- bool Changed = false;
+ return {Changed, CFGChanged};
// First, split any FCA loads and stores touching this alloca to promote
// better splitting and promotion opportunities.
AllocaSlices AS(DL, AI);
LLVM_DEBUG(AS.print(dbgs()));
if (AS.isEscaped())
- return Changed;
+ return {Changed, CFGChanged};
// Delete all the dead users of this alloca before splitting and rewriting it.
for (Instruction *DeadUser : AS.getDeadUsers()) {
// No slices to split. Leave the dead alloca for a later pass to clean up.
if (AS.begin() == AS.end())
- return Changed;
+ return {Changed, CFGChanged};
Changed |= splitAlloca(AI, AS);
while (!SpeculatablePHIs.empty())
speculatePHINodeLoads(IRB, *SpeculatablePHIs.pop_back_val());
- LLVM_DEBUG(dbgs() << " Speculating Selects\n");
- while (!SpeculatableSelects.empty())
- speculateSelectInstLoads(IRB, *SpeculatableSelects.pop_back_val());
+ LLVM_DEBUG(dbgs() << " Rewriting Selects\n");
+ auto RemainingSelectsToRewrite = SelectsToRewrite.takeVector();
+ while (!RemainingSelectsToRewrite.empty()) {
+ const auto [K, V] = RemainingSelectsToRewrite.pop_back_val();
+ CFGChanged |=
+ rewriteSelectInstLoads(*K, V, IRB, PreserveCFG ? nullptr : DTU);
+ }
- return Changed;
+ return {Changed, CFGChanged};
}
/// Delete the dead instructions accumulated in this run.
NumPromoted += PromotableAllocas.size();
LLVM_DEBUG(dbgs() << "Promoting allocas with mem2reg...\n");
- PromoteMemToReg(PromotableAllocas, *DT, AC);
+ PromoteMemToReg(PromotableAllocas, DTU->getDomTree(), AC);
PromotableAllocas.clear();
return true;
}
-PreservedAnalyses SROAPass::runImpl(Function &F, DominatorTree &RunDT,
+PreservedAnalyses SROAPass::runImpl(Function &F, DomTreeUpdater &RunDTU,
AssumptionCache &RunAC) {
LLVM_DEBUG(dbgs() << "SROA function: " << F.getName() << "\n");
C = &F.getContext();
- DT = &RunDT;
+ DTU = &RunDTU;
AC = &RunAC;
BasicBlock &EntryBB = F.getEntryBlock();
}
bool Changed = false;
+ bool CFGChanged = false;
// A set of deleted alloca instruction pointers which should be removed from
// the list of promotable allocas.
SmallPtrSet<AllocaInst *, 4> DeletedAllocas;
do {
while (!Worklist.empty()) {
- Changed |= runOnAlloca(*Worklist.pop_back_val());
+ auto [IterationChanged, IterationCFGChanged] =
+ runOnAlloca(*Worklist.pop_back_val());
+ Changed |= IterationChanged;
+ CFGChanged |= IterationCFGChanged;
+
Changed |= deleteDeadInstructions(DeletedAllocas);
// Remove the deleted allocas from various lists so that we don't try to
PostPromotionWorklist.clear();
} while (!Worklist.empty());
+ assert((!CFGChanged || Changed) && "Can not only modify the CFG.");
+ assert((!CFGChanged || !PreserveCFG) &&
+ "Should not have modified the CFG when told to preserve it.");
+
if (!Changed)
return PreservedAnalyses::all();
PreservedAnalyses PA;
- PA.preserveSet<CFGAnalyses>();
+ if (!CFGChanged)
+ PA.preserveSet<CFGAnalyses>();
+ PA.preserve<DominatorTreeAnalysis>();
return PA;
}
+PreservedAnalyses SROAPass::runImpl(Function &F, DominatorTree &RunDT,
+ AssumptionCache &RunAC) {
+ DomTreeUpdater DTU(RunDT, DomTreeUpdater::UpdateStrategy::Lazy);
+ return runImpl(F, DTU, RunAC);
+}
+
PreservedAnalyses SROAPass::run(Function &F, FunctionAnalysisManager &AM) {
return runImpl(F, AM.getResult<DominatorTreeAnalysis>(F),
AM.getResult<AssumptionAnalysis>(F));
}
+void SROAPass::printPipeline(
+ raw_ostream &OS, function_ref<StringRef(StringRef)> MapClassName2PassName) {
+ static_cast<PassInfoMixin<SROAPass> *>(this)->printPipeline(
+ OS, MapClassName2PassName);
+ OS << (PreserveCFG ? "<preserve-cfg>" : "<modify-cfg>");
+}
+
+SROAPass::SROAPass(SROAOptions PreserveCFG_)
+ : PreserveCFG(PreserveCFG_ == SROAOptions::PreserveCFG) {}
+
/// A legacy pass for the legacy pass manager that wraps the \c SROA pass.
///
/// This is in the llvm namespace purely to allow it to be a friend of the \c
public:
static char ID;
- SROALegacyPass() : FunctionPass(ID) {
+ SROALegacyPass(SROAOptions PreserveCFG = SROAOptions::PreserveCFG)
+ : FunctionPass(ID), Impl(PreserveCFG) {
initializeSROALegacyPassPass(*PassRegistry::getPassRegistry());
}
AU.addRequired<AssumptionCacheTracker>();
AU.addRequired<DominatorTreeWrapperPass>();
AU.addPreserved<GlobalsAAWrapperPass>();
- AU.setPreservesCFG();
+ AU.addPreserved<DominatorTreeWrapperPass>();
}
StringRef getPassName() const override { return "SROA"; }
char SROALegacyPass::ID = 0;
-FunctionPass *llvm::createSROAPass() { return new SROALegacyPass(); }
+FunctionPass *llvm::createSROAPass(bool PreserveCFG) {
+ return new SROALegacyPass(PreserveCFG ? SROAOptions::PreserveCFG
+ : SROAOptions::ModifyCFG);
+}
INITIALIZE_PASS_BEGIN(SROALegacyPass, "sroa",
"Scalar Replacement Of Aggregates", false, false)
void llvm::SplitBlockAndInsertIfThenElse(Value *Cond, Instruction *SplitBefore,
Instruction **ThenTerm,
Instruction **ElseTerm,
- MDNode *BranchWeights) {
+ MDNode *BranchWeights,
+ DomTreeUpdater *DTU) {
BasicBlock *Head = SplitBefore->getParent();
+
+ SmallPtrSet<BasicBlock *, 8> UniqueOrigSuccessors;
+ if (DTU)
+ UniqueOrigSuccessors.insert(succ_begin(Head), succ_end(Head));
+
BasicBlock *Tail = Head->splitBasicBlock(SplitBefore->getIterator());
Instruction *HeadOldTerm = Head->getTerminator();
LLVMContext &C = Head->getContext();
BranchInst::Create(/*ifTrue*/ThenBlock, /*ifFalse*/ElseBlock, Cond);
HeadNewTerm->setMetadata(LLVMContext::MD_prof, BranchWeights);
ReplaceInstWithInst(HeadOldTerm, HeadNewTerm);
+ if (DTU) {
+ SmallVector<DominatorTree::UpdateType, 8> Updates;
+ Updates.reserve(4 + 2 * UniqueOrigSuccessors.size());
+ for (BasicBlock *Succ : successors(Head)) {
+ Updates.push_back({DominatorTree::Insert, Head, Succ});
+ Updates.push_back({DominatorTree::Insert, Succ, Tail});
+ }
+ for (BasicBlock *UniqueOrigSuccessor : UniqueOrigSuccessors)
+ Updates.push_back({DominatorTree::Insert, Tail, UniqueOrigSuccessor});
+ for (BasicBlock *UniqueOrigSuccessor : UniqueOrigSuccessors)
+ Updates.push_back({DominatorTree::Delete, Head, UniqueOrigSuccessor});
+ DTU->applyUpdates(Updates);
+ }
}
BranchInst *llvm::GetIfCondition(BasicBlock *BB, BasicBlock *&IfTrue,
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
; rdar://6417724
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0
attributes #0 = { argmemonly nofree nosync nounwind willreturn }
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
target datalayout = "e-p:64:64:64-p1:16:16:16-p3:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-n8:16:32:64"
declare void @llvm.memcpy.p0.p0.i32(ptr nocapture, ptr nocapture readonly, i32, i1)
store i64 %v2, ptr addrspace(1) %complex2
ret void
}
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
target datalayout = "e-p:64:64:64-p1:16:16:16-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-n8:16:32:64"
}
define void @select_addrspacecast_const_op(i1 %a, i1 %b) {
-; CHECK-LABEL: @select_addrspacecast_const_op(
-; CHECK-NEXT: [[C:%.*]] = alloca i64, align 8
-; CHECK-NEXT: [[C_0_ASC_SROA_CAST:%.*]] = addrspacecast ptr [[C]] to ptr addrspace(1)
-; CHECK-NEXT: [[COND_IN:%.*]] = select i1 [[B:%.*]], ptr addrspace(1) [[C_0_ASC_SROA_CAST]], ptr addrspace(1) null
-; CHECK-NEXT: [[COND:%.*]] = load i64, ptr addrspace(1) [[COND_IN]], align 8
-; CHECK-NEXT: ret void
+; CHECK-PRESERVE-CFG-LABEL: @select_addrspacecast_const_op(
+; CHECK-PRESERVE-CFG-NEXT: [[C:%.*]] = alloca i64, align 8
+; CHECK-PRESERVE-CFG-NEXT: [[C_0_ASC_SROA_CAST:%.*]] = addrspacecast ptr [[C]] to ptr addrspace(1)
+; CHECK-PRESERVE-CFG-NEXT: [[COND_IN:%.*]] = select i1 [[B:%.*]], ptr addrspace(1) [[C_0_ASC_SROA_CAST]], ptr addrspace(1) null
+; CHECK-PRESERVE-CFG-NEXT: [[COND:%.*]] = load i64, ptr addrspace(1) [[COND_IN]], align 8
+; CHECK-PRESERVE-CFG-NEXT: ret void
+;
+; CHECK-MODIFY-CFG-LABEL: @select_addrspacecast_const_op(
+; CHECK-MODIFY-CFG-NEXT: br i1 [[B:%.*]], label [[DOTCONT:%.*]], label [[DOTELSE:%.*]]
+; CHECK-MODIFY-CFG: .else:
+; CHECK-MODIFY-CFG-NEXT: [[COND_ELSE_VAL:%.*]] = load i64, ptr addrspace(1) null, align 8
+; CHECK-MODIFY-CFG-NEXT: br label [[DOTCONT]]
+; CHECK-MODIFY-CFG: .cont:
+; CHECK-MODIFY-CFG-NEXT: [[COND:%.*]] = phi i64 [ undef, [[TMP0:%.*]] ], [ [[COND_ELSE_VAL]], [[DOTELSE]] ]
+; CHECK-MODIFY-CFG-NEXT: ret void
;
%c = alloca i64, align 8
%p.0.c = select i1 %a, ptr %c, ptr %c
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
-; RUN: opt -passes='debugify,function(sroa)' -S < %s | FileCheck %s -check-prefix CHECK-DEBUGLOC
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
+; RUN: opt -passes='debugify,function(sroa<preserve-cfg>)' -S < %s | FileCheck %s -check-prefix CHECK-DEBUGLOC
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-n8:16:32:64"
}
declare void @populate(ptr)
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
target datalayout = "e-p:64:64:64-p1:16:16:16-p2:32:32-p3:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-n8:16:32:64-A2"
declare void @llvm.memcpy.p2.p2.i32(ptr addrspace(2) nocapture, ptr addrspace(2) nocapture readonly, i32, i1)
}
declare void @llvm.lifetime.start.p0(i64 %size, ptr nocapture %ptr)
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
declare void @llvm.assume(i1) #0
attributes #0 = { nofree norecurse nounwind willreturn }
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
target datalayout = "e-p:64:64:64-p1:16:16:16-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-n8:16:32:64"
}
define void @PR15805(i1 %a, i1 %b) {
-; CHECK-LABEL: @PR15805(
-; CHECK-NEXT: [[COND_SROA_SPECULATED:%.*]] = select i1 [[B:%.*]], i64 undef, i64 undef
-; CHECK-NEXT: ret void
+; CHECK-PRESERVE-CFG-LABEL: @PR15805(
+; CHECK-PRESERVE-CFG-NEXT: [[C:%.*]] = alloca i64, align 8
+; CHECK-PRESERVE-CFG-NEXT: [[COND_IN:%.*]] = select i1 [[B:%.*]], ptr [[C]], ptr [[C]]
+; CHECK-PRESERVE-CFG-NEXT: [[COND:%.*]] = load i64, ptr [[COND_IN]], align 8
+; CHECK-PRESERVE-CFG-NEXT: ret void
+;
+; CHECK-MODIFY-CFG-LABEL: @PR15805(
+; CHECK-MODIFY-CFG-NEXT: br i1 [[B:%.*]], label [[DOTCONT:%.*]], label [[DOTELSE:%.*]]
+; CHECK-MODIFY-CFG: .else:
+; CHECK-MODIFY-CFG-NEXT: br label [[DOTCONT]]
+; CHECK-MODIFY-CFG: .cont:
+; CHECK-MODIFY-CFG-NEXT: [[COND:%.*]] = phi i64 [ undef, [[TMP0:%.*]] ], [ undef, [[DOTELSE]] ]
+; CHECK-MODIFY-CFG-NEXT: ret void
;
%c = alloca i64, align 8
%p.0.c = select i1 %a, ptr %c, ptr %c
; order in which the uses of the alloca are visited.
;
;
-; CHECK-LABEL: @PR15805.1(
-; CHECK-NEXT: br label [[EXIT:%.*]]
-; CHECK: loop:
-; CHECK-NEXT: [[COND_SROA_SPECULATED:%.*]] = select i1 [[A:%.*]], i64 undef, i64 undef
-; CHECK-NEXT: br i1 [[C2:%.*]], label [[LOOP:%.*]], label [[EXIT]]
-; CHECK: exit:
-; CHECK-NEXT: ret void
+; CHECK-PRESERVE-CFG-LABEL: @PR15805.1(
+; CHECK-PRESERVE-CFG-NEXT: [[C:%.*]] = alloca i64, align 8
+; CHECK-PRESERVE-CFG-NEXT: br label [[EXIT:%.*]]
+; CHECK-PRESERVE-CFG: loop:
+; CHECK-PRESERVE-CFG-NEXT: [[COND_IN:%.*]] = select i1 [[A:%.*]], ptr [[C]], ptr [[C]]
+; CHECK-PRESERVE-CFG-NEXT: [[COND:%.*]] = load i64, ptr [[COND_IN]], align 8
+; CHECK-PRESERVE-CFG-NEXT: br i1 [[C2:%.*]], label [[LOOP:%.*]], label [[EXIT]]
+; CHECK-PRESERVE-CFG: exit:
+; CHECK-PRESERVE-CFG-NEXT: ret void
+;
+; CHECK-MODIFY-CFG-LABEL: @PR15805.1(
+; CHECK-MODIFY-CFG-NEXT: br label [[EXIT:%.*]]
+; CHECK-MODIFY-CFG: loop:
+; CHECK-MODIFY-CFG-NEXT: [[C_0_LOAD:%.*]] = load i64, ptr poison, align 8
+; CHECK-MODIFY-CFG-NEXT: br i1 [[A:%.*]], label [[LOOP_CONT:%.*]], label [[LOOP_ELSE:%.*]]
+; CHECK-MODIFY-CFG: loop.else:
+; CHECK-MODIFY-CFG-NEXT: [[C_0_LOAD1:%.*]] = load i64, ptr poison, align 8
+; CHECK-MODIFY-CFG-NEXT: br label [[LOOP_CONT]]
+; CHECK-MODIFY-CFG: loop.cont:
+; CHECK-MODIFY-CFG-NEXT: [[COND:%.*]] = phi i64 [ [[C_0_LOAD]], [[LOOP:%.*]] ], [ [[C_0_LOAD1]], [[LOOP_ELSE]] ]
+; CHECK-MODIFY-CFG-NEXT: br i1 [[C2:%.*]], label [[LOOP]], label [[EXIT]]
+; CHECK-MODIFY-CFG: exit:
+; CHECK-MODIFY-CFG-NEXT: ret void
;
%c = alloca i64, align 8
br label %exit
; bail on select instructions.
;
;
-; CHECK-LABEL: @PR16651.2(
-; CHECK-NEXT: entry:
-; CHECK-NEXT: [[TV1_SROA_0:%.*]] = alloca <2 x float>, align 8
-; CHECK-NEXT: store <2 x float> [[VAL:%.*]], ptr [[TV1_SROA_0]], align 8
-; CHECK-NEXT: [[COND105_IN_I_I:%.*]] = select i1 [[C1:%.*]], ptr null, ptr [[TV1_SROA_0]]
-; CHECK-NEXT: [[COND105_I_I:%.*]] = load float, ptr [[COND105_IN_I_I]], align 8
-; CHECK-NEXT: ret void
+; CHECK-PRESERVE-CFG-LABEL: @PR16651.2(
+; CHECK-PRESERVE-CFG-NEXT: entry:
+; CHECK-PRESERVE-CFG-NEXT: [[TV1_SROA_0:%.*]] = alloca <2 x float>, align 8
+; CHECK-PRESERVE-CFG-NEXT: store <2 x float> [[VAL:%.*]], ptr [[TV1_SROA_0]], align 8
+; CHECK-PRESERVE-CFG-NEXT: [[COND105_IN_I_I:%.*]] = select i1 [[C1:%.*]], ptr null, ptr [[TV1_SROA_0]]
+; CHECK-PRESERVE-CFG-NEXT: [[COND105_I_I:%.*]] = load float, ptr [[COND105_IN_I_I]], align 8
+; CHECK-PRESERVE-CFG-NEXT: ret void
+;
+; CHECK-MODIFY-CFG-LABEL: @PR16651.2(
+; CHECK-MODIFY-CFG-NEXT: entry:
+; CHECK-MODIFY-CFG-NEXT: [[TV1_SROA_0_0_VEC_EXTRACT:%.*]] = extractelement <2 x float> [[VAL:%.*]], i32 0
+; CHECK-MODIFY-CFG-NEXT: br i1 [[C1:%.*]], label [[ENTRY_THEN:%.*]], label [[ENTRY_CONT:%.*]]
+; CHECK-MODIFY-CFG: entry.then:
+; CHECK-MODIFY-CFG-NEXT: [[COND105_I_I_THEN_VAL:%.*]] = load float, ptr null, align 8
+; CHECK-MODIFY-CFG-NEXT: br label [[ENTRY_CONT]]
+; CHECK-MODIFY-CFG: entry.cont:
+; CHECK-MODIFY-CFG-NEXT: [[COND105_I_I:%.*]] = phi float [ [[COND105_I_I_THEN_VAL]], [[ENTRY_THEN]] ], [ [[TV1_SROA_0_0_VEC_EXTRACT]], [[ENTRY:%.*]] ]
+; CHECK-MODIFY-CFG-NEXT: ret void
;
entry:
%tv1 = alloca { <2 x float>, <2 x float> }, align 8
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
target datalayout = "E-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-n8:16:32:64"
}
declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1)
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -use-dbg-addr -passes=sroa -S < %s | FileCheck %s
+; RUN: opt -use-dbg-addr -passes='sroa<preserve-cfg>' -S < %s | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt -use-dbg-addr -passes='sroa<modify-cfg>' -S < %s | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
; ModuleID = '<stdin>'
source_filename = "newvars.c"
!52 = !{i64 0, i64 4, !53, i64 4, i64 4, !53}
!53 = !{!31, !31, i64 0}
!54 = !DILocation(line: 14, column: 1, scope: !8)
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; Test that SROA can deal with allocas that have more than one
; dbg.declare hanging off of it.
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
source_filename = "/tmp/inlinesplit.cpp"
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.15.0"
!24 = distinct !DILocation(line: 10, column: 10, scope: !8)
!25 = !DILocation(line: 6, column: 12, scope: !22, inlinedAt: !24)
!26 = !DILocation(line: 10, column: 3, scope: !8)
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -passes=sroa %s -S | FileCheck %s
+; RUN: opt -passes='sroa<preserve-cfg>' %s -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt -passes='sroa<modify-cfg>' %s -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
%foo = type { [8 x i8], [8 x i8] }
!7 = !DIExpression()
!8 = !DILocation(line: 947, column: 35, scope: !2)
!9 = distinct !DICompileUnit(language: DW_LANG_Julia, file: !3)
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes='bdce,sroa<preserve-cfg>,bdce' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='bdce,sroa<modify-cfg>,bdce' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
+
; SROA fails to rewrite allocs but does rewrite some phis and delete
; dead instructions. Ensure that this invalidates analyses required
; for other passes.
-; RUN: opt < %s -passes=bdce,sroa,bdce -o %t -debug-pass-manager 2>&1 | FileCheck %s
target datalayout = "e-m:e-i64:64-n32:64"
target triple = "powerpc64le-grtev4-linux-gnu"
; Function Attrs: nounwind
define void @H(ptr noalias nocapture readnone, [2 x i64], ptr %ptr, i32 signext %v, i64 %l, i64 %idx, ptr nonnull dereferenceable(32) %ptr2) {
+; CHECK-LABEL: @H(
+; CHECK-NEXT: [[TMP3:%.*]] = alloca [[CLASS_B:%.*]], align 8
+; CHECK-NEXT: [[TMP4:%.*]] = extractvalue [2 x i64] [[TMP1:%.*]], 1
+; CHECK-NEXT: switch i64 [[TMP4]], label [[TMP6:%.*]] [
+; CHECK-NEXT: i64 4, label [[FOO:%.*]]
+; CHECK-NEXT: i64 5, label [[TMP5:%.*]]
+; CHECK-NEXT: ]
+; CHECK: 5:
+; CHECK-NEXT: br label [[TMP12:%.*]]
+; CHECK: 6:
+; CHECK-NEXT: [[TMP7:%.*]] = icmp ugt i64 [[TMP4]], 5
+; CHECK-NEXT: br i1 [[TMP7]], label [[TMP8:%.*]], label [[TMP12]]
+; CHECK: 8:
+; CHECK-NEXT: [[TMP9:%.*]] = load i8, ptr inttoptr (i64 4 to ptr), align 4
+; CHECK-NEXT: [[TMP10:%.*]] = icmp eq i8 [[TMP9]], 47
+; CHECK-NEXT: [[TMP11:%.*]] = select i1 [[TMP10]], i64 5, i64 4
+; CHECK-NEXT: br label [[TMP12]]
+; CHECK: 12:
+; CHECK-NEXT: [[TMP13:%.*]] = phi i64 [ 4, [[TMP5]] ], [ [[TMP11]], [[TMP8]] ], [ 4, [[TMP6]] ]
+; CHECK-NEXT: [[TMP14:%.*]] = icmp ne i64 [[TMP4]], 0
+; CHECK-NEXT: [[TMP15:%.*]] = icmp ugt i64 [[TMP4]], [[TMP13]]
+; CHECK-NEXT: [[TMP16:%.*]] = and i1 [[TMP14]], [[TMP15]]
+; CHECK-NEXT: br i1 [[TMP16]], label [[TMP17:%.*]], label [[A_EXIT:%.*]]
+; CHECK: 17:
+; CHECK-NEXT: [[TMP18:%.*]] = tail call ptr @memchr(ptr [[PTR:%.*]], i32 signext [[V:%.*]], i64 [[L:%.*]])
+; CHECK-NEXT: [[TMP19:%.*]] = icmp eq ptr [[TMP18]], null
+; CHECK-NEXT: [[TMP20:%.*]] = sext i1 [[TMP19]] to i64
+; CHECK-NEXT: br label [[A_EXIT]]
+; CHECK: a.exit:
+; CHECK-NEXT: [[TMP21:%.*]] = phi i64 [ -1, [[TMP12]] ], [ [[TMP20]], [[TMP17]] ]
+; CHECK-NEXT: [[TMP22:%.*]] = inttoptr i64 0 to ptr
+; CHECK-NEXT: [[TMP23:%.*]] = sub nsw i64 [[TMP21]], [[TMP13]]
+; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[TMP3]])
+; CHECK-NEXT: [[TMP24:%.*]] = icmp ult i64 [[TMP23]], 2
+; CHECK-NEXT: br i1 [[TMP24]], label [[G_EXIT:%.*]], label [[TMP25:%.*]]
+; CHECK: 25:
+; CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds i8, ptr [[TMP22]], i64 [[IDX:%.*]]
+; CHECK-NEXT: [[TMP27:%.*]] = icmp eq ptr [[TMP26]], null
+; CHECK-NEXT: br i1 [[TMP27]], label [[TMP28:%.*]], label [[TMP29:%.*]]
+; CHECK: 28:
+; CHECK-NEXT: unreachable
+; CHECK: 29:
+; CHECK-NEXT: call void @D(ptr nonnull sret([[CLASS_B]]) [[TMP3]], ptr nonnull dereferenceable(32) [[PTR2:%.*]])
+; CHECK-NEXT: br label [[G_EXIT]]
+; CHECK: G.exit:
+; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[TMP3]])
+; CHECK-NEXT: br label [[FOO]]
+; CHECK: foo:
+; CHECK-NEXT: ret void
+;
%3 = alloca %class.b, align 8
%.sroa.0 = alloca i64, align 8
store i64 0, ptr %.sroa.0, align 8
; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.end.p0(i64, ptr nocapture)
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
-; CHECK: {{.*}}
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
+
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-n8:16:32:64"
define { i32, i32 } @test0(i32 %x, i32 %y, { i32, i32 } %v) {
store volatile { i32, i32 } %result, ptr %b
ret { i32, i32 } %result
}
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
declare void @llvm.assume(i1)
declare void @llvm.lifetime.start.p0(i64 %size, ptr nocapture %ptr)
call void @llvm.assume(i1 true) ["nonnull"(ptr %A), "align"(ptr %A, i64 2), "nonnull"(ptr %A)]
ret void
}
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -passes=sroa -S -o - < %s | FileCheck %s
+; RUN: opt -passes='sroa<preserve-cfg>' -S -o - < %s | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt -passes='sroa<modify-cfg>' -S -o - < %s | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
%t = type { i32, i32 }
}
!0 = !{}
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
%S = type { [4 x i8] }
%3 = zext i17 %2 to i32
ret i32 %3
}
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
%i32x2 = type { [2 x i32] }
attributes #0 = { alwaysinline nounwind }
attributes #1 = { argmemonly nounwind }
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
;
; Make sure the llvm.access.group meta-data is preserved
; when a load/store is replaced with another load/store by sroa
}
!0 = distinct !{}
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
;
; Make sure the llvm.access.group meta-data is preserved
; when a load/store is replaced with another load/store by sroa
!4 = distinct !{!4, !5, !"_ZNK7ComplexplERKS_: %agg.result"}
!5 = distinct !{!5, !"_ZNK7ComplexplERKS_"}
!11 = distinct !{}
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s --check-prefix=CHECK
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
define i32 @alloca_used_in_call(ptr %data, i64 %n) {
; CHECK-LABEL: @alloca_used_in_call(
declare dso_local i32 @__gxx_personality_v0(...)
declare void @llvm.memcpy.p0.p0.i32(ptr, ptr, i32, i1)
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -passes=sroa -S < %s | FileCheck %s
+; RUN: opt -passes='sroa<preserve-cfg>' -S < %s | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt -passes='sroa<modify-cfg>' -S < %s | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
; This test checks that SROA does not introduce ptrtoint and inttoptr
; casts from and to non-integral pointers. The "ni:4" bit in the
}
declare void @llvm.memset.p0.i64(ptr, i8, i64, i1)
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-n8:16:32:64"
define i32 @test1() {
; Don't speculate a load based on an earlier volatile operation.
define i8 @volatile_select(ptr %p, i1 %b) {
-; CHECK-LABEL: @volatile_select(
-; CHECK-NEXT: [[P2:%.*]] = alloca i8, align 1
-; CHECK-NEXT: store i8 0, ptr [[P2]], align 1
-; CHECK-NEXT: store volatile i8 0, ptr [[P:%.*]], align 1
-; CHECK-NEXT: [[PX:%.*]] = select i1 [[B:%.*]], ptr [[P]], ptr [[P2]]
-; CHECK-NEXT: [[V2:%.*]] = load i8, ptr [[PX]], align 1
-; CHECK-NEXT: ret i8 [[V2]]
+; CHECK-PRESERVE-CFG-LABEL: @volatile_select(
+; CHECK-PRESERVE-CFG-NEXT: [[P2:%.*]] = alloca i8, align 1
+; CHECK-PRESERVE-CFG-NEXT: store i8 0, ptr [[P2]], align 1
+; CHECK-PRESERVE-CFG-NEXT: store volatile i8 0, ptr [[P:%.*]], align 1
+; CHECK-PRESERVE-CFG-NEXT: [[PX:%.*]] = select i1 [[B:%.*]], ptr [[P]], ptr [[P2]]
+; CHECK-PRESERVE-CFG-NEXT: [[V2:%.*]] = load i8, ptr [[PX]], align 1
+; CHECK-PRESERVE-CFG-NEXT: ret i8 [[V2]]
+;
+; CHECK-MODIFY-CFG-LABEL: @volatile_select(
+; CHECK-MODIFY-CFG-NEXT: store volatile i8 0, ptr [[P:%.*]], align 1
+; CHECK-MODIFY-CFG-NEXT: br i1 [[B:%.*]], label [[DOTTHEN:%.*]], label [[DOTCONT:%.*]]
+; CHECK-MODIFY-CFG: .then:
+; CHECK-MODIFY-CFG-NEXT: [[V2_THEN_VAL:%.*]] = load i8, ptr [[P]], align 1
+; CHECK-MODIFY-CFG-NEXT: br label [[DOTCONT]]
+; CHECK-MODIFY-CFG: .cont:
+; CHECK-MODIFY-CFG-NEXT: [[V2:%.*]] = phi i8 [ [[V2_THEN_VAL]], [[DOTTHEN]] ], [ 0, [[TMP0:%.*]] ]
+; CHECK-MODIFY-CFG-NEXT: ret i8 [[V2]]
;
%p2 = alloca i8
store i8 0, ptr %p2
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown"
store i32 0, ptr %tmp11, align 4
unreachable
}
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -S -passes=sroa < %s | FileCheck %s
+; RUN: opt -S -passes='sroa<preserve-cfg>' < %s | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt -S -passes='sroa<modify-cfg>' < %s | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
%pair = type { i32, i32 }
declare i32 @__gxx_personality_v0(...)
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg)
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -passes=sroa < %s -S | FileCheck %s
+; RUN: opt -passes='sroa<preserve-cfg>' < %s -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt -passes='sroa<modify-cfg>' < %s -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
define void @f(i1 %i) {
; CHECK-LABEL: @f(
declare void @use32(i32)
declare void @use64(i64)
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-n8:16:32:64"
@a = external global i16, align 1
cleanup7: ; preds = %cleanup
ret void
}
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
target datalayout = "e-p:64:64:64:32"
%struct.test = type { %struct.basic, %struct.basic }
}
declare void @llvm.memcpy.p0.p0.i32(ptr nocapture writeonly, ptr nocapture readonly, i32, i1)
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
target datalayout = "E-m:e-i64:64-n32:64"
target triple = "powerpc64-unknown-linux-gnu"
attributes #0 = { nounwind }
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux"
}
declare void @llvm.lifetime.end.p0(i64, ptr nocapture)
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
target datalayout = "E-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-n32:64-S128"
target triple = "sparcv9-sun-solaris"
%rc = add i16 %_tmp13, %_tmp16
ret i16 %rc
}
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
;
; Make sure that SROA doesn't lose nonnull metadata
; on loads from allocas that get optimized out.
}
!0 = !{}
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
; This test checks that SROA runs mem2reg on scalable vectors.
}
declare void @llvm.memcpy.p0.p0.i64(ptr nocapture, ptr nocapture, i64, i1) nounwind
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -S -passes=sroa < %s | FileCheck %s
+; RUN: opt -S -passes='sroa<preserve-cfg>' < %s | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt -S -passes='sroa<modify-cfg>' < %s | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
%pair = type { i32, i32 }
}
define i32 @test_sroa_select_gep_poison(i1 %cond) {
-; CHECK-LABEL: @test_sroa_select_gep_poison(
-; CHECK-NEXT: bb:
-; CHECK-NEXT: [[A_SROA_0:%.*]] = alloca i32, align 4
-; CHECK-NEXT: [[SELECT_SROA_SEL:%.*]] = select i1 [[COND:%.*]], ptr [[A_SROA_0]], ptr poison
-; CHECK-NEXT: [[LOAD:%.*]] = load i32, ptr [[SELECT_SROA_SEL]], align 4
-; CHECK-NEXT: ret i32 [[LOAD]]
+; CHECK-PRESERVE-CFG-LABEL: @test_sroa_select_gep_poison(
+; CHECK-PRESERVE-CFG-NEXT: bb:
+; CHECK-PRESERVE-CFG-NEXT: [[A_SROA_0:%.*]] = alloca i32, align 4
+; CHECK-PRESERVE-CFG-NEXT: [[SELECT_SROA_SEL:%.*]] = select i1 [[COND:%.*]], ptr [[A_SROA_0]], ptr poison
+; CHECK-PRESERVE-CFG-NEXT: [[LOAD:%.*]] = load i32, ptr [[SELECT_SROA_SEL]], align 4
+; CHECK-PRESERVE-CFG-NEXT: ret i32 [[LOAD]]
+;
+; CHECK-MODIFY-CFG-LABEL: @test_sroa_select_gep_poison(
+; CHECK-MODIFY-CFG-NEXT: bb:
+; CHECK-MODIFY-CFG-NEXT: br i1 [[COND:%.*]], label [[BB_CONT:%.*]], label [[BB_ELSE:%.*]]
+; CHECK-MODIFY-CFG: bb.else:
+; CHECK-MODIFY-CFG-NEXT: [[LOAD_ELSE_VAL:%.*]] = load i32, ptr poison, align 4
+; CHECK-MODIFY-CFG-NEXT: br label [[BB_CONT]]
+; CHECK-MODIFY-CFG: bb.cont:
+; CHECK-MODIFY-CFG-NEXT: [[LOAD:%.*]] = phi i32 [ undef, [[BB:%.*]] ], [ [[LOAD_ELSE_VAL]], [[BB_ELSE]] ]
+; CHECK-MODIFY-CFG-NEXT: ret i32 [[LOAD]]
;
bb:
%a = alloca %pair, align 4
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -S -passes=sroa < %s | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
%st.half = type { half }
; We should recursively evaluate select's.
define i32 @clamp_load_to_constant_range(ptr %data, i64 %indvars.iv) {
-; CHECK-LABEL: @clamp_load_to_constant_range(
-; CHECK-NEXT: [[MIN:%.*]] = alloca i32, align 4
-; CHECK-NEXT: [[MAX:%.*]] = alloca i32, align 4
-; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[DATA:%.*]], i64 [[INDVARS_IV:%.*]]
-; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr [[MIN]])
-; CHECK-NEXT: store i32 0, ptr [[MIN]], align 4
-; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr [[MAX]])
-; CHECK-NEXT: store i32 4095, ptr [[MAX]], align 4
-; CHECK-NEXT: [[I1:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
-; CHECK-NEXT: [[CMP_I_I:%.*]] = icmp slt i32 [[I1]], 0
-; CHECK-NEXT: [[I2:%.*]] = tail call i32 @llvm.smax.i32(i32 [[I1]], i32 0)
-; CHECK-NEXT: [[__B___A_I_I:%.*]] = select i1 [[CMP_I_I]], ptr [[MIN]], ptr [[ARRAYIDX]]
-; CHECK-NEXT: [[CMP_I1_I:%.*]] = icmp ugt i32 [[I2]], 4095
-; CHECK-NEXT: [[__B___A_I2_I:%.*]] = select i1 [[CMP_I1_I]], ptr [[MAX]], ptr [[__B___A_I_I]]
-; CHECK-NEXT: [[I3:%.*]] = load i32, ptr [[__B___A_I2_I]], align 4
-; CHECK-NEXT: ret i32 [[I3]]
+; CHECK-PRESERVE-CFG-LABEL: @clamp_load_to_constant_range(
+; CHECK-PRESERVE-CFG-NEXT: [[MIN:%.*]] = alloca i32, align 4
+; CHECK-PRESERVE-CFG-NEXT: [[MAX:%.*]] = alloca i32, align 4
+; CHECK-PRESERVE-CFG-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[DATA:%.*]], i64 [[INDVARS_IV:%.*]]
+; CHECK-PRESERVE-CFG-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr [[MIN]])
+; CHECK-PRESERVE-CFG-NEXT: store i32 0, ptr [[MIN]], align 4
+; CHECK-PRESERVE-CFG-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr [[MAX]])
+; CHECK-PRESERVE-CFG-NEXT: store i32 4095, ptr [[MAX]], align 4
+; CHECK-PRESERVE-CFG-NEXT: [[I1:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
+; CHECK-PRESERVE-CFG-NEXT: [[CMP_I_I:%.*]] = icmp slt i32 [[I1]], 0
+; CHECK-PRESERVE-CFG-NEXT: [[I2:%.*]] = tail call i32 @llvm.smax.i32(i32 [[I1]], i32 0)
+; CHECK-PRESERVE-CFG-NEXT: [[__B___A_I_I:%.*]] = select i1 [[CMP_I_I]], ptr [[MIN]], ptr [[ARRAYIDX]]
+; CHECK-PRESERVE-CFG-NEXT: [[CMP_I1_I:%.*]] = icmp ugt i32 [[I2]], 4095
+; CHECK-PRESERVE-CFG-NEXT: [[__B___A_I2_I:%.*]] = select i1 [[CMP_I1_I]], ptr [[MAX]], ptr [[__B___A_I_I]]
+; CHECK-PRESERVE-CFG-NEXT: [[I3:%.*]] = load i32, ptr [[__B___A_I2_I]], align 4
+; CHECK-PRESERVE-CFG-NEXT: ret i32 [[I3]]
+;
+; CHECK-MODIFY-CFG-LABEL: @clamp_load_to_constant_range(
+; CHECK-MODIFY-CFG-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[DATA:%.*]], i64 [[INDVARS_IV:%.*]]
+; CHECK-MODIFY-CFG-NEXT: [[I1:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
+; CHECK-MODIFY-CFG-NEXT: [[CMP_I_I:%.*]] = icmp slt i32 [[I1]], 0
+; CHECK-MODIFY-CFG-NEXT: [[I2:%.*]] = tail call i32 @llvm.smax.i32(i32 [[I1]], i32 0)
+; CHECK-MODIFY-CFG-NEXT: [[CMP_I1_I:%.*]] = icmp ugt i32 [[I2]], 4095
+; CHECK-MODIFY-CFG-NEXT: br i1 [[CMP_I1_I]], label [[DOTCONT:%.*]], label [[DOTELSE:%.*]]
+; CHECK-MODIFY-CFG: .else:
+; CHECK-MODIFY-CFG-NEXT: br i1 [[CMP_I_I]], label [[DOTELSE_CONT:%.*]], label [[DOTELSE_ELSE:%.*]]
+; CHECK-MODIFY-CFG: .else.else:
+; CHECK-MODIFY-CFG-NEXT: [[I3_ELSE_VAL_ELSE_VAL:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
+; CHECK-MODIFY-CFG-NEXT: br label [[DOTELSE_CONT]]
+; CHECK-MODIFY-CFG: .else.cont:
+; CHECK-MODIFY-CFG-NEXT: [[I3_ELSE_VAL:%.*]] = phi i32 [ 0, [[DOTELSE]] ], [ [[I3_ELSE_VAL_ELSE_VAL]], [[DOTELSE_ELSE]] ]
+; CHECK-MODIFY-CFG-NEXT: br label [[DOTCONT]]
+; CHECK-MODIFY-CFG: .cont:
+; CHECK-MODIFY-CFG-NEXT: [[I3:%.*]] = phi i32 [ 4095, [[TMP0:%.*]] ], [ [[I3_ELSE_VAL]], [[DOTELSE_CONT]] ]
+; CHECK-MODIFY-CFG-NEXT: ret i32 [[I3]]
;
%min = alloca i32, align 4
%max = alloca i32, align 4
}
define i32 @non_speculatable_load_of_select(i1 %cond, ptr %else.addr) {
-; CHECK-LABEL: @non_speculatable_load_of_select(
-; CHECK-NEXT: entry:
-; CHECK-NEXT: [[MIN:%.*]] = alloca i32, align 4
-; CHECK-NEXT: store i32 0, ptr [[MIN]], align 4
-; CHECK-NEXT: [[ADDR:%.*]] = select i1 [[COND:%.*]], ptr [[MIN]], ptr [[ELSE_ADDR:%.*]], !prof [[PROF0:![0-9]+]]
-; CHECK-NEXT: [[R:%.*]] = load i32, ptr [[ADDR]], align 4
-; CHECK-NEXT: ret i32 [[R]]
+; CHECK-PRESERVE-CFG-LABEL: @non_speculatable_load_of_select(
+; CHECK-PRESERVE-CFG-NEXT: entry:
+; CHECK-PRESERVE-CFG-NEXT: [[MIN:%.*]] = alloca i32, align 4
+; CHECK-PRESERVE-CFG-NEXT: store i32 0, ptr [[MIN]], align 4
+; CHECK-PRESERVE-CFG-NEXT: [[ADDR:%.*]] = select i1 [[COND:%.*]], ptr [[MIN]], ptr [[ELSE_ADDR:%.*]], !prof [[PROF0:![0-9]+]]
+; CHECK-PRESERVE-CFG-NEXT: [[R:%.*]] = load i32, ptr [[ADDR]], align 4
+; CHECK-PRESERVE-CFG-NEXT: ret i32 [[R]]
+;
+; CHECK-MODIFY-CFG-LABEL: @non_speculatable_load_of_select(
+; CHECK-MODIFY-CFG-NEXT: entry:
+; CHECK-MODIFY-CFG-NEXT: br i1 [[COND:%.*]], label [[ENTRY_CONT:%.*]], label [[ENTRY_ELSE:%.*]], !prof [[PROF0:![0-9]+]]
+; CHECK-MODIFY-CFG: entry.else:
+; CHECK-MODIFY-CFG-NEXT: [[R_ELSE_VAL:%.*]] = load i32, ptr [[ELSE_ADDR:%.*]], align 4
+; CHECK-MODIFY-CFG-NEXT: br label [[ENTRY_CONT]]
+; CHECK-MODIFY-CFG: entry.cont:
+; CHECK-MODIFY-CFG-NEXT: [[R:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[R_ELSE_VAL]], [[ENTRY_ELSE]] ]
+; CHECK-MODIFY-CFG-NEXT: ret i32 [[R]]
;
entry:
%min = alloca i32, align 4
ret i32 %r
}
define i32 @non_speculatable_load_of_select_inverted(i1 %cond, ptr %then.addr) {
-; CHECK-LABEL: @non_speculatable_load_of_select_inverted(
-; CHECK-NEXT: entry:
-; CHECK-NEXT: [[MAX:%.*]] = alloca i32, align 4
-; CHECK-NEXT: store i32 4095, ptr [[MAX]], align 4
-; CHECK-NEXT: [[ADDR:%.*]] = select i1 [[COND:%.*]], ptr [[THEN_ADDR:%.*]], ptr [[MAX]], !prof [[PROF0]]
-; CHECK-NEXT: [[R:%.*]] = load i32, ptr [[ADDR]], align 4
-; CHECK-NEXT: ret i32 [[R]]
+; CHECK-PRESERVE-CFG-LABEL: @non_speculatable_load_of_select_inverted(
+; CHECK-PRESERVE-CFG-NEXT: entry:
+; CHECK-PRESERVE-CFG-NEXT: [[MAX:%.*]] = alloca i32, align 4
+; CHECK-PRESERVE-CFG-NEXT: store i32 4095, ptr [[MAX]], align 4
+; CHECK-PRESERVE-CFG-NEXT: [[ADDR:%.*]] = select i1 [[COND:%.*]], ptr [[THEN_ADDR:%.*]], ptr [[MAX]], !prof [[PROF0]]
+; CHECK-PRESERVE-CFG-NEXT: [[R:%.*]] = load i32, ptr [[ADDR]], align 4
+; CHECK-PRESERVE-CFG-NEXT: ret i32 [[R]]
+;
+; CHECK-MODIFY-CFG-LABEL: @non_speculatable_load_of_select_inverted(
+; CHECK-MODIFY-CFG-NEXT: entry:
+; CHECK-MODIFY-CFG-NEXT: br i1 [[COND:%.*]], label [[ENTRY_THEN:%.*]], label [[ENTRY_CONT:%.*]], !prof [[PROF1:![0-9]+]]
+; CHECK-MODIFY-CFG: entry.then:
+; CHECK-MODIFY-CFG-NEXT: [[R_THEN_VAL:%.*]] = load i32, ptr [[THEN_ADDR:%.*]], align 4
+; CHECK-MODIFY-CFG-NEXT: br label [[ENTRY_CONT]]
+; CHECK-MODIFY-CFG: entry.cont:
+; CHECK-MODIFY-CFG-NEXT: [[R:%.*]] = phi i32 [ [[R_THEN_VAL]], [[ENTRY_THEN]] ], [ 4095, [[ENTRY:%.*]] ]
+; CHECK-MODIFY-CFG-NEXT: ret i32 [[R]]
;
entry:
%max = alloca i32, align 4
}
define i32 @non_speculatable_volatile_load_of_select(i1 %cond, ptr %else.addr) {
-; CHECK-LABEL: @non_speculatable_volatile_load_of_select(
-; CHECK-NEXT: entry:
-; CHECK-NEXT: [[MIN:%.*]] = alloca i32, align 4
-; CHECK-NEXT: store i32 0, ptr [[MIN]], align 4
-; CHECK-NEXT: [[ADDR:%.*]] = select i1 [[COND:%.*]], ptr [[MIN]], ptr [[ELSE_ADDR:%.*]], !prof [[PROF0]]
-; CHECK-NEXT: [[R:%.*]] = load volatile i32, ptr [[ADDR]], align 4
-; CHECK-NEXT: ret i32 [[R]]
+; CHECK-PRESERVE-CFG-LABEL: @non_speculatable_volatile_load_of_select(
+; CHECK-PRESERVE-CFG-NEXT: entry:
+; CHECK-PRESERVE-CFG-NEXT: [[MIN:%.*]] = alloca i32, align 4
+; CHECK-PRESERVE-CFG-NEXT: store i32 0, ptr [[MIN]], align 4
+; CHECK-PRESERVE-CFG-NEXT: [[ADDR:%.*]] = select i1 [[COND:%.*]], ptr [[MIN]], ptr [[ELSE_ADDR:%.*]], !prof [[PROF0]]
+; CHECK-PRESERVE-CFG-NEXT: [[R:%.*]] = load volatile i32, ptr [[ADDR]], align 4
+; CHECK-PRESERVE-CFG-NEXT: ret i32 [[R]]
+;
+; CHECK-MODIFY-CFG-LABEL: @non_speculatable_volatile_load_of_select(
+; CHECK-MODIFY-CFG-NEXT: entry:
+; CHECK-MODIFY-CFG-NEXT: [[MIN:%.*]] = alloca i32, align 4
+; CHECK-MODIFY-CFG-NEXT: store i32 0, ptr [[MIN]], align 4
+; CHECK-MODIFY-CFG-NEXT: [[ADDR:%.*]] = select i1 [[COND:%.*]], ptr [[MIN]], ptr [[ELSE_ADDR:%.*]], !prof [[PROF1]]
+; CHECK-MODIFY-CFG-NEXT: [[R:%.*]] = load volatile i32, ptr [[ADDR]], align 4
+; CHECK-MODIFY-CFG-NEXT: ret i32 [[R]]
;
entry:
%min = alloca i32, align 4
ret i32 %r
}
define i32 @non_speculatable_volatile_load_of_select_inverted(i1 %cond, ptr %then.addr) {
-; CHECK-LABEL: @non_speculatable_volatile_load_of_select_inverted(
-; CHECK-NEXT: entry:
-; CHECK-NEXT: [[MAX:%.*]] = alloca i32, align 4
-; CHECK-NEXT: store i32 4095, ptr [[MAX]], align 4
-; CHECK-NEXT: [[ADDR:%.*]] = select i1 [[COND:%.*]], ptr [[THEN_ADDR:%.*]], ptr [[MAX]], !prof [[PROF0]]
-; CHECK-NEXT: [[R:%.*]] = load volatile i32, ptr [[ADDR]], align 4
-; CHECK-NEXT: ret i32 [[R]]
+; CHECK-PRESERVE-CFG-LABEL: @non_speculatable_volatile_load_of_select_inverted(
+; CHECK-PRESERVE-CFG-NEXT: entry:
+; CHECK-PRESERVE-CFG-NEXT: [[MAX:%.*]] = alloca i32, align 4
+; CHECK-PRESERVE-CFG-NEXT: store i32 4095, ptr [[MAX]], align 4
+; CHECK-PRESERVE-CFG-NEXT: [[ADDR:%.*]] = select i1 [[COND:%.*]], ptr [[THEN_ADDR:%.*]], ptr [[MAX]], !prof [[PROF0]]
+; CHECK-PRESERVE-CFG-NEXT: [[R:%.*]] = load volatile i32, ptr [[ADDR]], align 4
+; CHECK-PRESERVE-CFG-NEXT: ret i32 [[R]]
+;
+; CHECK-MODIFY-CFG-LABEL: @non_speculatable_volatile_load_of_select_inverted(
+; CHECK-MODIFY-CFG-NEXT: entry:
+; CHECK-MODIFY-CFG-NEXT: [[MAX:%.*]] = alloca i32, align 4
+; CHECK-MODIFY-CFG-NEXT: store i32 4095, ptr [[MAX]], align 4
+; CHECK-MODIFY-CFG-NEXT: [[ADDR:%.*]] = select i1 [[COND:%.*]], ptr [[THEN_ADDR:%.*]], ptr [[MAX]], !prof [[PROF1]]
+; CHECK-MODIFY-CFG-NEXT: [[R:%.*]] = load volatile i32, ptr [[ADDR]], align 4
+; CHECK-MODIFY-CFG-NEXT: ret i32 [[R]]
;
entry:
%max = alloca i32, align 4
}
define i32 @non_speculatable_atomic_unord_load_of_select(i1 %cond, ptr %else.addr) {
-; CHECK-LABEL: @non_speculatable_atomic_unord_load_of_select(
-; CHECK-NEXT: entry:
-; CHECK-NEXT: [[MIN:%.*]] = alloca i32, align 4
-; CHECK-NEXT: store i32 0, ptr [[MIN]], align 4
-; CHECK-NEXT: [[ADDR:%.*]] = select i1 [[COND:%.*]], ptr [[MIN]], ptr [[ELSE_ADDR:%.*]], !prof [[PROF0]]
-; CHECK-NEXT: [[R:%.*]] = load atomic i32, ptr [[ADDR]] unordered, align 4
-; CHECK-NEXT: ret i32 [[R]]
+; CHECK-PRESERVE-CFG-LABEL: @non_speculatable_atomic_unord_load_of_select(
+; CHECK-PRESERVE-CFG-NEXT: entry:
+; CHECK-PRESERVE-CFG-NEXT: [[MIN:%.*]] = alloca i32, align 4
+; CHECK-PRESERVE-CFG-NEXT: store i32 0, ptr [[MIN]], align 4
+; CHECK-PRESERVE-CFG-NEXT: [[ADDR:%.*]] = select i1 [[COND:%.*]], ptr [[MIN]], ptr [[ELSE_ADDR:%.*]], !prof [[PROF0]]
+; CHECK-PRESERVE-CFG-NEXT: [[R:%.*]] = load atomic i32, ptr [[ADDR]] unordered, align 4
+; CHECK-PRESERVE-CFG-NEXT: ret i32 [[R]]
+;
+; CHECK-MODIFY-CFG-LABEL: @non_speculatable_atomic_unord_load_of_select(
+; CHECK-MODIFY-CFG-NEXT: entry:
+; CHECK-MODIFY-CFG-NEXT: br i1 [[COND:%.*]], label [[ENTRY_THEN:%.*]], label [[ENTRY_ELSE:%.*]], !prof [[PROF1]]
+; CHECK-MODIFY-CFG: entry.then:
+; CHECK-MODIFY-CFG-NEXT: br label [[ENTRY_CONT:%.*]]
+; CHECK-MODIFY-CFG: entry.else:
+; CHECK-MODIFY-CFG-NEXT: [[R_ELSE_VAL:%.*]] = load atomic i32, ptr [[ELSE_ADDR:%.*]] unordered, align 4
+; CHECK-MODIFY-CFG-NEXT: br label [[ENTRY_CONT]]
+; CHECK-MODIFY-CFG: entry.cont:
+; CHECK-MODIFY-CFG-NEXT: [[R:%.*]] = phi i32 [ 0, [[ENTRY_THEN]] ], [ [[R_ELSE_VAL]], [[ENTRY_ELSE]] ]
+; CHECK-MODIFY-CFG-NEXT: ret i32 [[R]]
;
entry:
%min = alloca i32, align 4
ret i32 %r
}
define i32 @non_speculatable_atomic_unord_load_of_select_inverted(i1 %cond, ptr %then.addr) {
-; CHECK-LABEL: @non_speculatable_atomic_unord_load_of_select_inverted(
-; CHECK-NEXT: entry:
-; CHECK-NEXT: [[MAX:%.*]] = alloca i32, align 4
-; CHECK-NEXT: store i32 4095, ptr [[MAX]], align 4
-; CHECK-NEXT: [[ADDR:%.*]] = select i1 [[COND:%.*]], ptr [[THEN_ADDR:%.*]], ptr [[MAX]], !prof [[PROF0]]
-; CHECK-NEXT: [[R:%.*]] = load atomic i32, ptr [[ADDR]] unordered, align 4
-; CHECK-NEXT: ret i32 [[R]]
+; CHECK-PRESERVE-CFG-LABEL: @non_speculatable_atomic_unord_load_of_select_inverted(
+; CHECK-PRESERVE-CFG-NEXT: entry:
+; CHECK-PRESERVE-CFG-NEXT: [[MAX:%.*]] = alloca i32, align 4
+; CHECK-PRESERVE-CFG-NEXT: store i32 4095, ptr [[MAX]], align 4
+; CHECK-PRESERVE-CFG-NEXT: [[ADDR:%.*]] = select i1 [[COND:%.*]], ptr [[THEN_ADDR:%.*]], ptr [[MAX]], !prof [[PROF0]]
+; CHECK-PRESERVE-CFG-NEXT: [[R:%.*]] = load atomic i32, ptr [[ADDR]] unordered, align 4
+; CHECK-PRESERVE-CFG-NEXT: ret i32 [[R]]
+;
+; CHECK-MODIFY-CFG-LABEL: @non_speculatable_atomic_unord_load_of_select_inverted(
+; CHECK-MODIFY-CFG-NEXT: entry:
+; CHECK-MODIFY-CFG-NEXT: br i1 [[COND:%.*]], label [[ENTRY_THEN:%.*]], label [[ENTRY_ELSE:%.*]], !prof [[PROF1]]
+; CHECK-MODIFY-CFG: entry.then:
+; CHECK-MODIFY-CFG-NEXT: [[R_THEN_VAL:%.*]] = load atomic i32, ptr [[THEN_ADDR:%.*]] unordered, align 4
+; CHECK-MODIFY-CFG-NEXT: br label [[ENTRY_CONT:%.*]]
+; CHECK-MODIFY-CFG: entry.else:
+; CHECK-MODIFY-CFG-NEXT: br label [[ENTRY_CONT]]
+; CHECK-MODIFY-CFG: entry.cont:
+; CHECK-MODIFY-CFG-NEXT: [[R:%.*]] = phi i32 [ [[R_THEN_VAL]], [[ENTRY_THEN]] ], [ 4095, [[ENTRY_ELSE]] ]
+; CHECK-MODIFY-CFG-NEXT: ret i32 [[R]]
;
entry:
%max = alloca i32, align 4
}
define i32 @non_speculatable_load_of_select_outer(i1 %cond_inner, i1 %cond_outer, ptr %data_then, ptr %data_else) {
-; CHECK-LABEL: @non_speculatable_load_of_select_outer(
-; CHECK-NEXT: entry:
-; CHECK-NEXT: [[MIN:%.*]] = alloca i32, align 4
-; CHECK-NEXT: store i32 0, ptr [[MIN]], align 4
-; CHECK-NEXT: [[ADDR_DATA:%.*]] = select i1 [[COND_INNER:%.*]], ptr [[DATA_THEN:%.*]], ptr [[DATA_ELSE:%.*]], !prof [[PROF0]]
-; CHECK-NEXT: [[ADDR:%.*]] = select i1 [[COND_OUTER:%.*]], ptr [[MIN]], ptr [[ADDR_DATA]], !prof [[PROF0]]
-; CHECK-NEXT: [[R:%.*]] = load i32, ptr [[ADDR]], align 4
-; CHECK-NEXT: ret i32 [[R]]
+; CHECK-PRESERVE-CFG-LABEL: @non_speculatable_load_of_select_outer(
+; CHECK-PRESERVE-CFG-NEXT: entry:
+; CHECK-PRESERVE-CFG-NEXT: [[MIN:%.*]] = alloca i32, align 4
+; CHECK-PRESERVE-CFG-NEXT: store i32 0, ptr [[MIN]], align 4
+; CHECK-PRESERVE-CFG-NEXT: [[ADDR_DATA:%.*]] = select i1 [[COND_INNER:%.*]], ptr [[DATA_THEN:%.*]], ptr [[DATA_ELSE:%.*]], !prof [[PROF0]]
+; CHECK-PRESERVE-CFG-NEXT: [[ADDR:%.*]] = select i1 [[COND_OUTER:%.*]], ptr [[MIN]], ptr [[ADDR_DATA]], !prof [[PROF0]]
+; CHECK-PRESERVE-CFG-NEXT: [[R:%.*]] = load i32, ptr [[ADDR]], align 4
+; CHECK-PRESERVE-CFG-NEXT: ret i32 [[R]]
+;
+; CHECK-MODIFY-CFG-LABEL: @non_speculatable_load_of_select_outer(
+; CHECK-MODIFY-CFG-NEXT: entry:
+; CHECK-MODIFY-CFG-NEXT: [[ADDR_DATA:%.*]] = select i1 [[COND_INNER:%.*]], ptr [[DATA_THEN:%.*]], ptr [[DATA_ELSE:%.*]], !prof [[PROF1]]
+; CHECK-MODIFY-CFG-NEXT: br i1 [[COND_OUTER:%.*]], label [[ENTRY_CONT:%.*]], label [[ENTRY_ELSE:%.*]], !prof [[PROF0]]
+; CHECK-MODIFY-CFG: entry.else:
+; CHECK-MODIFY-CFG-NEXT: [[R_ELSE_VAL:%.*]] = load i32, ptr [[ADDR_DATA]], align 4
+; CHECK-MODIFY-CFG-NEXT: br label [[ENTRY_CONT]]
+; CHECK-MODIFY-CFG: entry.cont:
+; CHECK-MODIFY-CFG-NEXT: [[R:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[R_ELSE_VAL]], [[ENTRY_ELSE]] ]
+; CHECK-MODIFY-CFG-NEXT: ret i32 [[R]]
;
entry:
%min = alloca i32, align 4
ret i32 %r
}
define i32 @non_speculatable_load_of_select_outer_inverted(i1 %cond_inner, i1 %cond_outer, ptr %data_then, ptr %data_else) {
-; CHECK-LABEL: @non_speculatable_load_of_select_outer_inverted(
-; CHECK-NEXT: entry:
-; CHECK-NEXT: [[MIN:%.*]] = alloca i32, align 4
-; CHECK-NEXT: store i32 0, ptr [[MIN]], align 4
-; CHECK-NEXT: [[ADDR_DATA:%.*]] = select i1 [[COND_INNER:%.*]], ptr [[DATA_THEN:%.*]], ptr [[DATA_ELSE:%.*]], !prof [[PROF0]]
-; CHECK-NEXT: [[ADDR:%.*]] = select i1 [[COND_OUTER:%.*]], ptr [[ADDR_DATA]], ptr [[MIN]], !prof [[PROF0]]
-; CHECK-NEXT: [[R:%.*]] = load i32, ptr [[ADDR]], align 4
-; CHECK-NEXT: ret i32 [[R]]
+; CHECK-PRESERVE-CFG-LABEL: @non_speculatable_load_of_select_outer_inverted(
+; CHECK-PRESERVE-CFG-NEXT: entry:
+; CHECK-PRESERVE-CFG-NEXT: [[MIN:%.*]] = alloca i32, align 4
+; CHECK-PRESERVE-CFG-NEXT: store i32 0, ptr [[MIN]], align 4
+; CHECK-PRESERVE-CFG-NEXT: [[ADDR_DATA:%.*]] = select i1 [[COND_INNER:%.*]], ptr [[DATA_THEN:%.*]], ptr [[DATA_ELSE:%.*]], !prof [[PROF0]]
+; CHECK-PRESERVE-CFG-NEXT: [[ADDR:%.*]] = select i1 [[COND_OUTER:%.*]], ptr [[ADDR_DATA]], ptr [[MIN]], !prof [[PROF0]]
+; CHECK-PRESERVE-CFG-NEXT: [[R:%.*]] = load i32, ptr [[ADDR]], align 4
+; CHECK-PRESERVE-CFG-NEXT: ret i32 [[R]]
+;
+; CHECK-MODIFY-CFG-LABEL: @non_speculatable_load_of_select_outer_inverted(
+; CHECK-MODIFY-CFG-NEXT: entry:
+; CHECK-MODIFY-CFG-NEXT: [[ADDR_DATA:%.*]] = select i1 [[COND_INNER:%.*]], ptr [[DATA_THEN:%.*]], ptr [[DATA_ELSE:%.*]], !prof [[PROF1]]
+; CHECK-MODIFY-CFG-NEXT: br i1 [[COND_OUTER:%.*]], label [[ENTRY_THEN:%.*]], label [[ENTRY_CONT:%.*]], !prof [[PROF1]]
+; CHECK-MODIFY-CFG: entry.then:
+; CHECK-MODIFY-CFG-NEXT: [[R_THEN_VAL:%.*]] = load i32, ptr [[ADDR_DATA]], align 4
+; CHECK-MODIFY-CFG-NEXT: br label [[ENTRY_CONT]]
+; CHECK-MODIFY-CFG: entry.cont:
+; CHECK-MODIFY-CFG-NEXT: [[R:%.*]] = phi i32 [ [[R_THEN_VAL]], [[ENTRY_THEN]] ], [ 0, [[ENTRY:%.*]] ]
+; CHECK-MODIFY-CFG-NEXT: ret i32 [[R]]
;
entry:
%min = alloca i32, align 4
}
define i32 @non_speculatable_load_of_select_inner(i1 %cond_inner, i1 %cond_outer, ptr %data_else, ptr %min_else) {
-; CHECK-LABEL: @non_speculatable_load_of_select_inner(
-; CHECK-NEXT: entry:
-; CHECK-NEXT: [[MIN:%.*]] = alloca i32, align 4
-; CHECK-NEXT: store i32 0, ptr [[MIN]], align 4
-; CHECK-NEXT: [[MIN_ADDR_DATA:%.*]] = select i1 [[COND_INNER:%.*]], ptr [[MIN]], ptr [[MIN_ELSE:%.*]], !prof [[PROF0]]
-; CHECK-NEXT: [[ADDR:%.*]] = select i1 [[COND_OUTER:%.*]], ptr [[MIN_ADDR_DATA]], ptr [[DATA_ELSE:%.*]], !prof [[PROF0]]
-; CHECK-NEXT: [[R:%.*]] = load i32, ptr [[ADDR]], align 4
-; CHECK-NEXT: ret i32 [[R]]
+; CHECK-PRESERVE-CFG-LABEL: @non_speculatable_load_of_select_inner(
+; CHECK-PRESERVE-CFG-NEXT: entry:
+; CHECK-PRESERVE-CFG-NEXT: [[MIN:%.*]] = alloca i32, align 4
+; CHECK-PRESERVE-CFG-NEXT: store i32 0, ptr [[MIN]], align 4
+; CHECK-PRESERVE-CFG-NEXT: [[MIN_ADDR_DATA:%.*]] = select i1 [[COND_INNER:%.*]], ptr [[MIN]], ptr [[MIN_ELSE:%.*]], !prof [[PROF0]]
+; CHECK-PRESERVE-CFG-NEXT: [[ADDR:%.*]] = select i1 [[COND_OUTER:%.*]], ptr [[MIN_ADDR_DATA]], ptr [[DATA_ELSE:%.*]], !prof [[PROF0]]
+; CHECK-PRESERVE-CFG-NEXT: [[R:%.*]] = load i32, ptr [[ADDR]], align 4
+; CHECK-PRESERVE-CFG-NEXT: ret i32 [[R]]
+;
+; CHECK-MODIFY-CFG-LABEL: @non_speculatable_load_of_select_inner(
+; CHECK-MODIFY-CFG-NEXT: entry:
+; CHECK-MODIFY-CFG-NEXT: [[MIN:%.*]] = alloca i32, align 4
+; CHECK-MODIFY-CFG-NEXT: store i32 0, ptr [[MIN]], align 4
+; CHECK-MODIFY-CFG-NEXT: [[MIN_ADDR_DATA:%.*]] = select i1 [[COND_INNER:%.*]], ptr [[MIN]], ptr [[MIN_ELSE:%.*]], !prof [[PROF1]]
+; CHECK-MODIFY-CFG-NEXT: [[ADDR:%.*]] = select i1 [[COND_OUTER:%.*]], ptr [[MIN_ADDR_DATA]], ptr [[DATA_ELSE:%.*]], !prof [[PROF1]]
+; CHECK-MODIFY-CFG-NEXT: [[R:%.*]] = load i32, ptr [[ADDR]], align 4
+; CHECK-MODIFY-CFG-NEXT: ret i32 [[R]]
;
entry:
%min = alloca i32, align 4
ret i32 %r
}
define i32 @non_speculatable_load_of_select_inner_inverted(i1 %cond_inner, i1 %cond_outer, ptr %data_else, ptr %min_then) {
-; CHECK-LABEL: @non_speculatable_load_of_select_inner_inverted(
-; CHECK-NEXT: entry:
-; CHECK-NEXT: [[MIN:%.*]] = alloca i32, align 4
-; CHECK-NEXT: store i32 0, ptr [[MIN]], align 4
-; CHECK-NEXT: [[MIN_ADDR_DATA:%.*]] = select i1 [[COND_INNER:%.*]], ptr [[MIN_THEN:%.*]], ptr [[MIN]], !prof [[PROF0]]
-; CHECK-NEXT: [[ADDR:%.*]] = select i1 [[COND_OUTER:%.*]], ptr [[MIN_ADDR_DATA]], ptr [[DATA_ELSE:%.*]], !prof [[PROF0]]
-; CHECK-NEXT: [[R:%.*]] = load i32, ptr [[ADDR]], align 4
-; CHECK-NEXT: ret i32 [[R]]
+; CHECK-PRESERVE-CFG-LABEL: @non_speculatable_load_of_select_inner_inverted(
+; CHECK-PRESERVE-CFG-NEXT: entry:
+; CHECK-PRESERVE-CFG-NEXT: [[MIN:%.*]] = alloca i32, align 4
+; CHECK-PRESERVE-CFG-NEXT: store i32 0, ptr [[MIN]], align 4
+; CHECK-PRESERVE-CFG-NEXT: [[MIN_ADDR_DATA:%.*]] = select i1 [[COND_INNER:%.*]], ptr [[MIN_THEN:%.*]], ptr [[MIN]], !prof [[PROF0]]
+; CHECK-PRESERVE-CFG-NEXT: [[ADDR:%.*]] = select i1 [[COND_OUTER:%.*]], ptr [[MIN_ADDR_DATA]], ptr [[DATA_ELSE:%.*]], !prof [[PROF0]]
+; CHECK-PRESERVE-CFG-NEXT: [[R:%.*]] = load i32, ptr [[ADDR]], align 4
+; CHECK-PRESERVE-CFG-NEXT: ret i32 [[R]]
+;
+; CHECK-MODIFY-CFG-LABEL: @non_speculatable_load_of_select_inner_inverted(
+; CHECK-MODIFY-CFG-NEXT: entry:
+; CHECK-MODIFY-CFG-NEXT: [[MIN:%.*]] = alloca i32, align 4
+; CHECK-MODIFY-CFG-NEXT: store i32 0, ptr [[MIN]], align 4
+; CHECK-MODIFY-CFG-NEXT: [[MIN_ADDR_DATA:%.*]] = select i1 [[COND_INNER:%.*]], ptr [[MIN_THEN:%.*]], ptr [[MIN]], !prof [[PROF1]]
+; CHECK-MODIFY-CFG-NEXT: [[ADDR:%.*]] = select i1 [[COND_OUTER:%.*]], ptr [[MIN_ADDR_DATA]], ptr [[DATA_ELSE:%.*]], !prof [[PROF1]]
+; CHECK-MODIFY-CFG-NEXT: [[R:%.*]] = load i32, ptr [[ADDR]], align 4
+; CHECK-MODIFY-CFG-NEXT: ret i32 [[R]]
;
entry:
%min = alloca i32, align 4
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
target datalayout = "e-p:64:64:64-p1:16:16:16-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-n8:16:32:64"
declare void @llvm.memcpy.p0.p0.i32(ptr nocapture, ptr nocapture, i32, i1) nounwind
%b0 = load i63, ptr %b
ret void
}
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
target datalayout = "e-p:64:64:64-p1:16:16:16-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-f80:128-v64:64:64-v128:128:128-a0:0:64-n8:16:32:64"
declare void @llvm.memcpy.p0.p0.i32(ptr nocapture, ptr nocapture, i32, i1) nounwind
%L2 = load i1, ptr %A
ret i1 %L2
}
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -passes=sroa -S < %s | FileCheck %s
+; RUN: opt -passes='sroa<preserve-cfg>' -S < %s | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt -passes='sroa<modify-cfg>' -S < %s | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
%"struct.a" = type { <8 x half> }
%"struct.b" = type { %"struct.a" }
declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1) nounwind
attributes #0 = { nounwind readonly }
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
%i9 = load float, ptr %i8, align 4
ret float %i9
}
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -S -passes=sroa %s | FileCheck %s
+; RUN: opt -S -passes='sroa<preserve-cfg>' %s | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt -S -passes='sroa<modify-cfg>' %s | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
; SROA should keep `!tbaa.struct` metadata
!8 = !{!"float", !4, i64 0}
!10 = !{i64 0, i64 4, !11, i64 4, i64 4, !11}
!11 = !{!8, !8, i64 0}
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -S -passes=sroa %s | FileCheck %s
+; RUN: opt -S -passes='sroa<preserve-cfg>' %s | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt -S -passes='sroa<modify-cfg>' %s | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
; SROA should correctly offset `!tbaa.struct` metadata
!6 = !{!"Simple C++ TBAA"}
!7 = !{!8, !8, i64 0}
!8 = !{!"int", !5, i64 0}
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -S -passes=sroa %s | FileCheck %s
+; RUN: opt -S -passes='sroa<preserve-cfg>' %s | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt -S -passes='sroa<modify-cfg>' %s | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
; This should not crash
!7 = !{!8, !3, i64 8}
!8 = !{!"_ZTSZN2ax2baEMS_FvvE2an2arE3$_0", !9, i64 0, !3, i64 8}
!9 = !{!"_ZTS2ar"}
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-n8:16:32:64"
define <4 x i64> @vector_ptrtoint({<2 x ptr>, <2 x ptr>} %x) {
ret <16 x i8> %vec
}
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -passes=sroa -S < %s | FileCheck %s
+; RUN: opt -passes='sroa<preserve-cfg>' -S < %s | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt -passes='sroa<modify-cfg>' -S < %s | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
target datalayout = "e-p:64:32-i64:32-v32:32-n32-S64"
declare void @wombat3(<3 x float>) #0
attributes #0 = { nounwind }
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-n8:16:32:64"
define <4 x i1> @vector_bitcast() {
%load = load <64 x i16>, ptr %p
ret <64 x i16> %load
}
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa -S | FileCheck %s
+; RUN: opt < %s -passes='sroa<preserve-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt < %s -passes='sroa<modify-cfg>' -S | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-n8:16:32:64"
%S1 = type { i64, [42 x float] }
declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1)
declare void @llvm.lifetime.end.p0(i64, ptr)
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=sroa
+; RUN: opt -S -passes='sroa<preserve-cfg>' < %s | FileCheck %s --check-prefixes=CHECK,CHECK-PRESERVE-CFG
+; RUN: opt -S -passes='sroa<modify-cfg>' < %s | FileCheck %s --check-prefixes=CHECK,CHECK-MODIFY-CFG
; Make sure we don't crash on this one.
target triple = "x86_64-apple-macosx10.8.0"
define void @foo(i1 %c1, i1 %c2) {
+; CHECK-LABEL: @foo(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br i1 [[C1:%.*]], label [[BB0_EXIT158:%.*]], label [[IF_THEN_I_I_I_I_I138:%.*]]
+; CHECK: if.then.i.i.i.i.i138:
+; CHECK-NEXT: ret void
+; CHECK: bb0.exit158:
+; CHECK-NEXT: br i1 [[C2:%.*]], label [[BB0_EXIT257:%.*]], label [[IF_THEN_I_I_I_I_I237:%.*]]
+; CHECK: if.then.i.i.i.i.i237:
+; CHECK-NEXT: unreachable
+; CHECK: bb0.exit257:
+; CHECK-NEXT: ret void
+;
entry:
%Args.i = alloca <2 x ptr>, align 16
br i1 %c1, label %bb0.exit158, label %if.then.i.i.i.i.i138
%0 = load <2 x ptr>, ptr %Args.i, align 16
ret void
}
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-MODIFY-CFG: {{.*}}
+; CHECK-PRESERVE-CFG: {{.*}}