Skip to content

Commit 3d08ade

Browse files
authored
[ExtendLifetimes] Implement llvm.fake.use to extend variable lifetimes (llvm#86149)
This patch is part of a set of patches that add an `-fextend-lifetimes` flag to clang, which extends the lifetimes of local variables and parameters for improved debuggability. In addition to that flag, the patch series adds a pragma to selectively disable `-fextend-lifetimes`, and an `-fextend-this-ptr` flag which functions as `-fextend-lifetimes` for this pointers only. All changes and tests in these patches were written by Wolfgang Pieb (@wolfy1961), while Stephen Tozer (@SLTozer) has handled review and merging. The extend lifetimes flag is intended to eventually be set on by `-Og`, as discussed in the RFC here: https://discourse.llvm.org/t/rfc-redefine-og-o1-and-add-a-new-level-of-og/72850 This patch implements a new intrinsic instruction in LLVM, `llvm.fake.use` in IR and `FAKE_USE` in MIR, that takes a single operand and has no effect other than "using" its operand, to ensure that its operand remains live until after the fake use. This patch does not emit fake uses anywhere; the next patch in this sequence causes them to be emitted from the clang frontend, such that for each variable (or this) a fake.use operand is inserted at the end of that variable's scope, using that variable's value. This patch covers everything post-frontend, which is largely just the basic plumbing for a new intrinsic/instruction, along with a few steps to preserve the fake uses through optimizations (such as moving them ahead of a tail call or translating them through SROA). Co-authored-by: Stephen Tozer <[email protected]>
1 parent e5e38dd commit 3d08ade

File tree

76 files changed

+1609
-38
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+1609
-38
lines changed

llvm/docs/LangRef.rst

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29477,6 +29477,42 @@ execution, but is unknown at compile time.
2947729477
If the result value does not fit in the result type, then the result is
2947829478
a :ref:`poison value <poisonvalues>`.
2947929479

29480+
.. _llvm_fake_use:
29481+
29482+
'``llvm.fake.use``' Intrinsic
29483+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
29484+
29485+
Syntax:
29486+
"""""""
29487+
29488+
::
29489+
29490+
declare void @llvm.fake.use(...)
29491+
29492+
Overview:
29493+
"""""""""
29494+
29495+
The ``llvm.fake.use`` intrinsic is a no-op. It takes a single
29496+
value as an operand and is treated as a use of that operand, to force the
29497+
optimizer to preserve that value prior to the fake use. This is used for
29498+
extending the lifetimes of variables, where this intrinsic placed at the end of
29499+
a variable's scope helps prevent that variable from being optimized out.
29500+
29501+
Arguments:
29502+
""""""""""
29503+
29504+
The ``llvm.fake.use`` intrinsic takes one argument, which may be any
29505+
function-local SSA value. Note that the signature is variadic so that the
29506+
intrinsic can take any type of argument, but passing more than one argument will
29507+
result in an error.
29508+
29509+
Semantics:
29510+
""""""""""
29511+
29512+
This intrinsic does nothing, but optimizers must consider it a use of its single
29513+
operand and should try to preserve the intrinsic and its position in the
29514+
function.
29515+
2948029516

2948129517
Stack Map Intrinsics
2948229518
--------------------

llvm/include/llvm/Analysis/PtrUseVisitor.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,12 @@ class PtrUseVisitor : protected InstVisitor<DerivedT>,
278278
default:
279279
return Base::visitIntrinsicInst(II);
280280

281+
// We escape pointers used by a fake_use to prevent SROA from transforming
282+
// them.
283+
case Intrinsic::fake_use:
284+
PI.setEscaped(&II);
285+
return;
286+
281287
case Intrinsic::lifetime_start:
282288
case Intrinsic::lifetime_end:
283289
return; // No-op intrinsics.

llvm/include/llvm/CodeGen/ISDOpcodes.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1372,6 +1372,11 @@ enum NodeType {
13721372
LIFETIME_START,
13731373
LIFETIME_END,
13741374

1375+
/// FAKE_USE represents a use of the operand but does not do anything.
1376+
/// Its purpose is the extension of the operand's lifetime mainly for
1377+
/// debugging purposes.
1378+
FAKE_USE,
1379+
13751380
/// GC_TRANSITION_START/GC_TRANSITION_END - These operators mark the
13761381
/// beginning and end of GC transition sequence, and carry arbitrary
13771382
/// information that target might need for lowering. The first operand is

llvm/include/llvm/CodeGen/MachineInstr.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1435,6 +1435,8 @@ class MachineInstr
14351435
return getOpcode() == TargetOpcode::EXTRACT_SUBREG;
14361436
}
14371437

1438+
bool isFakeUse() const { return getOpcode() == TargetOpcode::FAKE_USE; }
1439+
14381440
/// Return true if the instruction behaves like a copy.
14391441
/// This does not include native copy instructions.
14401442
bool isCopyLike() const {

llvm/include/llvm/CodeGen/Passes.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,9 @@ namespace llvm {
440440
// metadata after llvm SanitizerBinaryMetadata pass.
441441
extern char &MachineSanitizerBinaryMetadataID;
442442

443+
/// RemoveLoadsIntoFakeUses pass.
444+
extern char &RemoveLoadsIntoFakeUsesID;
445+
443446
/// RemoveRedundantDebugValues pass.
444447
extern char &RemoveRedundantDebugValuesID;
445448

llvm/include/llvm/CodeGen/SelectionDAGISel.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ class SelectionDAGISel {
463463
void Select_READ_REGISTER(SDNode *Op);
464464
void Select_WRITE_REGISTER(SDNode *Op);
465465
void Select_UNDEF(SDNode *N);
466+
void Select_FAKE_USE(SDNode *N);
466467
void CannotYetSelect(SDNode *N);
467468

468469
void Select_FREEZE(SDNode *N);

llvm/include/llvm/IR/Intrinsics.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1835,6 +1835,9 @@ def int_is_constant : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_any_ty],
18351835
[IntrNoMem, IntrWillReturn, IntrConvergent],
18361836
"llvm.is.constant">;
18371837

1838+
// Introduce a use of the argument without generating any code.
1839+
def int_fake_use : Intrinsic<[], [llvm_vararg_ty]>;
1840+
18381841
// Intrinsic to mask out bits of a pointer.
18391842
// First argument must be pointer or vector of pointer. This is checked by the
18401843
// verifier.

llvm/include/llvm/InitializePasses.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ void initializeRegionOnlyViewerPass(PassRegistry &);
264264
void initializeRegionPrinterPass(PassRegistry &);
265265
void initializeRegionViewerPass(PassRegistry &);
266266
void initializeRegisterCoalescerPass(PassRegistry &);
267+
void initializeRemoveLoadsIntoFakeUsesPass(PassRegistry &);
267268
void initializeRemoveRedundantDebugValuesPass(PassRegistry &);
268269
void initializeRenameIndependentSubregsPass(PassRegistry &);
269270
void initializeReplaceWithVeclibLegacyPass(PassRegistry &);

llvm/include/llvm/Passes/MachinePassRegistry.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ DUMMY_MACHINE_FUNCTION_PASS("reg-usage-propagation", RegUsageInfoPropagationPass
250250
DUMMY_MACHINE_FUNCTION_PASS("regalloc", RegAllocPass)
251251
DUMMY_MACHINE_FUNCTION_PASS("regallocscoringpass", RegAllocScoringPass)
252252
DUMMY_MACHINE_FUNCTION_PASS("regbankselect", RegBankSelectPass)
253+
DUMMY_MACHINE_FUNCTION_PASS("remove-loads-into-fake-uses", RemoveLoadsIntoFakeUsesPass)
253254
DUMMY_MACHINE_FUNCTION_PASS("removeredundantdebugvalues", RemoveRedundantDebugValuesPass)
254255
DUMMY_MACHINE_FUNCTION_PASS("rename-independent-subregs", RenameIndependentSubregsPass)
255256
DUMMY_MACHINE_FUNCTION_PASS("reset-machine-function", ResetMachineFunctionPass)

llvm/include/llvm/Support/TargetOpcodes.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,9 @@ HANDLE_TARGET_OPCODE(PATCHABLE_TYPED_EVENT_CALL)
217217

218218
HANDLE_TARGET_OPCODE(ICALL_BRANCH_FUNNEL)
219219

220+
/// Represents a use of the operand but generates no code.
221+
HANDLE_TARGET_OPCODE(FAKE_USE)
222+
220223
// This is a fence with the singlethread scope. It represents a compiler memory
221224
// barrier, but does not correspond to any generated instruction.
222225
HANDLE_TARGET_OPCODE(MEMBARRIER)

llvm/include/llvm/Target/Target.td

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1418,6 +1418,16 @@ def FAULTING_OP : StandardPseudoInstruction {
14181418
let isTerminator = true;
14191419
let isBranch = true;
14201420
}
1421+
def FAKE_USE : StandardPseudoInstruction {
1422+
// An instruction that uses its operands but does nothing; this instruction
1423+
// will be treated specially by CodeGen passes, distinguishing it from any
1424+
// otherwise equivalent instructions.
1425+
let OutOperandList = (outs);
1426+
let InOperandList = (ins variable_ops);
1427+
let AsmString = "FAKE_USE";
1428+
let hasSideEffects = 0;
1429+
let isMeta = true;
1430+
}
14211431
def PATCHABLE_OP : StandardPseudoInstruction {
14221432
let OutOperandList = (outs);
14231433
let InOperandList = (ins variable_ops);

llvm/lib/CodeGen/Analysis.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,8 @@ bool llvm::isInTailCallPosition(const CallBase &Call, const TargetMachine &TM,
567567
if (const IntrinsicInst *II = dyn_cast<IntrinsicInst>(BBI))
568568
if (II->getIntrinsicID() == Intrinsic::lifetime_end ||
569569
II->getIntrinsicID() == Intrinsic::assume ||
570-
II->getIntrinsicID() == Intrinsic::experimental_noalias_scope_decl)
570+
II->getIntrinsicID() == Intrinsic::experimental_noalias_scope_decl ||
571+
II->getIntrinsicID() == Intrinsic::fake_use)
571572
continue;
572573
if (BBI->mayHaveSideEffects() || BBI->mayReadFromMemory() ||
573574
!isSafeToSpeculativelyExecute(&*BBI))

llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1131,6 +1131,21 @@ static void emitKill(const MachineInstr *MI, AsmPrinter &AP) {
11311131
AP.OutStreamer->addBlankLine();
11321132
}
11331133

1134+
static void emitFakeUse(const MachineInstr *MI, AsmPrinter &AP) {
1135+
std::string Str;
1136+
raw_string_ostream OS(Str);
1137+
OS << "fake_use:";
1138+
for (const MachineOperand &Op : MI->operands()) {
1139+
// In some circumstances we can end up with fake uses of constants; skip
1140+
// these.
1141+
if (!Op.isReg())
1142+
continue;
1143+
OS << ' ' << printReg(Op.getReg(), AP.MF->getSubtarget().getRegisterInfo());
1144+
}
1145+
AP.OutStreamer->AddComment(OS.str());
1146+
AP.OutStreamer->addBlankLine();
1147+
}
1148+
11341149
/// emitDebugValueComment - This method handles the target-independent form
11351150
/// of DBG_VALUE, returning true if it was able to do so. A false return
11361151
/// means the target will need to handle MI in EmitInstruction.
@@ -1799,6 +1814,10 @@ void AsmPrinter::emitFunctionBody() {
17991814
case TargetOpcode::KILL:
18001815
if (isVerbose()) emitKill(&MI, *this);
18011816
break;
1817+
case TargetOpcode::FAKE_USE:
1818+
if (isVerbose())
1819+
emitFakeUse(&MI, *this);
1820+
break;
18021821
case TargetOpcode::PSEUDO_PROBE:
18031822
emitPseudoProbe(MI);
18041823
break;

llvm/lib/CodeGen/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ add_llvm_component_library(LLVMCodeGen
200200
RegisterUsageInfo.cpp
201201
RegUsageInfoCollector.cpp
202202
RegUsageInfoPropagate.cpp
203+
RemoveLoadsIntoFakeUses.cpp
203204
ReplaceWithVeclib.cpp
204205
ResetMachineFunctionPass.cpp
205206
RegisterBank.cpp

llvm/lib/CodeGen/CodeGen.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ void llvm::initializeCodeGen(PassRegistry &Registry) {
116116
initializeRegUsageInfoCollectorPass(Registry);
117117
initializeRegUsageInfoPropagationPass(Registry);
118118
initializeRegisterCoalescerPass(Registry);
119+
initializeRemoveLoadsIntoFakeUsesPass(Registry);
119120
initializeRemoveRedundantDebugValuesPass(Registry);
120121
initializeRenameIndependentSubregsPass(Registry);
121122
initializeSafeStackLegacyPassPass(Registry);

llvm/lib/CodeGen/CodeGenPrepare.cpp

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2800,12 +2800,34 @@ bool CodeGenPrepare::dupRetToEnableTailCallOpts(BasicBlock *BB,
28002800
return false;
28012801
};
28022802

2803+
SmallVector<const IntrinsicInst *, 4> FakeUses;
2804+
2805+
auto isFakeUse = [&FakeUses](const Instruction *Inst) {
2806+
if (auto *II = dyn_cast<IntrinsicInst>(Inst);
2807+
II && II->getIntrinsicID() == Intrinsic::fake_use) {
2808+
// Record the instruction so it can be preserved when the exit block is
2809+
// removed. Do not preserve the fake use that uses the result of the
2810+
// PHI instruction.
2811+
// Do not copy fake uses that use the result of a PHI node.
2812+
// FIXME: If we do want to copy the fake use into the return blocks, we
2813+
// have to figure out which of the PHI node operands to use for each
2814+
// copy.
2815+
if (!isa<PHINode>(II->getOperand(0))) {
2816+
FakeUses.push_back(II);
2817+
}
2818+
return true;
2819+
}
2820+
2821+
return false;
2822+
};
2823+
28032824
// Make sure there are no instructions between the first instruction
28042825
// and return.
28052826
const Instruction *BI = BB->getFirstNonPHI();
28062827
// Skip over debug and the bitcast.
28072828
while (isa<DbgInfoIntrinsic>(BI) || BI == BCI || BI == EVI ||
2808-
isa<PseudoProbeInst>(BI) || isLifetimeEndOrBitCastFor(BI))
2829+
isa<PseudoProbeInst>(BI) || isLifetimeEndOrBitCastFor(BI) ||
2830+
isFakeUse(BI))
28092831
BI = BI->getNextNode();
28102832
if (BI != RetI)
28112833
return false;
@@ -2814,6 +2836,9 @@ bool CodeGenPrepare::dupRetToEnableTailCallOpts(BasicBlock *BB,
28142836
/// call.
28152837
const Function *F = BB->getParent();
28162838
SmallVector<BasicBlock *, 4> TailCallBBs;
2839+
// Record the call instructions so we can insert any fake uses
2840+
// that need to be preserved before them.
2841+
SmallVector<CallInst *, 4> CallInsts;
28172842
if (PN) {
28182843
for (unsigned I = 0, E = PN->getNumIncomingValues(); I != E; ++I) {
28192844
// Look through bitcasts.
@@ -2825,6 +2850,7 @@ bool CodeGenPrepare::dupRetToEnableTailCallOpts(BasicBlock *BB,
28252850
TLI->mayBeEmittedAsTailCall(CI) &&
28262851
attributesPermitTailCall(F, CI, RetI, *TLI)) {
28272852
TailCallBBs.push_back(PredBB);
2853+
CallInsts.push_back(CI);
28282854
} else {
28292855
// Consider the cases in which the phi value is indirectly produced by
28302856
// the tail call, for example when encountering memset(), memmove(),
@@ -2844,8 +2870,10 @@ bool CodeGenPrepare::dupRetToEnableTailCallOpts(BasicBlock *BB,
28442870
isIntrinsicOrLFToBeTailCalled(TLInfo, CI) &&
28452871
IncomingVal == CI->getArgOperand(0) &&
28462872
TLI->mayBeEmittedAsTailCall(CI) &&
2847-
attributesPermitTailCall(F, CI, RetI, *TLI))
2873+
attributesPermitTailCall(F, CI, RetI, *TLI)) {
28482874
TailCallBBs.push_back(PredBB);
2875+
CallInsts.push_back(CI);
2876+
}
28492877
}
28502878
}
28512879
} else {
@@ -2863,6 +2891,7 @@ bool CodeGenPrepare::dupRetToEnableTailCallOpts(BasicBlock *BB,
28632891
(isIntrinsicOrLFToBeTailCalled(TLInfo, CI) &&
28642892
V == CI->getArgOperand(0))) {
28652893
TailCallBBs.push_back(Pred);
2894+
CallInsts.push_back(CI);
28662895
}
28672896
}
28682897
}
@@ -2889,8 +2918,17 @@ bool CodeGenPrepare::dupRetToEnableTailCallOpts(BasicBlock *BB,
28892918
}
28902919

28912920
// If we eliminated all predecessors of the block, delete the block now.
2892-
if (Changed && !BB->hasAddressTaken() && pred_empty(BB))
2921+
if (Changed && !BB->hasAddressTaken() && pred_empty(BB)) {
2922+
// Copy the fake uses found in the original return block to all blocks
2923+
// that contain tail calls.
2924+
for (auto *CI : CallInsts) {
2925+
for (auto const *FakeUse : FakeUses) {
2926+
auto *ClonedInst = FakeUse->clone();
2927+
ClonedInst->insertBefore(CI);
2928+
}
2929+
}
28932930
BB->eraseFromParent();
2931+
}
28942932

28952933
return Changed;
28962934
}

llvm/lib/CodeGen/DeadMachineInstructionElim.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ bool DeadMachineInstructionElimImpl::isDead(const MachineInstr *MI) const {
8787
return false;
8888

8989
// Don't delete frame allocation labels.
90-
if (MI->getOpcode() == TargetOpcode::LOCAL_ESCAPE)
90+
if (MI->getOpcode() == TargetOpcode::LOCAL_ESCAPE ||
91+
MI->getOpcode() == TargetOpcode::FAKE_USE)
9192
return false;
9293

9394
// Don't delete instructions with side effects.

llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2193,6 +2193,14 @@ bool IRTranslator::translateKnownIntrinsic(const CallInst &CI, Intrinsic::ID ID,
21932193
}
21942194
return true;
21952195
}
2196+
case Intrinsic::fake_use: {
2197+
SmallVector<llvm::SrcOp, 4> VRegs;
2198+
for (const auto &Arg : CI.args())
2199+
for (auto VReg : getOrCreateVRegs(*Arg))
2200+
VRegs.push_back(VReg);
2201+
MIRBuilder.buildInstr(TargetOpcode::FAKE_USE, std::nullopt, VRegs);
2202+
return true;
2203+
}
21962204
case Intrinsic::dbg_declare: {
21972205
const DbgDeclareInst &DI = cast<DbgDeclareInst>(CI);
21982206
assert(DI.getVariable() && "Missing variable");

llvm/lib/CodeGen/GlobalISel/Utils.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,9 @@ bool llvm::isTriviallyDead(const MachineInstr &MI,
228228
// Don't delete frame allocation labels.
229229
if (MI.getOpcode() == TargetOpcode::LOCAL_ESCAPE)
230230
return false;
231+
// Don't delete fake uses.
232+
if (MI.getOpcode() == TargetOpcode::FAKE_USE)
233+
return false;
231234
// LIFETIME markers should be preserved even if they seem dead.
232235
if (MI.getOpcode() == TargetOpcode::LIFETIME_START ||
233236
MI.getOpcode() == TargetOpcode::LIFETIME_END)

llvm/lib/CodeGen/MachineCSE.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,8 @@ bool MachineCSE::PhysRegDefsReach(MachineInstr *CSMI, MachineInstr *MI,
406406

407407
bool MachineCSE::isCSECandidate(MachineInstr *MI) {
408408
if (MI->isPosition() || MI->isPHI() || MI->isImplicitDef() || MI->isKill() ||
409-
MI->isInlineAsm() || MI->isDebugInstr() || MI->isJumpTableDebugInfo())
409+
MI->isInlineAsm() || MI->isDebugInstr() || MI->isJumpTableDebugInfo() ||
410+
MI->isFakeUse())
410411
return false;
411412

412413
// Ignore copies.

llvm/lib/CodeGen/MachineScheduler.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,8 @@ static bool isSchedBoundary(MachineBasicBlock::iterator MI,
530530
MachineBasicBlock *MBB,
531531
MachineFunction *MF,
532532
const TargetInstrInfo *TII) {
533-
return MI->isCall() || TII->isSchedulingBoundary(*MI, MBB, *MF);
533+
return MI->isCall() || TII->isSchedulingBoundary(*MI, MBB, *MF) ||
534+
MI->isFakeUse();
534535
}
535536

536537
/// A region of an MBB for scheduling.

llvm/lib/CodeGen/MachineSink.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -833,7 +833,7 @@ bool MachineSinking::ProcessBlock(MachineBasicBlock &MBB) {
833833
if (!ProcessedBegin)
834834
--I;
835835

836-
if (MI.isDebugOrPseudoInstr()) {
836+
if (MI.isDebugOrPseudoInstr() || MI.isFakeUse()) {
837837
if (MI.isDebugValue())
838838
ProcessDbgInst(MI);
839839
continue;

0 commit comments

Comments
 (0)