diff --git a/llvm/include/llvm/Transforms/Scalar/IsolatePath.h b/llvm/include/llvm/Transforms/Scalar/IsolatePath.h new file mode 100644 index 0000000000000..b8d4aeedcd612 --- /dev/null +++ b/llvm/include/llvm/Transforms/Scalar/IsolatePath.h @@ -0,0 +1,77 @@ +//===- IsolatePath.h - Path isolation for undefined behavior ----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the interface for the IsolatePath pass. +// +// The pass identifies undefined behavior (UB) that is reachable via a PHI node +// that can select a null pointer. It then refactors the control-flow graph to +// isolate the UB-triggering path from the safe paths. +// +// Once isolated, the UB path is terminated, either with an 'unreachable' +// instruction or, optionally, with a 'trap' followed by 'unreachable'. This +// prevents the optimizer from making unsafe assumptions based on the presence +// of UB, which could otherwise lead to miscompilations. +// +// For example, a null pointer dereference is transformed from: +// +// bb: +// %phi = phi ptr [ %valid_ptr, %pred1 ], [ null, %pred2 ] +// %val = load i32, ptr %phi +// +// To: +// +// bb: +// %phi = phi ptr [ %valid_ptr, %pred1 ] +// %val = load i32, ptr %phi +// ... +// +// bb.ub.path: +// %phi.ub = phi ptr [ null, %pred2 ] +// unreachable +// +// Or to this with the optional trap-unreachable flag: +// +// bb.ub.path: +// %phi.ub = phi ptr [ null, %pred2 ] +// %val.ub = load volatile i32, ptr %phi.ub ; Optional trap +// call void @llvm.trap() +// unreachable +// +// This ensures that the presence of the null path does not interfere with +// valid code paths. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_SCALAR_ISOLATEPATH_H +#define LLVM_TRANSFORMS_SCALAR_ISOLATEPATH_H + +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/IR/PassManager.h" + +namespace llvm { + +class BasicBlock; +class DomTreeUpdater; +class Function; +class LoopInfo; + +/// A pass that isolates paths with undefined behavior and converts the UB into +/// a trap or unreachable instruction. +class IsolatePathPass : public PassInfoMixin { + SmallPtrSet SplitUBBlocks; + + bool ProcessPointerUndefinedBehavior(BasicBlock *BB, DomTreeUpdater *DTU, + LoopInfo *LI); + +public: + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); +}; + +} // end namespace llvm + +#endif // LLVM_TRANSFORMS_SCALAR_ISOLATEPATH_H diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp index 874fce05841e2..50b5fdb18e117 100644 --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -277,6 +277,7 @@ #include "llvm/Transforms/Scalar/InferAddressSpaces.h" #include "llvm/Transforms/Scalar/InferAlignment.h" #include "llvm/Transforms/Scalar/InstSimplifyPass.h" +#include "llvm/Transforms/Scalar/IsolatePath.h" #include "llvm/Transforms/Scalar/JumpTableToSwitch.h" #include "llvm/Transforms/Scalar/JumpThreading.h" #include "llvm/Transforms/Scalar/LICM.h" diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp index 98821bb1408a7..66fd96aaac602 100644 --- a/llvm/lib/Passes/PassBuilderPipelines.cpp +++ b/llvm/lib/Passes/PassBuilderPipelines.cpp @@ -98,6 +98,7 @@ #include "llvm/Transforms/Scalar/IndVarSimplify.h" #include "llvm/Transforms/Scalar/InferAlignment.h" #include "llvm/Transforms/Scalar/InstSimplifyPass.h" +#include "llvm/Transforms/Scalar/IsolatePath.h" #include "llvm/Transforms/Scalar/JumpTableToSwitch.h" #include "llvm/Transforms/Scalar/JumpThreading.h" #include "llvm/Transforms/Scalar/LICM.h" @@ -598,6 +599,10 @@ PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level, SimplifyCFGPass(SimplifyCFGOptions().convertSwitchRangeToICmp(true))); } + // Isolate paths with undefined behavior. At this point, all inlinable + // functions should be inlined and constants propagated. + FPM.addPass(IsolatePathPass()); + // Speculative execution if the target has divergent branches; otherwise nop. FPM.addPass(SpeculativeExecutionPass(/* OnlyIfDivergentTarget =*/true)); diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def index dd3dab3425975..ed473ceff9192 100644 --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -448,6 +448,7 @@ FUNCTION_PASS("interleaved-access", InterleavedAccessPass(TM)) FUNCTION_PASS("interleaved-load-combine", InterleavedLoadCombinePass(TM)) FUNCTION_PASS("invalidate", InvalidateAllAnalysesPass()) FUNCTION_PASS("irce", IRCEPass()) +FUNCTION_PASS("isolate-path", IsolatePathPass()); FUNCTION_PASS("jump-threading", JumpThreadingPass()) FUNCTION_PASS("jump-table-to-switch", JumpTableToSwitchPass()); FUNCTION_PASS("kcfi", KCFIPass()) diff --git a/llvm/lib/Transforms/Scalar/CMakeLists.txt b/llvm/lib/Transforms/Scalar/CMakeLists.txt index 84a5b02043d01..a1738aa7485df 100644 --- a/llvm/lib/Transforms/Scalar/CMakeLists.txt +++ b/llvm/lib/Transforms/Scalar/CMakeLists.txt @@ -24,6 +24,7 @@ add_llvm_component_library(LLVMScalarOpts InferAddressSpaces.cpp InferAlignment.cpp InstSimplifyPass.cpp + IsolatePath.cpp JumpThreading.cpp JumpTableToSwitch.cpp LICM.cpp diff --git a/llvm/lib/Transforms/Scalar/IsolatePath.cpp b/llvm/lib/Transforms/Scalar/IsolatePath.cpp new file mode 100644 index 0000000000000..c613a2bea72c2 --- /dev/null +++ b/llvm/lib/Transforms/Scalar/IsolatePath.cpp @@ -0,0 +1,352 @@ +//===- IsolatePath.cpp - Path isolation for undefined behavior -----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This pass identifies undefined behavior (UB) that is reachable via a PHI node +// that can select a null pointer. It then refactors the control-flow graph to +// isolate the UB-triggering path from the safe paths. +// +// Once isolated, the UB path is terminated, either with an 'unreachable' +// instruction or, optionally, with a 'trap' followed by 'unreachable'. This +// prevents the optimizer from making unsafe assumptions based on the presence +// of UB, which could otherwise lead to miscompilations. +// +// For example, a null pointer dereference is transformed from: +// +// bb: +// %phi = phi ptr [ %valid_ptr, %pred1 ], [ null, %pred2 ] +// %val = load i32, ptr %phi +// +// To: +// +// bb: +// %phi = phi ptr [ %valid_ptr, %pred1 ] +// %val = load i32, ptr %phi +// ... +// +// bb.ub.path: +// %phi.ub = phi ptr [ null, %pred2 ] +// unreachable +// +// Or to this with the optional trap-unreachable flag: +// +// bb.ub.path: +// %phi.ub = phi ptr [ null, %pred2 ] +// %val.ub = load volatile i32, ptr %phi.ub ; Optional trap +// call void @llvm.trap() +// unreachable +// +// This ensures that the presence of the null path does not interfere with +// valid code paths. +// +// The pass is conservative and only handles cases where UB is unambiguously +// caused by a null pointer flowing from a PHI node and used within the PHI +// node's block. It does not handle all forms of UB, but could be expanded +// as need be. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Scalar/IsolatePath.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/Analysis/DomTreeUpdater.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/PostDominators.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/Cloning.h" + +using namespace llvm; + +#define DEBUG_TYPE "isolate-path" + +STATISTIC(NumIsolatedBlocks, "Number of isolated blocks"); + +static cl::opt ConvertUBToTrapUnreachable( + "isolate-ub-path-to-trap-unreachable", cl::init(false), cl::Hidden, + cl::desc("Isolate the UB path into one with a 'trap-unreachable' pair.")); + +/// Look through GEPs to see if the nullptr is accessed. +static bool HasUBAccess(BasicBlock *Parent, GetElementPtrInst *GEP) { + for (Value *V : GEP->materialized_users()) { + if (auto *G = dyn_cast(V)) { + if (G->getParent() != Parent) + return false; + return HasUBAccess(Parent, G); + } else if (auto *LI = dyn_cast(V)) { + if (LI->getParent() != Parent) + return false; + if (GEP == LI->getPointerOperand()) + return true; + } else if (auto *SI = dyn_cast(V)) { + if (SI->getParent() != Parent) + return false; + if (GEP == SI->getPointerOperand()) + return true; + } + } + + return false; +} + +static std::pair GetFirstUBInst(BasicBlock *BB) { + // Find PHIs that have 'nullptr' inputs. + SmallPtrSet NullptrPhis; + for (PHINode &PN : BB->phis()) { + if (PN.getType()->isPointerTy() && + llvm::any_of(PN.incoming_values(), + [](Value *V) { return isa(V); })) + NullptrPhis.insert(&PN); + } + if (NullptrPhis.empty()) + return {}; + + // Grab instructions that may be UB and map them to the PHI that makes them + // so. + SmallDenseMap UBInstToPhiMap; + for (PHINode *PN : NullptrPhis) { + for (Value *V : PN->materialized_users()) { + auto *I = dyn_cast(V); + if (!I || I->getParent() != BB) + continue; + + if (isa(I) || isa(I)) { + UBInstToPhiMap[I] = PN; + } else if (auto *GEP = dyn_cast(I)) { + if (HasUBAccess(BB, GEP)) + UBInstToPhiMap[GEP] = PN; + } + } + } + if (UBInstToPhiMap.empty()) + return {}; + + // Get the first UB instruction by iterating through the block. + for (Instruction &I : *BB) { + if (auto It = UBInstToPhiMap.find(&I); It != UBInstToPhiMap.end()) + return {It->second, &I}; + } + + return {}; +} + +// Partition the predecessors of the PHI node that can have a null pointer into +// two sets: one for the UB path and one for the non-UB path. +static void PartitionPredecessors(PHINode *PN, + SmallPtrSetImpl &UBPreds, + SmallPtrSetImpl &NonUBPreds) { + for (unsigned i = 0, e = PN->getNumIncomingValues(); i != e; ++i) { + if (isa(PN->getIncomingValue(i))) + UBPreds.insert(PN->getIncomingBlock(i)); + else + NonUBPreds.insert(PN->getIncomingBlock(i)); + } +} + +// Remove the appropriate incoming values from the PHI nodes in the original +// and UB blocks. +static void FixupPHINodes(BasicBlock *OrigBB, BasicBlock *UBBlock, + const SmallPtrSetImpl &UBPreds, + const SmallPtrSetImpl &NonUBPreds) { + for (PHINode &PN : OrigBB->phis()) + PN.removeIncomingValueIf( + [&](unsigned i) { return UBPreds.count(PN.getIncomingBlock(i)); }); + + for (PHINode &PN : UBBlock->phis()) + PN.removeIncomingValueIf( + [&](unsigned i) { return NonUBPreds.count(PN.getIncomingBlock(i)); }); +} + +// Populate the UB block with either a trap or an unreachable instruction. +static void GenerateUBPathCode(BasicBlock *UBBlock, Instruction *FirstUBInst, + ValueToValueMapTy &VMap) { + if (ConvertUBToTrapUnreachable) { + // Find the cloned version of the first UB instruction. + auto *ClonedFirstUBInst = + cast_or_null(VMap.lookup(FirstUBInst)); + if (!ClonedFirstUBInst) { + // If the instruction wasn't cloned, it's because it was part of a larger + // structure that was cloned, and we need to find the new instruction. + // This can happen with GEPs that are part of a load or store. + for (auto &I : *UBBlock) { + if (I.getOpcode() == FirstUBInst->getOpcode() && + I.getOperand(0) == VMap.lookup(FirstUBInst->getOperand(0))) { + ClonedFirstUBInst = &I; + break; + } + } + } + + // Find the position of the first UB instruction in the new block. + unsigned UBIndex = 0; + if (ClonedFirstUBInst) { + for (auto I = UBBlock->begin(), E = UBBlock->end(); I != E; + ++I, ++UBIndex) { + if (&*I == ClonedFirstUBInst) { + if (isa(ClonedFirstUBInst) || + isa(ClonedFirstUBInst)) + ++UBIndex; + break; + } + } + } + + // Remove instructions after the UB instruction. + while (UBBlock->size() > UBIndex) + UBBlock->rbegin()->eraseFromParent(); + + // Make the faulting instruction volatile to ensure it traps. + if (auto *LI = dyn_cast(&*UBBlock->rbegin())) + LI->setVolatile(true); + else if (auto *SI = dyn_cast(&*UBBlock->rbegin())) + SI->setVolatile(true); + + // Add a trap call. + IRBuilder<> Builder(UBBlock); + Function *TrapDecl = Intrinsic::getOrInsertDeclaration(UBBlock->getModule(), + Intrinsic::trap); + Builder.CreateCall(TrapDecl); + } else { + // Remove all instructions from the UB block. + while (!UBBlock->empty()) + UBBlock->rbegin()->eraseFromParent(); + } + + // Terminate the UB block with an unreachable instruction. + IRBuilder<> Builder(UBBlock); + Builder.CreateUnreachable(); +} + +/// Convert any accesses of a nullptr within the BB into a trap. +bool IsolatePathPass::ProcessPointerUndefinedBehavior(BasicBlock *BB, + DomTreeUpdater *DTU, + LoopInfo *LI) { + if (!BB->canSplitPredecessors()) + return false; + + // Get the first UB instruction and associated PHI node. + auto [FirstUBPhiNode, FirstUBInst] = GetFirstUBInst(BB); + if (!FirstUBInst) + return false; + + // Partition the predecessors based on whether they feed a null pointer to the + // PHI. + SmallPtrSet UBPhiPreds, NonUBPhiPreds; + PartitionPredecessors(FirstUBPhiNode, UBPhiPreds, NonUBPhiPreds); + + if (NonUBPhiPreds.empty()) + // All paths have undefined behavior. Other passes will deal with this. + return false; + + // Clone the block to create a dedicated UB path. + ValueToValueMapTy VMap; + BasicBlock *UBBlock = CloneBasicBlock(BB, VMap, ".ub.path", BB->getParent()); + VMap[BB] = UBBlock; + ++NumIsolatedBlocks; + + // Reroute the predecessors that cause UB to the new UB block. + for (BasicBlock *Pred : UBPhiPreds) + Pred->getTerminator()->replaceSuccessorWith(BB, UBBlock); + + // Clean up the PHI nodes in both the original and the new UB block. + FixupPHINodes(BB, UBBlock, UBPhiPreds, NonUBPhiPreds); + + // Remap instructions in the cloned block to use values from the new block. + for (Instruction &I : *UBBlock) { + RemapDbgRecordRange(BB->getModule(), I.getDbgRecordRange(), VMap, + RF_NoModuleLevelChanges | RF_IgnoreMissingLocals); + RemapInstruction(&I, VMap, + RF_NoModuleLevelChanges | RF_IgnoreMissingLocals); + } + + // Update the dominator tree with the new CFG. + SmallVector Updates; + for (auto *Pred : UBPhiPreds) { + Updates.push_back({DominatorTree::Insert, Pred, UBBlock}); + Updates.push_back({DominatorTree::Delete, Pred, BB}); + } + if (!Updates.empty()) + DTU->applyUpdates(Updates); + + // If the original block was in a loop, add the new block to the same loop. + if (LI) + if (Loop *L = LI->getLoopFor(BB)) + L->addBasicBlockToLoop(UBBlock, *LI); + + // Populate the UB block with the appropriate terminating code. + GenerateUBPathCode(UBBlock, FirstUBInst, VMap); + + SplitUBBlocks.insert(UBBlock); + return true; +} + +PreservedAnalyses IsolatePathPass::run(Function &F, + FunctionAnalysisManager &FAM) { + bool Changed = false; + + auto &DT = FAM.getResult(F); + auto &PDT = FAM.getResult(F); + auto *LI = FAM.getCachedResult(F); + DomTreeUpdater DTU(&DT, &PDT, DomTreeUpdater::UpdateStrategy::Eager); + + // Use a worklist of blocks because we'll be adding new blocks to the + // function and potentially processing the same block multiple times. + std::vector Blocks; + Blocks.reserve(F.size()); + llvm::transform(F, std::back_inserter(Blocks), + [](BasicBlock &BB) { return &BB; }); + + while (!Blocks.empty()) { + BasicBlock *BB = Blocks.back(); + Blocks.pop_back(); + if (SplitUBBlocks.contains(BB)) + continue; + + // No PHI nodes. + if (BB->phis().empty()) + continue; + + // We don't handle landing pads or EH pads, as splitting them is complex and + // not a goal of this pass. + if (BB->isLandingPad() || BB->isEHPad()) + continue; + + // Support some of the more common predecessor terminators. + if (llvm::any_of(predecessors(BB), [&](BasicBlock *Pred) { + Instruction *TI = Pred->getTerminator(); + return !isa(TI) && !isa(TI) && + !isa(TI); + })) + continue; + + if (auto *BI = dyn_cast(BB->getTerminator())) + // If a BB has an edge to itself, then duplication of BB could result in + // reallocation of the BB's PHI nodes. + if (llvm::any_of(BI->successors(), + [&](BasicBlock *B) { return B == BB; })) + continue; + + if (ProcessPointerUndefinedBehavior(BB, &DTU, LI)) { + // Reprocess the block to handle further UB instructions. + Blocks.push_back(BB); + Changed = true; + } + } + + if (!Changed) + return PreservedAnalyses::all(); + + PreservedAnalyses PA; + PA.preserve(); + PA.preserve(); + PA.preserve(); + return PA; +} diff --git a/llvm/test/Other/new-pm-defaults.ll b/llvm/test/Other/new-pm-defaults.ll index c554fdbf4c799..491759b3d1e1a 100644 --- a/llvm/test/Other/new-pm-defaults.ll +++ b/llvm/test/Other/new-pm-defaults.ll @@ -156,6 +156,8 @@ ; CHECK-O-NEXT: Running pass: EarlyCSEPass ; CHECK-O-NEXT: Running analysis: MemorySSAAnalysis ; CHECK-O-NEXT: Running analysis: AAManager +; CHECK-O23SZ-NEXT: Running pass: IsolatePathPass +; CHECK-O23SZ-NEXT: Running analysis: PostDominatorTreeAnalysis ; CHECK-O23SZ-NEXT: Running pass: SpeculativeExecutionPass ; CHECK-O23SZ-NEXT: Running pass: JumpThreadingPass ; CHECK-O23SZ-NEXT: Running analysis: LazyValueAnalysis @@ -215,7 +217,6 @@ ; CHECK-O23SZ-NEXT: Invalidating analysis: LazyValueAnalysis ; CHECK-O1-NEXT: Running pass: CoroElidePass ; CHECK-O-NEXT: Running pass: ADCEPass -; CHECK-O23SZ-NEXT: Running analysis: PostDominatorTreeAnalysis ; CHECK-O23SZ-NEXT: Running pass: MemCpyOptPass ; CHECK-O23SZ-NEXT: Running pass: DSEPass ; CHECK-O23SZ-NEXT: Running pass: MoveAutoInitPass on foo diff --git a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll index 62bb02d9b3c40..6e038785eb763 100644 --- a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll @@ -86,6 +86,8 @@ ; CHECK-O-NEXT: Running pass: EarlyCSEPass ; CHECK-O-NEXT: Running analysis: MemorySSAAnalysis ; CHECK-O-NEXT: Running analysis: AAManager +; CHECK-O23SZ-NEXT: Running pass: IsolatePathPass +; CHECK-O23SZ-NEXT: Running analysis: PostDominatorTreeAnalysis ; CHECK-O23SZ-NEXT: Running pass: SpeculativeExecutionPass ; CHECK-O23SZ-NEXT: Running pass: JumpThreadingPass ; CHECK-O23SZ-NEXT: Running analysis: LazyValueAnalysis @@ -140,7 +142,6 @@ ; CHECK-O23SZ-NEXT: Invalidating analysis: LazyValueAnalysis ; CHECK-O1-NEXT: Running pass: CoroElidePass ; CHECK-O-NEXT: Running pass: ADCEPass -; CHECK-O23SZ-NEXT: Running analysis: PostDominatorTreeAnalysis ; CHECK-O23SZ-NEXT: Running pass: MemCpyOptPass ; CHECK-O23SZ-NEXT: Running pass: DSEPass ; CHECK-O23SZ-NEXT: Running pass: MoveAutoInitPass on foo diff --git a/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll index 0da7a9f73bdce..bec6d67302066 100644 --- a/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll @@ -74,6 +74,7 @@ ; CHECK-O-NEXT: Running pass: EarlyCSEPass ; CHECK-O-NEXT: Running analysis: MemorySSAAnalysis ; CHECK-O-NEXT: Running analysis: AAManager +; CHECK-O23SZ-NEXT: Running pass: IsolatePathPass ; CHECK-O23SZ-NEXT: Running pass: SpeculativeExecutionPass ; CHECK-O23SZ-NEXT: Running pass: JumpThreadingPass ; CHECK-O23SZ-NEXT: Running analysis: LazyValueAnalysis diff --git a/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll index 38b7890682783..69ccba72c92e0 100644 --- a/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll @@ -83,6 +83,7 @@ ; CHECK-O-NEXT: Running pass: EarlyCSEPass ; CHECK-O-NEXT: Running analysis: MemorySSAAnalysis ; CHECK-O-NEXT: Running analysis: AAManager +; CHECK-O23SZ-NEXT: Running pass: IsolatePathPass ; CHECK-O23SZ-NEXT: Running pass: SpeculativeExecutionPass ; CHECK-O23SZ-NEXT: Running pass: JumpThreadingPass ; CHECK-O23SZ-NEXT: Running analysis: LazyValueAnalysis diff --git a/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll b/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll index 5aacd26def2be..68f4d421bb6de 100644 --- a/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll @@ -118,6 +118,8 @@ ; CHECK-O-NEXT: Running pass: EarlyCSEPass ; CHECK-O-NEXT: Running analysis: MemorySSAAnalysis ; CHECK-O-NEXT: Running analysis: AAManager +; CHECK-O23SZ-NEXT: Running pass: IsolatePathPass +; CHECK-O23SZ-NEXT: Running analysis: PostDominatorTreeAnalysis ; CHECK-O23SZ-NEXT: Running pass: SpeculativeExecutionPass ; CHECK-O23SZ-NEXT: Running pass: JumpThreadingPass ; CHECK-O23SZ-NEXT: Running analysis: LazyValueAnalysis @@ -172,7 +174,6 @@ ; CHECK-O23SZ-NEXT: Invalidating analysis: LazyValueAnalysis ; CHECK-O1-NEXT: Running pass: CoroElidePass ; CHECK-O-NEXT: Running pass: ADCEPass -; CHECK-O23SZ-NEXT: Running analysis: PostDominatorTreeAnalysis ; CHECK-O23SZ-NEXT: Running pass: MemCpyOptPass ; CHECK-O23SZ-NEXT: Running pass: DSEPass ; CHECK-O23SZ-NEXT: Running pass: MoveAutoInitPass diff --git a/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll index f6a9406596803..4842b7fb54bc4 100644 --- a/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll @@ -116,6 +116,8 @@ ; CHECK-O-NEXT: Running analysis: ScopedNoAliasAA ; CHECK-O-NEXT: Running analysis: TypeBasedAA ; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy +; CHECK-O23SZ-NEXT: Running pass: IsolatePathPass +; CHECK-O23SZ-NEXT: Running analysis: PostDominatorTreeAnalysis ; CHECK-O23SZ-NEXT: Running pass: SpeculativeExecutionPass ; CHECK-O23SZ-NEXT: Running pass: JumpThreadingPass ; CHECK-O23SZ-NEXT: Running analysis: LazyValueAnalysis @@ -127,7 +129,7 @@ ; CHECK-O-NEXT: Running analysis: BlockFrequencyAnalysis on foo ; CHECK-O-NEXT: Running analysis: BranchProbabilityAnalysis on foo ; CHECK-O-NEXT: Running analysis: LoopAnalysis on foo -; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis on foo +; CHECK-O1-NEXT: Running analysis: PostDominatorTreeAnalysis on foo ; CHECK-O23SZ-NEXT: Running pass: AggressiveInstCombinePass ; CHECK-O1-NEXT: Running pass: LibCallsShrinkWrapPass ; CHECK-O2-NEXT: Running pass: LibCallsShrinkWrapPass diff --git a/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll index 48a9433d24999..8bb9fff9e638f 100644 --- a/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll @@ -88,6 +88,7 @@ ; CHECK-O-NEXT: Running pass: EarlyCSEPass ; CHECK-O-NEXT: Running analysis: MemorySSAAnalysis ; CHECK-O-NEXT: Running analysis: AAManager +; CHECK-O23SZ-NEXT: Running pass: IsolatePathPass ; CHECK-O23SZ-NEXT: Running pass: SpeculativeExecutionPass ; CHECK-O23SZ-NEXT: Running pass: JumpThreadingPass ; CHECK-O23SZ-NEXT: Running analysis: LazyValueAnalysis diff --git a/llvm/test/Transforms/IsolatePath/ub-memory-accesses.ll b/llvm/test/Transforms/IsolatePath/ub-memory-accesses.ll new file mode 100644 index 0000000000000..679b84ef7b39f --- /dev/null +++ b/llvm/test/Transforms/IsolatePath/ub-memory-accesses.ll @@ -0,0 +1,799 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -passes=isolate-path -S < %s | FileCheck --check-prefix=UNREACHABLE %s +; RUN: opt -passes=isolate-path -isolate-ub-path-to-trap-unreachable -S < %s | FileCheck --check-prefix=TRAP-UNREACHABLE %s + +%struct.demangle_component = type { i32, i32 } + +define dso_local ptr @test1(ptr noundef readonly captures(none) %di) { +; UNREACHABLE-LABEL: define dso_local ptr @test1( +; UNREACHABLE-SAME: ptr noundef readonly captures(none) [[DI:%.*]]) { +; UNREACHABLE-NEXT: [[ENTRY:.*:]] +; UNREACHABLE-NEXT: [[NEXT_COMP_I:%.*]] = getelementptr inbounds nuw i8, ptr [[DI]], i64 8 +; UNREACHABLE-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEXT_COMP_I]], align 8 +; UNREACHABLE-NEXT: [[NUM_COMPS_I:%.*]] = getelementptr inbounds nuw i8, ptr [[DI]], i64 12 +; UNREACHABLE-NEXT: [[TMP1:%.*]] = load i32, ptr [[NUM_COMPS_I]], align 4 +; UNREACHABLE-NEXT: [[CMP_NOT_I:%.*]] = icmp slt i32 [[TMP0]], [[TMP1]] +; UNREACHABLE-NEXT: br i1 [[CMP_NOT_I]], label %[[IF_END_I:.*]], label %[[EXIT_UB_PATH:.*]] +; UNREACHABLE: [[IF_END_I]]: +; UNREACHABLE-NEXT: [[TMP2:%.*]] = load ptr, ptr [[DI]], align 8 +; UNREACHABLE-NEXT: [[IDXPROM_I:%.*]] = sext i32 [[TMP0]] to i64 +; UNREACHABLE-NEXT: [[ARRAYIDX_I:%.*]] = getelementptr inbounds [[STRUCT_DEMANGLE_COMPONENT:%.*]], ptr [[TMP2]], i64 [[IDXPROM_I]] +; UNREACHABLE-NEXT: br label %[[EXIT:.*]] +; UNREACHABLE: [[EXIT]]: +; UNREACHABLE-NEXT: [[RETVAL_0_I:%.*]] = phi ptr [ [[ARRAYIDX_I]], %[[IF_END_I]] ] +; UNREACHABLE-NEXT: [[TMP3:%.*]] = load i32, ptr [[RETVAL_0_I]], align 4 +; UNREACHABLE-NEXT: call void @foo(i32 noundef [[TMP3]]) +; UNREACHABLE-NEXT: [[ZZZ:%.*]] = getelementptr inbounds nuw i8, ptr [[RETVAL_0_I]], i64 4 +; UNREACHABLE-NEXT: [[TMP4:%.*]] = load i32, ptr [[ZZZ]], align 4 +; UNREACHABLE-NEXT: call void @bar(i32 noundef [[TMP4]]) +; UNREACHABLE-NEXT: ret ptr [[RETVAL_0_I]] +; UNREACHABLE: [[EXIT_UB_PATH]]: +; UNREACHABLE-NEXT: unreachable +; +; TRAP-UNREACHABLE-LABEL: define dso_local ptr @test1( +; TRAP-UNREACHABLE-SAME: ptr noundef readonly captures(none) [[DI:%.*]]) { +; TRAP-UNREACHABLE-NEXT: [[ENTRY:.*]]: +; TRAP-UNREACHABLE-NEXT: [[NEXT_COMP_I:%.*]] = getelementptr inbounds nuw i8, ptr [[DI]], i64 8 +; TRAP-UNREACHABLE-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEXT_COMP_I]], align 8 +; TRAP-UNREACHABLE-NEXT: [[NUM_COMPS_I:%.*]] = getelementptr inbounds nuw i8, ptr [[DI]], i64 12 +; TRAP-UNREACHABLE-NEXT: [[TMP1:%.*]] = load i32, ptr [[NUM_COMPS_I]], align 4 +; TRAP-UNREACHABLE-NEXT: [[CMP_NOT_I:%.*]] = icmp slt i32 [[TMP0]], [[TMP1]] +; TRAP-UNREACHABLE-NEXT: br i1 [[CMP_NOT_I]], label %[[IF_END_I:.*]], label %[[EXIT_UB_PATH:.*]] +; TRAP-UNREACHABLE: [[IF_END_I]]: +; TRAP-UNREACHABLE-NEXT: [[TMP2:%.*]] = load ptr, ptr [[DI]], align 8 +; TRAP-UNREACHABLE-NEXT: [[IDXPROM_I:%.*]] = sext i32 [[TMP0]] to i64 +; TRAP-UNREACHABLE-NEXT: [[ARRAYIDX_I:%.*]] = getelementptr inbounds [[STRUCT_DEMANGLE_COMPONENT:%.*]], ptr [[TMP2]], i64 [[IDXPROM_I]] +; TRAP-UNREACHABLE-NEXT: br label %[[EXIT:.*]] +; TRAP-UNREACHABLE: [[EXIT]]: +; TRAP-UNREACHABLE-NEXT: [[RETVAL_0_I:%.*]] = phi ptr [ [[ARRAYIDX_I]], %[[IF_END_I]] ] +; TRAP-UNREACHABLE-NEXT: [[TMP3:%.*]] = load i32, ptr [[RETVAL_0_I]], align 4 +; TRAP-UNREACHABLE-NEXT: call void @foo(i32 noundef [[TMP3]]) +; TRAP-UNREACHABLE-NEXT: [[ZZZ:%.*]] = getelementptr inbounds nuw i8, ptr [[RETVAL_0_I]], i64 4 +; TRAP-UNREACHABLE-NEXT: [[TMP4:%.*]] = load i32, ptr [[ZZZ]], align 4 +; TRAP-UNREACHABLE-NEXT: call void @bar(i32 noundef [[TMP4]]) +; TRAP-UNREACHABLE-NEXT: ret ptr [[RETVAL_0_I]] +; TRAP-UNREACHABLE: [[EXIT_UB_PATH]]: +; TRAP-UNREACHABLE-NEXT: [[RETVAL_0_I_UB_PATH:%.*]] = phi ptr [ null, %[[ENTRY]] ] +; TRAP-UNREACHABLE-NEXT: [[TMP5:%.*]] = load volatile i32, ptr [[RETVAL_0_I_UB_PATH]], align 4 +; TRAP-UNREACHABLE-NEXT: call void @llvm.trap() +; TRAP-UNREACHABLE-NEXT: unreachable +; +entry: + %next_comp.i = getelementptr inbounds nuw i8, ptr %di, i64 8 + %0 = load i32, ptr %next_comp.i, align 8 + %num_comps.i = getelementptr inbounds nuw i8, ptr %di, i64 12 + %1 = load i32, ptr %num_comps.i, align 4 + %cmp.not.i = icmp slt i32 %0, %1 + br i1 %cmp.not.i, label %if.end.i, label %exit + +if.end.i: + %2 = load ptr, ptr %di, align 8 + %idxprom.i = sext i32 %0 to i64 + %arrayidx.i = getelementptr inbounds %struct.demangle_component, ptr %2, i64 %idxprom.i + br label %exit + +exit: + %retval.0.i = phi ptr [ %arrayidx.i, %if.end.i ], [ null, %entry ] + %3 = load i32, ptr %retval.0.i, align 4 + call void @foo(i32 noundef %3) + %zzz = getelementptr inbounds nuw i8, ptr %retval.0.i, i64 4 + %4 = load i32, ptr %zzz, align 4 + call void @bar(i32 noundef %4) + ret ptr %retval.0.i +} + +define dso_local ptr @test2(ptr noundef readonly captures(none) %di) { +; UNREACHABLE-LABEL: define dso_local ptr @test2( +; UNREACHABLE-SAME: ptr noundef readonly captures(none) [[DI:%.*]]) { +; UNREACHABLE-NEXT: [[ENTRY:.*]]: +; UNREACHABLE-NEXT: [[NEXT_COMP_I:%.*]] = getelementptr inbounds nuw i8, ptr [[DI]], i64 8 +; UNREACHABLE-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEXT_COMP_I]], align 8 +; UNREACHABLE-NEXT: [[NUM_COMPS_I:%.*]] = getelementptr inbounds nuw i8, ptr [[DI]], i64 12 +; UNREACHABLE-NEXT: [[TMP1:%.*]] = load i32, ptr [[NUM_COMPS_I]], align 4 +; UNREACHABLE-NEXT: [[CMP_NOT_I:%.*]] = icmp slt i32 [[TMP0]], [[TMP1]] +; UNREACHABLE-NEXT: br i1 [[CMP_NOT_I]], label %[[IF_END_I:.*]], label %[[EXIT:.*]] +; UNREACHABLE: [[IF_END_I]]: +; UNREACHABLE-NEXT: [[TMP2:%.*]] = load ptr, ptr [[DI]], align 8 +; UNREACHABLE-NEXT: [[IDXPROM_I:%.*]] = sext i32 [[TMP0]] to i64 +; UNREACHABLE-NEXT: [[ARRAYIDX_I:%.*]] = getelementptr inbounds [[STRUCT_DEMANGLE_COMPONENT:%.*]], ptr [[TMP2]], i64 [[IDXPROM_I]] +; UNREACHABLE-NEXT: [[CMP:%.*]] = icmp slt i32 [[TMP0]], [[TMP1]] +; UNREACHABLE-NEXT: br i1 [[CMP]], label %[[EXIT]], label %[[RETURN:.*]] +; UNREACHABLE: [[EXIT]]: +; UNREACHABLE-NEXT: [[RETVAL_0_I:%.*]] = phi ptr [ null, %[[IF_END_I]] ], [ null, %[[ENTRY]] ] +; UNREACHABLE-NEXT: [[TMP3:%.*]] = load i32, ptr [[RETVAL_0_I]], align 4 +; UNREACHABLE-NEXT: call void @foo(i32 noundef [[TMP3]]) +; UNREACHABLE-NEXT: [[ZZZ:%.*]] = getelementptr inbounds nuw i8, ptr [[RETVAL_0_I]], i64 4 +; UNREACHABLE-NEXT: [[TMP4:%.*]] = load i32, ptr [[ZZZ]], align 4 +; UNREACHABLE-NEXT: call void @bar(i32 noundef [[TMP4]]) +; UNREACHABLE-NEXT: br label %[[RETURN]] +; UNREACHABLE: [[RETURN]]: +; UNREACHABLE-NEXT: ret ptr null +; +; TRAP-UNREACHABLE-LABEL: define dso_local ptr @test2( +; TRAP-UNREACHABLE-SAME: ptr noundef readonly captures(none) [[DI:%.*]]) { +; TRAP-UNREACHABLE-NEXT: [[ENTRY:.*]]: +; TRAP-UNREACHABLE-NEXT: [[NEXT_COMP_I:%.*]] = getelementptr inbounds nuw i8, ptr [[DI]], i64 8 +; TRAP-UNREACHABLE-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEXT_COMP_I]], align 8 +; TRAP-UNREACHABLE-NEXT: [[NUM_COMPS_I:%.*]] = getelementptr inbounds nuw i8, ptr [[DI]], i64 12 +; TRAP-UNREACHABLE-NEXT: [[TMP1:%.*]] = load i32, ptr [[NUM_COMPS_I]], align 4 +; TRAP-UNREACHABLE-NEXT: [[CMP_NOT_I:%.*]] = icmp slt i32 [[TMP0]], [[TMP1]] +; TRAP-UNREACHABLE-NEXT: br i1 [[CMP_NOT_I]], label %[[IF_END_I:.*]], label %[[EXIT:.*]] +; TRAP-UNREACHABLE: [[IF_END_I]]: +; TRAP-UNREACHABLE-NEXT: [[TMP2:%.*]] = load ptr, ptr [[DI]], align 8 +; TRAP-UNREACHABLE-NEXT: [[IDXPROM_I:%.*]] = sext i32 [[TMP0]] to i64 +; TRAP-UNREACHABLE-NEXT: [[ARRAYIDX_I:%.*]] = getelementptr inbounds [[STRUCT_DEMANGLE_COMPONENT:%.*]], ptr [[TMP2]], i64 [[IDXPROM_I]] +; TRAP-UNREACHABLE-NEXT: [[CMP:%.*]] = icmp slt i32 [[TMP0]], [[TMP1]] +; TRAP-UNREACHABLE-NEXT: br i1 [[CMP]], label %[[EXIT]], label %[[RETURN:.*]] +; TRAP-UNREACHABLE: [[EXIT]]: +; TRAP-UNREACHABLE-NEXT: [[RETVAL_0_I:%.*]] = phi ptr [ null, %[[IF_END_I]] ], [ null, %[[ENTRY]] ] +; TRAP-UNREACHABLE-NEXT: [[TMP3:%.*]] = load i32, ptr [[RETVAL_0_I]], align 4 +; TRAP-UNREACHABLE-NEXT: call void @foo(i32 noundef [[TMP3]]) +; TRAP-UNREACHABLE-NEXT: [[ZZZ:%.*]] = getelementptr inbounds nuw i8, ptr [[RETVAL_0_I]], i64 4 +; TRAP-UNREACHABLE-NEXT: [[TMP4:%.*]] = load i32, ptr [[ZZZ]], align 4 +; TRAP-UNREACHABLE-NEXT: call void @bar(i32 noundef [[TMP4]]) +; TRAP-UNREACHABLE-NEXT: br label %[[RETURN]] +; TRAP-UNREACHABLE: [[RETURN]]: +; TRAP-UNREACHABLE-NEXT: ret ptr null +; +entry: + %next_comp.i = getelementptr inbounds nuw i8, ptr %di, i64 8 + %0 = load i32, ptr %next_comp.i, align 8 + %num_comps.i = getelementptr inbounds nuw i8, ptr %di, i64 12 + %1 = load i32, ptr %num_comps.i, align 4 + %cmp.not.i = icmp slt i32 %0, %1 + br i1 %cmp.not.i, label %if.end.i, label %exit + +if.end.i: + %2 = load ptr, ptr %di, align 8 + %idxprom.i = sext i32 %0 to i64 + %arrayidx.i = getelementptr inbounds %struct.demangle_component, ptr %2, i64 %idxprom.i + %cmp = icmp slt i32 %0, %1 + br i1 %cmp, label %exit, label %return + +exit: + %retval.0.i = phi ptr [ null, %if.end.i ], [ null, %entry ] + %3 = load i32, ptr %retval.0.i, align 4 + call void @foo(i32 noundef %3) + %zzz = getelementptr inbounds nuw i8, ptr %retval.0.i, i64 4 + %4 = load i32, ptr %zzz, align 4 + call void @bar(i32 noundef %4) + br label %return + +return: + ret ptr null +} + +define dso_local ptr @test3(i32 %choice, ptr noundef %di, ptr noundef %si) { +; UNREACHABLE-LABEL: define dso_local ptr @test3( +; UNREACHABLE-SAME: i32 [[CHOICE:%.*]], ptr noundef [[DI:%.*]], ptr noundef [[SI:%.*]]) { +; UNREACHABLE-NEXT: [[ENTRY:.*:]] +; UNREACHABLE-NEXT: [[NEXT_COMP_I:%.*]] = getelementptr inbounds nuw i8, ptr [[DI]], i64 8 +; UNREACHABLE-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEXT_COMP_I]], align 8 +; UNREACHABLE-NEXT: [[NUM_COMPS_I:%.*]] = getelementptr inbounds nuw i8, ptr [[DI]], i64 12 +; UNREACHABLE-NEXT: [[TMP1:%.*]] = load i32, ptr [[NUM_COMPS_I]], align 4 +; UNREACHABLE-NEXT: [[CMP_NOT_I:%.*]] = icmp slt i32 [[TMP0]], [[TMP1]] +; UNREACHABLE-NEXT: switch i32 [[CHOICE]], label %[[SW_EPILOG_UB_PATH:.*]] [ +; UNREACHABLE-NEXT: i32 42, label %[[SW_BB:.*]] +; UNREACHABLE-NEXT: i32 3, label %[[SW_BB1:.*]] +; UNREACHABLE-NEXT: ] +; UNREACHABLE: [[SW_BB]]: +; UNREACHABLE-NEXT: [[TMP2:%.*]] = load ptr, ptr [[DI]], align 8 +; UNREACHABLE-NEXT: [[IDXPROM_I:%.*]] = sext i32 [[TMP0]] to i64 +; UNREACHABLE-NEXT: [[ARRAYIDX_I:%.*]] = getelementptr inbounds [[STRUCT_DEMANGLE_COMPONENT:%.*]], ptr [[TMP2]], i64 [[IDXPROM_I]] +; UNREACHABLE-NEXT: br label %[[SW_EPILOG:.*]] +; UNREACHABLE: [[SW_BB1]]: +; UNREACHABLE-NEXT: [[SI_VAL:%.*]] = load ptr, ptr [[SI]], align 8 +; UNREACHABLE-NEXT: [[IDXPROM_I_1:%.*]] = sext i32 [[TMP0]] to i64 +; UNREACHABLE-NEXT: [[ARRAYIDX_I_1:%.*]] = getelementptr inbounds [[STRUCT_DEMANGLE_COMPONENT]], ptr [[SI_VAL]], i64 [[IDXPROM_I_1]] +; UNREACHABLE-NEXT: br label %[[SW_EPILOG]] +; UNREACHABLE: [[SW_EPILOG]]: +; UNREACHABLE-NEXT: [[RETVAL_0_I:%.*]] = phi ptr [ [[ARRAYIDX_I]], %[[SW_BB]] ], [ [[ARRAYIDX_I_1]], %[[SW_BB1]] ] +; UNREACHABLE-NEXT: [[TMP3:%.*]] = load i32, ptr [[RETVAL_0_I]], align 4 +; UNREACHABLE-NEXT: call void @foo(i32 noundef [[TMP3]]) +; UNREACHABLE-NEXT: [[ZZZ:%.*]] = getelementptr inbounds nuw i8, ptr [[RETVAL_0_I]], i64 4 +; UNREACHABLE-NEXT: [[TMP4:%.*]] = load i32, ptr [[ZZZ]], align 4 +; UNREACHABLE-NEXT: call void @bar(i32 noundef [[TMP4]]) +; UNREACHABLE-NEXT: br label %[[EXIT:.*]] +; UNREACHABLE: [[EXIT]]: +; UNREACHABLE-NEXT: ret ptr [[RETVAL_0_I]] +; UNREACHABLE: [[SW_EPILOG_UB_PATH]]: +; UNREACHABLE-NEXT: unreachable +; +; TRAP-UNREACHABLE-LABEL: define dso_local ptr @test3( +; TRAP-UNREACHABLE-SAME: i32 [[CHOICE:%.*]], ptr noundef [[DI:%.*]], ptr noundef [[SI:%.*]]) { +; TRAP-UNREACHABLE-NEXT: [[ENTRY:.*]]: +; TRAP-UNREACHABLE-NEXT: [[NEXT_COMP_I:%.*]] = getelementptr inbounds nuw i8, ptr [[DI]], i64 8 +; TRAP-UNREACHABLE-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEXT_COMP_I]], align 8 +; TRAP-UNREACHABLE-NEXT: [[NUM_COMPS_I:%.*]] = getelementptr inbounds nuw i8, ptr [[DI]], i64 12 +; TRAP-UNREACHABLE-NEXT: [[TMP1:%.*]] = load i32, ptr [[NUM_COMPS_I]], align 4 +; TRAP-UNREACHABLE-NEXT: [[CMP_NOT_I:%.*]] = icmp slt i32 [[TMP0]], [[TMP1]] +; TRAP-UNREACHABLE-NEXT: switch i32 [[CHOICE]], label %[[SW_EPILOG_UB_PATH:.*]] [ +; TRAP-UNREACHABLE-NEXT: i32 42, label %[[SW_BB:.*]] +; TRAP-UNREACHABLE-NEXT: i32 3, label %[[SW_BB1:.*]] +; TRAP-UNREACHABLE-NEXT: ] +; TRAP-UNREACHABLE: [[SW_BB]]: +; TRAP-UNREACHABLE-NEXT: [[TMP2:%.*]] = load ptr, ptr [[DI]], align 8 +; TRAP-UNREACHABLE-NEXT: [[IDXPROM_I:%.*]] = sext i32 [[TMP0]] to i64 +; TRAP-UNREACHABLE-NEXT: [[ARRAYIDX_I:%.*]] = getelementptr inbounds [[STRUCT_DEMANGLE_COMPONENT:%.*]], ptr [[TMP2]], i64 [[IDXPROM_I]] +; TRAP-UNREACHABLE-NEXT: br label %[[SW_EPILOG:.*]] +; TRAP-UNREACHABLE: [[SW_BB1]]: +; TRAP-UNREACHABLE-NEXT: [[SI_VAL:%.*]] = load ptr, ptr [[SI]], align 8 +; TRAP-UNREACHABLE-NEXT: [[IDXPROM_I_1:%.*]] = sext i32 [[TMP0]] to i64 +; TRAP-UNREACHABLE-NEXT: [[ARRAYIDX_I_1:%.*]] = getelementptr inbounds [[STRUCT_DEMANGLE_COMPONENT]], ptr [[SI_VAL]], i64 [[IDXPROM_I_1]] +; TRAP-UNREACHABLE-NEXT: br label %[[SW_EPILOG]] +; TRAP-UNREACHABLE: [[SW_EPILOG]]: +; TRAP-UNREACHABLE-NEXT: [[RETVAL_0_I:%.*]] = phi ptr [ [[ARRAYIDX_I]], %[[SW_BB]] ], [ [[ARRAYIDX_I_1]], %[[SW_BB1]] ] +; TRAP-UNREACHABLE-NEXT: [[TMP3:%.*]] = load i32, ptr [[RETVAL_0_I]], align 4 +; TRAP-UNREACHABLE-NEXT: call void @foo(i32 noundef [[TMP3]]) +; TRAP-UNREACHABLE-NEXT: [[ZZZ:%.*]] = getelementptr inbounds nuw i8, ptr [[RETVAL_0_I]], i64 4 +; TRAP-UNREACHABLE-NEXT: [[TMP4:%.*]] = load i32, ptr [[ZZZ]], align 4 +; TRAP-UNREACHABLE-NEXT: call void @bar(i32 noundef [[TMP4]]) +; TRAP-UNREACHABLE-NEXT: br label %[[EXIT:.*]] +; TRAP-UNREACHABLE: [[EXIT]]: +; TRAP-UNREACHABLE-NEXT: ret ptr [[RETVAL_0_I]] +; TRAP-UNREACHABLE: [[SW_EPILOG_UB_PATH]]: +; TRAP-UNREACHABLE-NEXT: [[RETVAL_0_I_UB_PATH:%.*]] = phi ptr [ null, %[[ENTRY]] ] +; TRAP-UNREACHABLE-NEXT: [[TMP5:%.*]] = load volatile i32, ptr [[RETVAL_0_I_UB_PATH]], align 4 +; TRAP-UNREACHABLE-NEXT: call void @llvm.trap() +; TRAP-UNREACHABLE-NEXT: unreachable +; +entry: + %next_comp.i = getelementptr inbounds nuw i8, ptr %di, i64 8 + %0 = load i32, ptr %next_comp.i, align 8 + %num_comps.i = getelementptr inbounds nuw i8, ptr %di, i64 12 + %1 = load i32, ptr %num_comps.i, align 4 + %cmp.not.i = icmp slt i32 %0, %1 + switch i32 %choice, label %sw.epilog [ + i32 42, label %sw.bb + i32 3, label %sw.bb1 + ] + +sw.bb: + %2 = load ptr, ptr %di, align 8 + %idxprom.i = sext i32 %0 to i64 + %arrayidx.i = getelementptr inbounds %struct.demangle_component, ptr %2, i64 %idxprom.i + br label %sw.epilog + +sw.bb1: + %si.val = load ptr, ptr %si, align 8 + %idxprom.i.1 = sext i32 %0 to i64 + %arrayidx.i.1 = getelementptr inbounds %struct.demangle_component, ptr %si.val, i64 %idxprom.i.1 + br label %sw.epilog + +sw.epilog: + %retval.0.i = phi ptr [ %arrayidx.i, %sw.bb ], [ %arrayidx.i.1, %sw.bb1], [ null, %entry ] + %3 = load i32, ptr %retval.0.i, align 4 + call void @foo(i32 noundef %3) + %zzz = getelementptr inbounds nuw i8, ptr %retval.0.i, i64 4 + %4 = load i32, ptr %zzz, align 4 + call void @bar(i32 noundef %4) + br label %exit + +exit: + ret ptr %retval.0.i +} + +define dso_local ptr @test4(ptr noundef readonly captures(none) %di) { +; UNREACHABLE-LABEL: define dso_local ptr @test4( +; UNREACHABLE-SAME: ptr noundef readonly captures(none) [[DI:%.*]]) { +; UNREACHABLE-NEXT: [[ENTRY:.*:]] +; UNREACHABLE-NEXT: [[NEXT_COMP_I:%.*]] = getelementptr inbounds nuw i8, ptr [[DI]], i64 8 +; UNREACHABLE-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEXT_COMP_I]], align 8 +; UNREACHABLE-NEXT: [[NUM_COMPS_I:%.*]] = getelementptr inbounds nuw i8, ptr [[DI]], i64 12 +; UNREACHABLE-NEXT: [[TMP1:%.*]] = load i32, ptr [[NUM_COMPS_I]], align 4 +; UNREACHABLE-NEXT: [[CMP_NOT_I:%.*]] = icmp slt i32 [[TMP0]], [[TMP1]] +; UNREACHABLE-NEXT: br i1 [[CMP_NOT_I]], label %[[IF_END_I:.*]], label %[[D_MAKE_EMPTY_EXIT_UB_PATH:.*]] +; UNREACHABLE: [[IF_END_I]]: +; UNREACHABLE-NEXT: [[TMP2:%.*]] = load ptr, ptr [[DI]], align 8 +; UNREACHABLE-NEXT: [[IDXPROM_I:%.*]] = sext i32 [[TMP0]] to i64 +; UNREACHABLE-NEXT: [[ARRAYIDX_I:%.*]] = getelementptr inbounds [[STRUCT_DEMANGLE_COMPONENT:%.*]], ptr [[TMP2]], i64 [[IDXPROM_I]] +; UNREACHABLE-NEXT: br label %[[D_MAKE_EMPTY_EXIT:.*]] +; UNREACHABLE: [[D_MAKE_EMPTY_EXIT]]: +; UNREACHABLE-NEXT: [[RETVAL_0_I:%.*]] = phi ptr [ [[ARRAYIDX_I]], %[[IF_END_I]] ] +; UNREACHABLE-NEXT: store i32 42, ptr [[RETVAL_0_I]], align 4 +; UNREACHABLE-NEXT: ret ptr [[RETVAL_0_I]] +; UNREACHABLE: [[D_MAKE_EMPTY_EXIT_UB_PATH]]: +; UNREACHABLE-NEXT: unreachable +; +; TRAP-UNREACHABLE-LABEL: define dso_local ptr @test4( +; TRAP-UNREACHABLE-SAME: ptr noundef readonly captures(none) [[DI:%.*]]) { +; TRAP-UNREACHABLE-NEXT: [[ENTRY:.*]]: +; TRAP-UNREACHABLE-NEXT: [[NEXT_COMP_I:%.*]] = getelementptr inbounds nuw i8, ptr [[DI]], i64 8 +; TRAP-UNREACHABLE-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEXT_COMP_I]], align 8 +; TRAP-UNREACHABLE-NEXT: [[NUM_COMPS_I:%.*]] = getelementptr inbounds nuw i8, ptr [[DI]], i64 12 +; TRAP-UNREACHABLE-NEXT: [[TMP1:%.*]] = load i32, ptr [[NUM_COMPS_I]], align 4 +; TRAP-UNREACHABLE-NEXT: [[CMP_NOT_I:%.*]] = icmp slt i32 [[TMP0]], [[TMP1]] +; TRAP-UNREACHABLE-NEXT: br i1 [[CMP_NOT_I]], label %[[IF_END_I:.*]], label %[[D_MAKE_EMPTY_EXIT_UB_PATH:.*]] +; TRAP-UNREACHABLE: [[IF_END_I]]: +; TRAP-UNREACHABLE-NEXT: [[TMP2:%.*]] = load ptr, ptr [[DI]], align 8 +; TRAP-UNREACHABLE-NEXT: [[IDXPROM_I:%.*]] = sext i32 [[TMP0]] to i64 +; TRAP-UNREACHABLE-NEXT: [[ARRAYIDX_I:%.*]] = getelementptr inbounds [[STRUCT_DEMANGLE_COMPONENT:%.*]], ptr [[TMP2]], i64 [[IDXPROM_I]] +; TRAP-UNREACHABLE-NEXT: br label %[[D_MAKE_EMPTY_EXIT:.*]] +; TRAP-UNREACHABLE: [[D_MAKE_EMPTY_EXIT]]: +; TRAP-UNREACHABLE-NEXT: [[RETVAL_0_I:%.*]] = phi ptr [ [[ARRAYIDX_I]], %[[IF_END_I]] ] +; TRAP-UNREACHABLE-NEXT: store i32 42, ptr [[RETVAL_0_I]], align 4 +; TRAP-UNREACHABLE-NEXT: ret ptr [[RETVAL_0_I]] +; TRAP-UNREACHABLE: [[D_MAKE_EMPTY_EXIT_UB_PATH]]: +; TRAP-UNREACHABLE-NEXT: [[RETVAL_0_I_UB_PATH:%.*]] = phi ptr [ null, %[[ENTRY]] ] +; TRAP-UNREACHABLE-NEXT: store volatile i32 42, ptr [[RETVAL_0_I_UB_PATH]], align 4 +; TRAP-UNREACHABLE-NEXT: call void @llvm.trap() +; TRAP-UNREACHABLE-NEXT: unreachable +; +entry: + %next_comp.i = getelementptr inbounds nuw i8, ptr %di, i64 8 + %0 = load i32, ptr %next_comp.i, align 8 + %num_comps.i = getelementptr inbounds nuw i8, ptr %di, i64 12 + %1 = load i32, ptr %num_comps.i, align 4 + %cmp.not.i = icmp slt i32 %0, %1 + br i1 %cmp.not.i, label %if.end.i, label %d_make_empty.exit + +if.end.i: ; preds = %entry + %2 = load ptr, ptr %di, align 8 + %idxprom.i = sext i32 %0 to i64 + %arrayidx.i = getelementptr inbounds %struct.demangle_component, ptr %2, i64 %idxprom.i + br label %d_make_empty.exit + +d_make_empty.exit: ; preds = %entry, %if.end.i + %retval.0.i = phi ptr [ %arrayidx.i, %if.end.i ], [ null, %entry ] + store i32 42, ptr %retval.0.i, align 4 + ret ptr %retval.0.i +} + +define i32 @test5() local_unnamed_addr { +; UNREACHABLE-LABEL: define i32 @test5() local_unnamed_addr { +; UNREACHABLE-NEXT: [[ENTRY:.*]]: +; UNREACHABLE-NEXT: [[TMP0:%.*]] = load ptr, ptr null, align 8 +; UNREACHABLE-NEXT: [[TMP1:%.*]] = load i32, ptr inttoptr (i64 128 to ptr), align 8 +; UNREACHABLE-NEXT: switch i32 [[TMP1]], label %[[SW_DEFAULT:.*]] [ +; UNREACHABLE-NEXT: i32 1, label %[[SW_EXIT:.*]] +; UNREACHABLE-NEXT: i32 7, label %[[SW_EXIT]] +; UNREACHABLE-NEXT: i32 8, label %[[SW_EXIT]] +; UNREACHABLE-NEXT: i32 0, label %[[SW_EXIT]] +; UNREACHABLE-NEXT: ] +; UNREACHABLE: [[SW_DEFAULT]]: +; UNREACHABLE-NEXT: br label %[[SW_EXIT_UB_PATH:.*]] +; UNREACHABLE: [[SW_EXIT]]: +; UNREACHABLE-NEXT: [[COMMON_RET1_OP_I_I:%.*]] = phi ptr [ [[TMP0]], %[[ENTRY]] ], [ [[TMP0]], %[[ENTRY]] ], [ [[TMP0]], %[[ENTRY]] ], [ [[TMP0]], %[[ENTRY]] ] +; UNREACHABLE-NEXT: [[DP_I:%.*]] = getelementptr i8, ptr [[COMMON_RET1_OP_I_I]], i64 480 +; UNREACHABLE-NEXT: store i64 0, ptr [[DP_I]], align 8 +; UNREACHABLE-NEXT: ret i32 0 +; UNREACHABLE: [[SW_EXIT_UB_PATH]]: +; UNREACHABLE-NEXT: unreachable +; +; TRAP-UNREACHABLE-LABEL: define i32 @test5() local_unnamed_addr { +; TRAP-UNREACHABLE-NEXT: [[ENTRY:.*]]: +; TRAP-UNREACHABLE-NEXT: [[TMP0:%.*]] = load ptr, ptr null, align 8 +; TRAP-UNREACHABLE-NEXT: [[TMP1:%.*]] = load i32, ptr inttoptr (i64 128 to ptr), align 8 +; TRAP-UNREACHABLE-NEXT: switch i32 [[TMP1]], label %[[SW_DEFAULT:.*]] [ +; TRAP-UNREACHABLE-NEXT: i32 1, label %[[SW_EXIT:.*]] +; TRAP-UNREACHABLE-NEXT: i32 7, label %[[SW_EXIT]] +; TRAP-UNREACHABLE-NEXT: i32 8, label %[[SW_EXIT]] +; TRAP-UNREACHABLE-NEXT: i32 0, label %[[SW_EXIT]] +; TRAP-UNREACHABLE-NEXT: ] +; TRAP-UNREACHABLE: [[SW_DEFAULT]]: +; TRAP-UNREACHABLE-NEXT: br label %[[SW_EXIT_UB_PATH:.*]] +; TRAP-UNREACHABLE: [[SW_EXIT]]: +; TRAP-UNREACHABLE-NEXT: [[COMMON_RET1_OP_I_I:%.*]] = phi ptr [ [[TMP0]], %[[ENTRY]] ], [ [[TMP0]], %[[ENTRY]] ], [ [[TMP0]], %[[ENTRY]] ], [ [[TMP0]], %[[ENTRY]] ] +; TRAP-UNREACHABLE-NEXT: [[DP_I:%.*]] = getelementptr i8, ptr [[COMMON_RET1_OP_I_I]], i64 480 +; TRAP-UNREACHABLE-NEXT: store i64 0, ptr [[DP_I]], align 8 +; TRAP-UNREACHABLE-NEXT: ret i32 0 +; TRAP-UNREACHABLE: [[SW_EXIT_UB_PATH]]: +; TRAP-UNREACHABLE-NEXT: [[COMMON_RET1_OP_I_I_UB_PATH:%.*]] = phi ptr [ null, %[[SW_DEFAULT]] ] +; TRAP-UNREACHABLE-NEXT: call void @llvm.trap() +; TRAP-UNREACHABLE-NEXT: unreachable +; +entry: + %0 = load ptr, ptr null, align 8 + %1 = load i32, ptr inttoptr (i64 128 to ptr), align 8 + switch i32 %1, label %sw.default [ + i32 1, label %sw.exit + i32 7, label %sw.exit + i32 8, label %sw.exit + i32 0, label %sw.exit + ] + +sw.default: ; preds = %entry + br label %sw.exit + +sw.exit: ; preds = %if.else.i.i, %entry, %entry, %entry, %entry + %common.ret1.op.i.i = phi ptr [ null, %sw.default ], [ %0, %entry ], [ %0, %entry ], [ %0, %entry ], [ %0, %entry ] + %dp.i = getelementptr i8, ptr %common.ret1.op.i.i, i64 480 + store i64 0, ptr %dp.i, align 8 + ret i32 0 +} + +define dso_local ptr @test6_multiple_ub(ptr noundef readonly %di) { +; UNREACHABLE-LABEL: define dso_local ptr @test6_multiple_ub( +; UNREACHABLE-SAME: ptr noundef readonly [[DI:%.*]]) { +; UNREACHABLE-NEXT: [[ENTRY:.*:]] +; UNREACHABLE-NEXT: [[CMP_NOT_I:%.*]] = icmp slt i32 0, 1 +; UNREACHABLE-NEXT: br i1 [[CMP_NOT_I]], label %[[IF_END_I:.*]], label %[[EXIT_UB_PATH:.*]] +; UNREACHABLE: [[IF_END_I]]: +; UNREACHABLE-NEXT: [[TMP0:%.*]] = load ptr, ptr [[DI]], align 8 +; UNREACHABLE-NEXT: br label %[[EXIT:.*]] +; UNREACHABLE: [[EXIT]]: +; UNREACHABLE-NEXT: [[RETVAL_0_I:%.*]] = phi ptr [ [[TMP0]], %[[IF_END_I]] ] +; UNREACHABLE-NEXT: [[TMP1:%.*]] = load i32, ptr [[RETVAL_0_I]], align 4 +; UNREACHABLE-NEXT: store i32 [[TMP1]], ptr [[RETVAL_0_I]], align 4 +; UNREACHABLE-NEXT: ret ptr [[RETVAL_0_I]] +; UNREACHABLE: [[EXIT_UB_PATH]]: +; UNREACHABLE-NEXT: unreachable +; +; TRAP-UNREACHABLE-LABEL: define dso_local ptr @test6_multiple_ub( +; TRAP-UNREACHABLE-SAME: ptr noundef readonly [[DI:%.*]]) { +; TRAP-UNREACHABLE-NEXT: [[ENTRY:.*]]: +; TRAP-UNREACHABLE-NEXT: [[CMP_NOT_I:%.*]] = icmp slt i32 0, 1 +; TRAP-UNREACHABLE-NEXT: br i1 [[CMP_NOT_I]], label %[[IF_END_I:.*]], label %[[EXIT_UB_PATH:.*]] +; TRAP-UNREACHABLE: [[IF_END_I]]: +; TRAP-UNREACHABLE-NEXT: [[TMP0:%.*]] = load ptr, ptr [[DI]], align 8 +; TRAP-UNREACHABLE-NEXT: br label %[[EXIT:.*]] +; TRAP-UNREACHABLE: [[EXIT]]: +; TRAP-UNREACHABLE-NEXT: [[RETVAL_0_I:%.*]] = phi ptr [ [[TMP0]], %[[IF_END_I]] ] +; TRAP-UNREACHABLE-NEXT: [[TMP1:%.*]] = load i32, ptr [[RETVAL_0_I]], align 4 +; TRAP-UNREACHABLE-NEXT: store i32 [[TMP1]], ptr [[RETVAL_0_I]], align 4 +; TRAP-UNREACHABLE-NEXT: ret ptr [[RETVAL_0_I]] +; TRAP-UNREACHABLE: [[EXIT_UB_PATH]]: +; TRAP-UNREACHABLE-NEXT: [[RETVAL_0_I_UB_PATH:%.*]] = phi ptr [ null, %[[ENTRY]] ] +; TRAP-UNREACHABLE-NEXT: [[TMP2:%.*]] = load volatile i32, ptr [[RETVAL_0_I_UB_PATH]], align 4 +; TRAP-UNREACHABLE-NEXT: call void @llvm.trap() +; TRAP-UNREACHABLE-NEXT: unreachable +; +entry: + %cmp.not.i = icmp slt i32 0, 1 + br i1 %cmp.not.i, label %if.end.i, label %exit + +if.end.i: + %2 = load ptr, ptr %di, align 8 + br label %exit + +exit: + %retval.0.i = phi ptr [ %2, %if.end.i ], [ null, %entry ] + %3 = load i32, ptr %retval.0.i, align 4 + store i32 %3, ptr %retval.0.i, align 4 + ret ptr %retval.0.i +} + +define dso_local ptr @test7_gep_chain(ptr noundef readonly %di) { +; UNREACHABLE-LABEL: define dso_local ptr @test7_gep_chain( +; UNREACHABLE-SAME: ptr noundef readonly [[DI:%.*]]) { +; UNREACHABLE-NEXT: [[ENTRY:.*:]] +; UNREACHABLE-NEXT: [[CMP_NOT_I:%.*]] = icmp slt i32 0, 1 +; UNREACHABLE-NEXT: br i1 [[CMP_NOT_I]], label %[[IF_END_I:.*]], label %[[EXIT_UB_PATH:.*]] +; UNREACHABLE: [[IF_END_I]]: +; UNREACHABLE-NEXT: [[TMP0:%.*]] = load ptr, ptr [[DI]], align 8 +; UNREACHABLE-NEXT: br label %[[EXIT:.*]] +; UNREACHABLE: [[EXIT]]: +; UNREACHABLE-NEXT: [[RETVAL_0_I:%.*]] = phi ptr [ [[TMP0]], %[[IF_END_I]] ] +; UNREACHABLE-NEXT: [[GEP1:%.*]] = getelementptr inbounds [[STRUCT_DEMANGLE_COMPONENT:%.*]], ptr [[RETVAL_0_I]], i64 0, i32 1 +; UNREACHABLE-NEXT: [[GEP2:%.*]] = getelementptr inbounds i32, ptr [[GEP1]], i64 1 +; UNREACHABLE-NEXT: [[TMP1:%.*]] = load i32, ptr [[GEP2]], align 4 +; UNREACHABLE-NEXT: ret ptr [[RETVAL_0_I]] +; UNREACHABLE: [[EXIT_UB_PATH]]: +; UNREACHABLE-NEXT: unreachable +; +; TRAP-UNREACHABLE-LABEL: define dso_local ptr @test7_gep_chain( +; TRAP-UNREACHABLE-SAME: ptr noundef readonly [[DI:%.*]]) { +; TRAP-UNREACHABLE-NEXT: [[ENTRY:.*]]: +; TRAP-UNREACHABLE-NEXT: [[CMP_NOT_I:%.*]] = icmp slt i32 0, 1 +; TRAP-UNREACHABLE-NEXT: br i1 [[CMP_NOT_I]], label %[[IF_END_I:.*]], label %[[EXIT_UB_PATH:.*]] +; TRAP-UNREACHABLE: [[IF_END_I]]: +; TRAP-UNREACHABLE-NEXT: [[TMP0:%.*]] = load ptr, ptr [[DI]], align 8 +; TRAP-UNREACHABLE-NEXT: br label %[[EXIT:.*]] +; TRAP-UNREACHABLE: [[EXIT]]: +; TRAP-UNREACHABLE-NEXT: [[RETVAL_0_I:%.*]] = phi ptr [ [[TMP0]], %[[IF_END_I]] ] +; TRAP-UNREACHABLE-NEXT: [[GEP1:%.*]] = getelementptr inbounds [[STRUCT_DEMANGLE_COMPONENT:%.*]], ptr [[RETVAL_0_I]], i64 0, i32 1 +; TRAP-UNREACHABLE-NEXT: [[GEP2:%.*]] = getelementptr inbounds i32, ptr [[GEP1]], i64 1 +; TRAP-UNREACHABLE-NEXT: [[TMP1:%.*]] = load i32, ptr [[GEP2]], align 4 +; TRAP-UNREACHABLE-NEXT: ret ptr [[RETVAL_0_I]] +; TRAP-UNREACHABLE: [[EXIT_UB_PATH]]: +; TRAP-UNREACHABLE-NEXT: [[RETVAL_0_I_UB_PATH:%.*]] = phi ptr [ null, %[[ENTRY]] ] +; TRAP-UNREACHABLE-NEXT: call void @llvm.trap() +; TRAP-UNREACHABLE-NEXT: unreachable +; +entry: + %cmp.not.i = icmp slt i32 0, 1 + br i1 %cmp.not.i, label %if.end.i, label %exit + +if.end.i: + %2 = load ptr, ptr %di, align 8 + br label %exit + +exit: + %retval.0.i = phi ptr [ %2, %if.end.i ], [ null, %entry ] + %gep1 = getelementptr inbounds %struct.demangle_component, ptr %retval.0.i, i64 0, i32 1 + %gep2 = getelementptr inbounds i32, ptr %gep1, i64 1 + %3 = load i32, ptr %gep2, align 4 + ret ptr %retval.0.i +} + +define dso_local ptr @test8_no_op(ptr noundef readonly %di) { +; UNREACHABLE-LABEL: define dso_local ptr @test8_no_op( +; UNREACHABLE-SAME: ptr noundef readonly [[DI:%.*]]) { +; UNREACHABLE-NEXT: [[ENTRY:.*]]: +; UNREACHABLE-NEXT: [[CMP_NOT_I:%.*]] = icmp slt i32 0, 1 +; UNREACHABLE-NEXT: br i1 [[CMP_NOT_I]], label %[[IF_END_I:.*]], label %[[EXIT:.*]] +; UNREACHABLE: [[IF_END_I]]: +; UNREACHABLE-NEXT: [[TMP0:%.*]] = load ptr, ptr [[DI]], align 8 +; UNREACHABLE-NEXT: br label %[[EXIT]] +; UNREACHABLE: [[EXIT]]: +; UNREACHABLE-NEXT: [[RETVAL_0_I:%.*]] = phi ptr [ [[TMP0]], %[[IF_END_I]] ], [ null, %[[ENTRY]] ] +; UNREACHABLE-NEXT: ret ptr [[RETVAL_0_I]] +; +; TRAP-UNREACHABLE-LABEL: define dso_local ptr @test8_no_op( +; TRAP-UNREACHABLE-SAME: ptr noundef readonly [[DI:%.*]]) { +; TRAP-UNREACHABLE-NEXT: [[ENTRY:.*]]: +; TRAP-UNREACHABLE-NEXT: [[CMP_NOT_I:%.*]] = icmp slt i32 0, 1 +; TRAP-UNREACHABLE-NEXT: br i1 [[CMP_NOT_I]], label %[[IF_END_I:.*]], label %[[EXIT:.*]] +; TRAP-UNREACHABLE: [[IF_END_I]]: +; TRAP-UNREACHABLE-NEXT: [[TMP0:%.*]] = load ptr, ptr [[DI]], align 8 +; TRAP-UNREACHABLE-NEXT: br label %[[EXIT]] +; TRAP-UNREACHABLE: [[EXIT]]: +; TRAP-UNREACHABLE-NEXT: [[RETVAL_0_I:%.*]] = phi ptr [ [[TMP0]], %[[IF_END_I]] ], [ null, %[[ENTRY]] ] +; TRAP-UNREACHABLE-NEXT: ret ptr [[RETVAL_0_I]] +; +entry: + %cmp.not.i = icmp slt i32 0, 1 + br i1 %cmp.not.i, label %if.end.i, label %exit + +if.end.i: + %2 = load ptr, ptr %di, align 8 + br label %exit + +exit: + %retval.0.i = phi ptr [ %2, %if.end.i ], [ null, %entry ] + ret ptr %retval.0.i +} + +define dso_local ptr @test9_volatile(ptr noundef readonly %di) { +; UNREACHABLE-LABEL: define dso_local ptr @test9_volatile( +; UNREACHABLE-SAME: ptr noundef readonly [[DI:%.*]]) { +; UNREACHABLE-NEXT: [[ENTRY:.*:]] +; UNREACHABLE-NEXT: [[CMP_NOT_I:%.*]] = icmp slt i32 0, 1 +; UNREACHABLE-NEXT: br i1 [[CMP_NOT_I]], label %[[IF_END_I:.*]], label %[[EXIT_UB_PATH:.*]] +; UNREACHABLE: [[IF_END_I]]: +; UNREACHABLE-NEXT: [[TMP0:%.*]] = load ptr, ptr [[DI]], align 8 +; UNREACHABLE-NEXT: br label %[[EXIT:.*]] +; UNREACHABLE: [[EXIT]]: +; UNREACHABLE-NEXT: [[RETVAL_0_I:%.*]] = phi ptr [ [[TMP0]], %[[IF_END_I]] ] +; UNREACHABLE-NEXT: [[TMP1:%.*]] = load volatile i32, ptr [[RETVAL_0_I]], align 4 +; UNREACHABLE-NEXT: ret ptr [[RETVAL_0_I]] +; UNREACHABLE: [[EXIT_UB_PATH]]: +; UNREACHABLE-NEXT: unreachable +; +; TRAP-UNREACHABLE-LABEL: define dso_local ptr @test9_volatile( +; TRAP-UNREACHABLE-SAME: ptr noundef readonly [[DI:%.*]]) { +; TRAP-UNREACHABLE-NEXT: [[ENTRY:.*]]: +; TRAP-UNREACHABLE-NEXT: [[CMP_NOT_I:%.*]] = icmp slt i32 0, 1 +; TRAP-UNREACHABLE-NEXT: br i1 [[CMP_NOT_I]], label %[[IF_END_I:.*]], label %[[EXIT_UB_PATH:.*]] +; TRAP-UNREACHABLE: [[IF_END_I]]: +; TRAP-UNREACHABLE-NEXT: [[TMP0:%.*]] = load ptr, ptr [[DI]], align 8 +; TRAP-UNREACHABLE-NEXT: br label %[[EXIT:.*]] +; TRAP-UNREACHABLE: [[EXIT]]: +; TRAP-UNREACHABLE-NEXT: [[RETVAL_0_I:%.*]] = phi ptr [ [[TMP0]], %[[IF_END_I]] ] +; TRAP-UNREACHABLE-NEXT: [[TMP1:%.*]] = load volatile i32, ptr [[RETVAL_0_I]], align 4 +; TRAP-UNREACHABLE-NEXT: ret ptr [[RETVAL_0_I]] +; TRAP-UNREACHABLE: [[EXIT_UB_PATH]]: +; TRAP-UNREACHABLE-NEXT: [[RETVAL_0_I_UB_PATH:%.*]] = phi ptr [ null, %[[ENTRY]] ] +; TRAP-UNREACHABLE-NEXT: [[TMP2:%.*]] = load volatile i32, ptr [[RETVAL_0_I_UB_PATH]], align 4 +; TRAP-UNREACHABLE-NEXT: call void @llvm.trap() +; TRAP-UNREACHABLE-NEXT: unreachable +; +entry: + %cmp.not.i = icmp slt i32 0, 1 + br i1 %cmp.not.i, label %if.end.i, label %exit + +if.end.i: + %2 = load ptr, ptr %di, align 8 + br label %exit + +exit: + %retval.0.i = phi ptr [ %2, %if.end.i ], [ null, %entry ] + %3 = load volatile i32, ptr %retval.0.i, align 4 + ret ptr %retval.0.i +} + +define dso_local ptr @test10_store(ptr noundef readonly %di) { +; UNREACHABLE-LABEL: define dso_local ptr @test10_store( +; UNREACHABLE-SAME: ptr noundef readonly [[DI:%.*]]) { +; UNREACHABLE-NEXT: [[ENTRY:.*:]] +; UNREACHABLE-NEXT: [[CMP_NOT_I:%.*]] = icmp slt i32 0, 1 +; UNREACHABLE-NEXT: br i1 [[CMP_NOT_I]], label %[[IF_END_I:.*]], label %[[EXIT_UB_PATH:.*]] +; UNREACHABLE: [[IF_END_I]]: +; UNREACHABLE-NEXT: [[TMP0:%.*]] = load ptr, ptr [[DI]], align 8 +; UNREACHABLE-NEXT: br label %[[EXIT:.*]] +; UNREACHABLE: [[EXIT]]: +; UNREACHABLE-NEXT: [[RETVAL_0_I:%.*]] = phi ptr [ [[TMP0]], %[[IF_END_I]] ] +; UNREACHABLE-NEXT: store i32 0, ptr [[RETVAL_0_I]], align 4 +; UNREACHABLE-NEXT: ret ptr [[RETVAL_0_I]] +; UNREACHABLE: [[EXIT_UB_PATH]]: +; UNREACHABLE-NEXT: unreachable +; +; TRAP-UNREACHABLE-LABEL: define dso_local ptr @test10_store( +; TRAP-UNREACHABLE-SAME: ptr noundef readonly [[DI:%.*]]) { +; TRAP-UNREACHABLE-NEXT: [[ENTRY:.*]]: +; TRAP-UNREACHABLE-NEXT: [[CMP_NOT_I:%.*]] = icmp slt i32 0, 1 +; TRAP-UNREACHABLE-NEXT: br i1 [[CMP_NOT_I]], label %[[IF_END_I:.*]], label %[[EXIT_UB_PATH:.*]] +; TRAP-UNREACHABLE: [[IF_END_I]]: +; TRAP-UNREACHABLE-NEXT: [[TMP0:%.*]] = load ptr, ptr [[DI]], align 8 +; TRAP-UNREACHABLE-NEXT: br label %[[EXIT:.*]] +; TRAP-UNREACHABLE: [[EXIT]]: +; TRAP-UNREACHABLE-NEXT: [[RETVAL_0_I:%.*]] = phi ptr [ [[TMP0]], %[[IF_END_I]] ] +; TRAP-UNREACHABLE-NEXT: store i32 0, ptr [[RETVAL_0_I]], align 4 +; TRAP-UNREACHABLE-NEXT: ret ptr [[RETVAL_0_I]] +; TRAP-UNREACHABLE: [[EXIT_UB_PATH]]: +; TRAP-UNREACHABLE-NEXT: [[RETVAL_0_I_UB_PATH:%.*]] = phi ptr [ null, %[[ENTRY]] ] +; TRAP-UNREACHABLE-NEXT: store volatile i32 0, ptr [[RETVAL_0_I_UB_PATH]], align 4 +; TRAP-UNREACHABLE-NEXT: call void @llvm.trap() +; TRAP-UNREACHABLE-NEXT: unreachable +; +entry: + %cmp.not.i = icmp slt i32 0, 1 + br i1 %cmp.not.i, label %if.end.i, label %exit + +if.end.i: + %2 = load ptr, ptr %di, align 8 + br label %exit + +exit: + %retval.0.i = phi ptr [ %2, %if.end.i ], [ null, %entry ] + store i32 0, ptr %retval.0.i, align 4 + ret ptr %retval.0.i +} + +define void @test11_loop_ub(ptr %p, i1 %cond) { +; UNREACHABLE-LABEL: define void @test11_loop_ub( +; UNREACHABLE-SAME: ptr [[P:%.*]], i1 [[COND:%.*]]) { +; UNREACHABLE-NEXT: [[ENTRY:.*:]] +; UNREACHABLE-NEXT: br i1 [[COND]], label %[[SETUP_UB:.*]], label %[[SETUP_OK:.*]] +; UNREACHABLE: [[SETUP_UB]]: +; UNREACHABLE-NEXT: br label %[[LOOP_HEADER:.*]] +; UNREACHABLE: [[SETUP_OK]]: +; UNREACHABLE-NEXT: br label %[[LOOP_HEADER]] +; UNREACHABLE: [[LOOP_HEADER]]: +; UNREACHABLE-NEXT: [[I:%.*]] = phi i32 [ 0, %[[SETUP_UB]] ], [ 0, %[[SETUP_OK]] ], [ [[I_NEXT:%.*]], %[[LOOP_LATCH:.*]] ] +; UNREACHABLE-NEXT: [[PHI_PTR:%.*]] = phi ptr [ null, %[[SETUP_UB]] ], [ [[P]], %[[SETUP_OK]] ], [ [[PHI_PTR]], %[[LOOP_LATCH]] ] +; UNREACHABLE-NEXT: [[CMP:%.*]] = icmp slt i32 [[I]], 10 +; UNREACHABLE-NEXT: br i1 [[CMP]], label %[[LOOP_BODY:.*]], label %[[EXIT:.*]] +; UNREACHABLE: [[LOOP_BODY]]: +; UNREACHABLE-NEXT: [[VAL:%.*]] = load i32, ptr [[PHI_PTR]], align 4 +; UNREACHABLE-NEXT: call void @foo(i32 [[VAL]]) +; UNREACHABLE-NEXT: [[I_NEXT]] = add i32 [[I]], 1 +; UNREACHABLE-NEXT: br label %[[LOOP_LATCH]] +; UNREACHABLE: [[LOOP_LATCH]]: +; UNREACHABLE-NEXT: br label %[[LOOP_HEADER]] +; UNREACHABLE: [[EXIT]]: +; UNREACHABLE-NEXT: ret void +; +; TRAP-UNREACHABLE-LABEL: define void @test11_loop_ub( +; TRAP-UNREACHABLE-SAME: ptr [[P:%.*]], i1 [[COND:%.*]]) { +; TRAP-UNREACHABLE-NEXT: [[ENTRY:.*:]] +; TRAP-UNREACHABLE-NEXT: br i1 [[COND]], label %[[SETUP_UB:.*]], label %[[SETUP_OK:.*]] +; TRAP-UNREACHABLE: [[SETUP_UB]]: +; TRAP-UNREACHABLE-NEXT: br label %[[LOOP_HEADER:.*]] +; TRAP-UNREACHABLE: [[SETUP_OK]]: +; TRAP-UNREACHABLE-NEXT: br label %[[LOOP_HEADER]] +; TRAP-UNREACHABLE: [[LOOP_HEADER]]: +; TRAP-UNREACHABLE-NEXT: [[I:%.*]] = phi i32 [ 0, %[[SETUP_UB]] ], [ 0, %[[SETUP_OK]] ], [ [[I_NEXT:%.*]], %[[LOOP_LATCH:.*]] ] +; TRAP-UNREACHABLE-NEXT: [[PHI_PTR:%.*]] = phi ptr [ null, %[[SETUP_UB]] ], [ [[P]], %[[SETUP_OK]] ], [ [[PHI_PTR]], %[[LOOP_LATCH]] ] +; TRAP-UNREACHABLE-NEXT: [[CMP:%.*]] = icmp slt i32 [[I]], 10 +; TRAP-UNREACHABLE-NEXT: br i1 [[CMP]], label %[[LOOP_BODY:.*]], label %[[EXIT:.*]] +; TRAP-UNREACHABLE: [[LOOP_BODY]]: +; TRAP-UNREACHABLE-NEXT: [[VAL:%.*]] = load i32, ptr [[PHI_PTR]], align 4 +; TRAP-UNREACHABLE-NEXT: call void @foo(i32 [[VAL]]) +; TRAP-UNREACHABLE-NEXT: [[I_NEXT]] = add i32 [[I]], 1 +; TRAP-UNREACHABLE-NEXT: br label %[[LOOP_LATCH]] +; TRAP-UNREACHABLE: [[LOOP_LATCH]]: +; TRAP-UNREACHABLE-NEXT: br label %[[LOOP_HEADER]] +; TRAP-UNREACHABLE: [[EXIT]]: +; TRAP-UNREACHABLE-NEXT: ret void +; +entry: + br i1 %cond, label %setup.ub, label %setup.ok + +setup.ub: + br label %loop.header + +setup.ok: + br label %loop.header + +loop.header: + %i = phi i32 [ 0, %setup.ub ], [ 0, %setup.ok ], [ %i.next, %loop.latch ] + %phi.ptr = phi ptr [ null, %setup.ub ], [ %p, %setup.ok ], [ %phi.ptr, %loop.latch ] + %cmp = icmp slt i32 %i, 10 + br i1 %cmp, label %loop.body, label %exit + +loop.body: + %val = load i32, ptr %phi.ptr, align 4 + call void @foo(i32 %val) + %i.next = add i32 %i, 1 + br label %loop.latch + +loop.latch: + br label %loop.header + +exit: + ret void +} + +define void @test12_switch_terminator(ptr %p, i1 %cond, i32 %val) { +; UNREACHABLE-LABEL: define void @test12_switch_terminator( +; UNREACHABLE-SAME: ptr [[P:%.*]], i1 [[COND:%.*]], i32 [[VAL:%.*]]) { +; UNREACHABLE-NEXT: [[ENTRY:.*:]] +; UNREACHABLE-NEXT: br i1 [[COND]], label %[[UB:.*]], label %[[OK:.*]] +; UNREACHABLE: [[UB]]: +; UNREACHABLE-NEXT: br label %[[TEST_UB_PATH:.*]] +; UNREACHABLE: [[OK]]: +; UNREACHABLE-NEXT: br label %[[TEST:.*]] +; UNREACHABLE: [[TEST]]: +; UNREACHABLE-NEXT: [[PHI_PTR:%.*]] = phi ptr [ [[P]], %[[OK]] ] +; UNREACHABLE-NEXT: [[V:%.*]] = load i32, ptr [[PHI_PTR]], align 4 +; UNREACHABLE-NEXT: switch i32 [[V]], label %[[DEFAULT:.*]] [ +; UNREACHABLE-NEXT: i32 0, label %[[CASE0:.*]] +; UNREACHABLE-NEXT: i32 1, label %[[CASE1:.*]] +; UNREACHABLE-NEXT: ] +; UNREACHABLE: [[CASE0]]: +; UNREACHABLE-NEXT: ret void +; UNREACHABLE: [[CASE1]]: +; UNREACHABLE-NEXT: ret void +; UNREACHABLE: [[DEFAULT]]: +; UNREACHABLE-NEXT: ret void +; UNREACHABLE: [[TEST_UB_PATH]]: +; UNREACHABLE-NEXT: unreachable +; +; TRAP-UNREACHABLE-LABEL: define void @test12_switch_terminator( +; TRAP-UNREACHABLE-SAME: ptr [[P:%.*]], i1 [[COND:%.*]], i32 [[VAL:%.*]]) { +; TRAP-UNREACHABLE-NEXT: [[ENTRY:.*:]] +; TRAP-UNREACHABLE-NEXT: br i1 [[COND]], label %[[UB:.*]], label %[[OK:.*]] +; TRAP-UNREACHABLE: [[UB]]: +; TRAP-UNREACHABLE-NEXT: br label %[[TEST_UB_PATH:.*]] +; TRAP-UNREACHABLE: [[OK]]: +; TRAP-UNREACHABLE-NEXT: br label %[[TEST:.*]] +; TRAP-UNREACHABLE: [[TEST]]: +; TRAP-UNREACHABLE-NEXT: [[PHI_PTR:%.*]] = phi ptr [ [[P]], %[[OK]] ] +; TRAP-UNREACHABLE-NEXT: [[V:%.*]] = load i32, ptr [[PHI_PTR]], align 4 +; TRAP-UNREACHABLE-NEXT: switch i32 [[V]], label %[[DEFAULT:.*]] [ +; TRAP-UNREACHABLE-NEXT: i32 0, label %[[CASE0:.*]] +; TRAP-UNREACHABLE-NEXT: i32 1, label %[[CASE1:.*]] +; TRAP-UNREACHABLE-NEXT: ] +; TRAP-UNREACHABLE: [[CASE0]]: +; TRAP-UNREACHABLE-NEXT: ret void +; TRAP-UNREACHABLE: [[CASE1]]: +; TRAP-UNREACHABLE-NEXT: ret void +; TRAP-UNREACHABLE: [[DEFAULT]]: +; TRAP-UNREACHABLE-NEXT: ret void +; TRAP-UNREACHABLE: [[TEST_UB_PATH]]: +; TRAP-UNREACHABLE-NEXT: [[PHI_PTR_UB_PATH:%.*]] = phi ptr [ null, %[[UB]] ] +; TRAP-UNREACHABLE-NEXT: [[V_UB_PATH:%.*]] = load volatile i32, ptr [[PHI_PTR_UB_PATH]], align 4 +; TRAP-UNREACHABLE-NEXT: call void @llvm.trap() +; TRAP-UNREACHABLE-NEXT: unreachable +; +entry: + br i1 %cond, label %ub, label %ok + +ub: + br label %test + +ok: + br label %test + +test: + %phi.ptr = phi ptr [ null, %ub ], [ %p, %ok ] + %v = load i32, ptr %phi.ptr, align 4 + switch i32 %v, label %default [ + i32 0, label %case0 + i32 1, label %case1 + ] + +case0: + ret void + +case1: + ret void + +default: + ret void +} + +declare void @foo(i32 noundef) + +declare void @bar(i32 noundef)