Index: llvm/include/llvm/IR/Intrinsics.h =================================================================== --- llvm/include/llvm/IR/Intrinsics.h +++ llvm/include/llvm/IR/Intrinsics.h @@ -53,11 +53,20 @@ StringRef getName(ID id); /// Return the LLVM name for an intrinsic, such as "llvm.ppc.altivec.lvx". - /// Note, this version of getName supports overloads, but is less efficient - /// than the StringRef version of this function. If no overloads are - /// requried, it is safe to use this version, but better to use the StringRef - /// version. - std::string getName(ID id, ArrayRef Tys); + /// Note, this version of getName supports overloads, but not unnamed types. + /// It is less efficient than the StringRef version of this function. If no + /// overloads are required, it is safe to use this version, but better to use + /// the StringRef version. + std::string getName(ID id, ArrayRef Tys); + + /// Return the LLVM name for an intrinsic, such as "llvm.ssa.copy.p0s_s.1". + /// Note, this version of getName supports overloads and unnamed types, but is + /// less efficient than the StringRef version of this function. If no + /// overloads are required, it is safe to use this version, but better to use + /// the StringRef version. The function type can be provided as a speedup. + /// It is used (or computed) if one of the types is based on an unnamed type. + std::string getName(ID id, ArrayRef Tys, Module *M, + llvm::FunctionType *FT); /// Return the function type for an intrinsic. FunctionType *getType(LLVMContext &Context, ID id, Index: llvm/include/llvm/IR/Module.h =================================================================== --- llvm/include/llvm/IR/Module.h +++ llvm/include/llvm/IR/Module.h @@ -197,6 +197,12 @@ ///< Format: (arch)(sub)-(vendor)-(sys0-(abi) NamedMDSymTabType NamedMDSymTab; ///< NamedMDNode names. DataLayout DL; ///< DataLayout associated with the module + StringMap + CurrentIntrinsicIds; ///< Keep track of the current unique id count for + ///< the specified intrinsic basename + DenseMap, unsigned> + UniquedIntrinsicNames; ///< Keep track of uniqued names of intrinsics + ///< based on unnamed types friend class Constant; @@ -335,6 +341,11 @@ std::vector getIdentifiedStructTypes() const; + /// Return a unique name for an intrinsic whose mangling is based on an + /// unnamed type. The Proto represents the function prototype. + std::string getUniqueIntrinsicName(StringRef BaseName, Intrinsic::ID id, + const FunctionType *Proto); + /// @} /// @name Function Accessors /// @{ Index: llvm/lib/IR/Function.cpp =================================================================== --- llvm/lib/IR/Function.cpp +++ llvm/lib/IR/Function.cpp @@ -711,30 +711,34 @@ /// which can't be confused with it's prefix. This ensures we don't have /// collisions between two unrelated function types. Otherwise, you might /// parse ffXX as f(fXX) or f(fX)X. (X is a placeholder for any other type.) -/// -static std::string getMangledTypeStr(Type* Ty) { +/// The HasUnnamedType boolean is set if an unnamed type was encountered, +/// indicating that extra care must be taken to ensure a unique name. +static std::string getMangledTypeStr(Type *Ty, bool &HasUnnamedType) { std::string Result; if (PointerType* PTyp = dyn_cast(Ty)) { Result += "p" + utostr(PTyp->getAddressSpace()) + - getMangledTypeStr(PTyp->getElementType()); + getMangledTypeStr(PTyp->getElementType(), HasUnnamedType); } else if (ArrayType* ATyp = dyn_cast(Ty)) { Result += "a" + utostr(ATyp->getNumElements()) + - getMangledTypeStr(ATyp->getElementType()); + getMangledTypeStr(ATyp->getElementType(), HasUnnamedType); } else if (StructType *STyp = dyn_cast(Ty)) { if (!STyp->isLiteral()) { Result += "s_"; - Result += STyp->getName(); + if (STyp->hasName()) + Result += STyp->getName(); + else + HasUnnamedType = true; } else { Result += "sl_"; for (auto Elem : STyp->elements()) - Result += getMangledTypeStr(Elem); + Result += getMangledTypeStr(Elem, HasUnnamedType); } // Ensure nested structs are distinguishable. Result += "s"; } else if (FunctionType *FT = dyn_cast(Ty)) { - Result += "f_" + getMangledTypeStr(FT->getReturnType()); + Result += "f_" + getMangledTypeStr(FT->getReturnType(), HasUnnamedType); for (size_t i = 0; i < FT->getNumParams(); i++) - Result += getMangledTypeStr(FT->getParamType(i)); + Result += getMangledTypeStr(FT->getParamType(i), HasUnnamedType); if (FT->isVarArg()) Result += "vararg"; // Ensure nested function types are distinguishable. @@ -744,7 +748,7 @@ if (EC.isScalable()) Result += "nx"; Result += "v" + utostr(EC.getKnownMinValue()) + - getMangledTypeStr(VTy->getElementType()); + getMangledTypeStr(VTy->getElementType(), HasUnnamedType); } else if (Ty) { switch (Ty->getTypeID()) { default: llvm_unreachable("Unhandled type"); @@ -773,15 +777,29 @@ return IntrinsicNameTable[id]; } -std::string Intrinsic::getName(ID id, ArrayRef Tys) { +std::string Intrinsic::getName(ID id, ArrayRef Tys, Module *M, + FunctionType *FT) { assert(id < num_intrinsics && "Invalid intrinsic ID!"); + bool HasUnnamedType = false; std::string Result(IntrinsicNameTable[id]); for (Type *Ty : Tys) { - Result += "." + getMangledTypeStr(Ty); + Result += "." + getMangledTypeStr(Ty, HasUnnamedType); + } + if (M && HasUnnamedType) { + if (FT == nullptr) + FT = getType(M->getContext(), id, Tys); + else + assert((FT == getType(M->getContext(), id, Tys)) && + "Provided FunctionType must match arguments"); + return M->getUniqueIntrinsicName(Result, id, FT); } return Result; } +std::string Intrinsic::getName(ID id, ArrayRef Tys) { + return getName(id, Tys, nullptr, nullptr); +} + /// IIT_Info - These are enumerators that describe the entries returned by the /// getIntrinsicInfoTableEntries function. /// @@ -1236,10 +1254,9 @@ Function *Intrinsic::getDeclaration(Module *M, ID id, ArrayRef Tys) { // There can never be multiple globals with the same name of different types, // because intrinsics must be a specific type. + auto *FT = getType(M->getContext(), id, Tys); return cast( - M->getOrInsertFunction(getName(id, Tys), - getType(M->getContext(), id, Tys)) - .getCallee()); + M->getOrInsertFunction(getName(id, Tys, M, FT), FT).getCallee()); } // This defines the "Intrinsic::getIntrinsicForGCCBuiltin()" method. @@ -1549,7 +1566,8 @@ Intrinsic::ID ID = F->getIntrinsicID(); StringRef Name = F->getName(); - if (Name == Intrinsic::getName(ID, ArgTys)) + if (Name == + Intrinsic::getName(ID, ArgTys, F->getParent(), F->getFunctionType())) return None; auto NewDecl = Intrinsic::getDeclaration(F->getParent(), ID, ArgTys); Index: llvm/lib/IR/Module.cpp =================================================================== --- llvm/lib/IR/Module.cpp +++ llvm/lib/IR/Module.cpp @@ -473,6 +473,56 @@ return Ret; } +std::string Module::getUniqueIntrinsicName(StringRef BaseName, Intrinsic::ID id, + const FunctionType *Proto) { + auto encode = [&](unsigned Suffix) { + return (Twine(BaseName) + "." + Twine(Suffix)).str(); + }; + + { + // fast path - the prototype is already known + auto UinItInserted = UniquedIntrinsicNames.insert({{id, Proto}, 0}); + if (!UinItInserted.second) + return encode(UinItInserted.first->second); + } + + // Not known yet. A new entry was created with index 0. Check if there already + // exists a matching declaration, or select a new entry. + + // Start looking for names with the current known maximum count (or 0). + auto NiidItInserted = CurrentIntrinsicIds.insert({BaseName, 0}); + unsigned Count = NiidItInserted.first->second; + + // This might be slow if a whole population of intrinsics already existed, but + // we cache the values for later usage. + std::string NewName; + while (true) { + NewName = encode(Count); + GlobalValue *F = getNamedValue(NewName); + if (F == nullptr) { + // Reserve this entry for the new proto + UniquedIntrinsicNames[{id, Proto}] = Count; + break; + } + + // A declaration with this name already exists. Remember it. + FunctionType *FT = dyn_cast(F->getType()->getElementType()); + auto UinItInserted = UniquedIntrinsicNames.insert({{id, FT}, Count}); + if (FT == Proto) { + // It was a declaration for our prototype. This entry was allocated in the + // beginning. Update the count to match the existing declaration. + UinItInserted.first->second = Count; + break; + } + + ++Count; + } + + NiidItInserted.first->second = Count + 1; + + return NewName; +} + // dropAllReferences() - This function causes all the subelements to "let go" // of all references that they are maintaining. This allows one to 'delete' a // whole module at a time, even though there may be circular references... first Index: llvm/lib/IR/Verifier.cpp =================================================================== --- llvm/lib/IR/Verifier.cpp +++ llvm/lib/IR/Verifier.cpp @@ -4517,7 +4517,8 @@ // know they are legal for the intrinsic!) get the intrinsic name through the // usual means. This allows us to verify the mangling of argument types into // the name. - const std::string ExpectedName = Intrinsic::getName(ID, ArgTys); + const std::string ExpectedName = + Intrinsic::getName(ID, ArgTys, IF->getParent(), IFTy); Assert(ExpectedName == IF->getName(), "Intrinsic name not mangled correctly for type arguments! " "Should be: " + Index: llvm/test/Bitcode/intrinsics-with-unnamed-types.ll =================================================================== --- /dev/null +++ llvm/test/Bitcode/intrinsics-with-unnamed-types.ll @@ -0,0 +1,31 @@ +; RUN: llvm-as -o - %s | llvm-dis -o - 2>&1 | FileCheck %s + +; Make sure we can assemble and disassemble IR containing intrinsics with +; unnamed types. + +%1 = type opaque +%0 = type opaque + +; CHECK-LABEL: @f0( +; CHECK: %c1 = call %0* @llvm.ssa.copy.p0s_s.0(%0* %arg) +; CHECK: %c2 = call %1* @llvm.ssa.copy.p0s_s.1(%1* %tmp) +; CHECK: %c3 = call %0** @llvm.ssa.copy.p0p0s_s.1(%0** %arg2) +; CHECK: %c4 = call %1** @llvm.ssa.copy.p0p0s_s.0(%1** %tmp2) + +define void @f0(%0* %arg, %1* %tmp, %1** %tmp2, %0** %arg2) { +bb: + %cmp1 = icmp ne %0* %arg, null + %c1 = call %0* @llvm.ssa.copy.p0s_s.0(%0* %arg) + %c2 = call %1* @llvm.ssa.copy.p0s_s.1(%1* %tmp) + %c3 = call %0** @llvm.ssa.copy.p0p0s_s.1(%0** %arg2) + %c4 = call %1** @llvm.ssa.copy.p0p0s_s.0(%1** %tmp2) + ret void +} + +declare %0* @llvm.ssa.copy.p0s_s.0(%0* returned) + +declare %1* @llvm.ssa.copy.p0s_s.1(%1* returned) + +declare %0** @llvm.ssa.copy.p0p0s_s.1(%0** returned) + +declare %1** @llvm.ssa.copy.p0p0s_s.0(%1** returned)