Author: Peter Collingbourne Date: 2025-04-15T11:12:05-07:00 New Revision: a5aa0c46c3274eaf25dde4d792a1abd6191cccf9
URL: https://github.com/llvm/llvm-project/commit/a5aa0c46c3274eaf25dde4d792a1abd6191cccf9 DIFF: https://github.com/llvm/llvm-project/commit/a5aa0c46c3274eaf25dde4d792a1abd6191cccf9.diff LOG: Introduce -funique-source-file-names flag. The purpose of this flag is to allow the compiler to assume that each object file passed to the linker has been compiled using a unique source file name. This is useful for reducing link times when doing ThinLTO in combination with whole-program devirtualization or CFI, as it allows modules without exported symbols to be built with ThinLTO. Reviewers: vitalybuka, teresajohnson Reviewed By: teresajohnson Pull Request: https://github.com/llvm/llvm-project/pull/135728 Added: clang/test/CodeGen/unique-source-file-names.c clang/test/Driver/unique-source-file-names.c llvm/test/Transforms/ThinLTOBitcodeWriter/unique-source-file-names.ll Modified: clang/docs/ControlFlowIntegrity.rst clang/docs/UsersManual.rst clang/include/clang/Basic/CodeGenOptions.def clang/include/clang/Driver/Options.td clang/lib/CodeGen/CodeGenModule.cpp clang/lib/Driver/ToolChains/Clang.cpp llvm/lib/Transforms/Utils/ModuleUtils.cpp Removed: ################################################################################ diff --git a/clang/docs/ControlFlowIntegrity.rst b/clang/docs/ControlFlowIntegrity.rst index 2f2f8ccf4481b..baff9ab54ff26 100644 --- a/clang/docs/ControlFlowIntegrity.rst +++ b/clang/docs/ControlFlowIntegrity.rst @@ -42,6 +42,11 @@ default visibility setting is ``-fvisibility=default``, which would disable CFI checks for classes without visibility attributes. Most users will want to specify ``-fvisibility=hidden``, which enables CFI checks for such classes. +When using ``-fsanitize=cfi*`` with ``-flto=thin``, it is recommended +to reduce link times by passing `-funique-source-file-names +<UsersManual.html#cmdoption-f-no-unique-source-file-names>`_, provided +that your program is compatible with it. + Experimental support for :ref:`cross-DSO control flow integrity <cfi-cross-dso>` exists that does not require classes to have hidden LTO visibility. This cross-DSO support has unstable ABI at this time. diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index 2a93c2552d7dc..d4656a7e63c99 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -2297,6 +2297,16 @@ are listed below. pure ThinLTO, as all split regular LTO modules are merged and LTO linked with regular LTO. +.. option:: -f[no-]unique-source-file-names + + When enabled, allows the compiler to assume that each object file + passed to the linker has been compiled using a unique source file + name. This is useful for reducing link times when doing ThinLTO + in combination with whole-program devirtualization or CFI. + + A misuse of this flag will generally result in a duplicate symbol + error at link time. + .. option:: -fforce-emit-vtables In order to improve devirtualization, forces emitting of vtables even in diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index a436c0ec98d5b..c5990fb248689 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -278,6 +278,8 @@ CODEGENOPT(SanitizeCfiICallNormalizeIntegers, 1, 0) ///< Normalize integer types ///< CFI icall function signatures CODEGENOPT(SanitizeCfiCanonicalJumpTables, 1, 0) ///< Make jump table symbols canonical ///< instead of creating a local jump table. +CODEGENOPT(UniqueSourceFileNames, 1, 0) ///< Allow the compiler to assume that TUs + ///< have unique source file names at link time CODEGENOPT(SanitizeKcfiArity, 1, 0) ///< Embed arity in KCFI patchable function prefix CODEGENOPT(SanitizeCoverageType, 2, 0) ///< Type of sanitizer coverage ///< instrumentation. diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index c9d2bc5e81976..e9acb20348654 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -4140,6 +4140,13 @@ def ftrigraphs : Flag<["-"], "ftrigraphs">, Group<f_Group>, def fno_trigraphs : Flag<["-"], "fno-trigraphs">, Group<f_Group>, HelpText<"Do not process trigraph sequences">, Visibility<[ClangOption, CC1Option]>; +defm unique_source_file_names: BoolOption<"f", "unique-source-file-names", + CodeGenOpts<"UniqueSourceFileNames">, DefaultFalse, + PosFlag<SetTrue, [], [CC1Option], "Allow">, + NegFlag<SetFalse, [], [], "Do not allow">, + BothFlags<[], [ClangOption], " the compiler to assume that each translation unit has a unique " + "source file name at link time">>, + Group<f_clang_Group>; def funsigned_bitfields : Flag<["-"], "funsigned-bitfields">, Group<f_Group>; def funsigned_char : Flag<["-"], "funsigned-char">, Group<f_Group>; def fno_unsigned_char : Flag<["-"], "fno-unsigned-char">; diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 4a48c2f35ff23..26e09fe239242 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1144,6 +1144,10 @@ void CodeGenModule::Release() { 1); } + if (CodeGenOpts.UniqueSourceFileNames) { + getModule().addModuleFlag(llvm::Module::Max, "Unique Source File Names", 1); + } + if (LangOpts.Sanitize.has(SanitizerKind::KCFI)) { getModule().addModuleFlag(llvm::Module::Override, "kcfi", 1); // KCFI assumes patchable-function-prefix is the same for all indirectly diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 65910e7fdaaa6..8506a5c00e7bc 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -7744,6 +7744,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.addOptInFlag(CmdArgs, options::OPT_fexperimental_late_parse_attributes, options::OPT_fno_experimental_late_parse_attributes); + Args.addOptInFlag(CmdArgs, options::OPT_funique_source_file_names, + options::OPT_fno_unique_source_file_names); + // Setup statistics file output. SmallString<128> StatsFile = getStatsFileName(Args, Output, Input, D); if (!StatsFile.empty()) { diff --git a/clang/test/CodeGen/unique-source-file-names.c b/clang/test/CodeGen/unique-source-file-names.c new file mode 100644 index 0000000000000..1d5a4a5e8e4c5 --- /dev/null +++ b/clang/test/CodeGen/unique-source-file-names.c @@ -0,0 +1,2 @@ +// RUN: %clang_cc1 -funique-source-file-names -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s +// CHECK: !{i32 7, !"Unique Source File Names", i32 1} diff --git a/clang/test/Driver/unique-source-file-names.c b/clang/test/Driver/unique-source-file-names.c new file mode 100644 index 0000000000000..8322f0e37b0c7 --- /dev/null +++ b/clang/test/Driver/unique-source-file-names.c @@ -0,0 +1,5 @@ +// RUN: %clang -funique-source-file-names -### %s 2> %t +// RUN: FileCheck < %t %s + +// CHECK: "-cc1" +// CHECK: "-funique-source-file-names" diff --git a/llvm/lib/Transforms/Utils/ModuleUtils.cpp b/llvm/lib/Transforms/Utils/ModuleUtils.cpp index 1c31e851ef4b2..10efdd61d4553 100644 --- a/llvm/lib/Transforms/Utils/ModuleUtils.cpp +++ b/llvm/lib/Transforms/Utils/ModuleUtils.cpp @@ -345,27 +345,25 @@ void llvm::filterDeadComdatFunctions( std::string llvm::getUniqueModuleId(Module *M) { MD5 Md5; - bool ExportsSymbols = false; - auto AddGlobal = [&](GlobalValue &GV) { - if (GV.isDeclaration() || GV.getName().starts_with("llvm.") || - !GV.hasExternalLinkage() || GV.hasComdat()) - return; - ExportsSymbols = true; - Md5.update(GV.getName()); - Md5.update(ArrayRef<uint8_t>{0}); - }; - - for (auto &F : *M) - AddGlobal(F); - for (auto &GV : M->globals()) - AddGlobal(GV); - for (auto &GA : M->aliases()) - AddGlobal(GA); - for (auto &IF : M->ifuncs()) - AddGlobal(IF); - - if (!ExportsSymbols) - return ""; + + auto *UniqueSourceFileNames = mdconst::extract_or_null<ConstantInt>( + M->getModuleFlag("Unique Source File Names")); + if (UniqueSourceFileNames && UniqueSourceFileNames->getZExtValue()) { + Md5.update(M->getSourceFileName()); + } else { + bool ExportsSymbols = false; + for (auto &GV : M->global_values()) { + if (GV.isDeclaration() || GV.getName().starts_with("llvm.") || + !GV.hasExternalLinkage() || GV.hasComdat()) + continue; + ExportsSymbols = true; + Md5.update(GV.getName()); + Md5.update(ArrayRef<uint8_t>{0}); + } + + if (!ExportsSymbols) + return ""; + } MD5::MD5Result R; Md5.final(R); diff --git a/llvm/test/Transforms/ThinLTOBitcodeWriter/unique-source-file-names.ll b/llvm/test/Transforms/ThinLTOBitcodeWriter/unique-source-file-names.ll new file mode 100644 index 0000000000000..0f3fd566f9b1c --- /dev/null +++ b/llvm/test/Transforms/ThinLTOBitcodeWriter/unique-source-file-names.ll @@ -0,0 +1,22 @@ +; RUN: opt -thinlto-bc -thin-link-bitcode-file=%t2 -thinlto-split-lto-unit -o %t %s +; RUN: llvm-modextract -b -n 1 -o %t1 %t +; RUN: llvm-dis -o - %t1 | FileCheck %s + +source_filename = "unique-source-file-names.c" + +@llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @f, ptr null }] + +; CHECK: @g.45934e8a5251fb7adbecfff71a4e70ed = +@g = internal global i8 42, !type !0 + +declare void @sink(ptr) + +define internal void @f() { + call void @sink(ptr @g) + ret void +} + +!0 = !{i32 0, !"typeid"} + +!llvm.module.flags = !{!1} +!1 = !{i32 1, !"Unique Source File Names", i32 1} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits