Skip to content

[InlineCost] Allow simplifying to non-Constant values (NFCI) #145083

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 2 commits into from
Jun 23, 2025
Merged
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
76 changes: 51 additions & 25 deletions llvm/lib/Analysis/InlineCost.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,8 @@ class CallAnalyzer : public InstVisitor<CallAnalyzer, bool> {
/// likely simplifications post-inlining. The most important aspect we track
/// is CFG altering simplifications -- when we prove a basic block dead, that
/// can cause dramatic shifts in the cost of inlining a function.
DenseMap<Value *, Constant *> SimplifiedValues;
/// Note: The simplified Value may be owned by the caller function.
DenseMap<Value *, Value *> SimplifiedValues;

/// Keep track of the values which map back (through function arguments) to
/// allocas on the caller stack which could be simplified through SROA.
Expand Down Expand Up @@ -432,7 +433,7 @@ class CallAnalyzer : public InstVisitor<CallAnalyzer, bool> {
template <typename T> T *getDirectOrSimplifiedValue(Value *V) const {
if (auto *Direct = dyn_cast<T>(V))
return Direct;
return dyn_cast_if_present<T>(SimplifiedValues.lookup(V));
return getSimplifiedValue<T>(V);
}

// Custom simplification helper routines.
Expand Down Expand Up @@ -525,11 +526,33 @@ class CallAnalyzer : public InstVisitor<CallAnalyzer, bool> {

InlineResult analyze();

std::optional<Constant *> getSimplifiedValue(Instruction *I) {
auto It = SimplifiedValues.find(I);
if (It != SimplifiedValues.end())
return It->second;
return std::nullopt;
/// Lookup simplified Value. May return a value owned by the caller.
Value *getSimplifiedValueUnchecked(Value *V) const {
return SimplifiedValues.lookup(V);
}

/// Lookup simplified Value, but return nullptr if the simplified value is
/// owned by the caller.
template <typename T> T *getSimplifiedValue(Value *V) const {
Value *SimpleV = SimplifiedValues.lookup(V);
if (!SimpleV)
return nullptr;

// Skip checks if we know T is a global. This has a small, but measurable
// impact on compile-time.
if constexpr (std::is_base_of_v<Constant, T>)
return dyn_cast<T>(SimpleV);

// Make sure the simplified Value is owned by this function
if (auto *I = dyn_cast<Instruction>(SimpleV)) {
if (I->getFunction() != &F)
return nullptr;
} else if (auto *Arg = dyn_cast<Argument>(SimpleV)) {
if (Arg->getParent() != &F)
return nullptr;
} else if (!isa<Constant>(SimpleV))
return nullptr;
return dyn_cast<T>(SimpleV);
}

// Keep a bunch of stats about the cost savings found so we can print them
Expand Down Expand Up @@ -921,12 +944,11 @@ class InlineCostCallAnalyzer final : public CallAnalyzer {
if (BranchInst *BI = dyn_cast<BranchInst>(&I)) {
// Count a conditional branch as savings if it becomes unconditional.
if (BI->isConditional() &&
isa_and_nonnull<ConstantInt>(
SimplifiedValues.lookup(BI->getCondition()))) {
getSimplifiedValue<ConstantInt>(BI->getCondition())) {
CurrentSavings += InstrCost;
}
} else if (SwitchInst *SI = dyn_cast<SwitchInst>(&I)) {
if (isa_and_present<ConstantInt>(SimplifiedValues.lookup(SI->getCondition())))
if (getSimplifiedValue<ConstantInt>(SI->getCondition()))
CurrentSavings += InstrCost;
} else if (Value *V = dyn_cast<Value>(&I)) {
// Count an instruction as savings if we can fold it.
Expand Down Expand Up @@ -1423,10 +1445,17 @@ void InlineCostAnnotationWriter::emitInstructionAnnot(
if (Record->hasThresholdChanged())
OS << ", threshold delta = " << Record->getThresholdDelta();
}
auto C = ICCA->getSimplifiedValue(const_cast<Instruction *>(I));
if (C) {
auto *V = ICCA->getSimplifiedValueUnchecked(const_cast<Instruction *>(I));
if (V) {
OS << ", simplified to ";
(*C)->print(OS, true);
V->print(OS, true);
if (auto *VI = dyn_cast<Instruction>(V)) {
if (VI->getFunction() != I->getFunction())
OS << " (caller instruction)";
} else if (auto *VArg = dyn_cast<Argument>(V)) {
if (VArg->getParent() != I->getFunction())
OS << " (caller argument)";
}
}
OS << "\n";
}
Expand Down Expand Up @@ -1483,7 +1512,7 @@ bool CallAnalyzer::isGEPFree(GetElementPtrInst &GEP) {
SmallVector<Value *, 4> Operands;
Operands.push_back(GEP.getOperand(0));
for (const Use &Op : GEP.indices())
if (Constant *SimpleOp = SimplifiedValues.lookup(Op))
if (Constant *SimpleOp = getSimplifiedValue<Constant>(Op))
Operands.push_back(SimpleOp);
else
Operands.push_back(Op);
Expand All @@ -1498,7 +1527,7 @@ bool CallAnalyzer::visitAlloca(AllocaInst &I) {
// Check whether inlining will turn a dynamic alloca into a static
// alloca and handle that case.
if (I.isArrayAllocation()) {
Constant *Size = SimplifiedValues.lookup(I.getArraySize());
Constant *Size = getSimplifiedValue<Constant>(I.getArraySize());
if (auto *AllocSize = dyn_cast_or_null<ConstantInt>(Size)) {
// Sometimes a dynamic alloca could be converted into a static alloca
// after this constant prop, and become a huge static alloca on an
Expand Down Expand Up @@ -2388,7 +2417,7 @@ bool CallAnalyzer::visitCallBase(CallBase &Call) {
// Check if this happens to be an indirect function call to a known function
// in this inline context. If not, we've done all we can.
Value *Callee = Call.getCalledOperand();
F = dyn_cast_or_null<Function>(SimplifiedValues.lookup(Callee));
F = getSimplifiedValue<Function>(Callee);
if (!F || F->getFunctionType() != Call.getFunctionType()) {
onCallArgumentSetup(Call);

Expand Down Expand Up @@ -2483,8 +2512,7 @@ bool CallAnalyzer::visitSelectInst(SelectInst &SI) {

Constant *TrueC = getDirectOrSimplifiedValue<Constant>(TrueVal);
Constant *FalseC = getDirectOrSimplifiedValue<Constant>(FalseVal);
Constant *CondC =
dyn_cast_or_null<Constant>(SimplifiedValues.lookup(SI.getCondition()));
Constant *CondC = getSimplifiedValue<Constant>(SI.getCondition());

if (!CondC) {
// Select C, X, X => X
Expand Down Expand Up @@ -2833,8 +2861,9 @@ InlineResult CallAnalyzer::analyze() {
auto CAI = CandidateCall.arg_begin();
for (Argument &FAI : F.args()) {
assert(CAI != CandidateCall.arg_end());
if (Constant *C = dyn_cast<Constant>(CAI))
SimplifiedValues[&FAI] = C;
SimplifiedValues[&FAI] = *CAI;
if (isa<Constant>(*CAI))
++NumConstantArgs;

Value *PtrArg = *CAI;
if (ConstantInt *C = stripAndComputeInBoundsConstantOffsets(PtrArg)) {
Expand All @@ -2849,7 +2878,6 @@ InlineResult CallAnalyzer::analyze() {
}
++CAI;
}
NumConstantArgs = SimplifiedValues.size();
NumConstantOffsetPtrArgs = ConstantOffsetPtrs.size();
NumAllocaArgs = SROAArgValues.size();

Expand Down Expand Up @@ -2911,8 +2939,7 @@ InlineResult CallAnalyzer::analyze() {
if (BranchInst *BI = dyn_cast<BranchInst>(TI)) {
if (BI->isConditional()) {
Value *Cond = BI->getCondition();
if (ConstantInt *SimpleCond =
dyn_cast_or_null<ConstantInt>(SimplifiedValues.lookup(Cond))) {
if (ConstantInt *SimpleCond = getSimplifiedValue<ConstantInt>(Cond)) {
BasicBlock *NextBB = BI->getSuccessor(SimpleCond->isZero() ? 1 : 0);
BBWorklist.insert(NextBB);
KnownSuccessors[BB] = NextBB;
Expand All @@ -2922,8 +2949,7 @@ InlineResult CallAnalyzer::analyze() {
}
} else if (SwitchInst *SI = dyn_cast<SwitchInst>(TI)) {
Value *Cond = SI->getCondition();
if (ConstantInt *SimpleCond =
dyn_cast_or_null<ConstantInt>(SimplifiedValues.lookup(Cond))) {
if (ConstantInt *SimpleCond = getSimplifiedValue<ConstantInt>(Cond)) {
BasicBlock *NextBB = SI->findCaseValue(SimpleCond)->getCaseSuccessor();
BBWorklist.insert(NextBB);
KnownSuccessors[BB] = NextBB;
Expand Down
Loading