[llvm] d0b5523 - [Attributor] Introduce limit for indirect call specialization

Johannes Doerfert via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 25 14:36:57 PDT 2023


Author: Johannes Doerfert
Date: 2023-08-25T14:36:42-07:00
New Revision: d0b5523632202c52b29a0b4802e918d6f75e12b2

URL: https://github.com/llvm/llvm-project/commit/d0b5523632202c52b29a0b4802e918d6f75e12b2
DIFF: https://github.com/llvm/llvm-project/commit/d0b5523632202c52b29a0b4802e918d6f75e12b2.diff

LOG: [Attributor] Introduce limit for indirect call specialization

The user can now limit the number of indirect calls specialized for a
given call site with `-attributor-max-specializations-per-call-base=N`
or the AttributorConfig callback. We further attach the `!callee`
metadata if all remaining callees are known.

Added: 
    

Modified: 
    llvm/include/llvm/Transforms/IPO/Attributor.h
    llvm/lib/Transforms/IPO/Attributor.cpp
    llvm/lib/Transforms/IPO/AttributorAttributes.cpp
    llvm/test/Transforms/Attributor/callgraph.ll

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h
index 0deb91fe02c306..004570344af2f6 100644
--- a/llvm/include/llvm/Transforms/IPO/Attributor.h
+++ b/llvm/include/llvm/Transforms/IPO/Attributor.h
@@ -1417,6 +1417,11 @@ struct AttributorConfig {
   std::function<void(Attributor &A, const Function &F)> InitializationCallback =
       nullptr;
 
+  /// Callback function to determine if an indirect call targets should be made
+  /// direct call targets (with an if-cascade).
+  std::function<bool(Attributor &A, CallBase &CB, Function &AssummedCallee)>
+      IndirectCalleeSpecializationCallback = nullptr;
+
   /// Helper to update an underlying call graph and to delete functions.
   CallGraphUpdater &CGUpdater;
 
@@ -1688,6 +1693,15 @@ struct Attributor {
   /// Return true if this is a module pass, false otherwise.
   bool isModulePass() const { return Configuration.IsModulePass; }
 
+  /// Return true if we should specialize the call site \b CB for the potential
+  /// callee \p Fn.
+  bool shouldSpecializeCallSiteForCallee(CallBase &CB, Function &Callee) {
+    return Configuration.IndirectCalleeSpecializationCallback
+               ? Configuration.IndirectCalleeSpecializationCallback(*this, CB,
+                                                                    Callee)
+               : true;
+  }
+
   /// Return true if we derive attributes for \p Fn
   bool isRunOn(Function &Fn) const { return isRunOn(&Fn); }
   bool isRunOn(Function *Fn) const {

diff  --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp
index 892502ac034a52..52d2921fd11a95 100644
--- a/llvm/lib/Transforms/IPO/Attributor.cpp
+++ b/llvm/lib/Transforms/IPO/Attributor.cpp
@@ -18,6 +18,7 @@
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/PointerIntPair.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/Statistic.h"
 #include "llvm/Analysis/AliasAnalysis.h"
 #include "llvm/Analysis/CallGraph.h"
@@ -50,6 +51,7 @@
 #include "llvm/Transforms/Utils/Cloning.h"
 #include "llvm/Transforms/Utils/Local.h"
 #include <cstdint>
+#include <memory>
 
 #ifdef EXPENSIVE_CHECKS
 #include "llvm/IR/Verifier.h"
@@ -93,6 +95,13 @@ static cl::opt<unsigned>
                           cl::desc("Maximal number of fixpoint iterations."),
                           cl::init(32));
 
+static cl::opt<unsigned>
+    MaxSpecializationPerCB("attributor-max-specializations-per-call-base",
+                           cl::Hidden,
+                           cl::desc("Maximal number of callees specialized for "
+                                    "a call base"),
+                           cl::init(UINT32_MAX));
+
 static cl::opt<unsigned, true> MaxInitializationChainLengthX(
     "attributor-max-initialization-chain-length", cl::Hidden,
     cl::desc(
@@ -3738,6 +3747,25 @@ static bool runAttributorOnFunctions(InformationCache &InfoCache,
   AttributorConfig AC(CGUpdater);
   AC.IsModulePass = IsModulePass;
   AC.DeleteFns = DeleteFns;
+
+  /// Tracking callback for specialization of indirect calls.
+  DenseMap<CallBase *, std::unique_ptr<SmallPtrSet<Function *, 8>>>
+      IndirectCalleeTrackingMap;
+  if (MaxSpecializationPerCB.getNumOccurrences()) {
+    AC.IndirectCalleeSpecializationCallback = [&](Attributor &, CallBase &CB,
+                                                  Function &Callee) {
+      if (MaxSpecializationPerCB == 0)
+        return false;
+      auto &Set = IndirectCalleeTrackingMap[&CB];
+      if (!Set)
+        Set = std::make_unique<SmallPtrSet<Function *, 8>>();
+      if (Set->size() >= MaxSpecializationPerCB)
+        return Set->contains(&Callee);
+      Set->insert(&Callee);
+      return true;
+    };
+  }
+
   Attributor A(Functions, InfoCache, AC);
 
   // Create shallow wrappers for all functions that are not IPO amendable

diff  --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
index 0669772af40435..8c6fa1ab53082e 100644
--- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
+++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
@@ -11,6 +11,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "llvm/IR/MDBuilder.h"
 #include "llvm/Transforms/IPO/Attributor.h"
 
 #include "llvm/ADT/APInt.h"
@@ -12167,9 +12168,19 @@ struct AAIndirectCallInfoCallSite : public AAIndirectCallInfo {
     // else ...
     // ```
     //
+    bool SpecializedForAnyCallees = false;
+    bool SpecializedForAllCallees = AllCalleesKnown;
     ICmpInst *LastCmp = nullptr;
+    SmallVector<Function *, 8> SkippedAssumedCallees;
     SmallVector<std::pair<CallInst *, Instruction *>> NewCalls;
     for (Function *NewCallee : AssumedCallees) {
+      if (!A.shouldSpecializeCallSiteForCallee(*CB, *NewCallee)) {
+        SkippedAssumedCallees.push_back(NewCallee);
+        SpecializedForAllCallees = false;
+        continue;
+      }
+      SpecializedForAnyCallees = true;
+
       LastCmp = new ICmpInst(IP, llvm::CmpInst::ICMP_EQ, FP, NewCallee);
       Instruction *ThenTI =
           SplitBlockAndInsertIfThen(LastCmp, IP, /* Unreachable */ false);
@@ -12198,8 +12209,20 @@ struct AAIndirectCallInfoCallSite : public AAIndirectCallInfo {
       NewCalls.push_back({NewCall, RetBC});
     }
 
+    auto AttachCalleeMetadata = [&](CallBase &IndirectCB) {
+      if (!AllCalleesKnown)
+        return ChangeStatus::UNCHANGED;
+      MDBuilder MDB(IndirectCB.getContext());
+      MDNode *Callees = MDB.createCallees(SkippedAssumedCallees);
+      IndirectCB.setMetadata(LLVMContext::MD_callees, Callees);
+      return ChangeStatus::CHANGED;
+    };
+
+    if (!SpecializedForAnyCallees)
+      return AttachCalleeMetadata(*CB);
+
     // Check if we need the fallback indirect call still.
-    if (AllCalleesKnown) {
+    if (SpecializedForAllCallees) {
       LastCmp->replaceAllUsesWith(ConstantInt::getTrue(LastCmp->getContext()));
       LastCmp->eraseFromParent();
       new UnreachableInst(IP->getContext(), IP);
@@ -12209,6 +12232,7 @@ struct AAIndirectCallInfoCallSite : public AAIndirectCallInfo {
       CBClone->setName(CB->getName());
       CBClone->insertBefore(IP);
       NewCalls.push_back({CBClone, nullptr});
+      AttachCalleeMetadata(*CBClone);
     }
 
     // Check if we need a PHI to merge the results.

diff  --git a/llvm/test/Transforms/Attributor/callgraph.ll b/llvm/test/Transforms/Attributor/callgraph.ll
index 492c17a4aeb341..5904fc364e6e1c 100644
--- a/llvm/test/Transforms/Attributor/callgraph.ll
+++ b/llvm/test/Transforms/Attributor/callgraph.ll
@@ -1,6 +1,8 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -passes=attributor -S < %s | FileCheck %s --check-prefixes=CHECK
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals
+; RUN: opt -passes=attributor -S < %s | FileCheck %s --check-prefixes=CHECK,UPTO2,UNLIM
 ; RUN: opt -passes=attributor -attributor-print-call-graph -S -disable-output < %s | FileCheck %s --check-prefixes=DOT
+; RUN: opt -passes=attributor --attributor-max-specializations-per-call-base=2 -S < %s | FileCheck %s --check-prefixes=CHECK,UPTO2,LIMI2
+; RUN: opt -passes=attributor --attributor-max-specializations-per-call-base=0 -S < %s | FileCheck %s --check-prefixes=CHECK,LIMI0
 
 define dso_local void @func1() {
 ; CHECK-LABEL: @func1(
@@ -37,23 +39,29 @@ define dso_local void @func2() {
 
 
 define void @func5(i32 %0) {
-; CHECK-LABEL: @func5(
-; CHECK-NEXT:    [[TMP2:%.*]] = icmp ne i32 [[TMP0:%.*]], 0
-; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[TMP2]], ptr @func4, ptr @func3
-; CHECK-NEXT:    [[TMP4:%.*]] = icmp eq ptr [[TMP3]], @func3
-; CHECK-NEXT:    br i1 [[TMP4]], label [[TMP5:%.*]], label [[TMP6:%.*]]
-; CHECK:       5:
-; CHECK-NEXT:    call void @func3()
-; CHECK-NEXT:    br label [[TMP9:%.*]]
-; CHECK:       6:
-; CHECK-NEXT:    br i1 true, label [[TMP7:%.*]], label [[TMP8:%.*]]
-; CHECK:       7:
-; CHECK-NEXT:    call void @func4()
-; CHECK-NEXT:    br label [[TMP9]]
-; CHECK:       8:
-; CHECK-NEXT:    unreachable
-; CHECK:       9:
-; CHECK-NEXT:    ret void
+; UPTO2-LABEL: @func5(
+; UPTO2-NEXT:    [[TMP2:%.*]] = icmp ne i32 [[TMP0:%.*]], 0
+; UPTO2-NEXT:    [[TMP3:%.*]] = select i1 [[TMP2]], ptr @func4, ptr @func3
+; UPTO2-NEXT:    [[TMP4:%.*]] = icmp eq ptr [[TMP3]], @func3
+; UPTO2-NEXT:    br i1 [[TMP4]], label [[TMP5:%.*]], label [[TMP6:%.*]]
+; UPTO2:       5:
+; UPTO2-NEXT:    call void @func3()
+; UPTO2-NEXT:    br label [[TMP9:%.*]]
+; UPTO2:       6:
+; UPTO2-NEXT:    br i1 true, label [[TMP7:%.*]], label [[TMP8:%.*]]
+; UPTO2:       7:
+; UPTO2-NEXT:    call void @func4()
+; UPTO2-NEXT:    br label [[TMP9]]
+; UPTO2:       8:
+; UPTO2-NEXT:    unreachable
+; UPTO2:       9:
+; UPTO2-NEXT:    ret void
+;
+; LIMI0-LABEL: @func5(
+; LIMI0-NEXT:    [[TMP2:%.*]] = icmp ne i32 [[TMP0:%.*]], 0
+; LIMI0-NEXT:    [[TMP3:%.*]] = select i1 [[TMP2]], ptr @func4, ptr @func3
+; LIMI0-NEXT:    call void [[TMP3]](), !callees !0
+; LIMI0-NEXT:    ret void
 ;
   %2 = icmp ne i32 %0, 0
   %3 = select i1 %2, ptr @func4, ptr @func3
@@ -80,39 +88,68 @@ declare float @retFloatTakeFloat(float)
 declare void @void()
 
 define i32 @non_matching_fp1(i1 %c1, i1 %c2, i1 %c) {
-; CHECK-LABEL: @non_matching_fp1(
-; CHECK-NEXT:    [[FP1:%.*]] = select i1 [[C1:%.*]], ptr @retI32, ptr @takeI32
-; CHECK-NEXT:    [[FP2:%.*]] = select i1 [[C2:%.*]], ptr @retFloatTakeFloat, ptr @void
-; CHECK-NEXT:    [[FP:%.*]] = select i1 [[C:%.*]], ptr [[FP1]], ptr [[FP2]]
-; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq ptr [[FP]], @takeI32
-; CHECK-NEXT:    br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
-; CHECK:       2:
-; CHECK-NEXT:    [[CALL1:%.*]] = call i32 @takeI32(i32 42)
-; CHECK-NEXT:    br label [[TMP15:%.*]]
-; CHECK:       3:
-; CHECK-NEXT:    [[TMP4:%.*]] = icmp eq ptr [[FP]], @retI32
-; CHECK-NEXT:    br i1 [[TMP4]], label [[TMP5:%.*]], label [[TMP6:%.*]]
-; CHECK:       5:
-; CHECK-NEXT:    [[CALL2:%.*]] = call i32 @retI32(i32 42)
-; CHECK-NEXT:    br label [[TMP15]]
-; CHECK:       6:
-; CHECK-NEXT:    [[TMP7:%.*]] = icmp eq ptr [[FP]], @void
-; CHECK-NEXT:    br i1 [[TMP7]], label [[TMP8:%.*]], label [[TMP9:%.*]]
-; CHECK:       8:
-; CHECK-NEXT:    [[CALL3:%.*]] = call i32 @void(i32 42)
-; CHECK-NEXT:    br label [[TMP15]]
-; CHECK:       9:
-; CHECK-NEXT:    br i1 true, label [[TMP10:%.*]], label [[TMP14:%.*]]
-; CHECK:       10:
-; CHECK-NEXT:    [[TMP11:%.*]] = bitcast i32 42 to float
-; CHECK-NEXT:    [[TMP12:%.*]] = call float @retFloatTakeFloat(float [[TMP11]])
-; CHECK-NEXT:    [[TMP13:%.*]] = bitcast float [[TMP12]] to i32
-; CHECK-NEXT:    br label [[TMP15]]
-; CHECK:       14:
-; CHECK-NEXT:    unreachable
-; CHECK:       15:
-; CHECK-NEXT:    [[CALL_PHI:%.*]] = phi i32 [ [[CALL1]], [[TMP2]] ], [ [[CALL2]], [[TMP5]] ], [ [[CALL3]], [[TMP8]] ], [ [[TMP13]], [[TMP10]] ]
-; CHECK-NEXT:    ret i32 [[CALL_PHI]]
+; UNLIM-LABEL: @non_matching_fp1(
+; UNLIM-NEXT:    [[FP1:%.*]] = select i1 [[C1:%.*]], ptr @retI32, ptr @takeI32
+; UNLIM-NEXT:    [[FP2:%.*]] = select i1 [[C2:%.*]], ptr @retFloatTakeFloat, ptr @void
+; UNLIM-NEXT:    [[FP:%.*]] = select i1 [[C:%.*]], ptr [[FP1]], ptr [[FP2]]
+; UNLIM-NEXT:    [[TMP1:%.*]] = icmp eq ptr [[FP]], @takeI32
+; UNLIM-NEXT:    br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
+; UNLIM:       2:
+; UNLIM-NEXT:    [[CALL1:%.*]] = call i32 @takeI32(i32 42)
+; UNLIM-NEXT:    br label [[TMP15:%.*]]
+; UNLIM:       3:
+; UNLIM-NEXT:    [[TMP4:%.*]] = icmp eq ptr [[FP]], @retI32
+; UNLIM-NEXT:    br i1 [[TMP4]], label [[TMP5:%.*]], label [[TMP6:%.*]]
+; UNLIM:       5:
+; UNLIM-NEXT:    [[CALL2:%.*]] = call i32 @retI32(i32 42)
+; UNLIM-NEXT:    br label [[TMP15]]
+; UNLIM:       6:
+; UNLIM-NEXT:    [[TMP7:%.*]] = icmp eq ptr [[FP]], @void
+; UNLIM-NEXT:    br i1 [[TMP7]], label [[TMP8:%.*]], label [[TMP9:%.*]]
+; UNLIM:       8:
+; UNLIM-NEXT:    [[CALL3:%.*]] = call i32 @void(i32 42)
+; UNLIM-NEXT:    br label [[TMP15]]
+; UNLIM:       9:
+; UNLIM-NEXT:    br i1 true, label [[TMP10:%.*]], label [[TMP14:%.*]]
+; UNLIM:       10:
+; UNLIM-NEXT:    [[TMP11:%.*]] = bitcast i32 42 to float
+; UNLIM-NEXT:    [[TMP12:%.*]] = call float @retFloatTakeFloat(float [[TMP11]])
+; UNLIM-NEXT:    [[TMP13:%.*]] = bitcast float [[TMP12]] to i32
+; UNLIM-NEXT:    br label [[TMP15]]
+; UNLIM:       14:
+; UNLIM-NEXT:    unreachable
+; UNLIM:       15:
+; UNLIM-NEXT:    [[CALL_PHI:%.*]] = phi i32 [ [[CALL1]], [[TMP2]] ], [ [[CALL2]], [[TMP5]] ], [ [[CALL3]], [[TMP8]] ], [ [[TMP13]], [[TMP10]] ]
+; UNLIM-NEXT:    ret i32 [[CALL_PHI]]
+;
+; LIMI2-LABEL: @non_matching_fp1(
+; LIMI2-NEXT:    [[FP1:%.*]] = select i1 [[C1:%.*]], ptr @retI32, ptr @takeI32
+; LIMI2-NEXT:    [[FP2:%.*]] = select i1 [[C2:%.*]], ptr @retFloatTakeFloat, ptr @void
+; LIMI2-NEXT:    [[FP:%.*]] = select i1 [[C:%.*]], ptr [[FP1]], ptr [[FP2]]
+; LIMI2-NEXT:    [[TMP1:%.*]] = icmp eq ptr [[FP]], @takeI32
+; LIMI2-NEXT:    br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
+; LIMI2:       2:
+; LIMI2-NEXT:    [[CALL1:%.*]] = call i32 @takeI32(i32 42)
+; LIMI2-NEXT:    br label [[TMP7:%.*]]
+; LIMI2:       3:
+; LIMI2-NEXT:    [[TMP4:%.*]] = icmp eq ptr [[FP]], @retI32
+; LIMI2-NEXT:    br i1 [[TMP4]], label [[TMP5:%.*]], label [[TMP6:%.*]]
+; LIMI2:       5:
+; LIMI2-NEXT:    [[CALL2:%.*]] = call i32 @retI32(i32 42)
+; LIMI2-NEXT:    br label [[TMP7]]
+; LIMI2:       6:
+; LIMI2-NEXT:    [[CALL3:%.*]] = call i32 [[FP]](i32 42), !callees !0
+; LIMI2-NEXT:    br label [[TMP7]]
+; LIMI2:       7:
+; LIMI2-NEXT:    [[CALL_PHI:%.*]] = phi i32 [ [[CALL1]], [[TMP2]] ], [ [[CALL2]], [[TMP5]] ], [ [[CALL3]], [[TMP6]] ]
+; LIMI2-NEXT:    ret i32 [[CALL_PHI]]
+;
+; LIMI0-LABEL: @non_matching_fp1(
+; LIMI0-NEXT:    [[FP1:%.*]] = select i1 [[C1:%.*]], ptr @retI32, ptr @takeI32
+; LIMI0-NEXT:    [[FP2:%.*]] = select i1 [[C2:%.*]], ptr @retFloatTakeFloat, ptr @void
+; LIMI0-NEXT:    [[FP:%.*]] = select i1 [[C:%.*]], ptr [[FP1]], ptr [[FP2]]
+; LIMI0-NEXT:    [[CALL:%.*]] = call i32 [[FP]](i32 42), !callees !1
+; LIMI0-NEXT:    ret i32 [[CALL]]
 ;
   %fp1 = select i1 %c1, ptr @retI32, ptr @takeI32
   %fp2 = select i1 %c2, ptr @retFloatTakeFloat, ptr @void
@@ -122,32 +159,60 @@ define i32 @non_matching_fp1(i1 %c1, i1 %c2, i1 %c) {
 }
 
 define void @non_matching_fp2(i1 %c1, i1 %c2, i1 %c, ptr %unknown) {
-; CHECK-LABEL: @non_matching_fp2(
-; CHECK-NEXT:    [[FP1:%.*]] = select i1 [[C1:%.*]], ptr @retI32, ptr @takeI32
-; CHECK-NEXT:    [[FP2:%.*]] = select i1 [[C2:%.*]], ptr @retFloatTakeFloat, ptr [[UNKNOWN:%.*]]
-; CHECK-NEXT:    [[FP:%.*]] = select i1 [[C:%.*]], ptr [[FP1]], ptr [[FP2]]
-; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq ptr [[FP]], @takeI32
-; CHECK-NEXT:    br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
-; CHECK:       2:
-; CHECK-NEXT:    call void @takeI32()
-; CHECK-NEXT:    br label [[TMP10:%.*]]
-; CHECK:       3:
-; CHECK-NEXT:    [[TMP4:%.*]] = icmp eq ptr [[FP]], @retI32
-; CHECK-NEXT:    br i1 [[TMP4]], label [[TMP5:%.*]], label [[TMP6:%.*]]
-; CHECK:       5:
-; CHECK-NEXT:    call void @retI32()
-; CHECK-NEXT:    br label [[TMP10]]
-; CHECK:       6:
-; CHECK-NEXT:    [[TMP7:%.*]] = icmp eq ptr [[FP]], @retFloatTakeFloat
-; CHECK-NEXT:    br i1 [[TMP7]], label [[TMP8:%.*]], label [[TMP9:%.*]]
-; CHECK:       8:
-; CHECK-NEXT:    call void @retFloatTakeFloat()
-; CHECK-NEXT:    br label [[TMP10]]
-; CHECK:       9:
-; CHECK-NEXT:    call void [[FP]]()
-; CHECK-NEXT:    br label [[TMP10]]
-; CHECK:       10:
-; CHECK-NEXT:    ret void
+; UNLIM-LABEL: @non_matching_fp2(
+; UNLIM-NEXT:    [[FP1:%.*]] = select i1 [[C1:%.*]], ptr @retI32, ptr @takeI32
+; UNLIM-NEXT:    [[FP2:%.*]] = select i1 [[C2:%.*]], ptr @retFloatTakeFloat, ptr [[UNKNOWN:%.*]]
+; UNLIM-NEXT:    [[FP:%.*]] = select i1 [[C:%.*]], ptr [[FP1]], ptr [[FP2]]
+; UNLIM-NEXT:    [[TMP1:%.*]] = icmp eq ptr [[FP]], @takeI32
+; UNLIM-NEXT:    br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
+; UNLIM:       2:
+; UNLIM-NEXT:    call void @takeI32()
+; UNLIM-NEXT:    br label [[TMP10:%.*]]
+; UNLIM:       3:
+; UNLIM-NEXT:    [[TMP4:%.*]] = icmp eq ptr [[FP]], @retI32
+; UNLIM-NEXT:    br i1 [[TMP4]], label [[TMP5:%.*]], label [[TMP6:%.*]]
+; UNLIM:       5:
+; UNLIM-NEXT:    call void @retI32()
+; UNLIM-NEXT:    br label [[TMP10]]
+; UNLIM:       6:
+; UNLIM-NEXT:    [[TMP7:%.*]] = icmp eq ptr [[FP]], @retFloatTakeFloat
+; UNLIM-NEXT:    br i1 [[TMP7]], label [[TMP8:%.*]], label [[TMP9:%.*]]
+; UNLIM:       8:
+; UNLIM-NEXT:    call void @retFloatTakeFloat()
+; UNLIM-NEXT:    br label [[TMP10]]
+; UNLIM:       9:
+; UNLIM-NEXT:    call void [[FP]]()
+; UNLIM-NEXT:    br label [[TMP10]]
+; UNLIM:       10:
+; UNLIM-NEXT:    ret void
+;
+; LIMI2-LABEL: @non_matching_fp2(
+; LIMI2-NEXT:    [[FP1:%.*]] = select i1 [[C1:%.*]], ptr @retI32, ptr @takeI32
+; LIMI2-NEXT:    [[FP2:%.*]] = select i1 [[C2:%.*]], ptr @retFloatTakeFloat, ptr [[UNKNOWN:%.*]]
+; LIMI2-NEXT:    [[FP:%.*]] = select i1 [[C:%.*]], ptr [[FP1]], ptr [[FP2]]
+; LIMI2-NEXT:    [[TMP1:%.*]] = icmp eq ptr [[FP]], @takeI32
+; LIMI2-NEXT:    br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
+; LIMI2:       2:
+; LIMI2-NEXT:    call void @takeI32()
+; LIMI2-NEXT:    br label [[TMP7:%.*]]
+; LIMI2:       3:
+; LIMI2-NEXT:    [[TMP4:%.*]] = icmp eq ptr [[FP]], @retI32
+; LIMI2-NEXT:    br i1 [[TMP4]], label [[TMP5:%.*]], label [[TMP6:%.*]]
+; LIMI2:       5:
+; LIMI2-NEXT:    call void @retI32()
+; LIMI2-NEXT:    br label [[TMP7]]
+; LIMI2:       6:
+; LIMI2-NEXT:    call void [[FP]]()
+; LIMI2-NEXT:    br label [[TMP7]]
+; LIMI2:       7:
+; LIMI2-NEXT:    ret void
+;
+; LIMI0-LABEL: @non_matching_fp2(
+; LIMI0-NEXT:    [[FP1:%.*]] = select i1 [[C1:%.*]], ptr @retI32, ptr @takeI32
+; LIMI0-NEXT:    [[FP2:%.*]] = select i1 [[C2:%.*]], ptr @retFloatTakeFloat, ptr [[UNKNOWN:%.*]]
+; LIMI0-NEXT:    [[FP:%.*]] = select i1 [[C:%.*]], ptr [[FP1]], ptr [[FP2]]
+; LIMI0-NEXT:    call void [[FP]]()
+; LIMI0-NEXT:    ret void
 ;
   %fp1 = select i1 %c1, ptr @retI32, ptr @takeI32
   %fp2 = select i1 %c2, ptr @retFloatTakeFloat, ptr %unknown
@@ -157,19 +222,24 @@ define void @non_matching_fp2(i1 %c1, i1 %c2, i1 %c, ptr %unknown) {
 }
 
 define i32 @non_matching_unknown(i1 %c, ptr %fn) {
-; CHECK-LABEL: @non_matching_unknown(
-; CHECK-NEXT:    [[FP:%.*]] = select i1 [[C:%.*]], ptr @retI32, ptr [[FN:%.*]]
-; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq ptr [[FP]], @retI32
-; CHECK-NEXT:    br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
-; CHECK:       2:
-; CHECK-NEXT:    [[CALL1:%.*]] = call i32 @retI32(i32 42)
-; CHECK-NEXT:    br label [[TMP4:%.*]]
-; CHECK:       3:
-; CHECK-NEXT:    [[CALL2:%.*]] = call i32 [[FP]](i32 42)
-; CHECK-NEXT:    br label [[TMP4]]
-; CHECK:       4:
-; CHECK-NEXT:    [[CALL_PHI:%.*]] = phi i32 [ [[CALL1]], [[TMP2]] ], [ [[CALL2]], [[TMP3]] ]
-; CHECK-NEXT:    ret i32 [[CALL_PHI]]
+; UPTO2-LABEL: @non_matching_unknown(
+; UPTO2-NEXT:    [[FP:%.*]] = select i1 [[C:%.*]], ptr @retI32, ptr [[FN:%.*]]
+; UPTO2-NEXT:    [[TMP1:%.*]] = icmp eq ptr [[FP]], @retI32
+; UPTO2-NEXT:    br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
+; UPTO2:       2:
+; UPTO2-NEXT:    [[CALL1:%.*]] = call i32 @retI32(i32 42)
+; UPTO2-NEXT:    br label [[TMP4:%.*]]
+; UPTO2:       3:
+; UPTO2-NEXT:    [[CALL2:%.*]] = call i32 [[FP]](i32 42)
+; UPTO2-NEXT:    br label [[TMP4]]
+; UPTO2:       4:
+; UPTO2-NEXT:    [[CALL_PHI:%.*]] = phi i32 [ [[CALL1]], [[TMP2]] ], [ [[CALL2]], [[TMP3]] ]
+; UPTO2-NEXT:    ret i32 [[CALL_PHI]]
+;
+; LIMI0-LABEL: @non_matching_unknown(
+; LIMI0-NEXT:    [[FP:%.*]] = select i1 [[C:%.*]], ptr @retI32, ptr [[FN:%.*]]
+; LIMI0-NEXT:    [[CALL:%.*]] = call i32 [[FP]](i32 42)
+; LIMI0-NEXT:    ret i32 [[CALL]]
 ;
   %fp = select i1 %c, ptr @retI32, ptr %fn
   %call = call i32 %fp(i32 42)
@@ -195,21 +265,25 @@ define void @func6() {
 }
 
 define void @func7(ptr %unknown) {
-; CHECK-LABEL: @func7(
-; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq ptr [[UNKNOWN:%.*]], @func3
-; CHECK-NEXT:    br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
-; CHECK:       2:
-; CHECK-NEXT:    call void @func3()
-; CHECK-NEXT:    br label [[TMP6:%.*]]
-; CHECK:       3:
-; CHECK-NEXT:    br i1 true, label [[TMP4:%.*]], label [[TMP5:%.*]]
-; CHECK:       4:
-; CHECK-NEXT:    call void @func4()
-; CHECK-NEXT:    br label [[TMP6]]
-; CHECK:       5:
-; CHECK-NEXT:    unreachable
-; CHECK:       6:
-; CHECK-NEXT:    ret void
+; UPTO2-LABEL: @func7(
+; UPTO2-NEXT:    [[TMP1:%.*]] = icmp eq ptr [[UNKNOWN:%.*]], @func3
+; UPTO2-NEXT:    br i1 [[TMP1]], label [[TMP2:%.*]], label [[TMP3:%.*]]
+; UPTO2:       2:
+; UPTO2-NEXT:    call void @func3()
+; UPTO2-NEXT:    br label [[TMP6:%.*]]
+; UPTO2:       3:
+; UPTO2-NEXT:    br i1 true, label [[TMP4:%.*]], label [[TMP5:%.*]]
+; UPTO2:       4:
+; UPTO2-NEXT:    call void @func4()
+; UPTO2-NEXT:    br label [[TMP6]]
+; UPTO2:       5:
+; UPTO2-NEXT:    unreachable
+; UPTO2:       6:
+; UPTO2-NEXT:    ret void
+;
+; LIMI0-LABEL: @func7(
+; LIMI0-NEXT:    call void [[UNKNOWN:%.*]](), !callees !0
+; LIMI0-NEXT:    ret void
 ;
   call void %unknown(), !callees !2
   ret void
@@ -217,10 +291,20 @@ define void @func7(ptr %unknown) {
 
 ; Check there's no crash if something that isn't a function appears in !callees
 define void @undef_in_callees() {
-; CHECK-LABEL: @undef_in_callees(
-; CHECK-NEXT:  cond.end.i:
-; CHECK-NEXT:    call void undef(ptr undef, i32 undef, ptr undef), !callees !2
-; CHECK-NEXT:    ret void
+; UNLIM-LABEL: @undef_in_callees(
+; UNLIM-NEXT:  cond.end.i:
+; UNLIM-NEXT:    call void undef(ptr undef, i32 undef, ptr undef), !callees !2
+; UNLIM-NEXT:    ret void
+;
+; LIMI2-LABEL: @undef_in_callees(
+; LIMI2-NEXT:  cond.end.i:
+; LIMI2-NEXT:    call void undef(ptr undef, i32 undef, ptr undef), !callees !3
+; LIMI2-NEXT:    ret void
+;
+; LIMI0-LABEL: @undef_in_callees(
+; LIMI0-NEXT:  cond.end.i:
+; LIMI0-NEXT:    call void undef(ptr undef, i32 undef, ptr undef), !callees !4
+; LIMI0-NEXT:    ret void
 ;
 cond.end.i:
   call void undef(ptr undef, i32 undef, ptr undef), !callees !3
@@ -257,5 +341,21 @@ cond.end.i:
 ; These ones are added because of the callees metadata.
 ; DOT-DAG: Node[[FUNC7]] -> Node[[FUNC3]];
 ; DOT-DAG: Node[[FUNC7]] -> Node[[FUNC4]];
+;.
+; UNLIM: [[META0:![0-9]+]] = !{!1}
+; UNLIM: [[META1:![0-9]+]] = !{i64 0, i1 false}
+; UNLIM: [[META2:![0-9]+]] = distinct !{ptr undef, ptr null}
+;.
+; LIMI2: [[META0:![0-9]+]] = !{ptr @void, ptr @retFloatTakeFloat}
+; LIMI2: [[META1:![0-9]+]] = !{!2}
+; LIMI2: [[META2:![0-9]+]] = !{i64 0, i1 false}
+; LIMI2: [[META3:![0-9]+]] = distinct !{ptr undef, ptr null}
+;.
+; LIMI0: [[META0:![0-9]+]] = !{ptr @func3, ptr @func4}
+; LIMI0: [[META1:![0-9]+]] = !{ptr @takeI32, ptr @retI32, ptr @void, ptr @retFloatTakeFloat}
+; LIMI0: [[META2:![0-9]+]] = !{!3}
+; LIMI0: [[META3:![0-9]+]] = !{i64 0, i1 false}
+; LIMI0: [[META4:![0-9]+]] = distinct !{ptr undef, ptr null}
+;.
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; DOT: {{.*}}


        


More information about the llvm-commits mailing list