diff --git a/clang/test/Driver/linker-wrapper-sycl.cpp b/clang/test/Driver/linker-wrapper-sycl.cpp index 18cf3c91ad5b7..163c1496cb882 100644 --- a/clang/test/Driver/linker-wrapper-sycl.cpp +++ b/clang/test/Driver/linker-wrapper-sycl.cpp @@ -23,19 +23,23 @@ // CHK-CMDS-NEXT: "{{.*}}llc" -filetype=obj -o [[LLCOUT:.*]] [[WRAPPEROUT]].bc // CHK-CMDS-NEXT: "{{.*}}/ld" -- HOST_LINKER_FLAGS -dynamic-linker HOST_DYN_LIB -o a.out [[LLCOUT]] HOST_LIB_PATH HOST_STAT_LIB {{.*}}.o -// Check sycl-module-split-mode command line option. +// Check sycl-module-split-mode and sycl-spec-const-handling-mode command line options // This option uses split library instead of sycl-post-link tool. -// RUN: clang-linker-wrapper -sycl-module-split-mode=auto -sycl-device-libraries=%t.devicelib.o -sycl-post-link-options="SYCL_POST_LINK_OPTIONS" -llvm-spirv-options="LLVM_SPIRV_OPTIONS" "--host-triple=x86_64-unknown-linux-gnu" "--triple=spir64" "--linker-path=/usr/bin/ld" "--" HOST_LINKER_FLAGS "-dynamic-linker" HOST_DYN_LIB "-o" "a.out" HOST_LIB_PATH HOST_STAT_LIB %t.o --dry-run 2>&1 | FileCheck -check-prefix=CHK-SPLIT-CMDS %s +// RUN: clang-linker-wrapper -sycl-module-split-mode=auto -sycl-spec-const-handling-mode=native -sycl-device-libraries=%t.devicelib.o -llvm-spirv-options="LLVM_SPIRV_OPTIONS" "--host-triple=x86_64-unknown-linux-gnu" "--triple=spir64" "--linker-path=/usr/bin/ld" "--" HOST_LINKER_FLAGS "-dynamic-linker" HOST_DYN_LIB "-o" "a.out" HOST_LIB_PATH HOST_STAT_LIB %t.o --dry-run 2>&1 | FileCheck -check-prefix=CHK-SPLIT-CMDS %s // CHK-SPLIT-CMDS: "{{.*}}spirv-to-ir-wrapper" {{.*}} -o [[FIRSTLLVMLINKIN:.*]].bc --llvm-spirv-opts --spirv-preserve-auxdata --spirv-target-env=SPV-IR --spirv-builtin-format=global // CHK-SPLIT-CMDS-NEXT: "{{.*}}llvm-link" [[FIRSTLLVMLINKIN]].bc -o [[FIRSTLLVMLINKOUT:.*]].bc --suppress-warnings // CHK-SPLIT-CMDS-NEXT: "{{.*}}llvm-link" -only-needed [[FIRSTLLVMLINKOUT]].bc {{.*}}.bc -o [[SECONDLLVMLINKOUT:.*]].bc --suppress-warnings -// CHK-SPLIT-CMDS-NEXT: sycl-module-split: input: [[SECONDLLVMLINKOUT]].bc, output: [[SYCLMODULESPLITOUT:.*]].bc +// CHK-SPLIT-CMDS-NEXT: sycl-module-split: input: [[SECONDLLVMLINKOUT]].bc, output: [[SYCLMODULESPLITOUT:.*]].bc, split_mode: auto, output_assembly: false, output_prefix: , spec_const_mode: native // CHK-SPLIT-CMDS-NEXT: "{{.*}}llvm-spirv"{{.*}} LLVM_SPIRV_OPTIONS -o [[SPIRVOUT:.*]].spv [[SYCLMODULESPLITOUT]].bc // LLVM-SPIRV is not called in dry-run // CHK-SPLIT-CMDS-NEXT: offload-wrapper: input: [[SPIRVOUT]].spv, output: [[WRAPPEROUT:.*]].bc // CHK-SPLIT-CMDS-NEXT: "{{.*}}llc" -filetype=obj -o [[LLCOUT:.*]] [[WRAPPEROUT]].bc // CHK-SPLIT-CMDS-NEXT: "{{.*}}/ld" -- HOST_LINKER_FLAGS -dynamic-linker HOST_DYN_LIB -o a.out [[LLCOUT]] HOST_LIB_PATH HOST_STAT_LIB {{.*}}.o +// Check incompatibility of -sycl-spec-const-handling-mode command line option with sycl-post-link tool path in clang-linker-wrapper. +// RUN: not clang-linker-wrapper -sycl-spec-const-handling-mode=native -sycl-device-libraries=%t.devicelib.o -llvm-spirv-options="LLVM_SPIRV_OPTIONS" "--host-triple=x86_64-unknown-linux-gnu" "--triple=spir64" "--linker-path=/usr/bin/ld" "--" HOST_LINKER_FLAGS "-dynamic-linker" HOST_DYN_LIB "-o" "a.out" HOST_LIB_PATH HOST_STAT_LIB %t.o --dry-run 2>&1 | FileCheck -check-prefix=CHK-SPEC-CONST-ERROR %s +// CHK-SPEC-CONST-ERROR: error: -sycl-spec-const-handling command line option should be used in conjunction with -sycl-module-split-mode command line option. + /// check for PIC for device wrap compilation when using -shared // RUN: clang-linker-wrapper -sycl-device-libraries=%t.devicelib.o -sycl-post-link-options="SYCL_POST_LINK_OPTIONS" -llvm-spirv-options="LLVM_SPIRV_OPTIONS" "--host-triple=x86_64-unknown-linux-gnu" "--triple=spir64" "--linker-path=/usr/bin/ld" -shared "--" HOST_LINKER_FLAGS "-dynamic-linker" HOST_DYN_LIB "-o" "a.out" HOST_LIB_PATH HOST_STAT_LIB %t.o --dry-run 2>&1 | FileCheck -check-prefix=CHK-SHARED %s // CHK-SHARED: "{{.*}}llc"{{.*}} -relocation-model=pic diff --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp index edb97cf431d1e..bcba0d25abdb5 100644 --- a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp +++ b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp @@ -754,26 +754,18 @@ runSYCLPostLinkTool(ArrayRef InputFiles, const ArgList &Args) { static Expected> runSYCLSplitLibrary(ArrayRef InputFiles, const ArgList &Args, module_split::IRSplitMode Mode) { - std::vector SplitModules; - if (DryRun) { - auto OutputFileOrErr = createOutputFile( - sys::path::filename(ExecutableName) + ".sycl.split.image", "bc"); - if (!OutputFileOrErr) - return OutputFileOrErr.takeError(); - - StringRef OutputFilePath = *OutputFileOrErr; - auto InputFilesStr = llvm::join(InputFiles.begin(), InputFiles.end(), ","); - errs() << formatv("sycl-module-split: input: {0}, output: {1}\n", - InputFilesStr, OutputFilePath); - SplitModules.emplace_back(OutputFilePath, util::PropertySetRegistry(), ""); - return SplitModules; - } - llvm::module_split::ModuleSplitterSettings Settings; Settings.Mode = Mode; Settings.OutputPrefix = ""; + if (Args.hasArg(OPT_sycl_spec_const_handling_mode_EQ)) + Settings.SpecConstantMode = convertStringToSpecConstMode( + Args.getLastArgValue(OPT_sycl_spec_const_handling_mode_EQ)); + std::vector SplitModules; for (StringRef InputFile : InputFiles) { + if (DryRun) + break; + SMDiagnostic Err; LLVMContext C; std::unique_ptr M = parseIRFile(InputFile, Err, C); @@ -790,7 +782,17 @@ runSYCLSplitLibrary(ArrayRef InputFiles, const ArgList &Args, NewSplitModules.end()); } - if (Verbose) { + if (Verbose || DryRun) { + if (DryRun) { + auto OutputFileOrErr = createOutputFile( + sys::path::filename(ExecutableName) + ".sycl.split.image", "bc"); + if (!OutputFileOrErr) + return OutputFileOrErr.takeError(); + + SplitModules.emplace_back(*OutputFileOrErr, util::PropertySetRegistry(), + ""); + } + auto InputFilesStr = llvm::join(InputFiles.begin(), InputFiles.end(), ","); std::string SplitOutputFilesStr; for (size_t I = 0, E = SplitModules.size(); I != E; ++I) { @@ -800,8 +802,9 @@ runSYCLSplitLibrary(ArrayRef InputFiles, const ArgList &Args, SplitOutputFilesStr += SplitModules[I].ModuleFilePath; } - errs() << formatv("sycl-module-split: input: {0}, output: {1}\n", - InputFilesStr, SplitOutputFilesStr); + errs() << formatv("sycl-module-split: input: {0}, output: {1}, {2}\n", + InputFilesStr, SplitOutputFilesStr, + convertSplitterSettingsToString(Settings)); } return SplitModules; @@ -2771,6 +2774,12 @@ int main(int Argc, char **Argv) { SPIRVDumpDir = Dir; } + if (Args.hasArg(OPT_sycl_spec_const_handling_mode_EQ) && + !Args.hasArg(OPT_sycl_module_split_mode_EQ)) + reportError(createStringError( + "-sycl-spec-const-handling command line option should be used in " + "conjunction with -sycl-module-split-mode command line option.")); + { llvm::TimeTraceScope TimeScope("Execute linker wrapper"); diff --git a/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td b/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td index c9effdacdda31..1f9f705e26761 100644 --- a/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td +++ b/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td @@ -178,6 +178,13 @@ def sycl_module_split_mode_EQ : Flags<[WrapperOnlyOption]>, HelpText<"Option that turns on split library with the given split mode">; +def sycl_spec_const_handling_mode_EQ : Joined<["--", "-"], "sycl-spec-const-handling-mode=">, + Flags<[WrapperOnlyOption]>, + HelpText<"Specialization constant's handling mode. Modes: native, emulation, default_values. " + "Native mode lowers specialization constants to native SPIRV instructions. " + "Emulation mode removes specialization constants and replaces it with emulation. " + "default_values mode generates new device images which are copies of output devices but contain specialization constants replaced with default values from specialization ids.">; + // Special option to pass in llvm-spirv options def llvm_spirv_options_EQ : Joined<["--", "-"], "llvm-spirv-options=">, Flags<[WrapperOnlyOption]>, diff --git a/llvm/include/llvm/SYCLLowerIR/ModuleSplitter.h b/llvm/include/llvm/SYCLLowerIR/ModuleSplitter.h index 0da3706ad3626..b20f8f98a12e0 100644 --- a/llvm/include/llvm/SYCLLowerIR/ModuleSplitter.h +++ b/llvm/include/llvm/SYCLLowerIR/ModuleSplitter.h @@ -14,6 +14,7 @@ #define LLVM_SYCLLOWERIR_MODULE_SPLITTER_H #include "SYCLDeviceRequirements.h" +#include "SpecConstants.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/StringRef.h" @@ -50,6 +51,8 @@ enum IRSplitMode { // returned. std::optional convertStringToSplitMode(StringRef S); +StringRef convertSplitModeToString(IRSplitMode SM); + // A vector that contains all entry point functions in a split module. using EntryPointSet = SetVector; @@ -221,6 +224,8 @@ class ModuleDesc { return *Reqs; } + bool processSpecConstants(SpecConstantsPass::HandlingMode Mode); + #ifndef NDEBUG void verifyESIMDProperty() const; void dump() const; @@ -310,8 +315,12 @@ struct ModuleSplitterSettings { IRSplitMode Mode; bool OutputAssembly = false; // Bitcode or LLVM IR. StringRef OutputPrefix; + std::optional SpecConstantMode; }; +SmallString<64> +convertSplitterSettingsToString(const ModuleSplitterSettings &S); + /// Parses the output table file from sycl-post-link tool. Expected> parseSplitModulesFromFile(StringRef File); diff --git a/llvm/include/llvm/SYCLLowerIR/SpecConstants.h b/llvm/include/llvm/SYCLLowerIR/SpecConstants.h index 8bf8bdf894d07..ec0cabe7ed1b8 100644 --- a/llvm/include/llvm/SYCLLowerIR/SpecConstants.h +++ b/llvm/include/llvm/SYCLLowerIR/SpecConstants.h @@ -76,6 +76,11 @@ class SpecConstantsPass : public PassInfoMixin { HandlingMode Mode; }; +std::optional +convertStringToSpecConstMode(StringRef S); + +StringRef convertSpecConstModeToString(SpecConstantsPass::HandlingMode HM); + bool checkModuleContainsSpecConsts(const Module &M); } // namespace llvm diff --git a/llvm/lib/SYCLLowerIR/ModuleSplitter.cpp b/llvm/lib/SYCLLowerIR/ModuleSplitter.cpp index b9eda1376663f..0c4159c9571b2 100644 --- a/llvm/lib/SYCLLowerIR/ModuleSplitter.cpp +++ b/llvm/lib/SYCLLowerIR/ModuleSplitter.cpp @@ -29,6 +29,7 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/LineIterator.h" #include "llvm/Transforms/IPO.h" #include "llvm/Transforms/IPO/GlobalDCE.h" @@ -458,6 +459,20 @@ std::optional convertStringToSplitMode(StringRef S) { return It->second; } +StringRef convertSplitModeToString(IRSplitMode SM) { + static const DenseMap Values = { + {SPLIT_PER_KERNEL, "kernel"}, + {SPLIT_PER_TU, "source"}, + {SPLIT_AUTO, "auto"}, + {SPLIT_NONE, "none"}}; + + auto It = Values.find(SM); + if (It == Values.end()) + llvm_unreachable("SplitMode value is unhandled!"); + + return It->second; +} + bool isESIMDFunction(const Function &F) { return F.getMetadata(ESIMD_MARKER_MD) != nullptr; } @@ -467,6 +482,23 @@ cl::OptionCategory &getModuleSplitCategory() { return ModuleSplitCategory; } +bool ModuleDesc::processSpecConstants(SpecConstantsPass::HandlingMode Mode) { + Props.SpecConstsMet = false; + if (Mode == SpecConstantsPass::HandlingMode::default_values) + return false; + + Props.SpecConstsMet = false; + ModulePassManager MPM; + ModuleAnalysisManager MAM; + SpecConstantsPass SCP(Mode); + MAM.registerPass([] { return PassInstrumentationAnalysis(); }); + MPM.addPass(std::move(SCP)); + + PreservedAnalyses Res = MPM.run(*M, MAM); + Props.SpecConstsMet = !Res.areAllPreserved(); + return Props.SpecConstsMet; +} + Error ModuleSplitterBase::verifyNoCrossModuleDeviceGlobalUsage() { const Module &M = getInputModule(); // Early exit if there is only one group @@ -1355,7 +1387,7 @@ Expected> parseSplitModulesFromFile(StringRef File) { Expected> splitSYCLModule(std::unique_ptr M, ModuleSplitterSettings Settings) { - ModuleDesc MD = std::move(M); // makeModuleDesc() ? + ModuleDesc MD = std::move(M); // FIXME: false arguments are temporary for now. auto Splitter = getDeviceCodeSplitter(std::move(MD), Settings.Mode, /*IROutputOnly=*/false, @@ -1368,6 +1400,13 @@ splitSYCLModule(std::unique_ptr M, ModuleSplitterSettings Settings) { MD2.fixupLinkageOfDirectInvokeSimdTargets(); std::string OutIRFileName = (Settings.OutputPrefix + "_" + Twine(ID)).str(); + // TODO: Presented processSpecConstants functionality should be invoked + // later combined with SPIRV Backend conversion. This is happening here + // until SPIRV translator is being used. + auto SCMode = Settings.SpecConstantMode; + if (SCMode.has_value()) + MD2.processSpecConstants(SCMode.value()); + auto SplittedImageOrErr = saveModuleDesc(MD2, OutIRFileName, Settings.OutputAssembly); if (!SplittedImageOrErr) @@ -1413,5 +1452,17 @@ bool canBeImportedFunction(const Function &F) { return ReturnValue; } +SmallString<64> +convertSplitterSettingsToString(const ModuleSplitterSettings &S) { + StringRef SCMStr = S.SpecConstantMode + ? convertSpecConstModeToString(*S.SpecConstantMode) + : ""; + return formatv("split_mode: {0}, output_assembly: {1}, output_prefix: {2}, " + "spec_const_mode: {3}", + convertSplitModeToString(S.Mode), S.OutputAssembly, + S.OutputPrefix, SCMStr) + .sstr<64>(); +} + } // namespace module_split } // namespace llvm diff --git a/llvm/lib/SYCLLowerIR/SpecConstants.cpp b/llvm/lib/SYCLLowerIR/SpecConstants.cpp index 4f43a22e95fd9..8c3fabff546d3 100644 --- a/llvm/lib/SYCLLowerIR/SpecConstants.cpp +++ b/llvm/lib/SYCLLowerIR/SpecConstants.cpp @@ -9,8 +9,6 @@ //===----------------------------------------------------------------------===// #include "llvm/SYCLLowerIR/SpecConstants.h" -#include "llvm/SYCLLowerIR/Support.h" - #include "llvm/ADT/APInt.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" @@ -23,6 +21,7 @@ #include "llvm/IR/PatternMatch.h" #include "llvm/TargetParser/Triple.h" +#include #include #define DEBUG_TYPE "SpecConst" @@ -111,25 +110,25 @@ StringRef getStringLiteralArg(const CallInst *CI, unsigned ArgNo, V = X; return isa(V); }; - AssertRelease(ValueIsAlloca(TmpPtr), "unexpected instruction type"); + assert(ValueIsAlloca(TmpPtr) && "unexpected instruction type"); // find the store of the literal address into TmpPtr StoreInst *Store = nullptr; for (User *U : TmpPtr->users()) { if (StoreInst *St = dyn_cast(U)) { - AssertRelease(!Store, "single store expected"); + assert(!Store && "single store expected"); Store = St; #ifndef NDEBUG break; #endif // NDEBUG } } - AssertRelease(Store, "unexpected spec const IR pattern 0"); + assert(Store && "unexpected spec const IR pattern 0"); DelInsts.push_back(Store); #ifndef NDEBUG // verify there are no intervening stores/calls - AssertRelease(L->getParent() == Store->getParent(), "same BB expected"); + assert(L->getParent() == Store->getParent() && "same BB expected"); for (const Instruction *I = Store->getNextNode(); I; I = I->getNextNode()) { if (I == L) { @@ -137,12 +136,11 @@ StringRef getStringLiteralArg(const CallInst *CI, unsigned ArgNo, L = nullptr; // mark as met break; } - AssertRelease(!I->mayHaveSideEffects(), - "unexpected spec const IR pattern 1"); + assert(!I->mayHaveSideEffects() && "unexpected spec const IR pattern 1"); } - AssertRelease(!L, "load not met after the store"); + assert(!L && "load not met after the store"); #endif // NDEBUG - AssertRelease(Store, "store not met"); + assert(Store && "store not met"); V = Store->getValueOperand()->stripPointerCasts(); } const Constant *Init = cast(V)->getInitializer(); @@ -1089,6 +1087,34 @@ bool SpecConstantsPass::collectSpecConstantDefaultValuesMetadata( return true; } +std::optional +llvm::convertStringToSpecConstMode(StringRef S) { + static const StringMap Values = { + {"default_values", SpecConstantsPass::HandlingMode::default_values}, + {"emulation", SpecConstantsPass::HandlingMode::emulation}, + {"native", SpecConstantsPass::HandlingMode::native}}; + + auto It = Values.find(S); + if (It == Values.end()) + return std::nullopt; + + return It->second; +} + +StringRef +llvm::convertSpecConstModeToString(llvm::SpecConstantsPass::HandlingMode HM) { + static const DenseMap Values = { + {SpecConstantsPass::HandlingMode::default_values, "default_values"}, + {SpecConstantsPass::HandlingMode::emulation, "emulation"}, + {SpecConstantsPass::HandlingMode::native, "native"}}; + + auto It = Values.find(HM); + if (It == Values.end()) + llvm_unreachable("HandlingMode value is unhandled!"); + + return It->second; +} + bool llvm::checkModuleContainsSpecConsts(const Module &M) { for (const Function &F : M.functions()) { if (F.getName().starts_with(SYCL_GET_SCALAR_2020_SPEC_CONST_VAL) || diff --git a/llvm/tools/sycl-post-link/sycl-post-link.cpp b/llvm/tools/sycl-post-link/sycl-post-link.cpp index 734e71e91183a..c8d28837f1b95 100644 --- a/llvm/tools/sycl-post-link/sycl-post-link.cpp +++ b/llvm/tools/sycl-post-link/sycl-post-link.cpp @@ -463,27 +463,6 @@ module_split::ModuleDesc link(module_split::ModuleDesc &&MD1, return Res; } -bool processSpecConstants(module_split::ModuleDesc &MD) { - MD.Props.SpecConstsMet = false; - - if (SpecConstLower.getNumOccurrences() == 0) - return false; - - ModulePassManager RunSpecConst; - ModuleAnalysisManager MAM; - SpecConstantsPass SCP(SpecConstLower == SC_NATIVE_MODE - ? SpecConstantsPass::HandlingMode::native - : SpecConstantsPass::HandlingMode::emulation); - // Register required analysis - MAM.registerPass([&] { return PassInstrumentationAnalysis(); }); - RunSpecConst.addPass(std::move(SCP)); - - // Perform the spec constant intrinsics transformation on resulting module - PreservedAnalyses Res = RunSpecConst.run(MD.getModule(), MAM); - MD.Props.SpecConstsMet = !Res.areAllPreserved(); - return MD.Props.SpecConstsMet; -} - /// Function generates the copy of the given ModuleDesc where all uses of /// Specialization Constants are replaced by corresponding default values. /// If the Module in MD doesn't contain specialization constants then @@ -852,7 +831,10 @@ processInputModule(std::unique_ptr M) { MMsWithDefaultSpecConsts.push_back(std::move(*NewMD)); } - Modified |= processSpecConstants(MMs[I]); + Modified |= MMs[I].processSpecConstants( + SpecConstLower == SC_NATIVE_MODE + ? SpecConstantsPass::HandlingMode::native + : SpecConstantsPass::HandlingMode::emulation); } if (IROutputOnly) {