https://github.com/thrimbor created https://github.com/llvm/llvm-project/pull/165529
This adds support for FatLTO to COFF targets in clang and lld. The changes are adapted from https://github.com/llvm/llvm-project/commit/610fc5cbcc8b68879c562f6458608afe2473ab7f and https://github.com/llvm/llvm-project/commit/14e3bec8fc3e1f10c3dc57277ae3dbf9a4087b1c but much smaller because it just needed the COFF-specific parts wired in, and I tried my best to adapt the pre-existing ELF tests for the COFF version. My main goal is to be able to use this for shipping pre-built https://github.com/XboxDev/nxdk container images someday, which uses the `i386-pc-win32` target. >From b32b1124a4dfeb3cd463f2323cba843453f9479a Mon Sep 17 00:00:00 2001 From: Stefan Schmidt <[email protected]> Date: Mon, 27 Oct 2025 22:32:33 +0100 Subject: [PATCH 1/2] [clang][llvm] Enable fat-lto-object support for COFF targets --- clang/lib/Driver/ToolChains/Clang.cpp | 2 +- clang/test/CodeGen/fat-lto-objects.c | 14 ++++++++++++++ clang/test/Driver/fat-lto-objects.c | 8 ++++++++ llvm/lib/Transforms/IPO/EmbedBitcodePass.cpp | 2 +- 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 79edc561c551f..28cc91c620b0c 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -7789,7 +7789,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, options::OPT_fno_fat_lto_objects)) { if (IsUsingLTO && A->getOption().matches(options::OPT_ffat_lto_objects)) { assert(LTOMode == LTOK_Full || LTOMode == LTOK_Thin); - if (!Triple.isOSBinFormatELF()) { + if (!Triple.isOSBinFormatELF() && !Triple.isOSBinFormatCOFF()) { D.Diag(diag::err_drv_unsupported_opt_for_target) << A->getAsString(Args) << TC.getTripleString(); } diff --git a/clang/test/CodeGen/fat-lto-objects.c b/clang/test/CodeGen/fat-lto-objects.c index 36a73684e7bfe..64ccd8ae75a4d 100644 --- a/clang/test/CodeGen/fat-lto-objects.c +++ b/clang/test/CodeGen/fat-lto-objects.c @@ -35,6 +35,9 @@ // RUN: %clang -cc1 -triple x86_64-unknown-linux-gnu -flto=full -ffat-lto-objects -fsplit-lto-unit -S < %s -o - \ // RUN: | FileCheck %s --check-prefixes=ASM +// RUN: %clang -cc1 -triple i386-pc-win32 -flto=full -ffat-lto-objects -fsplit-lto-unit -S < %s -o - \ +// RUN: | FileCheck %s --check-prefixes=ASM-COFF + /// Make sure that FatLTO generates .llvm.lto sections that are the same as the output from normal LTO compilations // RUN: %clang -O2 --target=x86_64-unknown-linux-gnu -fPIE -flto=full -ffat-lto-objects -c %s -o %t.fatlto.full.o // RUN: llvm-objcopy --dump-section=.llvm.lto=%t.fatlto.full.bc %t.fatlto.full.o @@ -43,6 +46,13 @@ // RUN: llvm-dis < %t.nofat.full.bc -o %t.nofat.full.ll // RUN: diff %t.fatlto.full.ll %t.nofat.full.ll +// RUN: %clang -O2 --target=i386-pc-win32 -flto=full -ffat-lto-objects -c %s -o %t.fatlto.full.coff.o +// RUN: llvm-objcopy --dump-section=.llvm.lto=%t.fatlto.full.coff.bc %t.fatlto.full.coff.o +// RUN: llvm-dis < %t.fatlto.full.coff.bc -o %t.fatlto.full.coff.ll +// RUN: %clang -O2 --target=i386-pc-win32 -flto=full -c %s -o %t.nofat.full.coff.bc +// RUN: llvm-dis < %t.nofat.full.coff.bc -o %t.nofat.full.coff.ll +// RUN: diff %t.fatlto.full.coff.ll %t.nofat.full.coff.ll + // RUN: %clang -O2 --target=x86_64-unknown-linux-gnu -fPIE -flto=thin -ffat-lto-objects -c %s -o %t.fatlto.thin.o // RUN: llvm-objcopy --dump-section=.llvm.lto=%t.fatlto.thin.bc %t.fatlto.thin.o // RUN: llvm-dis < %t.fatlto.thin.bc -o %t.fatlto.thin.ll @@ -67,6 +77,10 @@ // ASM-NEXT: .asciz "BC // ASM-NEXT: .size .Lllvm.embedded.object +// ASM-COFF: .section .llvm.lto,"ynD" +// ASM-COFF-NEXT: L_llvm.embedded.object: +// ASM-COFF-NEXT: .asciz "BC + const char* foo = "foo"; int test(void) { diff --git a/clang/test/Driver/fat-lto-objects.c b/clang/test/Driver/fat-lto-objects.c index 7b87e2b468886..84dd945e208b9 100644 --- a/clang/test/Driver/fat-lto-objects.c +++ b/clang/test/Driver/fat-lto-objects.c @@ -1,10 +1,12 @@ // RUN: %clang --target=x86_64-unknown-linux-gnu -flto -ffat-lto-objects -### %s -c 2>&1 | FileCheck %s -check-prefix=CHECK-CC +// RUN: %clang --target=i386-pc-win32 -flto -ffat-lto-objects -### %s -c 2>&1 | FileCheck %s -check-prefix=CHECK-CC // CHECK-CC: -cc1 // CHECK-CC-SAME: -emit-obj // CHECK-CC-SAME: -ffat-lto-objects /// Without -flto -S will just emit normal ASM, so we don't expect -emit-{llvm,obj} or -ffat-lto-objects to be passed to cc1. // RUN: %clang --target=x86_64-unknown-linux-gnu -ffat-lto-objects -### %s -S 2>&1 | FileCheck %s -check-prefix=CHECK-CC-S +// RUN: %clang --target=i386-pc-win32 -ffat-lto-objects -### %s -S 2>&1 | FileCheck %s -check-prefix=CHECK-CC-S // CHECK-CC-S: -cc1 // CHECK-CC-S: -S // CHECK-CC-S-NOT: -emit-obj @@ -13,35 +15,41 @@ /// When fat LTO is enabled with -S, we expect asm output and -ffat-lto-objects to be passed to cc1. // RUN: %clang --target=x86_64-unknown-linux-gnu -flto -ffat-lto-objects -### %s -S 2>&1 | FileCheck %s -check-prefix=CHECK-CC-S-LTO +// RUN: %clang --target=i386-pc-win32 -flto -ffat-lto-objects -### %s -S 2>&1 | FileCheck %s -check-prefix=CHECK-CC-S-LTO // CHECK-CC-S-LTO: -cc1 // CHECK-CC-S-NOT: -emit-llvm // CHECK-CC-S-LTO-SAME: -ffat-lto-objects /// When fat LTO is enabled with -S and -emit-llvm, we expect IR output and -ffat-lto-objects to be passed to cc1. // RUN: %clang --target=x86_64-unknown-linux-gnu -flto -ffat-lto-objects -### %s -S -emit-llvm 2>&1 | FileCheck %s -check-prefix=CHECK-CC-S-EL-LTO +// RUN: %clang --target=i386-pc-win32 -flto -ffat-lto-objects -### %s -S -emit-llvm 2>&1 | FileCheck %s -check-prefix=CHECK-CC-S-EL-LTO // CHECK-CC-S-EL-LTO: -cc1 // CHECK-CC-S-EL-LTO-SAME: -emit-llvm // CHECK-CC-S-EL-LTO-SAME: -ffat-lto-objects /// When fat LTO is enabled without -S we expect native object output and -ffat-lto-object to be passed to cc1. // RUN: %clang --target=x86_64-unknown-linux-gnu -flto -ffat-lto-objects -### %s -c 2>&1 | FileCheck %s -check-prefix=CHECK-CC-C-LTO +// RUN: %clang --target=i386-pc-win32 -flto -ffat-lto-objects -### %s -c 2>&1 | FileCheck %s -check-prefix=CHECK-CC-C-LTO // CHECK-CC-C-LTO: -cc1 // CHECK-CC-C-LTO-SAME: -emit-obj // CHECK-CC-C-LTO-SAME: -ffat-lto-objects /// When fat LTO is enabled with -c and -emit-llvm we expect bitcode output and -ffat-lto-object to be passed to cc1. // RUN: %clang --target=x86_64-unknown-linux-gnu -flto -ffat-lto-objects -### %s -c -emit-llvm 2>&1 | FileCheck %s -check-prefix=CHECK-CC-C-EL-LTO +// RUN: %clang --target=i386-pc-win32 -flto -ffat-lto-objects -### %s -c -emit-llvm 2>&1 | FileCheck %s -check-prefix=CHECK-CC-C-EL-LTO // CHECK-CC-C-EL-LTO: -cc1 // CHECK-CC-C-EL-LTO-SAME: -emit-llvm-bc // CHECK-CC-C-EL-LTO-SAME: -ffat-lto-objects /// Make sure we don't have a warning for -ffat-lto-objects being unused // RUN: %clang --target=x86_64-unknown-linux-gnu -ffat-lto-objects -fdriver-only -Werror -v %s -c 2>&1 | FileCheck %s -check-prefix=CHECK-CC-NOLTO +// RUN: %clang --target=i386-pc-win32 -ffat-lto-objects -fdriver-only -Werror -v %s -c 2>&1 | FileCheck %s -check-prefix=CHECK-CC-NOLTO // CHECK-CC-NOLTO: -cc1 // CHECK-CC-NOLTO-SAME: -emit-obj // CHECK-CC-NOLTO-NOT: -ffat-lto-objects // RUN: %clang --target=x86_64-unknown-linux-gnu -flto -ffat-lto-objects -fno-fat-lto-objects -### %s -c 2>&1 | FileCheck %s -check-prefix=CHECK-CC-NOLTO +// RUN: %clang --target=i386-pc-win32 -flto -ffat-lto-objects -fno-fat-lto-objects -### %s -c 2>&1 | FileCheck %s -check-prefix=CHECK-CC-NOLTO /// We need to pass an additional flag (--fat-lto-objects) to lld when linking w/ -flto -ffat-lto-objects /// But it should not be there when LTO is disabled w/ -fno-lto diff --git a/llvm/lib/Transforms/IPO/EmbedBitcodePass.cpp b/llvm/lib/Transforms/IPO/EmbedBitcodePass.cpp index f02256f8a83b9..1c9183c03dab4 100644 --- a/llvm/lib/Transforms/IPO/EmbedBitcodePass.cpp +++ b/llvm/lib/Transforms/IPO/EmbedBitcodePass.cpp @@ -27,7 +27,7 @@ PreservedAnalyses EmbedBitcodePass::run(Module &M, ModuleAnalysisManager &AM) { reportFatalUsageError("Can only embed the module once"); Triple T(M.getTargetTriple()); - if (T.getObjectFormat() != Triple::ELF) + if (T.getObjectFormat() != Triple::ELF && T.getObjectFormat() != Triple::COFF) reportFatalUsageError( "EmbedBitcode pass currently only supports ELF object format"); >From 606a966a030f4601a24a14eadcf8fcdf5c4356d6 Mon Sep 17 00:00:00 2001 From: Stefan Schmidt <[email protected]> Date: Mon, 27 Oct 2025 22:56:22 +0100 Subject: [PATCH 2/2] [LLD][COFF] Add fat-lto-object support --- lld/COFF/Config.h | 3 + lld/COFF/Driver.cpp | 35 +++++++- lld/COFF/Driver.h | 2 + lld/COFF/Options.td | 3 + lld/test/COFF/fatlto/fatlto-invalid.s | 8 ++ lld/test/COFF/fatlto/fatlto.test | 111 ++++++++++++++++++++++++++ 6 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 lld/test/COFF/fatlto/fatlto-invalid.s create mode 100644 lld/test/COFF/fatlto/fatlto.test diff --git a/lld/COFF/Config.h b/lld/COFF/Config.h index 16997e4de780e..c5c0338c99e57 100644 --- a/lld/COFF/Config.h +++ b/lld/COFF/Config.h @@ -207,6 +207,9 @@ struct Configuration { // Used for /thinlto-remote-compiler-arg:<arg> llvm::SmallVector<llvm::StringRef, 0> dtltoCompilerArgs; + // Used for /fat-lto-objects + bool fatLTOObjects = false; + // Used for /opt:[no]ltodebugpassmanager bool ltoDebugPassManager = false; diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp index 0e528de9c3652..ac5942a3ac2d8 100644 --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -28,6 +28,7 @@ #include "llvm/Config/llvm-config.h" #include "llvm/LTO/LTO.h" #include "llvm/Object/COFFImportFile.h" +#include "llvm/Object/IRObjectFile.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" @@ -256,6 +257,23 @@ MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> mb) { return mbref; } +InputFile *LinkerDriver::tryCreateFatLTOFile(MemoryBufferRef mb, + StringRef archiveName, + uint64_t offsetInArchive, + bool lazy) { + if (!ctx.config.fatLTOObjects) + return nullptr; + + Expected<MemoryBufferRef> fatLTOData = + IRObjectFile::findBitcodeInMemBuffer(mb); + + if (errorToBool(fatLTOData.takeError())) + return nullptr; + + return BitcodeFile::create(ctx, *fatLTOData, archiveName, offsetInArchive, + lazy); +} + void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb, bool wholeArchive, bool lazy) { StringRef filename = mb->getBufferIdentifier(); @@ -289,7 +307,14 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb, case file_magic::bitcode: addFile(BitcodeFile::create(ctx, mbref, "", 0, lazy)); break; - case file_magic::coff_object: + case file_magic::coff_object: { + InputFile *obj = tryCreateFatLTOFile(mbref, "", 0, lazy); + if (obj) + addFile(obj); + else + addFile(ObjFile::create(ctx, mbref, lazy)); + break; + } case file_magic::coff_import_library: addFile(ObjFile::create(ctx, mbref, lazy)); break; @@ -374,7 +399,9 @@ void LinkerDriver::addArchiveBuffer(MemoryBufferRef mb, StringRef symName, InputFile *obj; if (magic == file_magic::coff_object) { - obj = ObjFile::create(ctx, mb); + obj = tryCreateFatLTOFile(mb, parentName, offsetInArchive, /*lazy=*/false); + if (!obj) + obj = ObjFile::create(ctx, mb); } else if (magic == file_magic::bitcode) { obj = BitcodeFile::create(ctx, mb, parentName, offsetInArchive, /*lazy=*/false); @@ -2121,6 +2148,10 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) { config->dtltoCompilerArgs = args::getStrings(args, OPT_thinlto_remote_compiler_arg); + // Handle /fat-lto-objects + config->fatLTOObjects = + args.hasFlag(OPT_fat_lto_objects, OPT_fat_lto_objects_no, false); + // Handle /dwodir config->dwoDir = args.getLastArgValue(OPT_dwodir); diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h index 14710d5853bcf..8b28bd9f3c617 100644 --- a/lld/COFF/Driver.h +++ b/lld/COFF/Driver.h @@ -169,6 +169,8 @@ class LinkerDriver { std::set<std::string> visitedLibs; + InputFile *tryCreateFatLTOFile(MemoryBufferRef mb, StringRef archiveName, + uint64_t offsetInArchive, bool lazy); void addBuffer(std::unique_ptr<MemoryBuffer> mb, bool wholeArchive, bool lazy); void addArchiveBuffer(MemoryBufferRef mbref, StringRef symName, diff --git a/lld/COFF/Options.td b/lld/COFF/Options.td index d77478fc9c987..911c5c55f249b 100644 --- a/lld/COFF/Options.td +++ b/lld/COFF/Options.td @@ -298,6 +298,9 @@ def thinlto_remote_compiler_prepend_arg : P<"thinlto-remote-compiler-prepend-arg def thinlto_remote_compiler_arg : P<"thinlto-remote-compiler-arg", "Compiler arguments for the ThinLTO distributor to pass for ThinLTO backend " "compilations">; +defm fat_lto_objects: B<"fat-lto-objects", + "Use the .llvm.lto section, which contains LLVM bitcode, in fat LTO object files to perform LTO.", + "Ignore the .llvm.lto section in relocatable object files (default).">; def lto_obj_path : P< "lto-obj-path", "output native object for merged LTO unit to this path">; diff --git a/lld/test/COFF/fatlto/fatlto-invalid.s b/lld/test/COFF/fatlto/fatlto-invalid.s new file mode 100644 index 0000000000000..5e60633ef5f11 --- /dev/null +++ b/lld/test/COFF/fatlto/fatlto-invalid.s @@ -0,0 +1,8 @@ +# RUN: llvm-mc -filetype=obj -triple=i386-pc-win32 %s -o %t +# RUN: env LLD_IN_TEST=1 not lld-link %t /out:/dev/null /fat-lto-objects 2>&1 | FileCheck %s + +# CHECK: error:{{.*}} Invalid bitcode signature + +.section .llvm.lto,"ynD" +L_llvm.embedded.object: + .asciz "BC\300\3365\000" diff --git a/lld/test/COFF/fatlto/fatlto.test b/lld/test/COFF/fatlto/fatlto.test new file mode 100644 index 0000000000000..19b62a94ea21a --- /dev/null +++ b/lld/test/COFF/fatlto/fatlto.test @@ -0,0 +1,111 @@ +;; Basic FatLTO tests. +; REQUIRES: x86 + +; RUN: rm -rf %t && split-file %s %t && cd %t + +;; Ensure that input files contain .llvm.lto section. +; RUN: llc a-LTO.ll --filetype=obj -o a-fatLTO.o --relocation-model=pic +; RUN: opt < a-LTO.ll --module-summary -o a-fatLTO.bc +; RUN: llvm-objcopy --add-section=.llvm.lto=a-fatLTO.bc --set-section-flags=.llvm.lto=exclude a-fatLTO.o + +; RUN: llc main-LTO.ll --filetype=obj -o main-fatLTO.o --relocation-model=pic +; RUN: opt < main-LTO.ll --module-summary -o main-fatLTO.bc +; RUN: llvm-objcopy --add-section=.llvm.lto=main-fatLTO.bc --set-section-flags=.llvm.lto=exclude main-fatLTO.o + +; RUN: llvm-readobj --sections a-fatLTO.o | FileCheck --check-prefix=HAS_LLVM_LTO %s +; RUN: llvm-readobj --sections main-fatLTO.o | FileCheck --check-prefix=HAS_LLVM_LTO %s + +;; Make sure that the section flags are set correctly +; HAS_LLVM_LTO: Name: .llvm.lto (2F 34 00 00 00 00 00 00) +; HAS_LLVM_LTO: Characteristics [ +; HAS_LLVM_LTO-SAME: (0xC0000800) +; HAS_LLVM_LTO-NEXT: IMAGE_SCN_LNK_REMOVE (0x800) +; HAS_LLVM_LTO-NEXT: IMAGE_SCN_MEM_READ (0x40000000) +; HAS_LLVM_LTO-NEXT: IMAGE_SCN_MEM_WRITE (0x80000000) +; HAS_LLVM_LTO-NEXT: ] + +;; Final executable should not have .llvm.lto section no matter what the target is. +; RUN: lld-link /timestamp:0 /out:foo-fatLTO /entry:main a-fatLTO.o main-fatLTO.o -fat-lto-objects +; RUN: llvm-readobj --sections foo-fatLTO | FileCheck --check-prefix=CHECK-LTO-TARGET %s + +;; Check that fat objects work w/ --start-lib. +; RUN: lld-link /out:foo-fatLTO.start_lib /entry:main -start-lib a-fatLTO.o main-fatLTO.o -fat-lto-objects +; RUN: llvm-readobj --sections foo-fatLTO.start_lib | FileCheck --check-prefix=CHECK-LTO-TARGET %s + +;; Check if .llvm.lto section gets aggregated in LTO target. +; CHECK-LTO-TARGET-NOT: Name: .llvm.lto + +;; Final executable should not have .llvm.lto section no matter what the target is. +; RUN: lld-link /timestamp:0 /out:foo-fatNoLTO /entry:main a-fatLTO.o %/t/main-fatLTO.o +; RUN: llvm-readobj --sections foo-fatNoLTO | FileCheck --check-prefix=CHECK-NON-LTO-TARGET %s + +;; Check if .llvm.lto section gets aggregated in non-LTO target. +; CHECK-NON-LTO-TARGET-NOT: Name: .llvm.lto + +;; Check if the LTO target executable produced from FatLTO object file is +;; identical to the one produced from LTO modules. +; RUN: opt < a-LTO.ll --module-summary -o a-LTO.bc +; RUN: opt < main-LTO.ll --module-summary -o main-LTO.bc +; RUN: lld-link /timestamp:0 /out:foo-LTO /entry:main a-LTO.bc main-LTO.bc +; RUN: cmp foo-fatLTO foo-LTO + +;; Check if the no-LTO target executable produced from FatLTO object file is +;; identical to the one produced from regular object files. +; RUN: llc a-LTO.ll --filetype=obj -o a.o +; RUN: llc main-LTO.ll --filetype=obj -o main.o +; RUN: lld-link /timestamp:0 /out:foo-noLTO /entry:main a.o main.o +; RUN: cmp foo-fatNoLTO foo-noLTO + +;; Check archive support. +; RUN: llvm-ar rcs a.a a-fatLTO.o +; RUN: lld-link /timestamp:0 /out:foo-fatLTO.archive /entry:main /wholearchive a.a main-LTO.bc -fat-lto-objects +; RUN: cmp foo-fatLTO.archive foo-LTO + +;--- a-LTO.ll +target datalayout = "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32-a:0:32-S32" +target triple = "i386-pc-windows-msvc19.33.0" + +; Function Attrs: noinline nounwind uwtable +define dso_local i32 @_start() #0 { +entry: + ret i32 0 +} + +attributes #0 = { noinline nounwind uwtable } + +!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6} + +!0 = !{i32 1, !"NumRegisterParameters", i32 0} +!1 = !{i32 2, !"Debug Info Version", i32 3} +!2 = !{i32 1, !"wchar_size", i32 2} +!3 = !{i32 7, !"frame-pointer", i32 2} +!4 = !{i32 1, !"MaxTLSAlign", i32 65536} +!5 = !{i32 1, !"ThinLTO", i32 0} +!6 = !{i32 1, !"EnableSplitLTOUnit", i32 1} + +;--- main-LTO.ll +target datalayout = "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32-a:0:32-S32" +target triple = "i386-pc-windows-msvc19.33.0" + +; Function Attrs: noinline nounwind uwtable +define dso_local i32 @main() #0 { +entry: + %retval = alloca i32, align 4 + store i32 0, ptr %retval, align 4 + %call = call i32 (...) @_start() + ret i32 %call +} + +declare i32 @_start(...) + +attributes #0 = { noinline nounwind uwtable } + +!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6} + +!0 = !{i32 1, !"NumRegisterParameters", i32 0} +!1 = !{i32 2, !"Debug Info Version", i32 3} +!2 = !{i32 1, !"wchar_size", i32 2} +!3 = !{i32 7, !"frame-pointer", i32 2} +!4 = !{i32 1, !"MaxTLSAlign", i32 65536} +!5 = !{i32 1, !"ThinLTO", i32 0} +!6 = !{i32 1, !"EnableSplitLTOUnit", i32 1} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
