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 &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 &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..2a6ff1959291e 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 &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,60 @@ 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 OverloadTys; + + if (IID != Intrinsic::not_intrinsic) { + if (!getDefaultArgUpgradeInfo(F, IID, OverloadTys)) + return false; + } else { + Function *BestMatch = nullptr; + SmallVector 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 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 +2135,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 &Infos, unsigned NumArgs, bool IsVarArg, SmallVectorImpl &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 &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 &Infos, unsigned NumArgs, bool IsVarArg, SmallVectorImpl &OverloadTys, - raw_ostream &OS) { + raw_ostream &OS, + unsigned NumMissingTrailingParams) { SmallVector 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 &OverloadTys, raw_ostream &OS) { + return isSignatureValid(ID, FT, OverloadTys, + /*NumMissingTrailingParams=*/0, OS); +} + +bool Intrinsic::isSignatureValid(Intrinsic::ID ID, FunctionType *FT, + SmallVectorImpl &OverloadTys, + unsigned NumMissingTrailingParams, + raw_ostream &OS) { if (!ID) return false; SmallVector 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, 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, DefaultValue<5>>]>; + +def int_test_overloaded_two_defaults : + Intrinsic<[llvm_anyint_ty], + [LLVMMatchType<0>, llvm_i1_ty, llvm_i32_ty], + [ImmArg, DefaultValue<0>>, + ImmArg, DefaultValue<7>>]>; + +def int_test_overloaded_ptr : + Intrinsic<[llvm_anyptr_ty], [LLVMMatchType<0>, llvm_i32_ty], + [ImmArg, 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 &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();