Skip to content

[ExtendLifetimes] Implement llvm.fake.use to extend variable lifetimes #86149

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29477,6 +29477,42 @@ execution, but is unknown at compile time.
If the result value does not fit in the result type, then the result is
a :ref:`poison value <poisonvalues>`.

.. _llvm_fake_use:

'``llvm.fake.use``' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Syntax:
"""""""

::

declare void @llvm.fake.use(...)

Overview:
"""""""""

The ``llvm.fake.use`` intrinsic is a no-op. It takes a single
value as an operand and is treated as a use of that operand, to force the
optimizer to preserve that value prior to the fake use. This is used for
extending the lifetimes of variables, where this intrinsic placed at the end of
a variable's scope helps prevent that variable from being optimized out.

Arguments:
""""""""""

The ``llvm.fake.use`` intrinsic takes one argument, which may be any
function-local SSA value. Note that the signature is variadic so that the
intrinsic can take any type of argument, but passing more than one argument will
result in an error.

Semantics:
""""""""""

This intrinsic does nothing, but optimizers must consider it a use of its single
operand and should try to preserve the intrinsic and its position in the
function.


Stack Map Intrinsics
--------------------
Expand Down
6 changes: 6 additions & 0 deletions llvm/include/llvm/Analysis/PtrUseVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,12 @@ class PtrUseVisitor : protected InstVisitor<DerivedT>,
default:
return Base::visitIntrinsicInst(II);

// We escape pointers used by a fake_use to prevent SROA from transforming
// them.
case Intrinsic::fake_use:
PI.setEscaped(&II);
return;

case Intrinsic::lifetime_start:
case Intrinsic::lifetime_end:
return; // No-op intrinsics.
Expand Down
5 changes: 5 additions & 0 deletions llvm/include/llvm/CodeGen/ISDOpcodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1372,6 +1372,11 @@ enum NodeType {
LIFETIME_START,
LIFETIME_END,

/// FAKE_USE represents a use of the operand but does not do anything.
/// Its purpose is the extension of the operand's lifetime mainly for
/// debugging purposes.
FAKE_USE,

/// GC_TRANSITION_START/GC_TRANSITION_END - These operators mark the
/// beginning and end of GC transition sequence, and carry arbitrary
/// information that target might need for lowering. The first operand is
Expand Down
2 changes: 2 additions & 0 deletions llvm/include/llvm/CodeGen/MachineInstr.h
Original file line number Diff line number Diff line change
Expand Up @@ -1435,6 +1435,8 @@ class MachineInstr
return getOpcode() == TargetOpcode::EXTRACT_SUBREG;
}

bool isFakeUse() const { return getOpcode() == TargetOpcode::FAKE_USE; }

/// Return true if the instruction behaves like a copy.
/// This does not include native copy instructions.
bool isCopyLike() const {
Expand Down
3 changes: 3 additions & 0 deletions llvm/include/llvm/CodeGen/Passes.h
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,9 @@ namespace llvm {
// metadata after llvm SanitizerBinaryMetadata pass.
extern char &MachineSanitizerBinaryMetadataID;

/// RemoveLoadsIntoFakeUses pass.
extern char &RemoveLoadsIntoFakeUsesID;

/// RemoveRedundantDebugValues pass.
extern char &RemoveRedundantDebugValuesID;

Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/CodeGen/SelectionDAGISel.h
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,7 @@ class SelectionDAGISel {
void Select_READ_REGISTER(SDNode *Op);
void Select_WRITE_REGISTER(SDNode *Op);
void Select_UNDEF(SDNode *N);
void Select_FAKE_USE(SDNode *N);
void CannotYetSelect(SDNode *N);

void Select_FREEZE(SDNode *N);
Expand Down
3 changes: 3 additions & 0 deletions llvm/include/llvm/IR/Intrinsics.td
Original file line number Diff line number Diff line change
Expand Up @@ -1835,6 +1835,9 @@ def int_is_constant : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_any_ty],
[IntrNoMem, IntrWillReturn, IntrConvergent],
"llvm.is.constant">;

// Introduce a use of the argument without generating any code.
def int_fake_use : Intrinsic<[], [llvm_vararg_ty]>;

// Intrinsic to mask out bits of a pointer.
// First argument must be pointer or vector of pointer. This is checked by the
// verifier.
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/InitializePasses.h
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ void initializeRegionOnlyViewerPass(PassRegistry &);
void initializeRegionPrinterPass(PassRegistry &);
void initializeRegionViewerPass(PassRegistry &);
void initializeRegisterCoalescerPass(PassRegistry &);
void initializeRemoveLoadsIntoFakeUsesPass(PassRegistry &);
void initializeRemoveRedundantDebugValuesPass(PassRegistry &);
void initializeRenameIndependentSubregsPass(PassRegistry &);
void initializeReplaceWithVeclibLegacyPass(PassRegistry &);
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/Passes/MachinePassRegistry.def
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ DUMMY_MACHINE_FUNCTION_PASS("reg-usage-propagation", RegUsageInfoPropagationPass
DUMMY_MACHINE_FUNCTION_PASS("regalloc", RegAllocPass)
DUMMY_MACHINE_FUNCTION_PASS("regallocscoringpass", RegAllocScoringPass)
DUMMY_MACHINE_FUNCTION_PASS("regbankselect", RegBankSelectPass)
DUMMY_MACHINE_FUNCTION_PASS("remove-loads-into-fake-uses", RemoveLoadsIntoFakeUsesPass)
DUMMY_MACHINE_FUNCTION_PASS("removeredundantdebugvalues", RemoveRedundantDebugValuesPass)
DUMMY_MACHINE_FUNCTION_PASS("rename-independent-subregs", RenameIndependentSubregsPass)
DUMMY_MACHINE_FUNCTION_PASS("reset-machine-function", ResetMachineFunctionPass)
Expand Down
3 changes: 3 additions & 0 deletions llvm/include/llvm/Support/TargetOpcodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,9 @@ HANDLE_TARGET_OPCODE(PATCHABLE_TYPED_EVENT_CALL)

HANDLE_TARGET_OPCODE(ICALL_BRANCH_FUNNEL)

/// Represents a use of the operand but generates no code.
HANDLE_TARGET_OPCODE(FAKE_USE)

// This is a fence with the singlethread scope. It represents a compiler memory
// barrier, but does not correspond to any generated instruction.
HANDLE_TARGET_OPCODE(MEMBARRIER)
Expand Down
10 changes: 10 additions & 0 deletions llvm/include/llvm/Target/Target.td
Original file line number Diff line number Diff line change
Expand Up @@ -1418,6 +1418,16 @@ def FAULTING_OP : StandardPseudoInstruction {
let isTerminator = true;
let isBranch = true;
}
def FAKE_USE : StandardPseudoInstruction {
// An instruction that uses its operands but does nothing; this instruction
// will be treated specially by CodeGen passes, distinguishing it from any
// otherwise equivalent instructions.
let OutOperandList = (outs);
let InOperandList = (ins variable_ops);
let AsmString = "FAKE_USE";
let hasSideEffects = 0;
let isMeta = true;
}
def PATCHABLE_OP : StandardPseudoInstruction {
let OutOperandList = (outs);
let InOperandList = (ins variable_ops);
Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/CodeGen/Analysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,8 @@ bool llvm::isInTailCallPosition(const CallBase &Call, const TargetMachine &TM,
if (const IntrinsicInst *II = dyn_cast<IntrinsicInst>(BBI))
if (II->getIntrinsicID() == Intrinsic::lifetime_end ||
II->getIntrinsicID() == Intrinsic::assume ||
II->getIntrinsicID() == Intrinsic::experimental_noalias_scope_decl)
II->getIntrinsicID() == Intrinsic::experimental_noalias_scope_decl ||
II->getIntrinsicID() == Intrinsic::fake_use)
continue;
if (BBI->mayHaveSideEffects() || BBI->mayReadFromMemory() ||
!isSafeToSpeculativelyExecute(&*BBI))
Expand Down
19 changes: 19 additions & 0 deletions llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1131,6 +1131,21 @@ static void emitKill(const MachineInstr *MI, AsmPrinter &AP) {
AP.OutStreamer->addBlankLine();
}

static void emitFakeUse(const MachineInstr *MI, AsmPrinter &AP) {
std::string Str;
raw_string_ostream OS(Str);
OS << "fake_use:";
for (const MachineOperand &Op : MI->operands()) {
// In some circumstances we can end up with fake uses of constants; skip
// these.
if (!Op.isReg())
continue;
OS << ' ' << printReg(Op.getReg(), AP.MF->getSubtarget().getRegisterInfo());
}
AP.OutStreamer->AddComment(OS.str());
AP.OutStreamer->addBlankLine();
}

/// emitDebugValueComment - This method handles the target-independent form
/// of DBG_VALUE, returning true if it was able to do so. A false return
/// means the target will need to handle MI in EmitInstruction.
Expand Down Expand Up @@ -1799,6 +1814,10 @@ void AsmPrinter::emitFunctionBody() {
case TargetOpcode::KILL:
if (isVerbose()) emitKill(&MI, *this);
break;
case TargetOpcode::FAKE_USE:
if (isVerbose())
emitFakeUse(&MI, *this);
break;
case TargetOpcode::PSEUDO_PROBE:
emitPseudoProbe(MI);
break;
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/CodeGen/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ add_llvm_component_library(LLVMCodeGen
RegisterUsageInfo.cpp
RegUsageInfoCollector.cpp
RegUsageInfoPropagate.cpp
RemoveLoadsIntoFakeUses.cpp
ReplaceWithVeclib.cpp
ResetMachineFunctionPass.cpp
RegisterBank.cpp
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/CodeGen/CodeGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ void llvm::initializeCodeGen(PassRegistry &Registry) {
initializeRegUsageInfoCollectorPass(Registry);
initializeRegUsageInfoPropagationPass(Registry);
initializeRegisterCoalescerPass(Registry);
initializeRemoveLoadsIntoFakeUsesPass(Registry);
initializeRemoveRedundantDebugValuesPass(Registry);
initializeRenameIndependentSubregsPass(Registry);
initializeSafeStackLegacyPassPass(Registry);
Expand Down
44 changes: 41 additions & 3 deletions llvm/lib/CodeGen/CodeGenPrepare.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2800,12 +2800,34 @@ bool CodeGenPrepare::dupRetToEnableTailCallOpts(BasicBlock *BB,
return false;
};

SmallVector<const IntrinsicInst *, 4> FakeUses;

auto isFakeUse = [&FakeUses](const Instruction *Inst) {
if (auto *II = dyn_cast<IntrinsicInst>(Inst);
II && II->getIntrinsicID() == Intrinsic::fake_use) {
// Record the instruction so it can be preserved when the exit block is
// removed. Do not preserve the fake use that uses the result of the
// PHI instruction.
// Do not copy fake uses that use the result of a PHI node.
// FIXME: If we do want to copy the fake use into the return blocks, we
// have to figure out which of the PHI node operands to use for each
// copy.
if (!isa<PHINode>(II->getOperand(0))) {
FakeUses.push_back(II);
}
return true;
}

return false;
};

// Make sure there are no instructions between the first instruction
// and return.
const Instruction *BI = BB->getFirstNonPHI();
// Skip over debug and the bitcast.
while (isa<DbgInfoIntrinsic>(BI) || BI == BCI || BI == EVI ||
isa<PseudoProbeInst>(BI) || isLifetimeEndOrBitCastFor(BI))
isa<PseudoProbeInst>(BI) || isLifetimeEndOrBitCastFor(BI) ||
isFakeUse(BI))
BI = BI->getNextNode();
if (BI != RetI)
return false;
Expand All @@ -2814,6 +2836,9 @@ bool CodeGenPrepare::dupRetToEnableTailCallOpts(BasicBlock *BB,
/// call.
const Function *F = BB->getParent();
SmallVector<BasicBlock *, 4> TailCallBBs;
// Record the call instructions so we can insert any fake uses
// that need to be preserved before them.
SmallVector<CallInst *, 4> CallInsts;
if (PN) {
for (unsigned I = 0, E = PN->getNumIncomingValues(); I != E; ++I) {
// Look through bitcasts.
Expand All @@ -2825,6 +2850,7 @@ bool CodeGenPrepare::dupRetToEnableTailCallOpts(BasicBlock *BB,
TLI->mayBeEmittedAsTailCall(CI) &&
attributesPermitTailCall(F, CI, RetI, *TLI)) {
TailCallBBs.push_back(PredBB);
CallInsts.push_back(CI);
} else {
// Consider the cases in which the phi value is indirectly produced by
// the tail call, for example when encountering memset(), memmove(),
Expand All @@ -2844,8 +2870,10 @@ bool CodeGenPrepare::dupRetToEnableTailCallOpts(BasicBlock *BB,
isIntrinsicOrLFToBeTailCalled(TLInfo, CI) &&
IncomingVal == CI->getArgOperand(0) &&
TLI->mayBeEmittedAsTailCall(CI) &&
attributesPermitTailCall(F, CI, RetI, *TLI))
attributesPermitTailCall(F, CI, RetI, *TLI)) {
TailCallBBs.push_back(PredBB);
CallInsts.push_back(CI);
}
}
}
} else {
Expand All @@ -2863,6 +2891,7 @@ bool CodeGenPrepare::dupRetToEnableTailCallOpts(BasicBlock *BB,
(isIntrinsicOrLFToBeTailCalled(TLInfo, CI) &&
V == CI->getArgOperand(0))) {
TailCallBBs.push_back(Pred);
CallInsts.push_back(CI);
}
}
}
Expand All @@ -2889,8 +2918,17 @@ bool CodeGenPrepare::dupRetToEnableTailCallOpts(BasicBlock *BB,
}

// If we eliminated all predecessors of the block, delete the block now.
if (Changed && !BB->hasAddressTaken() && pred_empty(BB))
if (Changed && !BB->hasAddressTaken() && pred_empty(BB)) {
// Copy the fake uses found in the original return block to all blocks
// that contain tail calls.
for (auto *CI : CallInsts) {
for (auto const *FakeUse : FakeUses) {
auto *ClonedInst = FakeUse->clone();
ClonedInst->insertBefore(CI);
}
}
BB->eraseFromParent();
}

return Changed;
}
Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/CodeGen/DeadMachineInstructionElim.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ bool DeadMachineInstructionElimImpl::isDead(const MachineInstr *MI) const {
return false;

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

// Don't delete instructions with side effects.
Expand Down
8 changes: 8 additions & 0 deletions llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2193,6 +2193,14 @@ bool IRTranslator::translateKnownIntrinsic(const CallInst &CI, Intrinsic::ID ID,
}
return true;
}
case Intrinsic::fake_use: {
SmallVector<llvm::SrcOp, 4> VRegs;
for (const auto &Arg : CI.args())
for (auto VReg : getOrCreateVRegs(*Arg))
VRegs.push_back(VReg);
MIRBuilder.buildInstr(TargetOpcode::FAKE_USE, std::nullopt, VRegs);
return true;
}
case Intrinsic::dbg_declare: {
const DbgDeclareInst &DI = cast<DbgDeclareInst>(CI);
assert(DI.getVariable() && "Missing variable");
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/CodeGen/GlobalISel/Utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,9 @@ bool llvm::isTriviallyDead(const MachineInstr &MI,
// Don't delete frame allocation labels.
if (MI.getOpcode() == TargetOpcode::LOCAL_ESCAPE)
return false;
// Don't delete fake uses.
if (MI.getOpcode() == TargetOpcode::FAKE_USE)
return false;
// LIFETIME markers should be preserved even if they seem dead.
if (MI.getOpcode() == TargetOpcode::LIFETIME_START ||
MI.getOpcode() == TargetOpcode::LIFETIME_END)
Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/CodeGen/MachineCSE.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,8 @@ bool MachineCSE::PhysRegDefsReach(MachineInstr *CSMI, MachineInstr *MI,

bool MachineCSE::isCSECandidate(MachineInstr *MI) {
if (MI->isPosition() || MI->isPHI() || MI->isImplicitDef() || MI->isKill() ||
MI->isInlineAsm() || MI->isDebugInstr() || MI->isJumpTableDebugInfo())
MI->isInlineAsm() || MI->isDebugInstr() || MI->isJumpTableDebugInfo() ||
MI->isFakeUse())
return false;

// Ignore copies.
Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/CodeGen/MachineScheduler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,8 @@ static bool isSchedBoundary(MachineBasicBlock::iterator MI,
MachineBasicBlock *MBB,
MachineFunction *MF,
const TargetInstrInfo *TII) {
return MI->isCall() || TII->isSchedulingBoundary(*MI, MBB, *MF);
return MI->isCall() || TII->isSchedulingBoundary(*MI, MBB, *MF) ||
MI->isFakeUse();
}

/// A region of an MBB for scheduling.
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/CodeGen/MachineSink.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -833,7 +833,7 @@ bool MachineSinking::ProcessBlock(MachineBasicBlock &MBB) {
if (!ProcessedBegin)
--I;

if (MI.isDebugOrPseudoInstr()) {
if (MI.isDebugOrPseudoInstr() || MI.isFakeUse()) {
if (MI.isDebugValue())
ProcessDbgInst(MI);
continue;
Expand Down
Loading
Loading