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

via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 13 22:02:40 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-tablegen

Author: Dharuni R Acharya (DharuniRAcharya)

<details>
<summary>Changes</summary>

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`.

---
Full diff: https://github.com/llvm/llvm-project/pull/216246.diff


5 Files Affected:

- (modified) llvm/include/llvm/IR/Intrinsics.h (+8) 
- (modified) llvm/lib/IR/AutoUpgrade.cpp (+61-17) 
- (modified) llvm/lib/IR/Intrinsics.cpp (+34-5) 
- (modified) llvm/test/TableGen/intrinsic-default-args.td (+21-4) 
- (modified) llvm/utils/TableGen/Basic/CodeGenIntrinsics.cpp (-10) 


``````````diff
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 5b6f50df5d4d0..bc55a7dcb1f0a 100644
--- a/llvm/lib/IR/AutoUpgrade.cpp
+++ b/llvm/lib/IR/AutoUpgrade.cpp
@@ -1383,25 +1383,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
@@ -1409,7 +1398,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;
 }
 
@@ -2093,7 +2137,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();

``````````

</details>


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


More information about the llvm-commits mailing list