diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -251,7 +251,8 @@ uint64_t mipsGotSize; uint64_t zStackSize; unsigned ltoPartitions; - unsigned ltoo; + unsigned ltoOptLevel; + unsigned ltoSizeLevel; unsigned optimize; StringRef thinLTOJobs; unsigned timeTraceGranularity; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -833,6 +833,38 @@ return ret; } +static unsigned getOptLevel(opt::InputArgList &Args) { + if (opt::Arg *A = Args.getLastArg(OPT_lto_O)) { + StringRef S = A->getValue(); + if (S == "s" || S == "z" || S.empty()) + return 2; + if (S == "g") + return 1; + int V; + if (!to_integer(A->getValue(), V)) { + StringRef Spelling = Args.getArgString(A->getIndex()); + error(Spelling + ": number expected, but got '" + S + "'"); + return 0; + } + return V; + } + return 2; +} + +static unsigned getSizeLevel(opt::InputArgList &Args) { + if (opt::Arg *A = Args.getLastArg(OPT_lto_O)) { + switch (A->getValue()[0]) { + default: + return 0; + case 's': + return 1; + case 'z': + return 2; + } + } + return 0; +} + // Parse the symbol ordering file and warn for any duplicate entries. static std::vector getSymbolOrderingFile(MemoryBufferRef mb) { SetVector names; @@ -924,7 +956,8 @@ config->ltoNewPmPasses = args.getLastArgValue(OPT_lto_newpm_passes); config->ltoWholeProgramVisibility = args.hasArg(OPT_lto_whole_program_visibility); - config->ltoo = args::getInteger(args, OPT_lto_O, 2); + config->ltoOptLevel = getOptLevel(args); + config->ltoSizeLevel = getSizeLevel(args); config->ltoObjPath = args.getLastArgValue(OPT_lto_obj_path_eq); config->ltoPartitions = args::getInteger(args, OPT_lto_partitions, 1); config->ltoSampleProfile = args.getLastArgValue(OPT_lto_sample_profile); @@ -1066,8 +1099,10 @@ if (auto *arg = args.getLastArg(OPT_thinlto_jobs)) config->thinLTOJobs = arg->getValue(); - if (config->ltoo > 3) - error("invalid optimization level for LTO: " + Twine(config->ltoo)); + if (config->ltoOptLevel > 3) + error("invalid optimization level for LTO: " + Twine(config->ltoOptLevel)); + if (config->ltoSizeLevel > 2) + error("invalid size level for LTO: " + Twine(config->ltoSizeLevel)); if (config->ltoPartitions == 0) error("--lto-partitions: number of threads must be > 0"); if (!get_threadpool_strategy(config->thinLTOJobs)) diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp --- a/lld/ELF/LTO.cpp +++ b/lld/ELF/LTO.cpp @@ -114,10 +114,11 @@ c.CodeModel = getCodeModelFromCMModel(); c.DisableVerify = config->disableVerify; c.DiagHandler = diagnosticHandler; - c.OptLevel = config->ltoo; + c.OptLevel = config->ltoOptLevel; + c.SizeLevel = config->ltoSizeLevel; c.CPU = getCPUStr(); c.MAttrs = getMAttrs(); - c.CGOptLevel = args::getCGOptLevel(config->ltoo); + c.CGOptLevel = args::getCGOptLevel(config->ltoOptLevel); c.PTO.LoopVectorization = c.OptLevel > 1; c.PTO.SLPVectorization = c.OptLevel > 1; diff --git a/lld/test/ELF/lto/opt-level.ll b/lld/test/ELF/lto/opt-level.ll --- a/lld/test/ELF/lto/opt-level.ll +++ b/lld/test/ELF/lto/opt-level.ll @@ -8,8 +8,16 @@ ; RUN: llvm-nm %t2 | FileCheck --check-prefix=CHECK-O2 %s ; RUN: ld.lld -o %t2a -e main %t.o ; RUN: llvm-nm %t2a | FileCheck --check-prefix=CHECK-O2 %s -; RUN: ld.lld -o %t2 -e main %t.o --plugin-opt O2 +; RUN: ld.lld -o %t2 -e main %t.o --plugin-opt=O2 ; RUN: llvm-nm %t2 | FileCheck --check-prefix=CHECK-O2 %s +; RUN: ld.lld -o %t0 -e main --lto-Oz %t.o +; RUN: llvm-nm %t0 | FileCheck --check-prefix=CHECK-OZ %s +; RUN: ld.lld -o %tz -e main %t.o --plugin-opt=Oz +; RUN: llvm-nm %tz | FileCheck --check-prefix=CHECK-OZ %s +; RUN: ld.lld -o %t0 -e main --lto-Os %t.o +; RUN: llvm-nm %t0 | FileCheck --check-prefix=CHECK-OS %s +; RUN: ld.lld -o %tz -e main %t.o --plugin-opt=Os +; RUN: llvm-nm %tz | FileCheck --check-prefix=CHECK-OS %s ; Reject invalid optimization levels. ; RUN: not ld.lld -o /dev/null -e main --lto-O6 %t.o 2>&1 | \ @@ -33,6 +41,8 @@ ; CHECK-O0: foo ; CHECK-O2-NOT: foo +; CHECK-OZ-NOT: foo +; CHECK-OS-NOT: foo define internal void @foo() { ret void } diff --git a/llvm/include/llvm/LTO/Config.h b/llvm/include/llvm/LTO/Config.h --- a/llvm/include/llvm/LTO/Config.h +++ b/llvm/include/llvm/LTO/Config.h @@ -47,6 +47,7 @@ CodeGenOpt::Level CGOptLevel = CodeGenOpt::Default; CodeGenFileType CGFileType = CGFT_ObjectFile; unsigned OptLevel = 2; + unsigned SizeLevel = 0; bool DisableVerify = false; /// Use the new pass manager diff --git a/llvm/lib/LTO/LTOBackend.cpp b/llvm/lib/LTO/LTOBackend.cpp --- a/llvm/lib/LTO/LTOBackend.cpp +++ b/llvm/lib/LTO/LTOBackend.cpp @@ -180,8 +180,7 @@ } static void runNewPMPasses(const Config &Conf, Module &Mod, TargetMachine *TM, - unsigned OptLevel, bool IsThinLTO, - ModuleSummaryIndex *ExportSummary, + bool IsThinLTO, ModuleSummaryIndex *ExportSummary, const ModuleSummaryIndex *ImportSummary) { Optional PGOOpt; if (!Conf.SampleProfile.empty()) @@ -227,7 +226,7 @@ PassBuilder::OptimizationLevel OL; - switch (OptLevel) { + switch (Conf.OptLevel) { default: llvm_unreachable("Invalid optimization level"); case 0: @@ -238,6 +237,19 @@ break; case 2: OL = PassBuilder::OptimizationLevel::O2; + switch (Conf.SizeLevel) { + default: + llvm_unreachable("Invalid optimization level for size"); + case 0: + OL = PassBuilder::OptimizationLevel::O2; + break; + case 1: + OL = PassBuilder::OptimizationLevel::Os; + break; + case 2: + OL = PassBuilder::OptimizationLevel::Oz; + break; + } break; case 3: OL = PassBuilder::OptimizationLevel::O3; @@ -317,6 +329,7 @@ PMB.LoopVectorize = true; PMB.SLPVectorize = true; PMB.OptLevel = Conf.OptLevel; + PMB.SizeLevel = Conf.SizeLevel; PMB.PGOSampleUse = Conf.SampleProfile; PMB.EnablePGOCSInstrGen = Conf.RunCSIRInstr; if (!Conf.RunCSIRInstr && !Conf.CSIRProfile.empty()) { @@ -338,8 +351,7 @@ runNewPMCustomPasses(Conf, Mod, TM, Conf.OptPipeline, Conf.AAPipeline, Conf.DisableVerify); else if (Conf.UseNewPM) - runNewPMPasses(Conf, Mod, TM, Conf.OptLevel, IsThinLTO, ExportSummary, - ImportSummary); + runNewPMPasses(Conf, Mod, TM, IsThinLTO, ExportSummary, ImportSummary); else runOldPMPasses(Conf, Mod, TM, IsThinLTO, ExportSummary, ImportSummary); return !Conf.PostOptModuleHook || Conf.PostOptModuleHook(Task, Mod);