[llvm] [LLVM][Auto-Upgrade] Support default args on overloaded intrinsics and undeclared multi-call upgrades (PR #216246)

Dharuni R Acharya via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 20 03:49:07 PDT 2026


https://github.com/DharuniRAcharya updated https://github.com/llvm/llvm-project/pull/216246

>From 313cbb5bd5f3fa9f9f0e642d2478074aeca97d62 Mon Sep 17 00:00:00 2001
From: DharuniRAcharya <dharunira at nvidia.com>
Date: Fri, 14 Aug 2026 04:58:08 +0000
Subject: [PATCH 1/3] [LLVM][Auto-Upgrade] Support default args on overloaded
 intrinsics and undeclared multi-call upgrades

This patch extends intrinsic DefaultValue auto-upgrade to overloaded intrinsics and to calls that omit an explicit declare.

Multiple undeclared calls can leave uniquified temps; this patch recovers the intrinsic via prefix matching in upgradeIntrinsicWithDefaultArgs,
then validate the partial signature before filling default ImmArgs.

Signed-off-by: DharuniRAcharya <dharunira at nvidia.com>
---
 llvm/include/llvm/IR/Intrinsics.h             |  8 ++
 llvm/lib/IR/AutoUpgrade.cpp                   | 78 +++++++++++++++----
 llvm/lib/IR/Intrinsics.cpp                    | 39 ++++++++--
 llvm/test/TableGen/intrinsic-default-args.td  | 25 +++++-
 .../TableGen/Basic/CodeGenIntrinsics.cpp      | 10 ---
 5 files changed, 124 insertions(+), 36 deletions(-)

diff --git a/llvm/include/llvm/IR/Intrinsics.h b/llvm/include/llvm/IR/Intrinsics.h
index a4799751832bf..c63a04ce06565 100644
--- a/llvm/include/llvm/IR/Intrinsics.h
+++ b/llvm/include/llvm/IR/Intrinsics.h
@@ -308,6 +308,14 @@ LLVM_ABI bool isSignatureValid(Function *F,
                                SmallVectorImpl<Type *> &OverloadTys,
                                raw_ostream &OS = nulls());
 
+/// Same as previous, but \p FT may omit exactly \p NumMissingTrailingParams
+/// trailing parameters. The omitted parameters must have concrete integer type
+/// so that all overload types can be resolved from the provided signature.
+LLVM_ABI bool isSignatureValid(Intrinsic::ID ID, FunctionType *FT,
+                               SmallVectorImpl<Type *> &OverloadTys,
+                               unsigned NumMissingTrailingParams,
+                               raw_ostream &OS = nulls());
+
 // Checks if the intrinsic name matches with its signature and if not
 // returns the declaration with the same signature and remangled name.
 // An existing GlobalValue with the wanted name but with a wrong prototype
diff --git a/llvm/lib/IR/AutoUpgrade.cpp b/llvm/lib/IR/AutoUpgrade.cpp
index 67f6e55eb500f..86f9fdbbe5cb0 100644
--- a/llvm/lib/IR/AutoUpgrade.cpp
+++ b/llvm/lib/IR/AutoUpgrade.cpp
@@ -1467,25 +1467,14 @@ static bool convertIntrinsicValidType(StringRef Name,
   return false;
 }
 
-static bool upgradeIntrinsicDeclWithDefaultArgs(Function *F, Function *&NewFn) {
-  Intrinsic::ID IID = Intrinsic::lookupIntrinsicID(F->getName());
-  if (IID == Intrinsic::not_intrinsic)
-    return false;
-
+static bool getDefaultArgUpgradeInfo(
+    Function *F, Intrinsic::ID IID, SmallVectorImpl<Type *> &OverloadTys) {
   auto [FirstDefault, Defaults] = Intrinsic::getAllDefaultArgValues(IID);
   if (Defaults.empty())
     return false;
 
-  // Overloaded intrinsics are out of scope for the default-arg feature
-  // and will be supported in a follow-up.
-  if (Intrinsic::isOverloaded(IID))
-    return false;
-
-  // Get the canonical full declaration for this intrinsic.
-  Function *FullDecl = Intrinsic::getOrInsertDeclaration(F->getParent(), IID);
-
-  // If the existing declaration already has all args, nothing to upgrade
-  if (F->arg_size() >= FullDecl->arg_size())
+  unsigned FullArgCount = FirstDefault + Defaults.size();
+  if (F->arg_size() >= FullArgCount)
     return false;
 
   // Defaults are a contiguous trailing block, so checking the first missing
@@ -1493,7 +1482,62 @@ static bool upgradeIntrinsicDeclWithDefaultArgs(Function *F, Function *&NewFn) {
   if (F->arg_size() < FirstDefault)
     return false;
 
-  NewFn = FullDecl;
+  unsigned NumMissingTrailingParams = FullArgCount - F->arg_size();
+  if (!Intrinsic::isSignatureValid(IID, F->getFunctionType(), OverloadTys,
+                                   NumMissingTrailingParams))
+    return false;
+
+  return true;
+}
+
+static bool upgradeIntrinsicWithDefaultArgs(Function *F, Function *&NewFn) {
+  Intrinsic::ID IID = F->getIntrinsicID();
+  SmallVector<Type *, 4> OverloadTys;
+
+  if (IID != Intrinsic::not_intrinsic) {
+    if (!getDefaultArgUpgradeInfo(F, IID, OverloadTys))
+      return false;
+  } else {
+    Function *BestMatch = nullptr;
+    SmallVector<Type *, 4> BestOverloadTys;
+    for (Function &Candidate : *F->getParent()) {
+      Intrinsic::ID CandidateIID = Candidate.getIntrinsicID();
+      if (CandidateIID == Intrinsic::not_intrinsic)
+        continue;
+
+      StringRef CandidateName = Candidate.getName();
+      StringRef Suffix = F->getName();
+      if (!Suffix.consume_front(CandidateName) ||
+          !Suffix.consume_front(".") || Suffix.empty())
+        continue;
+
+      unsigned UniqueID;
+      if (Suffix.getAsInteger(10, UniqueID))
+        continue;
+
+      SmallVector<Type *, 4> CandidateOverloadTys;
+      if (!getDefaultArgUpgradeInfo(F, CandidateIID, CandidateOverloadTys))
+        continue;
+
+      if (!BestMatch ||
+          CandidateName.size() > BestMatch->getName().size()) {
+        BestMatch = &Candidate;
+        IID = CandidateIID;
+        BestOverloadTys = std::move(CandidateOverloadTys);
+      }
+    }
+
+    if (!BestMatch)
+      return false;
+    OverloadTys = std::move(BestOverloadTys);
+  }
+
+  auto [FirstDefault, Defaults] = Intrinsic::getAllDefaultArgValues(IID);
+  unsigned FullArgCount = FirstDefault + Defaults.size();
+  NewFn =
+      Intrinsic::getOrInsertDeclaration(F->getParent(), IID, OverloadTys);
+  assert(NewFn->arg_size() == FullArgCount &&
+         "default argument table does not match intrinsic signature");
   return true;
 }
 
@@ -2179,7 +2223,7 @@ static bool upgradeIntrinsicFunction1(Function *F, Function *&NewFn,
   //  to both detect an intrinsic which needs upgrading, and to provide the
   //  upgraded form of the intrinsic. We should perhaps have two separate
   //  functions for this.
-  if (upgradeIntrinsicDeclWithDefaultArgs(F, NewFn))
+  if (upgradeIntrinsicWithDefaultArgs(F, NewFn))
     return true;
 
   return false;
diff --git a/llvm/lib/IR/Intrinsics.cpp b/llvm/lib/IR/Intrinsics.cpp
index 266af8e06a230..f976f05fe9b80 100644
--- a/llvm/lib/IR/Intrinsics.cpp
+++ b/llvm/lib/IR/Intrinsics.cpp
@@ -44,7 +44,8 @@ static bool isSignatureValid(FunctionType *FTy,
                              ArrayRef<Intrinsic::IITDescriptor> &Infos,
                              unsigned NumArgs, bool IsVarArg,
                              SmallVectorImpl<Type *> &OverloadTys,
-                             raw_ostream &OS);
+                             raw_ostream &OS,
+                             unsigned NumMissingTrailingParams = 0);
 
 /// Table of string intrinsic names indexed by enum value.
 #define GET_INTRINSIC_NAME_TABLE
@@ -1333,13 +1334,18 @@ matchIntrinsicType(Type *Ty, ArrayRef<Intrinsic::IITDescriptor> &Infos,
 /// \p IsVarArg. The overloaded types for the intrinsic are pushed to the
 /// \p OverloadTys vector.
 ///
+/// If \p NumMissingTrailingParams is non-zero, \p FTy may omit exactly that
+/// many trailing parameters. Omitted parameters must have concrete integer
+/// types and therefore cannot contribute an unresolved overload type.
+///
 /// If the type is not valid, returns false and prints an error message to
 /// \p OS.
 static bool isSignatureValid(FunctionType *FTy,
                              ArrayRef<Intrinsic::IITDescriptor> &Infos,
                              unsigned NumArgs, bool IsVarArg,
                              SmallVectorImpl<Type *> &OverloadTys,
-                             raw_ostream &OS) {
+                             raw_ostream &OS,
+                             unsigned NumMissingTrailingParams) {
   SmallVector<DeferredIntrinsicMatchInfo, 2> DeferredChecks;
 
   assert(!Infos.empty() && "Table consistency error");
@@ -1352,9 +1358,10 @@ static bool isSignatureValid(FunctionType *FTy,
                          DeferredChecks, false, OS))
     return false;
 
-  if (FTy->getNumParams() != NumArgs) {
+  unsigned ProvidedArgs = FTy->getNumParams();
+  if (ProvidedArgs + NumMissingTrailingParams != NumArgs) {
     OS << "intrinsic has incorrect number of args. Expected " << NumArgs
-       << ", but got " << FTy->getNumParams();
+       << ", but got " << ProvidedArgs;
     return false;
   }
 
@@ -1373,6 +1380,19 @@ static bool isSignatureValid(FunctionType *FTy,
       return false;
   }
 
+  if (NumMissingTrailingParams) {
+    // Default arguments are materialized as ConstantInt values, requiring one
+    // concrete integer descriptor per omitted parameter.
+    if (Infos.size() != NumMissingTrailingParams ||
+        llvm::any_of(Infos, [](Intrinsic::IITDescriptor D) {
+          return D.Kind != Intrinsic::IITDescriptor::Integer;
+        })) {
+      OS << "intrinsic has unresolved trailing argument types!";
+      return false;
+    }
+    Infos = {};
+  }
+
   if (!Infos.empty()) {
     OS << "intrinsic has too few arguments!";
     return false;
@@ -1399,13 +1419,22 @@ bool Intrinsic::hasStructReturnType(ID id) {
 bool Intrinsic::isSignatureValid(Intrinsic::ID ID, FunctionType *FT,
                                  SmallVectorImpl<Type *> &OverloadTys,
                                  raw_ostream &OS) {
+  return isSignatureValid(ID, FT, OverloadTys,
+                          /*NumMissingTrailingParams=*/0, OS);
+}
+
+bool Intrinsic::isSignatureValid(Intrinsic::ID ID, FunctionType *FT,
+                                 SmallVectorImpl<Type *> &OverloadTys,
+                                 unsigned NumMissingTrailingParams,
+                                 raw_ostream &OS) {
   if (!ID)
     return false;
 
   SmallVector<Intrinsic::IITDescriptor, 8> Table;
   auto [TableRef, NumArgs, IsVarArg] = getIntrinsicInfoTableEntries(ID, Table);
 
-  return ::isSignatureValid(FT, TableRef, NumArgs, IsVarArg, OverloadTys, OS);
+  return ::isSignatureValid(FT, TableRef, NumArgs, IsVarArg, OverloadTys, OS,
+                            NumMissingTrailingParams);
 }
 
 bool Intrinsic::isSignatureValid(Function *F,
diff --git a/llvm/test/TableGen/intrinsic-default-args.td b/llvm/test/TableGen/intrinsic-default-args.td
index e372a870880e7..1afffc964360b 100644
--- a/llvm/test/TableGen/intrinsic-default-args.td
+++ b/llvm/test/TableGen/intrinsic-default-args.td
@@ -6,7 +6,7 @@
 // RUN: not llvm-tblgen -gen-intrinsic-impl -I %p/../../include %s -DTEST_INTRINSICS_SUPPRESS_DEFS -DERROR_NEGATIVE 2>&1 | FileCheck %s --check-prefix=ERR-NEGATIVE
 // RUN: not llvm-tblgen -gen-intrinsic-impl -I %p/../../include %s -DTEST_INTRINSICS_SUPPRESS_DEFS -DERROR_RANGE 2>&1 | FileCheck %s --check-prefix=ERR-RANGE
 // RUN: not llvm-tblgen -gen-intrinsic-impl -I %p/../../include %s -DTEST_INTRINSICS_SUPPRESS_DEFS -DERROR_NONINT 2>&1 | FileCheck %s --check-prefix=ERR-NONINT
-// RUN: not llvm-tblgen -gen-intrinsic-impl -I %p/../../include %s -DTEST_INTRINSICS_SUPPRESS_DEFS -DERROR_OVERLOADED 2>&1 | FileCheck %s --check-prefix=ERR-OVERLOADED
+// RUN: llvm-tblgen -gen-intrinsic-impl -I %p/../../include %s -DTEST_INTRINSICS_SUPPRESS_DEFS -DTEST_OVERLOADED | FileCheck %s --check-prefix=OVERLOADED
 // RUN: not llvm-tblgen -gen-intrinsic-impl -I %p/../../include %s -DTEST_INTRINSICS_SUPPRESS_DEFS -DERROR_GAP 2>&1 | FileCheck %s --check-prefix=ERR-GAP
 
 include "llvm/IR/Intrinsics.td"
@@ -77,12 +77,29 @@ def int_test_nonint :
               [ImmArg<ArgIndex<1>, DefaultValue<1>>]>;
 #endif
 
-// A default on an overloaded intrinsic is rejected (not yet supported).
-#ifdef ERROR_OVERLOADED
-// ERR-OVERLOADED: error: default argument values are not supported for overloaded intrinsics
+#ifdef TEST_OVERLOADED
 def int_test_overloaded :
     Intrinsic<[llvm_anyint_ty], [llvm_anyint_ty, llvm_i32_ty],
               [ImmArg<ArgIndex<1>, DefaultValue<5>>]>;
+
+def int_test_overloaded_two_defaults :
+    Intrinsic<[llvm_anyint_ty],
+              [LLVMMatchType<0>, llvm_i1_ty, llvm_i32_ty],
+              [ImmArg<ArgIndex<1>, DefaultValue<0>>,
+               ImmArg<ArgIndex<2>, DefaultValue<7>>]>;
+
+def int_test_overloaded_ptr :
+    Intrinsic<[llvm_anyptr_ty], [LLVMMatchType<0>, llvm_i32_ty],
+              [ImmArg<ArgIndex<1>, DefaultValue<1>>]>;
+
+// Header (1 << 32) | 1 = 4294967297 for single trailing defaults.
+// Header (2 << 32) | 1 = 8589934593 for two trailing defaults.
+// OVERLOADED:      static constexpr uint64_t DefaultArgValuesTable[] = {
+// OVERLOADED-NEXT:   0, // offset 0: sentinel for intrinsics without defaults
+// OVERLOADED-DAG: {{.*}}4294967297,{{.*}}5,{{.*}}0,
+// OVERLOADED-DAG: {{.*}}8589934593,{{.*}}0,{{.*}}7,{{.*}}0,
+// OVERLOADED-DAG: {{.*}}4294967297,{{.*}}1,{{.*}}0,
+// OVERLOADED: Intrinsic::getAllDefaultArgValues(ID IID) {
 #endif
 
 // Defaults must form a contiguous trailing block.
diff --git a/llvm/utils/TableGen/Basic/CodeGenIntrinsics.cpp b/llvm/utils/TableGen/Basic/CodeGenIntrinsics.cpp
index 76fc2a4f38811..7bcfcc6d0f87f 100644
--- a/llvm/utils/TableGen/Basic/CodeGenIntrinsics.cpp
+++ b/llvm/utils/TableGen/Basic/CodeGenIntrinsics.cpp
@@ -388,16 +388,6 @@ CodeGenIntrinsic::CodeGenIntrinsic(const Record *R,
   for (auto &Attrs : ArgumentAttributes)
     llvm::sort(Attrs);
 
-  // Default values are not yet supported for overloaded intrinsics
-  // (overloaded support will come in a follow-up).
-  if (isOverloaded &&
-      llvm::any_of(ParamDefaultValues, [](const std::optional<uint64_t> &DV) {
-        return DV.has_value();
-      }))
-    PrintFatalError(TheDef->getLoc(),
-                    "default argument values are not supported for "
-                    "overloaded intrinsics");
-
   // Validate: defaults must form a contiguous trailing block ending at
   // the last parameter (mirrors C++ default-argument rules).
   unsigned NumParams = IS.ParamTys.size();

>From 4746c3467b6ab2ef5a95c196c844c5e9efddbcc7 Mon Sep 17 00:00:00 2001
From: DharuniRAcharya <dharunira at nvidia.com>
Date: Fri, 14 Aug 2026 05:03:51 +0000
Subject: [PATCH 2/3] Fix formatting

---
 llvm/lib/IR/AutoUpgrade.cpp | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/IR/AutoUpgrade.cpp b/llvm/lib/IR/AutoUpgrade.cpp
index 86f9fdbbe5cb0..03979cf4047fa 100644
--- a/llvm/lib/IR/AutoUpgrade.cpp
+++ b/llvm/lib/IR/AutoUpgrade.cpp
@@ -1467,8 +1467,8 @@ static bool convertIntrinsicValidType(StringRef Name,
   return false;
 }
 
-static bool getDefaultArgUpgradeInfo(
-    Function *F, Intrinsic::ID IID, SmallVectorImpl<Type *> &OverloadTys) {
+static bool getDefaultArgUpgradeInfo(Function *F, Intrinsic::ID IID,
+                                     SmallVectorImpl<Type *> &OverloadTys) {
   auto [FirstDefault, Defaults] = Intrinsic::getAllDefaultArgValues(IID);
   if (Defaults.empty())
     return false;
@@ -1507,8 +1507,8 @@ static bool upgradeIntrinsicWithDefaultArgs(Function *F, Function *&NewFn) {
 
       StringRef CandidateName = Candidate.getName();
       StringRef Suffix = F->getName();
-      if (!Suffix.consume_front(CandidateName) ||
-          !Suffix.consume_front(".") || Suffix.empty())
+      if (!Suffix.consume_front(CandidateName) || !Suffix.consume_front(".") ||
+          Suffix.empty())
         continue;
 
       unsigned UniqueID;
@@ -1519,8 +1519,7 @@ static bool upgradeIntrinsicWithDefaultArgs(Function *F, Function *&NewFn) {
       if (!getDefaultArgUpgradeInfo(F, CandidateIID, CandidateOverloadTys))
         continue;
 
-      if (!BestMatch ||
-          CandidateName.size() > BestMatch->getName().size()) {
+      if (!BestMatch || CandidateName.size() > BestMatch->getName().size()) {
         BestMatch = &Candidate;
         IID = CandidateIID;
         BestOverloadTys = std::move(CandidateOverloadTys);
@@ -1534,8 +1533,7 @@ static bool upgradeIntrinsicWithDefaultArgs(Function *F, Function *&NewFn) {
 
   auto [FirstDefault, Defaults] = Intrinsic::getAllDefaultArgValues(IID);
   unsigned FullArgCount = FirstDefault + Defaults.size();
-  NewFn =
-      Intrinsic::getOrInsertDeclaration(F->getParent(), IID, OverloadTys);
+  NewFn = Intrinsic::getOrInsertDeclaration(F->getParent(), IID, OverloadTys);
   assert(NewFn->arg_size() == FullArgCount &&
          "default argument table does not match intrinsic signature");
   return true;

>From 2fbd3f46a7e146001a8b267fe507ca42ae89ef0a Mon Sep 17 00:00:00 2001
From: DharuniRAcharya <dharunira at nvidia.com>
Date: Thu, 20 Aug 2026 10:48:27 +0000
Subject: [PATCH 3/3] Address comments

---
 llvm/include/llvm/IR/Intrinsics.h             |  8 --
 llvm/include/llvm/IR/Intrinsics.td            |  4 +-
 llvm/lib/IR/AutoUpgrade.cpp                   | 87 +++++--------------
 llvm/lib/IR/Intrinsics.cpp                    | 39 ++-------
 llvm/test/TableGen/intrinsic-default-args.td  | 25 +-----
 .../TableGen/Basic/CodeGenIntrinsics.cpp      | 16 ++--
 .../utils/TableGen/Basic/IntrinsicEmitter.cpp | 21 +----
 7 files changed, 41 insertions(+), 159 deletions(-)

diff --git a/llvm/include/llvm/IR/Intrinsics.h b/llvm/include/llvm/IR/Intrinsics.h
index c63a04ce06565..a4799751832bf 100644
--- a/llvm/include/llvm/IR/Intrinsics.h
+++ b/llvm/include/llvm/IR/Intrinsics.h
@@ -308,14 +308,6 @@ LLVM_ABI bool isSignatureValid(Function *F,
                                SmallVectorImpl<Type *> &OverloadTys,
                                raw_ostream &OS = nulls());
 
-/// Same as previous, but \p FT may omit exactly \p NumMissingTrailingParams
-/// trailing parameters. The omitted parameters must have concrete integer type
-/// so that all overload types can be resolved from the provided signature.
-LLVM_ABI bool isSignatureValid(Intrinsic::ID ID, FunctionType *FT,
-                               SmallVectorImpl<Type *> &OverloadTys,
-                               unsigned NumMissingTrailingParams,
-                               raw_ostream &OS = nulls());
-
 // Checks if the intrinsic name matches with its signature and if not
 // returns the declaration with the same signature and remangled name.
 // An existing GlobalValue with the wanted name but with a wrong prototype
diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td
index 083b933e77d16..1d97eaa26bc64 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -136,9 +136,7 @@ class Returned<ArgIndex idx> : IntrinsicProperty {
   int ArgNo = idx.Value;
 }
 
-// DefaultValue - A value carrier paired with ImmArg to declare a default for
-// a missing trailing argument. AutoUpgrade fills the default when an old
-// .bc / .ll file is loaded.
+// Default value for a trailing ImmArg, materialized by AutoUpgrade.
 class DefaultValue<int val> {
   int Value = val;
 }
diff --git a/llvm/lib/IR/AutoUpgrade.cpp b/llvm/lib/IR/AutoUpgrade.cpp
index 03979cf4047fa..b03826fd6a57d 100644
--- a/llvm/lib/IR/AutoUpgrade.cpp
+++ b/llvm/lib/IR/AutoUpgrade.cpp
@@ -1468,23 +1468,18 @@ static bool convertIntrinsicValidType(StringRef Name,
 }
 
 static bool getDefaultArgUpgradeInfo(Function *F, Intrinsic::ID IID,
-                                     SmallVectorImpl<Type *> &OverloadTys) {
+                                     unsigned &FullArgCount) {
   auto [FirstDefault, Defaults] = Intrinsic::getAllDefaultArgValues(IID);
   if (Defaults.empty())
     return false;
 
-  unsigned FullArgCount = FirstDefault + Defaults.size();
-  if (F->arg_size() >= FullArgCount)
+  if (Intrinsic::isOverloaded(IID))
     return false;
 
-  // Defaults are a contiguous trailing block, so checking the first missing
-  // argument is enough.
-  if (F->arg_size() < FirstDefault)
-    return false;
+  FullArgCount = FirstDefault + Defaults.size();
 
-  unsigned NumMissingTrailingParams = FullArgCount - F->arg_size();
-  if (!Intrinsic::isSignatureValid(IID, F->getFunctionType(), OverloadTys,
-                                   NumMissingTrailingParams))
+  // Only trailing default arguments can be missing.
+  if (F->arg_size() < FirstDefault || F->arg_size() >= FullArgCount)
     return false;
 
   return true;
@@ -1492,48 +1487,13 @@ static bool getDefaultArgUpgradeInfo(Function *F, Intrinsic::ID IID,
 
 static bool upgradeIntrinsicWithDefaultArgs(Function *F, Function *&NewFn) {
   Intrinsic::ID IID = F->getIntrinsicID();
-  SmallVector<Type *, 4> OverloadTys;
-
-  if (IID != Intrinsic::not_intrinsic) {
-    if (!getDefaultArgUpgradeInfo(F, IID, OverloadTys))
-      return false;
-  } else {
-    Function *BestMatch = nullptr;
-    SmallVector<Type *, 4> BestOverloadTys;
-    for (Function &Candidate : *F->getParent()) {
-      Intrinsic::ID CandidateIID = Candidate.getIntrinsicID();
-      if (CandidateIID == Intrinsic::not_intrinsic)
-        continue;
-
-      StringRef CandidateName = Candidate.getName();
-      StringRef Suffix = F->getName();
-      if (!Suffix.consume_front(CandidateName) || !Suffix.consume_front(".") ||
-          Suffix.empty())
-        continue;
-
-      unsigned UniqueID;
-      if (Suffix.getAsInteger(10, UniqueID))
-        continue;
-
-      SmallVector<Type *, 4> CandidateOverloadTys;
-      if (!getDefaultArgUpgradeInfo(F, CandidateIID, CandidateOverloadTys))
-        continue;
-
-      if (!BestMatch || CandidateName.size() > BestMatch->getName().size()) {
-        BestMatch = &Candidate;
-        IID = CandidateIID;
-        BestOverloadTys = std::move(CandidateOverloadTys);
-      }
-    }
 
-    if (!BestMatch)
-      return false;
-    OverloadTys = std::move(BestOverloadTys);
-  }
+  unsigned FullArgCount;
+  if (!getDefaultArgUpgradeInfo(F, IID, FullArgCount))
+    return false;
 
-  auto [FirstDefault, Defaults] = Intrinsic::getAllDefaultArgValues(IID);
-  unsigned FullArgCount = FirstDefault + Defaults.size();
-  NewFn = Intrinsic::getOrInsertDeclaration(F->getParent(), IID, OverloadTys);
+  rename(F);
+  NewFn = Intrinsic::getOrInsertDeclaration(F->getParent(), IID);
   assert(NewFn->arg_size() == FullArgCount &&
          "default argument table does not match intrinsic signature");
   return true;
@@ -2217,12 +2177,13 @@ static bool upgradeIntrinsicFunction1(Function *F, Function *&NewFn,
     return true;
   }
 
+  if (upgradeIntrinsicWithDefaultArgs(F, NewFn))
+    return true;
+
   //  This may not belong here. This function is effectively being overloaded
   //  to both detect an intrinsic which needs upgrading, and to provide the
   //  upgraded form of the intrinsic. We should perhaps have two separate
   //  functions for this.
-  if (upgradeIntrinsicWithDefaultArgs(F, NewFn))
-    return true;
 
   return false;
 }
@@ -5436,33 +5397,32 @@ static bool upgradeIntrinsicCallWithDefaultArgs(CallBase *CI, Function *NewFn,
   unsigned OldArgCount = CI->arg_size();
   unsigned NewArgCount = NewFn->arg_size();
 
-  // If the caller already supplied all arguments (or more), nothing to do.
-  // This mirrors C++ semantics: an explicitly-passed value is never overridden.
-  if (OldArgCount >= NewArgCount)
+  if (OldArgCount > NewArgCount)
     return false;
 
-  // Start with the existing arguments from the old call.
-  SmallVector<Value *, 8> NewArgs(CI->args());
+  if (OldArgCount == NewArgCount) {
+    if (CI->getFunctionType() != NewFn->getFunctionType())
+      return false;
+    CI->setCalledFunction(NewFn);
+    return true;
+  }
 
-  // Defaults are a contiguous trailing block, so checking the first missing
-  // argument is enough.
   if (OldArgCount < FirstDefault)
     return false;
 
-  // Fill in each missing trailing argument from the table.
+  SmallVector<Value *, 8> NewArgs(CI->args());
+
   FunctionType *NewFT = NewFn->getFunctionType();
   for (unsigned Idx = OldArgCount; Idx < NewArgCount; ++Idx) {
     assert(Idx >= FirstDefault && Idx - FirstDefault < Defaults.size() &&
            "missing argument outside the default range");
     Type *ParamTy = NewFT->getParamType(Idx);
 
-    // Only integer types are supported (i1, i8, i16, i32, i64).
     if (!ParamTy->isIntegerTy())
       return false;
     NewArgs.push_back(ConstantInt::get(ParamTy, Defaults[Idx - FirstDefault]));
   }
 
-  // Preserve operand bundles by creating the call with them.
   SmallVector<OperandBundleDef, 1> OpBundles;
   CI->getOperandBundlesAsDefs(OpBundles);
   CallInst *NewCall = Builder.CreateCall(NewFn, NewArgs, OpBundles);
@@ -5588,9 +5548,6 @@ void llvm::UpgradeIntrinsicCall(CallBase *CI, Function *NewFn) {
   CallInst *NewCall = nullptr;
   switch (NewFn->getIntrinsicID()) {
   default: {
-    // Last resort: try the data-driven default-arg upgrade.
-    // Handles any intrinsic annotated with ImmArg<..., DefaultValue<...>>
-    // in its .td definition, without needing a dedicated case.
     if (upgradeIntrinsicCallWithDefaultArgs(CI, NewFn, Builder))
       return;
     DefaultCase();
diff --git a/llvm/lib/IR/Intrinsics.cpp b/llvm/lib/IR/Intrinsics.cpp
index f976f05fe9b80..266af8e06a230 100644
--- a/llvm/lib/IR/Intrinsics.cpp
+++ b/llvm/lib/IR/Intrinsics.cpp
@@ -44,8 +44,7 @@ static bool isSignatureValid(FunctionType *FTy,
                              ArrayRef<Intrinsic::IITDescriptor> &Infos,
                              unsigned NumArgs, bool IsVarArg,
                              SmallVectorImpl<Type *> &OverloadTys,
-                             raw_ostream &OS,
-                             unsigned NumMissingTrailingParams = 0);
+                             raw_ostream &OS);
 
 /// Table of string intrinsic names indexed by enum value.
 #define GET_INTRINSIC_NAME_TABLE
@@ -1334,18 +1333,13 @@ matchIntrinsicType(Type *Ty, ArrayRef<Intrinsic::IITDescriptor> &Infos,
 /// \p IsVarArg. The overloaded types for the intrinsic are pushed to the
 /// \p OverloadTys vector.
 ///
-/// If \p NumMissingTrailingParams is non-zero, \p FTy may omit exactly that
-/// many trailing parameters. Omitted parameters must have concrete integer
-/// types and therefore cannot contribute an unresolved overload type.
-///
 /// If the type is not valid, returns false and prints an error message to
 /// \p OS.
 static bool isSignatureValid(FunctionType *FTy,
                              ArrayRef<Intrinsic::IITDescriptor> &Infos,
                              unsigned NumArgs, bool IsVarArg,
                              SmallVectorImpl<Type *> &OverloadTys,
-                             raw_ostream &OS,
-                             unsigned NumMissingTrailingParams) {
+                             raw_ostream &OS) {
   SmallVector<DeferredIntrinsicMatchInfo, 2> DeferredChecks;
 
   assert(!Infos.empty() && "Table consistency error");
@@ -1358,10 +1352,9 @@ static bool isSignatureValid(FunctionType *FTy,
                          DeferredChecks, false, OS))
     return false;
 
-  unsigned ProvidedArgs = FTy->getNumParams();
-  if (ProvidedArgs + NumMissingTrailingParams != NumArgs) {
+  if (FTy->getNumParams() != NumArgs) {
     OS << "intrinsic has incorrect number of args. Expected " << NumArgs
-       << ", but got " << ProvidedArgs;
+       << ", but got " << FTy->getNumParams();
     return false;
   }
 
@@ -1380,19 +1373,6 @@ static bool isSignatureValid(FunctionType *FTy,
       return false;
   }
 
-  if (NumMissingTrailingParams) {
-    // Default arguments are materialized as ConstantInt values, requiring one
-    // concrete integer descriptor per omitted parameter.
-    if (Infos.size() != NumMissingTrailingParams ||
-        llvm::any_of(Infos, [](Intrinsic::IITDescriptor D) {
-          return D.Kind != Intrinsic::IITDescriptor::Integer;
-        })) {
-      OS << "intrinsic has unresolved trailing argument types!";
-      return false;
-    }
-    Infos = {};
-  }
-
   if (!Infos.empty()) {
     OS << "intrinsic has too few arguments!";
     return false;
@@ -1419,22 +1399,13 @@ bool Intrinsic::hasStructReturnType(ID id) {
 bool Intrinsic::isSignatureValid(Intrinsic::ID ID, FunctionType *FT,
                                  SmallVectorImpl<Type *> &OverloadTys,
                                  raw_ostream &OS) {
-  return isSignatureValid(ID, FT, OverloadTys,
-                          /*NumMissingTrailingParams=*/0, OS);
-}
-
-bool Intrinsic::isSignatureValid(Intrinsic::ID ID, FunctionType *FT,
-                                 SmallVectorImpl<Type *> &OverloadTys,
-                                 unsigned NumMissingTrailingParams,
-                                 raw_ostream &OS) {
   if (!ID)
     return false;
 
   SmallVector<Intrinsic::IITDescriptor, 8> Table;
   auto [TableRef, NumArgs, IsVarArg] = getIntrinsicInfoTableEntries(ID, Table);
 
-  return ::isSignatureValid(FT, TableRef, NumArgs, IsVarArg, OverloadTys, OS,
-                            NumMissingTrailingParams);
+  return ::isSignatureValid(FT, TableRef, NumArgs, IsVarArg, OverloadTys, OS);
 }
 
 bool Intrinsic::isSignatureValid(Function *F,
diff --git a/llvm/test/TableGen/intrinsic-default-args.td b/llvm/test/TableGen/intrinsic-default-args.td
index 1afffc964360b..e372a870880e7 100644
--- a/llvm/test/TableGen/intrinsic-default-args.td
+++ b/llvm/test/TableGen/intrinsic-default-args.td
@@ -6,7 +6,7 @@
 // RUN: not llvm-tblgen -gen-intrinsic-impl -I %p/../../include %s -DTEST_INTRINSICS_SUPPRESS_DEFS -DERROR_NEGATIVE 2>&1 | FileCheck %s --check-prefix=ERR-NEGATIVE
 // RUN: not llvm-tblgen -gen-intrinsic-impl -I %p/../../include %s -DTEST_INTRINSICS_SUPPRESS_DEFS -DERROR_RANGE 2>&1 | FileCheck %s --check-prefix=ERR-RANGE
 // RUN: not llvm-tblgen -gen-intrinsic-impl -I %p/../../include %s -DTEST_INTRINSICS_SUPPRESS_DEFS -DERROR_NONINT 2>&1 | FileCheck %s --check-prefix=ERR-NONINT
-// RUN: llvm-tblgen -gen-intrinsic-impl -I %p/../../include %s -DTEST_INTRINSICS_SUPPRESS_DEFS -DTEST_OVERLOADED | FileCheck %s --check-prefix=OVERLOADED
+// RUN: not llvm-tblgen -gen-intrinsic-impl -I %p/../../include %s -DTEST_INTRINSICS_SUPPRESS_DEFS -DERROR_OVERLOADED 2>&1 | FileCheck %s --check-prefix=ERR-OVERLOADED
 // RUN: not llvm-tblgen -gen-intrinsic-impl -I %p/../../include %s -DTEST_INTRINSICS_SUPPRESS_DEFS -DERROR_GAP 2>&1 | FileCheck %s --check-prefix=ERR-GAP
 
 include "llvm/IR/Intrinsics.td"
@@ -77,29 +77,12 @@ def int_test_nonint :
               [ImmArg<ArgIndex<1>, DefaultValue<1>>]>;
 #endif
 
-#ifdef TEST_OVERLOADED
+// A default on an overloaded intrinsic is rejected (not yet supported).
+#ifdef ERROR_OVERLOADED
+// ERR-OVERLOADED: error: default argument values are not supported for overloaded intrinsics
 def int_test_overloaded :
     Intrinsic<[llvm_anyint_ty], [llvm_anyint_ty, llvm_i32_ty],
               [ImmArg<ArgIndex<1>, DefaultValue<5>>]>;
-
-def int_test_overloaded_two_defaults :
-    Intrinsic<[llvm_anyint_ty],
-              [LLVMMatchType<0>, llvm_i1_ty, llvm_i32_ty],
-              [ImmArg<ArgIndex<1>, DefaultValue<0>>,
-               ImmArg<ArgIndex<2>, DefaultValue<7>>]>;
-
-def int_test_overloaded_ptr :
-    Intrinsic<[llvm_anyptr_ty], [LLVMMatchType<0>, llvm_i32_ty],
-              [ImmArg<ArgIndex<1>, DefaultValue<1>>]>;
-
-// Header (1 << 32) | 1 = 4294967297 for single trailing defaults.
-// Header (2 << 32) | 1 = 8589934593 for two trailing defaults.
-// OVERLOADED:      static constexpr uint64_t DefaultArgValuesTable[] = {
-// OVERLOADED-NEXT:   0, // offset 0: sentinel for intrinsics without defaults
-// OVERLOADED-DAG: {{.*}}4294967297,{{.*}}5,{{.*}}0,
-// OVERLOADED-DAG: {{.*}}8589934593,{{.*}}0,{{.*}}7,{{.*}}0,
-// OVERLOADED-DAG: {{.*}}4294967297,{{.*}}1,{{.*}}0,
-// OVERLOADED: Intrinsic::getAllDefaultArgValues(ID IID) {
 #endif
 
 // Defaults must form a contiguous trailing block.
diff --git a/llvm/utils/TableGen/Basic/CodeGenIntrinsics.cpp b/llvm/utils/TableGen/Basic/CodeGenIntrinsics.cpp
index 7bcfcc6d0f87f..b4d1264253af7 100644
--- a/llvm/utils/TableGen/Basic/CodeGenIntrinsics.cpp
+++ b/llvm/utils/TableGen/Basic/CodeGenIntrinsics.cpp
@@ -388,8 +388,14 @@ CodeGenIntrinsic::CodeGenIntrinsic(const Record *R,
   for (auto &Attrs : ArgumentAttributes)
     llvm::sort(Attrs);
 
-  // Validate: defaults must form a contiguous trailing block ending at
-  // the last parameter (mirrors C++ default-argument rules).
+  if (isOverloaded &&
+      llvm::any_of(ParamDefaultValues, [](const std::optional<uint64_t> &DV) {
+        return DV.has_value();
+      }))
+    PrintFatalError(TheDef->getLoc(),
+                    "default argument values are not supported for "
+                    "overloaded intrinsics");
+
   unsigned NumParams = IS.ParamTys.size();
   bool SeenDefault = false;
   for (unsigned i = 0; i < NumParams; ++i) {
@@ -403,8 +409,6 @@ CodeGenIntrinsic::CodeGenIntrinsic(const Record *R,
     }
   }
 
-  // Validate each declared default: the parameter must be an integer type and
-  // the value (an unsigned bit pattern) must fit in the declared width.
   for (unsigned i = 0; i < ParamDefaultValues.size(); ++i) {
     if (!ParamDefaultValues[i].has_value())
       continue;
@@ -528,14 +532,10 @@ void CodeGenIntrinsic::setProperty(const Record *R) {
     unsigned ArgNo = R->getValueAsInt("ArgNo");
     addArgAttribute(ArgNo, ImmArg);
 
-    // If a DefaultValue (not the NoDefault sentinel) was supplied, record it.
-    // NoDefault is recognized by its Value field being unset (?).
     const Record *DefaultField = R->getValueAsDef("Default");
     const RecordVal *ValueField = DefaultField->getValue("Value");
     if (ValueField && !isa<UnsetInit>(ValueField->getValue())) {
       int64_t Value = DefaultField->getValueAsInt("Value");
-      // Defaults are stored as an unsigned bit pattern; a negative literal
-      // would silently wrap, so reject it with a clear message.
       if (Value < 0)
         PrintFatalError(TheDef->getLoc(), "default argument value " +
                                               Twine(Value) + " on parameter " +
diff --git a/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp b/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp
index 23086c422ec1c..8b1129910e2a4 100644
--- a/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp
@@ -933,21 +933,10 @@ void Intrinsic::printImmArg(ID IID, unsigned ArgIdx, raw_ostream &OS, const Cons
 
 void IntrinsicEmitter::EmitDefaultArgValuesTable(
     const CodeGenIntrinsicTable &Ints, raw_ostream &OS) {
-  // Build the per-intrinsic default-value sequences:
-  //   [Header = (NumDefaults << 32) | FirstDefault, val0, val1, ...]
-  // Each value is the (non-negative) default for one parameter, stored as a
-  // uint64_t bit pattern.
-  //
-  // Offset 0 of the values table is reserved as the "no defaults" sentinel
-  // (a single 0 word, decoding to NumDefaults = 0). Intrinsics without
-  // defaults point to offset 0; real sequences are emitted after it.
-  // SequenceToOffsetTable deduplicates the real sequences.
-
+  // Sequences are [NumDefaults << 32 | FirstDefault, values...].
   using Sequence = SmallVector<uint64_t, 8>;
 
   SequenceToOffsetTable<Sequence> Table;
-  // An empty Sequence means "no defaults" (maps to the reserved offset 0);
-  // otherwise it holds the intrinsic's value sequence.
   SmallVector<Sequence> PerIntrinsic;
   PerIntrinsic.reserve(Ints.size());
 
@@ -957,7 +946,6 @@ void IntrinsicEmitter::EmitDefaultArgValuesTable(
       continue;
     }
 
-    // Find the first parameter with a default.
     unsigned FirstDefault = 0;
     for (size_t j = 0U, N = Int.ParamDefaultValues.size(); j < N; ++j) {
       if (Int.ParamDefaultValues[j].has_value()) {
@@ -984,17 +972,11 @@ void IntrinsicEmitter::EmitDefaultArgValuesTable(
 
   IfDefEmitter IfDef(OS, "GET_INTRINSIC_DEFAULT_ARG_VALUES");
 
-  // Emit the flat values table. Offset 0 is the reserved "no defaults"
-  // sentinel; the deduplicated real sequences follow it.
   OS << "static constexpr uint64_t DefaultArgValuesTable[] = {\n";
   OS << "  0, // offset 0: sentinel for intrinsics without defaults\n";
   Table.emit(OS, [](raw_ostream &OS, uint64_t Val) { OS << "  " << Val; });
   OS << "};\n\n";
 
-  // Emit the per-intrinsic offset table. Entry #0 is for the invalid
-  // Intrinsic::not_intrinsic (IID 0); it and every intrinsic without defaults
-  // point to the reserved sentinel at offset 0. Real sequences are shifted by
-  // +1 to skip past the sentinel slot.
   OS << "static constexpr uint32_t DefaultArgValuesTableOffset[] = {\n";
   OS << "  0, // not_intrinsic\n";
   for (const Sequence &Seq : PerIntrinsic) {
@@ -1005,7 +987,6 @@ void IntrinsicEmitter::EmitDefaultArgValuesTable(
   }
   OS << "};\n\n";
 
-  // Emit the lookup function body.
   OS << R"(
 std::pair<unsigned, ArrayRef<uint64_t>>
 Intrinsic::getAllDefaultArgValues(ID IID) {



More information about the llvm-commits mailing list