[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