Skip to content

Commit fe0c218

Browse files
committed
Place conversion to trap-unreachable behind a flag. Also if *all* paths are UB, allow further passes to handle them.
1 parent a2ef15c commit fe0c218

File tree

2 files changed

+334
-288
lines changed

2 files changed

+334
-288
lines changed

llvm/lib/Transforms/Scalar/IsolatePath.cpp

Lines changed: 74 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include "llvm/IR/IRBuilder.h"
4242
#include "llvm/IR/Instructions.h"
4343
#include "llvm/IR/IntrinsicInst.h"
44+
#include "llvm/Support/CommandLine.h"
4445
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
4546
#include "llvm/Transforms/Utils/Cloning.h"
4647

@@ -50,6 +51,10 @@ using namespace llvm;
5051

5152
STATISTIC(NumIsolatedBlocks, "Number of isolated blocks");
5253

54+
static cl::opt<bool> ConvertUBToTrapUnreachable(
55+
"isolate-ub-path-to-trap-unreachable", cl::init(false), cl::Hidden,
56+
cl::desc("Isolate the UB path into one with a 'trap-unreachable' pair."));
57+
5358
/// Look through GEPs to see if the nullptr is accessed.
5459
static bool HasUBAccess(BasicBlock *Parent, GetElementPtrInst *GEP) {
5560
for (Value *V : GEP->materialized_users()) {
@@ -152,81 +157,86 @@ bool IsolatePathPass::ProcessPointerUndefinedBehavior(BasicBlock *BB,
152157
else
153158
NonUBPhiPreds.insert(FirstUBPhiNode->getIncomingBlock(Index++));
154159

160+
if (NonUBPhiPreds.empty())
161+
// All paths have undefined behavior. Other passes will deal with this
162+
// code.
163+
return false;
164+
155165
SmallVector<DominatorTree::UpdateType, 8> Updates;
156166
BasicBlock *UBBlock = nullptr;
157-
if (NonUBPhiPreds.empty()) {
158-
// All PHI node values cause UB in the block. Just add the 'trap'
159-
// instruction without cloning.
160-
UBBlock = BB;
161-
162-
// Remove the block from any successors.
163-
for (BasicBlock *Succ : successors(BB)) {
164-
Succ->removePredecessor(BB);
165-
Updates.push_back({DominatorTree::Delete, BB, Succ});
166-
}
167-
} else {
168-
// Clone the block, isolating the UB instructions on their own path.
169-
ValueToValueMapTy VMap;
170-
UBBlock = CloneBasicBlock(BB, VMap, ".ub.path", BB->getParent());
171-
VMap[BB] = UBBlock;
172-
++NumIsolatedBlocks;
173-
174-
// Replace the UB predecessors' terminators' targets with the new block.
175-
llvm::for_each(UBPhiPreds, [&](BasicBlock *Pred) {
176-
Pred->getTerminator()->replaceSuccessorWith(BB, UBBlock);
167+
168+
// Clone the block, isolating the UB instructions on their own path.
169+
ValueToValueMapTy VMap;
170+
UBBlock = CloneBasicBlock(BB, VMap, ".ub.path", BB->getParent());
171+
VMap[BB] = UBBlock;
172+
++NumIsolatedBlocks;
173+
174+
// Replace the UB predecessors' terminators' targets with the new block.
175+
llvm::for_each(UBPhiPreds, [&](BasicBlock *Pred) {
176+
Pred->getTerminator()->replaceSuccessorWith(BB, UBBlock);
177+
});
178+
179+
// Remove predecessors of isolated paths from the original PHI nodes.
180+
for (PHINode &PN : BB->phis())
181+
PN.removeIncomingValueIf([&](unsigned I) {
182+
return UBPhiPreds.contains(PN.getIncomingBlock(I));
177183
});
178184

179-
// Remove predecessors of isolated paths from the original PHI nodes.
180-
for (PHINode &PN : BB->phis())
181-
PN.removeIncomingValueIf([&](unsigned I) {
182-
return UBPhiPreds.contains(PN.getIncomingBlock(I));
183-
});
184-
185-
// Remove predecessors of valid paths from the isolated path PHI nodes.
186-
for (PHINode &PN : UBBlock->phis())
187-
PN.removeIncomingValueIf([&](unsigned I) {
188-
return NonUBPhiPreds.contains(PN.getIncomingBlock(I));
189-
});
190-
191-
// Rewrite the instructions in the cloned block to refer to the instructions
192-
// in the cloned block.
193-
for (auto &I : *UBBlock) {
194-
RemapDbgRecordRange(BB->getModule(), I.getDbgRecordRange(), VMap,
195-
RF_NoModuleLevelChanges | RF_IgnoreMissingLocals);
196-
RemapInstruction(&I, VMap,
197-
RF_NoModuleLevelChanges | RF_IgnoreMissingLocals);
198-
}
185+
// Remove predecessors of valid paths from the isolated path PHI nodes.
186+
for (PHINode &PN : UBBlock->phis())
187+
PN.removeIncomingValueIf([&](unsigned I) {
188+
return NonUBPhiPreds.contains(PN.getIncomingBlock(I));
189+
});
199190

200-
// Update the dominator tree.
201-
for (auto *Pred : UBPhiPreds) {
202-
Updates.push_back({DominatorTree::Insert, Pred, UBBlock});
203-
Updates.push_back({DominatorTree::Delete, Pred, BB});
204-
}
191+
// Rewrite the instructions in the cloned block to refer to the instructions
192+
// in the cloned block.
193+
for (auto &I : *UBBlock) {
194+
RemapDbgRecordRange(BB->getModule(), I.getDbgRecordRange(), VMap,
195+
RF_NoModuleLevelChanges | RF_IgnoreMissingLocals);
196+
RemapInstruction(&I, VMap,
197+
RF_NoModuleLevelChanges | RF_IgnoreMissingLocals);
205198
}
206199

207-
// Get the index into the block of the first UB instruction.
208-
unsigned UBIndex = 0;
209-
for (auto Iter = BB->begin(); Iter != BB->end(); ++Iter, ++UBIndex)
210-
if (&*Iter == FirstUBInst) {
211-
if (isa<LoadInst>(FirstUBInst))
212-
++UBIndex;
213-
break;
214-
}
200+
// Update the dominator tree.
201+
for (auto *Pred : UBPhiPreds) {
202+
Updates.push_back({DominatorTree::Insert, Pred, UBBlock});
203+
Updates.push_back({DominatorTree::Delete, Pred, BB});
204+
}
205+
206+
if (ConvertUBToTrapUnreachable) {
207+
// Isolate the UB path into a trap-unreachable instruction.
208+
unsigned UBIndex = 0;
215209

216-
// Remove the instructions following the nullptr dereference.
217-
for (unsigned Index = UBBlock->size(); Index > UBIndex; --Index)
218-
UBBlock->rbegin()->eraseFromParent();
210+
// Get the index into the block of the first UB instruction.
211+
for (auto Iter = BB->begin(); Iter != BB->end(); ++Iter, ++UBIndex)
212+
if (&*Iter == FirstUBInst) {
213+
if (isa<LoadInst>(FirstUBInst))
214+
++UBIndex;
215+
break;
216+
}
219217

220-
// Allow the NULL dereference to actually occur so that code that wishes to
221-
// catch the signal can do so.
222-
if (const auto *LI = dyn_cast<LoadInst>(&*UBBlock->rbegin()))
223-
const_cast<LoadInst *>(LI)->setVolatile(true);
218+
// Remove the instructions following the nullptr dereference.
219+
for (unsigned Index = UBBlock->size(); Index > UBIndex; --Index)
220+
UBBlock->rbegin()->eraseFromParent();
221+
222+
// Allow the NULL dereference to actually occur so that code that wishes to
223+
// catch the signal can do so.
224+
if (const auto *LI = dyn_cast<LoadInst>(&*UBBlock->rbegin()))
225+
const_cast<LoadInst *>(LI)->setVolatile(true);
226+
227+
// Add a 'trap()' call.
228+
IRBuilder<> Builder(UBBlock);
229+
Function *TrapDecl =
230+
Intrinsic::getOrInsertDeclaration(BB->getModule(), Intrinsic::trap);
231+
Builder.CreateCall(TrapDecl);
232+
} else {
233+
// Remove all instructions.
234+
for (unsigned Index = UBBlock->size(); Index > 0; --Index)
235+
UBBlock->rbegin()->eraseFromParent();
236+
}
224237

225-
// Add a 'trap()' call followed by an 'unreachable' terminator.
238+
// End in an 'unreachable' instruction.
226239
IRBuilder<> Builder(UBBlock);
227-
Function *TrapDecl =
228-
Intrinsic::getOrInsertDeclaration(BB->getModule(), Intrinsic::trap);
229-
Builder.CreateCall(TrapDecl);
230240
Builder.CreateUnreachable();
231241

232242
if (!Updates.empty())

0 commit comments

Comments
 (0)