Skip to content

Commit 1041053

Browse files
committed
[CallGraph][FIX] Ensure generic intrinsics are represented in the CG
Intrinsics have historically been excluded from the call graph with an exception of 3 special ones added at some point. This meant that passes depending on the call graph needed to handle intrinsics explicitly as the underlying assumption, namely that intrinsics can't call or modify things, doesn't hold. We are slowly moving away from special handling of intrinsics, or at least towards explicitly checking what intrinsics we want to handle differently. This patch: - Includes most intrinsics in the call graph. Debug intrinsics are still excluded. - Removes the special handling of intrinsics in the GlobalsAA pass. - Removes the `IntrinsicInst::isLeaf` method. Properly Fixes: #52706 See also: https://discourse.llvm.org/t/intrinsics-are-not-special-stop-pretending-i-mean-it/67545 Differential Revision: https://reviews.llvm.org/D14119
1 parent f6ce39c commit 1041053

File tree

9 files changed

+22
-61
lines changed

9 files changed

+22
-61
lines changed

llvm/include/llvm/Analysis/CallGraph.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -240,9 +240,6 @@ class CallGraphNode {
240240

241241
/// Adds a function to the list of functions called by this one.
242242
void addCalledFunction(CallBase *Call, CallGraphNode *M) {
243-
assert(!Call || !Call->getCalledFunction() ||
244-
!Call->getCalledFunction()->isIntrinsic() ||
245-
!Intrinsic::isLeaf(Call->getCalledFunction()->getIntrinsicID()));
246243
CalledFunctions.emplace_back(Call ? std::optional<WeakTrackingVH>(Call)
247244
: std::optional<WeakTrackingVH>(),
248245
M);

llvm/include/llvm/IR/Intrinsics.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,6 @@ namespace Intrinsic {
7979
/// Returns true if the intrinsic can be overloaded.
8080
bool isOverloaded(ID id);
8181

82-
/// Returns true if the intrinsic is a leaf, i.e. it does not make any calls
83-
/// itself. Most intrinsics are leafs, the exceptions being the patchpoint
84-
/// and statepoint intrinsics. These call (or invoke) their "target" argument.
85-
bool isLeaf(ID id);
86-
8782
/// Return the attributes for an intrinsic.
8883
AttributeList getAttributes(LLVMContext &C, ID id);
8984

llvm/lib/Analysis/CallGraph.cpp

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
#include "llvm/IR/AbstractCallSite.h"
1515
#include "llvm/IR/Function.h"
1616
#include "llvm/IR/IntrinsicInst.h"
17-
#include "llvm/IR/Intrinsics.h"
1817
#include "llvm/IR/Module.h"
1918
#include "llvm/IR/PassManager.h"
2019
#include "llvm/InitializePasses.h"
@@ -92,20 +91,17 @@ void CallGraph::populateCallGraphNode(CallGraphNode *Node) {
9291

9392
// If this function is not defined in this translation unit, it could call
9493
// anything.
95-
if (F->isDeclaration() && !F->isIntrinsic())
94+
if (F->isDeclaration() && !F->hasFnAttribute(Attribute::NoCallback))
9695
Node->addCalledFunction(nullptr, CallsExternalNode.get());
9796

9897
// Look for calls by this function.
9998
for (BasicBlock &BB : *F)
10099
for (Instruction &I : BB) {
101100
if (auto *Call = dyn_cast<CallBase>(&I)) {
102101
const Function *Callee = Call->getCalledFunction();
103-
if (!Callee || !Intrinsic::isLeaf(Callee->getIntrinsicID()))
104-
// Indirect calls of intrinsics are not allowed so no need to check.
105-
// We can be more precise here by using TargetArg returned by
106-
// Intrinsic::isLeaf.
102+
if (!Callee)
107103
Node->addCalledFunction(Call, CallsExternalNode.get());
108-
else if (!Callee->isIntrinsic())
104+
else if (!isDbgInfoIntrinsic(Callee->getIntrinsicID()))
109105
Node->addCalledFunction(Call, getOrInsertFunction(Callee));
110106

111107
// Add reference to callback functions.

llvm/lib/Analysis/CallGraphSCCPass.cpp

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -267,15 +267,7 @@ bool CGPassManager::RefreshCallGraph(const CallGraphSCC &CurSCC, CallGraph &CG,
267267
// If we've already seen this call site, then the FunctionPass RAUW'd
268268
// one call with another, which resulted in two "uses" in the edge
269269
// list of the same call.
270-
Calls.count(Call) ||
271-
272-
// If the call edge is not from a call or invoke, or it is a
273-
// intrinsic call, then the function pass RAUW'd a call with
274-
// another value. This can happen when constant folding happens
275-
// of well known functions etc.
276-
(Call->getCalledFunction() &&
277-
Call->getCalledFunction()->isIntrinsic() &&
278-
Intrinsic::isLeaf(Call->getCalledFunction()->getIntrinsicID()))) {
270+
Calls.count(Call)) {
279271
assert(!CheckingMode &&
280272
"CallGraphSCCPass did not update the CallGraph correctly!");
281273

llvm/lib/Analysis/GlobalsModRef.cpp

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
#include "llvm/Analysis/ValueTracking.h"
2424
#include "llvm/IR/InstIterator.h"
2525
#include "llvm/IR/Instructions.h"
26-
#include "llvm/IR/IntrinsicInst.h"
2726
#include "llvm/IR/Module.h"
2827
#include "llvm/IR/PassManager.h"
2928
#include "llvm/InitializePasses.h"
@@ -593,20 +592,8 @@ void GlobalsAAResult::AnalyzeCallGraph(CallGraph &CG, Module &M) {
593592

594593
// We handle calls specially because the graph-relevant aspects are
595594
// handled above.
596-
if (auto *Call = dyn_cast<CallBase>(&I)) {
597-
if (Function *Callee = Call->getCalledFunction()) {
598-
// The callgraph doesn't include intrinsic calls.
599-
if (Callee->isIntrinsic()) {
600-
if (isa<DbgInfoIntrinsic>(Call))
601-
// Don't let dbg intrinsics affect alias info.
602-
continue;
603-
604-
MemoryEffects Behaviour = AAResultBase::getMemoryEffects(Callee);
605-
FI.addModRefInfo(Behaviour.getModRef());
606-
}
607-
}
595+
if (auto *Call = dyn_cast<CallBase>(&I))
608596
continue;
609-
}
610597

611598
// All non-call instructions we use the primary predicates for whether
612599
// they read or write memory.

llvm/lib/IR/Function.cpp

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1499,18 +1499,6 @@ bool Intrinsic::isOverloaded(ID id) {
14991499
#undef GET_INTRINSIC_OVERLOAD_TABLE
15001500
}
15011501

1502-
bool Intrinsic::isLeaf(ID id) {
1503-
switch (id) {
1504-
default:
1505-
return true;
1506-
1507-
case Intrinsic::experimental_gc_statepoint:
1508-
case Intrinsic::experimental_patchpoint_void:
1509-
case Intrinsic::experimental_patchpoint_i64:
1510-
return false;
1511-
}
1512-
}
1513-
15141502
/// This defines the "Intrinsic::getAttributes(ID id)" method.
15151503
#define GET_INTRINSIC_ATTRIBUTES
15161504
#include "llvm/IR/IntrinsicImpl.inc"

llvm/test/Analysis/CallGraph/ignore-assumelike-calls.ll

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,23 @@
1010
; CHECK-EMPTY:
1111
; CHECK-NEXT: Call graph node for function: 'bitcast_only'<<{{.*}}>> #uses=0
1212
; CHECK-EMPTY:
13-
; CHECK-NEXT: Call graph node for function: 'llvm.lifetime.start.p0'<<{{.*}}>> #uses=1
13+
; CHECK-NEXT: Call graph node for function: 'llvm.lifetime.start.p0'<<{{.*}}>> #uses=3
1414
; CHECK-EMPTY:
15-
; CHECK-NEXT: Call graph node for function: 'llvm.memset.p0.i64'<<{{.*}}>> #uses=1
15+
; CHECK-NEXT: Call graph node for function: 'llvm.memset.p0.i64'<<{{.*}}>> #uses=2
1616
; CHECK-EMPTY:
17-
; CHECK-NEXT: Call graph node for function: 'llvm.memset.p1.i64'<<{{.*}}>> #uses=1
17+
; CHECK-NEXT: Call graph node for function: 'llvm.memset.p1.i64'<<{{.*}}>> #uses=2
1818
; CHECK-EMPTY:
1919
; CHECK-NEXT: Call graph node for function: 'other_cast_intrinsic_use'<<{{.*}}>> #uses=1
20+
; CHECK-NEXT: CS<{{.*}}> calls function 'llvm.memset.p1.i64'
2021
; CHECK-EMPTY:
2122
; CHECK-NEXT: Call graph node for function: 'other_intrinsic_use'<<{{.*}}>> #uses=1
23+
; CHECK-NEXT: CS<{{.*}}> calls function 'llvm.memset.p0.i64'
2224
; CHECK-EMPTY:
2325
; CHECK-NEXT: Call graph node for function: 'used_by_lifetime'<<{{.*}}>> #uses=0
26+
; CHECK-NEXT: CS<{{.*}}> calls function 'llvm.lifetime.start.p0'
2427
; CHECK-EMPTY:
2528
; CHECK-NEXT: Call graph node for function: 'used_by_lifetime_cast'<<{{.*}}>> #uses=0
29+
; CHECK-NEXT: CS<{{.*}}> calls function 'llvm.lifetime.start.p0'
2630
; CHECK-EMPTY:
2731

2832
define internal void @used_by_lifetime() {

llvm/test/Analysis/CallGraph/non-leaf-intrinsics.ll

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,13 @@ entry:
2525
; CHECK: CS<None> calls function 'f'
2626

2727
; CHECK: Call graph node for function: 'calls_patchpoint'
28-
; CHECK-NEXT: CS<[[addr_1:[^>]+]]> calls external node
28+
; CS<{{.*}}> calls function 'llvm.experimental.patchpoint.void'
2929

3030
; CHECK: Call graph node for function: 'calls_statepoint'
31-
; CHECK-NEXT: CS<[[addr_0:[^>]+]]> calls external node
31+
; CS<{{.*}}> calls function 'llvm.experimental.gc.statepoint.p0'
32+
33+
; CHECK: Call graph node for function: 'llvm.experimental.gc.statepoint.p0'<<{{.*}}>> #uses=2
34+
; CHECK-NEXT: CS<[[addr_1:[^>]+]]> calls external node
35+
36+
; CHECK: Call graph node for function: 'llvm.experimental.patchpoint.void'<<{{.*}}>> #uses=2
37+
; CHECK-NEXT: CS<[[addr_1:[^>]+]]> calls external node

llvm/test/Transforms/GVN/intrinsics_in_cg.ll

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
; RUN: opt < %s -passes='require<globals-aa>,gvn' -S | FileCheck %s
33

44
; Ensure we do not hoist the load over the call.
5-
; FIXME: Currently broken until D141190 or similar lands.
65

76
@G1 = internal global i32 1
87
@G2 = internal global i32 1
@@ -32,16 +31,13 @@ check:
3231
define i32 @indirect_intrinsic(i1 %c) {
3332
; CHECK-LABEL: define {{[^@]+}}@indirect_intrinsic
3433
; CHECK-SAME: (i1 [[C:%.*]]) {
35-
; CHECK-NEXT: br i1 [[C]], label [[INIT:%.*]], label [[DOTCHECK_CRIT_EDGE:%.*]]
36-
; CHECK: .check_crit_edge:
37-
; CHECK-NEXT: [[V_PRE:%.*]] = load i32, ptr @G2, align 4
38-
; CHECK-NEXT: br label [[CHECK:%.*]]
34+
; CHECK-NEXT: br i1 [[C]], label [[INIT:%.*]], label [[CHECK:%.*]]
3935
; CHECK: init:
4036
; CHECK-NEXT: store i32 0, ptr @G2, align 4
4137
; CHECK-NEXT: br label [[CHECK]]
4238
; CHECK: check:
43-
; CHECK-NEXT: [[V:%.*]] = phi i32 [ [[V_PRE]], [[DOTCHECK_CRIT_EDGE]] ], [ 0, [[INIT]] ]
4439
; CHECK-NEXT: call void @intrinsic_caller()
40+
; CHECK-NEXT: [[V:%.*]] = load i32, ptr @G2, align 4
4541
; CHECK-NEXT: ret i32 [[V]]
4642
;
4743
br i1 %c, label %init, label %check

0 commit comments

Comments
 (0)