0
0
mirror of https://github.com/llvm/llvm-project.git synced 2025-04-21 10:36:49 +00:00

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
This commit is contained in:
Peter Collingbourne 2025-04-15 11:12:05 -07:00 committed by GitHub
parent 3f58ff20fe
commit a5aa0c46c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 77 additions and 19 deletions
clang
llvm
lib/Transforms/Utils
test/Transforms/ThinLTOBitcodeWriter

@ -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.

@ -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

@ -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.

@ -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">;

@ -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

@ -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()) {

@ -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}

@ -0,0 +1,5 @@
// RUN: %clang -funique-source-file-names -### %s 2> %t
// RUN: FileCheck < %t %s
// CHECK: "-cc1"
// CHECK: "-funique-source-file-names"

@ -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);
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 "";
if (!ExportsSymbols)
return "";
}
MD5::MD5Result R;
Md5.final(R);

@ -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}