Index: llvm/docs/LangRef.rst =================================================================== --- llvm/docs/LangRef.rst +++ llvm/docs/LangRef.rst @@ -3342,9 +3342,10 @@ :Overview: -Opaque structure types are used to represent named structure types that +Opaque structure types are used to represent structure types that do not have a body specified. This corresponds (for example) to the C -notion of a forward declared structure. +notion of a forward declared structure. They can be named (``%X``) or +unnamed (``%52``). :Syntax: @@ -11396,6 +11397,14 @@ type is matched against the return type, it does not require its own name suffix. +:ref:`Unnamed types ` are encoded as ``s_s``. Overloaded intrinsics +that depend on an unnamed type in one of its overloaded argument types get an +additional ``.`` suffix, that allows to differentiate such intrinsics. +(For example: ``llvm.ssa.copy.p0s_s.2(%42*)``) The number is tracked in the +LLVM module and it ensures unique names in a module. While linking together two +modules, it is still possible to get a name clash. In that case one of the +names will be changed. + For target developers who are defining intrinsics for back-end code generation, any intrinsic overloads based solely the distinction between integer or floating point types should not be relied upon for correct @@ -20062,7 +20071,7 @@ .. _int_ssa_copy: -'``llvm.ssa_copy``' Intrinsic +'``llvm.ssa.copy``' Intrinsic ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Syntax: @@ -20070,7 +20079,7 @@ :: - declare type @llvm.ssa_copy(type %operand) returned(1) readnone + declare type @llvm.ssa.copy(type %operand) returned(1) readnone Arguments: """""""""" @@ -20080,7 +20089,7 @@ Overview: """""""""" -The ``llvm.ssa_copy`` intrinsic can be used to attach information to +The ``llvm.ssa.copy`` intrinsic can be used to attach information to operations by copying them and giving them new names. For example, the PredicateInfo utility uses it to build Extended SSA form, and attach various forms of information to operands that dominate specific 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; @@ -331,6 +337,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 @@ -717,30 +717,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. @@ -750,7 +754,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"); @@ -780,17 +784,31 @@ 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!"); assert((Tys.empty() || Intrinsic::isOverloaded(id)) && "This version of getName is for overloaded intrinsics only"); + 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. /// @@ -1250,8 +1268,10 @@ 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(Tys.empty() ? getName(id) : getName(id, Tys), + M->getOrInsertFunction(Tys.empty() ? getName(id) + : getName(id, Tys, M, FT), getType(M->getContext(), id, Tys)) .getCallee()); } @@ -1564,7 +1584,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 @@ -4514,7 +4514,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/lib/Linker/IRMover.cpp =================================================================== --- llvm/lib/Linker/IRMover.cpp +++ llvm/lib/Linker/IRMover.cpp @@ -461,6 +461,14 @@ if (DGV->hasLocalLinkage()) return nullptr; + // If we found an intrinsic declaration with mismatching prototypes, we + // probably had a nameclash. Don't use that version. + if (auto *FDGV = dyn_cast(DGV)) + if (FDGV->isIntrinsic()) + if (auto *FSrcGV = dyn_cast(SrcGV)) + if (FDGV->getFunctionType() != TypeMap.get(FSrcGV->getFunctionType())) + return nullptr; + // Otherwise, we do in fact link to the destination global. return DGV; } @@ -988,6 +996,7 @@ return linkAppendingVarProto(cast_or_null(DGV), cast(SGV)); + bool NeedsRenaming = false; GlobalValue *NewGV; if (DGV && !ShouldLink) { NewGV = DGV; @@ -1000,15 +1009,21 @@ NewGV = copyGlobalValueProto(SGV, ShouldLink || ForIndirectSymbol); if (ShouldLink || !ForIndirectSymbol) - forceRenaming(NewGV, SGV->getName()); + NeedsRenaming = true; } // Overloaded intrinsics have overloaded types names as part of their // names. If we renamed overloaded types we should rename the intrinsic // as well. if (Function *F = dyn_cast(NewGV)) - if (auto Remangled = Intrinsic::remangleIntrinsicFunction(F)) + if (auto Remangled = Intrinsic::remangleIntrinsicFunction(F)) { + NewGV->eraseFromParent(); NewGV = Remangled.getValue(); + NeedsRenaming = false; + } + + if (NeedsRenaming) + forceRenaming(NewGV, SGV->getName()); if (ShouldLink || ForIndirectSymbol) { if (const Comdat *SC = SGV->getComdat()) { 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) Index: llvm/test/Linker/intrinsics-with-unnamed-types.ll =================================================================== --- /dev/null +++ llvm/test/Linker/intrinsics-with-unnamed-types.ll @@ -0,0 +1,101 @@ +; RUN: split-file %s %t +; RUN: llvm-as -o %t1.bc %t/f01.ll +; RUN: llvm-as -o %t2.bc %t/f02.ll +; RUN: llvm-link %t1.bc %t2.bc -o %t3.bc +; RUN: llvm-dis -o - %t3.bc | FileCheck %s + +; Make sure we can link files with clashing intrinsic names using unnamed types. + +;--- f01.ll +%1 = type opaque +%0 = type opaque + +; CHECK-LABEL: @test01( +; CHECK: %cmp1 = icmp ne %0* %arg, null +; CHECK-NEXT: %c1 = call %0* @llvm.ssa.copy.p0s_s.0(%0* %arg) +; CHECK-NEXT: %c2 = call %1* @llvm.ssa.copy.p0s_s.1(%1* %tmp) +; CHECK-NEXT: %c3a = call %0** @llvm.ssa.copy.p0p0s_s.0(%0** %arg2) +; CHECK-NEXT: %c3b = call %0** @llvm.ssa.copy.p0p0s_s.0(%0** %arg2) +; CHECK-NEXT: %c4a = call %1** @llvm.ssa.copy.p0p0s_s.1(%1** %tmp2) +; CHECK-NEXT: %c4ba = call %1** @llvm.ssa.copy.p0p0s_s.1(%1** %tmp2) +; CHECK-NEXT: %c5 = call %0*** @llvm.ssa.copy.p0p0p0s_s.0(%0*** %arg3) +; CHECK-NEXT: %c6 = call %1*** @llvm.ssa.copy.p0p0p0s_s.1(%1*** %tmp3) + +define void @test01(%0* %arg, %1* %tmp, %1** %tmp2, %0** %arg2, %1*** %tmp3, %0*** %arg3) { +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) + %c3a = call %0** @llvm.ssa.copy.p0p0s_s.1(%0** %arg2) + %c3b = call %0** @llvm.ssa.copy.p0p0s_s.1(%0** %arg2) + %c4a = call %1** @llvm.ssa.copy.p0p0s_s.0(%1** %tmp2) + %c4ba = call %1** @llvm.ssa.copy.p0p0s_s.0(%1** %tmp2) + %c5 = call %0*** @llvm.ssa.copy.p0p0p0s_s.1(%0*** %arg3) + %c6 = call %1*** @llvm.ssa.copy.p0p0p0s_s.0(%1*** %tmp3) + 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) + +declare %0*** @llvm.ssa.copy.p0p0p0s_s.1(%0*** returned) + +declare %1*** @llvm.ssa.copy.p0p0p0s_s.0(%1*** returned) + +; now with recycling of previous declarations: +; CHECK-LABEL: @test02( +; CHECK: %cmp1 = icmp ne %0* %arg, null +; CHECK-NEXT: %c4a = call %1** @llvm.ssa.copy.p0p0s_s.1(%1** %tmp2) +; CHECK-NEXT: %c6 = call %1*** @llvm.ssa.copy.p0p0p0s_s.1(%1*** %tmp3) +; CHECK-NEXT: %c1 = call %0* @llvm.ssa.copy.p0s_s.0(%0* %arg) +; CHECK-NEXT: %c2 = call %1* @llvm.ssa.copy.p0s_s.1(%1* %tmp) +; CHECK-NEXT: %c3b = call %0** @llvm.ssa.copy.p0p0s_s.0(%0** %arg2) +; CHECK-NEXT: %c4ba = call %1** @llvm.ssa.copy.p0p0s_s.1(%1** %tmp2) +; CHECK-NEXT: %c5 = call %0*** @llvm.ssa.copy.p0p0p0s_s.0(%0*** %arg3) + +define void @test02(%0* %arg, %1* %tmp, %1** %tmp2, %0** %arg2, %1*** %tmp3, %0*** %arg3) { +bb: + %cmp1 = icmp ne %0* %arg, null + %c4a = call %1** @llvm.ssa.copy.p0p0s_s.0(%1** %tmp2) + %c6 = call %1*** @llvm.ssa.copy.p0p0p0s_s.0(%1*** %tmp3) + %c1 = call %0* @llvm.ssa.copy.p0s_s.0(%0* %arg) + %c2 = call %1* @llvm.ssa.copy.p0s_s.1(%1* %tmp) + %c3b = call %0** @llvm.ssa.copy.p0p0s_s.1(%0** %arg2) + %c4ba = call %1** @llvm.ssa.copy.p0p0s_s.0(%1** %tmp2) + %c5 = call %0*** @llvm.ssa.copy.p0p0p0s_s.1(%0*** %arg3) + ret void +} + +;--- f02.ll +%1 = type opaque +%2 = type opaque + +; CHECK-LABEL: @test03( +; CHECK: %cmp1 = icmp ne %3* %arg, null +; CHECK-NEXT: %c1 = call %3* @llvm.ssa.copy.p0s_s.2(%3* %arg) +; CHECK-NEXT: %c2 = call %2* @llvm.ssa.copy.p0s_s.3(%2* %tmp) +; CHECK-NEXT: %c3 = call %3** @llvm.ssa.copy.p0p0s_s.2(%3** %arg2) +; CHECK-NEXT: %c4 = call %2** @llvm.ssa.copy.p0p0s_s.3(%2** %tmp2) + +define void @test03(%1* %tmp, %2* %arg, %1** %tmp2, %2** %arg2) { +bb: + %cmp1 = icmp ne %2* %arg, null + %c1 = call %2* @llvm.ssa.copy.p0s_s.0(%2* %arg) + %c2 = call %1* @llvm.ssa.copy.p0s_s.1(%1* %tmp) + %c3 = call %2** @llvm.ssa.copy.p0p0s_s.1(%2** %arg2) + %c4 = call %1** @llvm.ssa.copy.p0p0s_s.0(%1** %tmp2) + ret void +} + +declare %2* @llvm.ssa.copy.p0s_s.0(%2* returned) + +declare %1* @llvm.ssa.copy.p0s_s.1(%1* returned) + +declare %2** @llvm.ssa.copy.p0p0s_s.1(%2** returned) + +declare %1** @llvm.ssa.copy.p0p0s_s.0(%1** returned)