Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -1671,6 +1671,10 @@ MetaVarName<"">, HelpText<"Include file before parsing">, Flags<[CC1Option]>; def include_pch : Separate<["-"], "include-pch">, Group, Flags<[CC1Option]>, HelpText<"Include precompiled header file">, MetaVarName<"">; +def ir_type_names_EQ : Joined<["--"], "ir-type-names=">, + Flags<[CC1Option, HelpHidden]>, + HelpText<"Encoding of IR type names (option: none, terse, full)">, + Values<"none,terse,full">; def relocatable_pch : Flag<["-", "--"], "relocatable-pch">, Flags<[CC1Option]>, HelpText<"Whether to build a relocatable precompiled header">; def verify_pch : Flag<["-"], "verify-pch">, Group, Flags<[CC1Option]>, Index: include/clang/Frontend/CodeGenOptions.h =================================================================== --- include/clang/Frontend/CodeGenOptions.h +++ include/clang/Frontend/CodeGenOptions.h @@ -106,6 +106,19 @@ Embed_Marker // Embed a marker as a placeholder for bitcode. }; + /// Describes what kind of names should be specified for LLVM types. + /// + /// For example, LLVM structure types corresponding to template class + /// specializations may be assigned names with template arguments (as + /// "struct.Foo") or name with only version suffix (as "Foo.123"). + /// + enum class IRNameKind { + Unspecified, ///< No special option was passed. + None, ///< No type names. + Terse, ///< Only minimal names. + Full, ///< Full names (with template parameters). + }; + /// The code model to use (-mcmodel). std::string CodeModel; Index: include/clang/Frontend/CodeGenOptions.def =================================================================== --- include/clang/Frontend/CodeGenOptions.def +++ include/clang/Frontend/CodeGenOptions.def @@ -71,6 +71,11 @@ CODEGENOPT(EmulatedTLS , 1, 0) ///< Set when -femulated-tls is enabled. /// \brief Embed Bitcode mode (off/all/bitcode/marker). ENUM_CODEGENOPT(EmbedBitcode, EmbedBitcodeKind, 2, Embed_Off) + +/// What kind of names should be specified for LLVM IR types. +/// \see CodeGenOptions::IRNameKind. +ENUM_CODEGENOPT(IRTypeNames, IRNameKind, 4, IRNameKind::Unspecified) + CODEGENOPT(ForbidGuardVariables , 1, 0) ///< Issue errors if C++ guard variables ///< are required. CODEGENOPT(FunctionSections , 1, 0) ///< Set when -ffunction-sections is enabled. Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -492,6 +492,11 @@ getModule().addModuleFlag(llvm::Module::Error, "min_enum_size", EnumWidth); } + // Opaque type resolution made by IR linker relies on IR type names. + if (CodeGenOpts.getIRTypeNames() != CodeGenOptions::IRNameKind::Unspecified) + getModule().addModuleFlag(llvm::Module::Error, "type_names", + static_cast(CodeGenOpts.getIRTypeNames())); + if (CodeGenOpts.SanitizeCfiCrossDso) { // Indicate that we want cross-DSO control flow integrity checks. getModule().addModuleFlag(llvm::Module::Override, "Cross-DSO CFI", 1); Index: lib/CodeGen/CodeGenTypes.cpp =================================================================== --- lib/CodeGen/CodeGenTypes.cpp +++ lib/CodeGen/CodeGenTypes.cpp @@ -51,10 +51,13 @@ void CodeGenTypes::addRecordTypeName(const RecordDecl *RD, llvm::StructType *Ty, StringRef suffix) { + if (getCodeGenOpts().getIRTypeNames() == CodeGenOptions::IRNameKind::None) + return; + SmallString<256> TypeName; llvm::raw_svector_ostream OS(TypeName); OS << RD->getKindName() << '.'; - + // Name the codegen type after the typedef name // if there is no tag type name available if (RD->getIdentifier()) { @@ -64,6 +67,12 @@ RD->printQualifiedName(OS); else RD->printName(OS); + if (getCodeGenOpts().getIRTypeNames() == CodeGenOptions::IRNameKind::Full) + if (auto *Spec = dyn_cast(RD)) { + const TemplateArgumentList &TemplateArgs = Spec->getTemplateArgs(); + printTemplateArgumentList(OS, TemplateArgs.asArray(), + RD->getASTContext().getPrintingPolicy()); + } } else if (const TypedefNameDecl *TDD = RD->getTypedefNameForAnonDecl()) { // FIXME: We should not have to check for a null decl context here. // Right now we do it because the implicit Obj-C decls don't have one. Index: lib/Driver/ToolChains/Clang.cpp =================================================================== --- lib/Driver/ToolChains/Clang.cpp +++ lib/Driver/ToolChains/Clang.cpp @@ -3195,6 +3195,8 @@ if (C.getDriver().embedBitcodeMarkerOnly() && !C.getDriver().isUsingLTO()) CmdArgs.push_back("-fembed-bitcode=marker"); + Args.AddLastArg(CmdArgs, options::OPT_ir_type_names_EQ); + // We normally speed up the clang process a bit by skipping destructors at // exit, but when we're generating diagnostics we can rely on some of the // cleanup. Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -778,6 +778,20 @@ } } + if (Arg *A = Args.getLastArg(OPT_ir_type_names_EQ)) { + StringRef Name = A->getValue(); + auto Info = llvm::StringSwitch(Name) + .Case("none", CodeGenOptions::IRNameKind::None) + .Case("terse", CodeGenOptions::IRNameKind::Terse) + .Case("full", CodeGenOptions::IRNameKind::Full) + .Default(CodeGenOptions::IRNameKind::Unspecified); + if (Info == CodeGenOptions::IRNameKind::Unspecified) { + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name; + Success = false; + } else + Opts.setIRTypeNames(Info); + } + Opts.PreserveVec3Type = Args.hasArg(OPT_fpreserve_vec3_type); Opts.InstrumentFunctions = Args.hasArg(OPT_finstrument_functions); Opts.InstrumentFunctionsAfterInlining = Index: test/CodeGenCXX/template-types.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/template-types.cpp @@ -0,0 +1,118 @@ +// RUN: %clang_cc1 %s -S -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 %s -S -emit-llvm --ir-type-names=none -o - | FileCheck %s --check-prefix=CHECK-NONE +// RUN: %clang_cc1 %s -S -emit-llvm --ir-type-names=terse -o - | FileCheck %s --check-prefix=CHECK-TERSE +// RUN: %clang_cc1 %s -S -emit-llvm --ir-type-names=full -o - | FileCheck %s --check-prefix=CHECK-FULL + +struct Empty {}; +template struct ABC { + T v; +}; +struct Opaque; +template class OpaqueClass; + +ABC var_1; +ABC > var_2; +ABC var_3; +ABC *var_4; +OpaqueClass *var_5; +OpaqueClass *var_6; +OpaqueClass *var_7; +OpaqueClass > *var_8; + + +//------ without option '--ir-type-names' + +// CHECK: %struct.ABC = type { i32 } +// CHECK: %struct.ABC.0 = type { %struct.ABC.1 } +// CHECK: %struct.ABC.1 = type { i16 } +// CHECK: %struct.ABC.2 = type { %struct.Empty } +// CHECK: %struct.Empty = type { i8 } +// CHECK: %class.OpaqueClass = type opaque +// CHECK: %class.OpaqueClass.4 = type opaque +// CHECK: %class.OpaqueClass.5 = type opaque +// CHECK: %class.OpaqueClass.6 = type opaque + +// CHECK: @var_1 = global %struct.ABC zeroinitializer +// CHECK: @var_2 = global %struct.ABC.0 zeroinitializer +// CHECK: @var_3 = global %struct.ABC.2 zeroinitializer +// CHECK: @var_4 = global %struct.ABC.3* null +// CHECK: @var_5 = global %class.OpaqueClass* null +// CHECK: @var_6 = global %class.OpaqueClass.4* null +// CHECK: @var_7 = global %class.OpaqueClass.5* null +// CHECK: @var_8 = global %class.OpaqueClass.6* null + +// CHECK-NOT: !{{[0-9]+}} = !{{{.*}} !"type_names" + + +//------ with option '--ir-type-names=none' + +// CHECK-NONE: %0 = type { i32 } +// CHECK-NONE: %1 = type { %2 } +// CHECK-NONE: %2 = type { i16 } +// CHECK-NONE: %3 = type { %4 } +// CHECK-NONE: %4 = type { i8 } +// CHECK-NONE: %5 = type opaque +// CHECK-NONE: %6 = type opaque +// CHECK-NONE: %7 = type opaque +// CHECK-NONE: %8 = type opaque +// CHECK-NONE: %9 = type opaque + +// CHECK-NONE: @var_1 = global %0 zeroinitializer +// CHECK-NONE: @var_2 = global %1 zeroinitializer +// CHECK-NONE: @var_3 = global %3 zeroinitializer +// CHECK-NONE: @var_4 = global %5* null +// CHECK-NONE: @var_5 = global %6* null +// CHECK-NONE: @var_6 = global %7* null +// CHECK-NONE: @var_7 = global %8* null +// CHECK-NONE: @var_8 = global %9* null + +// CHECK-NONE: !{{[0-9]+}} = !{{{.*}} !"type_names", i32 1} + + +//------ with option '--ir-type-names=terse' + +// CHECK-TERSE: %struct.ABC = type { i32 } +// CHECK-TERSE: %struct.ABC.0 = type { %struct.ABC.1 } +// CHECK-TERSE: %struct.ABC.1 = type { i16 } +// CHECK-TERSE: %struct.ABC.2 = type { %struct.Empty } +// CHECK-TERSE: %struct.Empty = type { i8 } +// CHECK-TERSE: %class.OpaqueClass = type opaque +// CHECK-TERSE: %class.OpaqueClass.4 = type opaque +// CHECK-TERSE: %class.OpaqueClass.5 = type opaque +// CHECK-TERSE: %class.OpaqueClass.6 = type opaque + +// CHECK-TERSE: @var_1 = global %struct.ABC zeroinitializer +// CHECK-TERSE: @var_2 = global %struct.ABC.0 zeroinitializer +// CHECK-TERSE: @var_3 = global %struct.ABC.2 zeroinitializer +// CHECK-TERSE: @var_4 = global %struct.ABC.3* null +// CHECK-TERSE: @var_5 = global %class.OpaqueClass* null +// CHECK-TERSE: @var_6 = global %class.OpaqueClass.4* null +// CHECK-TERSE: @var_7 = global %class.OpaqueClass.5* null +// CHECK-TERSE: @var_8 = global %class.OpaqueClass.6* null + +// CHECK-TERSE: !{{[0-9]+}} = !{{{.*}} !"type_names", i32 2} + + +//------ with option '--ir-type-names=full' + +// CHECK-FULL: %"struct.ABC" = type { i32 } +// CHECK-FULL: %"struct.ABC >" = type { %"struct.ABC" } +// CHECK-FULL: %"struct.ABC" = type { i16 } +// CHECK-FULL: %"struct.ABC" = type { %struct.Empty } +// CHECK-FULL: %struct.Empty = type { i8 } +// CHECK-FULL: %"struct.ABC" = type opaque +// CHECK-FULL: %"class.OpaqueClass" = type opaque +// CHECK-FULL: %"class.OpaqueClass" = type opaque +// CHECK-FULL: %"class.OpaqueClass" = type opaque +// CHECK-FULL: %"class.OpaqueClass >" = type opaque + +// CHECK-FULL: @var_1 = global %"struct.ABC" zeroinitializer +// CHECK-FULL: @var_2 = global %"struct.ABC >" zeroinitializer +// CHECK-FULL: @var_3 = global %"struct.ABC" zeroinitializer +// CHECK-FULL: @var_4 = global %"struct.ABC"* null +// CHECK-FULL: @var_5 = global %"class.OpaqueClass"* null +// CHECK-FULL: @var_6 = global %"class.OpaqueClass"* null +// CHECK-FULL: @var_7 = global %"class.OpaqueClass"* null +// CHECK-FULL: @var_8 = global %"class.OpaqueClass >"* null + +// CHECK-FULL: !{{[0-9]+}} = !{{{.*}} !"type_names", i32 3}