Skip to content

Commit 2cdfef8

Browse files
committed
[LinkerWrapper] Remove in-house handling of LTO
Summary: This should be the linker's job if the user creates any bitcode files, then passing `-flto` to the linker for the toolchain should be able to handle it. Right now this path is only used in the case where someone does LTO w/ ld.gold targeting a CPU so I think we are safe here as that will still be forwarded, for bfd it'll be an error as it would on the host. I think I talked the SYCL team out of using this as well so I should be good to delete it.
1 parent f1be516 commit 2cdfef8

File tree

1 file changed

+2
-343
lines changed

1 file changed

+2
-343
lines changed

clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp

Lines changed: 2 additions & 343 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,6 @@ static std::list<SmallString<128>> TempFiles;
140140
/// Codegen flags for LTO backend.
141141
static codegen::RegisterCodeGenFlags CodeGenFlags;
142142

143-
/// Global flag to indicate that the LTO pipeline threw an error.
144-
static std::atomic<bool> LTOError;
145-
146143
using OffloadingImage = OffloadBinary::OffloadingImage;
147144

148145
namespace llvm {
@@ -293,12 +290,10 @@ Expected<std::string> findProgram(StringRef Name, ArrayRef<StringRef> Paths) {
293290
return *Path;
294291
}
295292

296-
/// We will defer LTO to the target's linker if we are not doing JIT and it is
297-
/// supported by the toolchain.
298293
bool linkerSupportsLTO(const ArgList &Args) {
299294
llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));
300295
return Triple.isNVPTX() || Triple.isAMDGPU() ||
301-
Args.getLastArgValue(OPT_linker_path_EQ).ends_with("ld.lld");
296+
Args.getLastArgValue(OPT_linker_path_EQ).ends_with("lld");
302297
}
303298

304299
/// Returns the hashed value for a constant string.
@@ -653,7 +648,6 @@ void diagnosticHandler(const DiagnosticInfo &DI) {
653648
switch (DI.getSeverity()) {
654649
case DS_Error:
655650
WithColor::error(errs(), LinkerExecutable) << ErrStorage << "\n";
656-
LTOError = true;
657651
break;
658652
case DS_Warning:
659653
WithColor::warning(errs(), LinkerExecutable) << ErrStorage << "\n";
@@ -667,334 +661,6 @@ void diagnosticHandler(const DiagnosticInfo &DI) {
667661
}
668662
}
669663

670-
// Get the list of target features from the input file and unify them such that
671-
// if there are multiple +xxx or -xxx features we only keep the last one.
672-
std::vector<std::string> getTargetFeatures(ArrayRef<OffloadFile> InputFiles) {
673-
SmallVector<StringRef> Features;
674-
for (const OffloadFile &File : InputFiles) {
675-
for (auto Arg : llvm::split(File.getBinary()->getString("feature"), ","))
676-
Features.emplace_back(Arg);
677-
}
678-
679-
// Only add a feature if it hasn't been seen before starting from the end.
680-
std::vector<std::string> UnifiedFeatures;
681-
DenseSet<StringRef> UsedFeatures;
682-
for (StringRef Feature : llvm::reverse(Features)) {
683-
if (UsedFeatures.insert(Feature.drop_front()).second)
684-
UnifiedFeatures.push_back(Feature.str());
685-
}
686-
687-
return UnifiedFeatures;
688-
}
689-
690-
template <typename ModuleHook = function_ref<bool(size_t, const Module &)>>
691-
std::unique_ptr<lto::LTO> createLTO(
692-
const ArgList &Args, const std::vector<std::string> &Features,
693-
ModuleHook Hook = [](size_t, const Module &) { return true; }) {
694-
const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));
695-
// We need to remove AMD's target-id from the processor if present.
696-
StringRef TargetID = Args.getLastArgValue(OPT_arch_EQ);
697-
StringRef Arch = clang::getProcessorFromTargetID(Triple, TargetID);
698-
lto::Config Conf;
699-
lto::ThinBackend Backend;
700-
// TODO: Handle index-only thin-LTO
701-
Backend =
702-
lto::createInProcessThinBackend(llvm::heavyweight_hardware_concurrency());
703-
704-
Conf.CPU = Arch.str();
705-
Conf.Options = codegen::InitTargetOptionsFromCodeGenFlags(Triple);
706-
707-
Conf.RemarksFilename = RemarksFilename;
708-
Conf.RemarksPasses = RemarksPasses;
709-
Conf.RemarksWithHotness = RemarksWithHotness;
710-
Conf.RemarksHotnessThreshold = RemarksHotnessThreshold;
711-
Conf.RemarksFormat = RemarksFormat;
712-
713-
StringRef OptLevel = Args.getLastArgValue(OPT_opt_level, "O2");
714-
Conf.MAttrs = Features;
715-
std::optional<CodeGenOptLevel> CGOptLevelOrNone =
716-
CodeGenOpt::parseLevel(OptLevel[1]);
717-
assert(CGOptLevelOrNone && "Invalid optimization level");
718-
Conf.CGOptLevel = *CGOptLevelOrNone;
719-
Conf.OptLevel = OptLevel[1] - '0';
720-
Conf.DefaultTriple = Triple.getTriple();
721-
722-
// TODO: Should we complain about combining --opt-level and -passes, as opt
723-
// does? That might be too limiting in clang-linker-wrapper, so for now we
724-
// just warn in the help entry for -passes that the default<O?> corresponding
725-
// to --opt-level=O? should be included there. The problem is that
726-
// --opt-level produces effects in clang-linker-wrapper beyond what -passes
727-
// appears to be able to achieve, so rejecting the combination of --opt-level
728-
// and -passes would apparently make it impossible to combine those effects
729-
// with a custom pass pipeline.
730-
Conf.OptPipeline = PassPipeline;
731-
Conf.PassPlugins = PassPlugins;
732-
733-
LTOError = false;
734-
Conf.DiagHandler = diagnosticHandler;
735-
736-
Conf.PTO.LoopVectorization = Conf.OptLevel > 1;
737-
Conf.PTO.SLPVectorization = Conf.OptLevel > 1;
738-
739-
if (SaveTemps) {
740-
std::string TempName = (sys::path::filename(ExecutableName) + "." +
741-
Triple.getTriple() + "." + TargetID)
742-
.str();
743-
Conf.PostInternalizeModuleHook = [=](size_t Task, const Module &M) {
744-
std::string File =
745-
!Task ? TempName + ".postlink.bc"
746-
: TempName + "." + std::to_string(Task) + ".postlink.bc";
747-
error_code EC;
748-
raw_fd_ostream LinkedBitcode(File, EC, sys::fs::OF_None);
749-
if (EC)
750-
reportError(errorCodeToError(EC));
751-
WriteBitcodeToFile(M, LinkedBitcode);
752-
return true;
753-
};
754-
Conf.PreCodeGenModuleHook = [=](size_t Task, const Module &M) {
755-
std::string File =
756-
!Task ? TempName + ".postopt.bc"
757-
: TempName + "." + std::to_string(Task) + ".postopt.bc";
758-
error_code EC;
759-
raw_fd_ostream LinkedBitcode(File, EC, sys::fs::OF_None);
760-
if (EC)
761-
reportError(errorCodeToError(EC));
762-
WriteBitcodeToFile(M, LinkedBitcode);
763-
return true;
764-
};
765-
}
766-
Conf.PostOptModuleHook = Hook;
767-
Conf.CGFileType = (Triple.isNVPTX() || SaveTemps)
768-
? CodeGenFileType::AssemblyFile
769-
: CodeGenFileType::ObjectFile;
770-
771-
// TODO: Handle remark files
772-
Conf.HasWholeProgramVisibility = Args.hasArg(OPT_whole_program);
773-
774-
return std::make_unique<lto::LTO>(std::move(Conf), Backend);
775-
}
776-
777-
// Returns true if \p S is valid as a C language identifier and will be given
778-
// `__start_` and `__stop_` symbols.
779-
bool isValidCIdentifier(StringRef S) {
780-
return !S.empty() && (isAlpha(S[0]) || S[0] == '_') &&
781-
llvm::all_of(llvm::drop_begin(S),
782-
[](char C) { return C == '_' || isAlnum(C); });
783-
}
784-
785-
Error linkBitcodeFiles(SmallVectorImpl<OffloadFile> &InputFiles,
786-
SmallVectorImpl<StringRef> &OutputFiles,
787-
const ArgList &Args) {
788-
llvm::TimeTraceScope TimeScope("Link bitcode files");
789-
const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));
790-
StringRef Arch = Args.getLastArgValue(OPT_arch_EQ);
791-
792-
SmallVector<OffloadFile, 4> BitcodeInputFiles;
793-
DenseSet<StringRef> StrongResolutions;
794-
DenseSet<StringRef> UsedInRegularObj;
795-
DenseSet<StringRef> UsedInSharedLib;
796-
BumpPtrAllocator Alloc;
797-
StringSaver Saver(Alloc);
798-
799-
// Search for bitcode files in the input and create an LTO input file. If
800-
// it is not a bitcode file, scan its symbol table for symbols we need to
801-
// save.
802-
for (OffloadFile &File : InputFiles) {
803-
MemoryBufferRef Buffer = MemoryBufferRef(File.getBinary()->getImage(), "");
804-
805-
file_magic Type = identify_magic(Buffer.getBuffer());
806-
switch (Type) {
807-
case file_magic::bitcode: {
808-
Expected<IRSymtabFile> IRSymtabOrErr = readIRSymtab(Buffer);
809-
if (!IRSymtabOrErr)
810-
return IRSymtabOrErr.takeError();
811-
812-
// Check for any strong resolutions we need to preserve.
813-
for (unsigned I = 0; I != IRSymtabOrErr->Mods.size(); ++I) {
814-
for (const auto &Sym : IRSymtabOrErr->TheReader.module_symbols(I)) {
815-
if (!Sym.isFormatSpecific() && Sym.isGlobal() && !Sym.isWeak() &&
816-
!Sym.isUndefined())
817-
StrongResolutions.insert(Saver.save(Sym.Name));
818-
}
819-
}
820-
BitcodeInputFiles.emplace_back(std::move(File));
821-
continue;
822-
}
823-
case file_magic::elf_relocatable:
824-
case file_magic::elf_shared_object: {
825-
Expected<std::unique_ptr<ObjectFile>> ObjFile =
826-
ObjectFile::createObjectFile(Buffer);
827-
if (!ObjFile)
828-
continue;
829-
830-
for (SymbolRef Sym : (*ObjFile)->symbols()) {
831-
Expected<StringRef> Name = Sym.getName();
832-
if (!Name)
833-
return Name.takeError();
834-
835-
// Record if we've seen these symbols in any object or shared
836-
// libraries.
837-
if ((*ObjFile)->isRelocatableObject())
838-
UsedInRegularObj.insert(Saver.save(*Name));
839-
else
840-
UsedInSharedLib.insert(Saver.save(*Name));
841-
}
842-
continue;
843-
}
844-
default:
845-
continue;
846-
}
847-
}
848-
849-
if (BitcodeInputFiles.empty())
850-
return Error::success();
851-
852-
// Remove all the bitcode files that we moved from the original input.
853-
llvm::erase_if(InputFiles, [](OffloadFile &F) { return !F.getBinary(); });
854-
855-
// LTO Module hook to output bitcode without running the backend.
856-
SmallVector<StringRef> BitcodeOutput;
857-
auto OutputBitcode = [&](size_t, const Module &M) {
858-
auto TempFileOrErr = createOutputFile(sys::path::filename(ExecutableName) +
859-
"-jit-" + Triple.getTriple(),
860-
"bc");
861-
if (!TempFileOrErr)
862-
reportError(TempFileOrErr.takeError());
863-
864-
std::error_code EC;
865-
raw_fd_ostream LinkedBitcode(*TempFileOrErr, EC, sys::fs::OF_None);
866-
if (EC)
867-
reportError(errorCodeToError(EC));
868-
WriteBitcodeToFile(M, LinkedBitcode);
869-
BitcodeOutput.push_back(*TempFileOrErr);
870-
return false;
871-
};
872-
873-
// We assume visibility of the whole program if every input file was
874-
// bitcode.
875-
auto Features = getTargetFeatures(BitcodeInputFiles);
876-
auto LTOBackend = Args.hasArg(OPT_embed_bitcode) ||
877-
Args.hasArg(OPT_builtin_bitcode_EQ) ||
878-
Args.hasArg(OPT_clang_backend)
879-
? createLTO(Args, Features, OutputBitcode)
880-
: createLTO(Args, Features);
881-
882-
// We need to resolve the symbols so the LTO backend knows which symbols
883-
// need to be kept or can be internalized. This is a simplified symbol
884-
// resolution scheme to approximate the full resolution a linker would do.
885-
uint64_t Idx = 0;
886-
DenseSet<StringRef> PrevailingSymbols;
887-
for (auto &BitcodeInput : BitcodeInputFiles) {
888-
// Get a semi-unique buffer identifier for Thin-LTO.
889-
StringRef Identifier = Saver.save(
890-
std::to_string(Idx++) + "." +
891-
BitcodeInput.getBinary()->getMemoryBufferRef().getBufferIdentifier());
892-
MemoryBufferRef Buffer =
893-
MemoryBufferRef(BitcodeInput.getBinary()->getImage(), Identifier);
894-
Expected<std::unique_ptr<lto::InputFile>> BitcodeFileOrErr =
895-
llvm::lto::InputFile::create(Buffer);
896-
if (!BitcodeFileOrErr)
897-
return BitcodeFileOrErr.takeError();
898-
899-
// Save the input file and the buffer associated with its memory.
900-
const auto Symbols = (*BitcodeFileOrErr)->symbols();
901-
SmallVector<lto::SymbolResolution, 16> Resolutions(Symbols.size());
902-
size_t Idx = 0;
903-
for (auto &Sym : Symbols) {
904-
lto::SymbolResolution &Res = Resolutions[Idx++];
905-
906-
// We will use this as the prevailing symbol definition in LTO unless
907-
// it is undefined or another definition has already been used.
908-
Res.Prevailing =
909-
!Sym.isUndefined() &&
910-
!(Sym.isWeak() && StrongResolutions.contains(Sym.getName())) &&
911-
PrevailingSymbols.insert(Saver.save(Sym.getName())).second;
912-
913-
// We need LTO to preseve the following global symbols:
914-
// 1) Symbols used in regular objects.
915-
// 2) Sections that will be given a __start/__stop symbol.
916-
// 3) Prevailing symbols that are needed visible to external
917-
// libraries.
918-
Res.VisibleToRegularObj =
919-
UsedInRegularObj.contains(Sym.getName()) ||
920-
isValidCIdentifier(Sym.getSectionName()) ||
921-
(Res.Prevailing &&
922-
(Sym.getVisibility() != GlobalValue::HiddenVisibility &&
923-
!Sym.canBeOmittedFromSymbolTable()));
924-
925-
// Identify symbols that must be exported dynamically and can be
926-
// referenced by other files.
927-
Res.ExportDynamic =
928-
Sym.getVisibility() != GlobalValue::HiddenVisibility &&
929-
(UsedInSharedLib.contains(Sym.getName()) ||
930-
!Sym.canBeOmittedFromSymbolTable());
931-
932-
// The final definition will reside in this linkage unit if the symbol
933-
// is defined and local to the module. This only checks for bitcode
934-
// files, full assertion will require complete symbol resolution.
935-
Res.FinalDefinitionInLinkageUnit =
936-
Sym.getVisibility() != GlobalValue::DefaultVisibility &&
937-
(!Sym.isUndefined() && !Sym.isCommon());
938-
939-
// We do not support linker redefined symbols (e.g. --wrap) for device
940-
// image linking, so the symbols will not be changed after LTO.
941-
Res.LinkerRedefined = false;
942-
}
943-
944-
// Add the bitcode file with its resolved symbols to the LTO job.
945-
if (Error Err = LTOBackend->add(std::move(*BitcodeFileOrErr), Resolutions))
946-
return Err;
947-
}
948-
949-
// Run the LTO job to compile the bitcode.
950-
size_t MaxTasks = LTOBackend->getMaxTasks();
951-
SmallVector<StringRef> Files(MaxTasks);
952-
auto AddStream =
953-
[&](size_t Task,
954-
const Twine &ModuleName) -> std::unique_ptr<CachedFileStream> {
955-
int FD = -1;
956-
auto &TempFile = Files[Task];
957-
StringRef Extension = (Triple.isNVPTX() || SaveTemps) ? "s" : "o";
958-
std::string TaskStr = Task ? "." + std::to_string(Task) : "";
959-
auto TempFileOrErr =
960-
createOutputFile(sys::path::filename(ExecutableName) + "." +
961-
Triple.getTriple() + "." + Arch + TaskStr,
962-
Extension);
963-
if (!TempFileOrErr)
964-
reportError(TempFileOrErr.takeError());
965-
TempFile = *TempFileOrErr;
966-
if (std::error_code EC = sys::fs::openFileForWrite(TempFile, FD))
967-
reportError(errorCodeToError(EC));
968-
return std::make_unique<CachedFileStream>(
969-
std::make_unique<llvm::raw_fd_ostream>(FD, true));
970-
};
971-
972-
if (Error Err = LTOBackend->run(AddStream))
973-
return Err;
974-
975-
if (LTOError)
976-
return createStringError("Errors encountered inside the LTO pipeline.");
977-
978-
// If we are embedding bitcode we only need the intermediate output.
979-
bool SingleOutput = Files.size() == 1;
980-
if (Args.hasArg(OPT_embed_bitcode)) {
981-
if (BitcodeOutput.size() != 1 || !SingleOutput)
982-
return createStringError("Cannot embed bitcode with multiple files.");
983-
OutputFiles.push_back(Args.MakeArgString(BitcodeOutput.front()));
984-
return Error::success();
985-
}
986-
987-
// Append the new inputs to the device linker input. If the user requested
988-
// an internalizing link we need to pass the bitcode to clang.
989-
for (StringRef File :
990-
Args.hasArg(OPT_clang_backend) || Args.hasArg(OPT_builtin_bitcode_EQ)
991-
? BitcodeOutput
992-
: Files)
993-
OutputFiles.push_back(File);
994-
995-
return Error::success();
996-
}
997-
998664
Expected<StringRef> writeOffloadFile(const OffloadFile &File) {
999665
const OffloadBinary &Binary = *File.getBinary();
1000666

@@ -1325,15 +991,8 @@ Expected<SmallVector<StringRef>> linkAndWrapDeviceFiles(
1325991
if (File.getBinary()->getOffloadKind() != OFK_None)
1326992
ActiveOffloadKinds.insert(File.getBinary()->getOffloadKind());
1327993

1328-
// First link and remove all the input files containing bitcode if
1329-
// the target linker does not support it natively.
994+
// Write any remaining device inputs to an output file.
1330995
SmallVector<StringRef> InputFiles;
1331-
if (!linkerSupportsLTO(LinkerArgs))
1332-
if (Error Err = linkBitcodeFiles(Input, InputFiles, LinkerArgs))
1333-
return Err;
1334-
1335-
// Write any remaining device inputs to an output file for the
1336-
// linker.
1337996
for (const OffloadFile &File : Input) {
1338997
auto FileNameOrErr = writeOffloadFile(File);
1339998
if (!FileNameOrErr)

0 commit comments

Comments
 (0)