|
|
@@ -8,6 +8,7 @@
|
|
|
#include <map>
|
|
|
#include <set>
|
|
|
#include <sstream>
|
|
|
+#include <unordered_set>
|
|
|
#include <utility>
|
|
|
|
|
|
#include <cm/memory>
|
|
|
@@ -25,6 +26,7 @@
|
|
|
#include "cmLocalGenerator.h"
|
|
|
#include "cmLocalNinjaGenerator.h"
|
|
|
#include "cmMakefile.h"
|
|
|
+#include "cmMessageType.h"
|
|
|
#include "cmNinjaLinkLineDeviceComputer.h"
|
|
|
#include "cmNinjaTypes.h"
|
|
|
#include "cmOSXBundleGenerator.h"
|
|
|
@@ -178,6 +180,33 @@ std::string cmNinjaNormalTargetGenerator::LanguageLinkerDeviceRule(
|
|
|
"_", config);
|
|
|
}
|
|
|
|
|
|
+std::string cmNinjaNormalTargetGenerator::LanguageLinkerCudaDeviceRule(
|
|
|
+ const std::string& config) const
|
|
|
+{
|
|
|
+ return cmStrCat(
|
|
|
+ this->TargetLinkLanguage(config), "_DEVICE_LINK__",
|
|
|
+ cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()),
|
|
|
+ '_', config);
|
|
|
+}
|
|
|
+
|
|
|
+std::string cmNinjaNormalTargetGenerator::LanguageLinkerCudaDeviceCompileRule(
|
|
|
+ const std::string& config) const
|
|
|
+{
|
|
|
+ return cmStrCat(
|
|
|
+ this->TargetLinkLanguage(config), "_DEVICE_LINK_COMPILE__",
|
|
|
+ cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()),
|
|
|
+ '_', config);
|
|
|
+}
|
|
|
+
|
|
|
+std::string cmNinjaNormalTargetGenerator::LanguageLinkerCudaFatbinaryRule(
|
|
|
+ const std::string& config) const
|
|
|
+{
|
|
|
+ return cmStrCat(
|
|
|
+ this->TargetLinkLanguage(config), "_FATBINARY__",
|
|
|
+ cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()),
|
|
|
+ '_', config);
|
|
|
+}
|
|
|
+
|
|
|
struct cmNinjaRemoveNoOpCommands
|
|
|
{
|
|
|
bool operator()(std::string const& cmd)
|
|
|
@@ -186,7 +215,7 @@ struct cmNinjaRemoveNoOpCommands
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-void cmNinjaNormalTargetGenerator::WriteDeviceLinkRule(
|
|
|
+void cmNinjaNormalTargetGenerator::WriteNvidiaDeviceLinkRule(
|
|
|
bool useResponseFile, const std::string& config)
|
|
|
{
|
|
|
cmNinjaRule rule(this->LanguageLinkerDeviceRule(config));
|
|
|
@@ -272,6 +301,55 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkRule(
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+void cmNinjaNormalTargetGenerator::WriteDeviceLinkRules(
|
|
|
+ const std::string& config)
|
|
|
+{
|
|
|
+ const cmMakefile* mf = this->GetMakefile();
|
|
|
+
|
|
|
+ cmNinjaRule rule(LanguageLinkerCudaDeviceRule(config));
|
|
|
+ rule.Command = this->GetLocalGenerator()->BuildCommandLine(
|
|
|
+ { cmStrCat(mf->GetRequiredDefinition("CMAKE_CUDA_DEVICE_LINKER"),
|
|
|
+ " -arch=$ARCH $REGISTER -o=$out $in") });
|
|
|
+ rule.Comment = "Rule for CUDA device linking.";
|
|
|
+ rule.Description = "Linking CUDA $out";
|
|
|
+ this->GetGlobalGenerator()->AddRule(rule);
|
|
|
+
|
|
|
+ cmRulePlaceholderExpander::RuleVariables vars;
|
|
|
+ vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str();
|
|
|
+ vars.CMTargetType =
|
|
|
+ cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()).c_str();
|
|
|
+
|
|
|
+ vars.Language = "CUDA";
|
|
|
+ vars.Object = "$out";
|
|
|
+ vars.Fatbinary = "$FATBIN";
|
|
|
+ vars.RegisterFile = "$REGISTER";
|
|
|
+
|
|
|
+ std::string flags = this->GetFlags("CUDA", config);
|
|
|
+ vars.Flags = flags.c_str();
|
|
|
+
|
|
|
+ std::string compileCmd = this->GetMakefile()->GetRequiredDefinition(
|
|
|
+ "CMAKE_CUDA_DEVICE_LINK_COMPILE");
|
|
|
+ std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
|
|
|
+ this->GetLocalGenerator()->CreateRulePlaceholderExpander());
|
|
|
+ rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
|
|
|
+ compileCmd, vars);
|
|
|
+
|
|
|
+ rule.Name = LanguageLinkerCudaDeviceCompileRule(config);
|
|
|
+ rule.Command = this->GetLocalGenerator()->BuildCommandLine({ compileCmd });
|
|
|
+ rule.Comment = "Rule for compiling CUDA device stubs.";
|
|
|
+ rule.Description = "Compiling CUDA device stub $out";
|
|
|
+ this->GetGlobalGenerator()->AddRule(rule);
|
|
|
+
|
|
|
+ rule.Name = LanguageLinkerCudaFatbinaryRule(config);
|
|
|
+ rule.Command = this->GetLocalGenerator()->BuildCommandLine(
|
|
|
+ { cmStrCat(mf->GetRequiredDefinition("CMAKE_CUDA_FATBINARY"),
|
|
|
+ " -64 -cmdline=--compile-only -compress-all -link "
|
|
|
+ "--embedded-fatbin=$out $PROFILES") });
|
|
|
+ rule.Comment = "Rule for CUDA fatbinaries.";
|
|
|
+ rule.Description = "Creating fatbinary $out";
|
|
|
+ this->GetGlobalGenerator()->AddRule(rule);
|
|
|
+}
|
|
|
+
|
|
|
void cmNinjaNormalTargetGenerator::WriteLinkRule(bool useResponseFile,
|
|
|
const std::string& config)
|
|
|
{
|
|
|
@@ -586,7 +664,6 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement(
|
|
|
|
|
|
// First and very important step is to make sure while inside this
|
|
|
// step our link language is set to CUDA
|
|
|
- std::string cudaLinkLanguage = "CUDA";
|
|
|
std::string const& objExt =
|
|
|
this->Makefile->GetSafeDefinition("CMAKE_CUDA_OUTPUT_EXTENSION");
|
|
|
|
|
|
@@ -598,6 +675,118 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement(
|
|
|
std::string targetOutputReal =
|
|
|
ConvertToNinjaPath(targetOutputDir + "cmake_device_link" + objExt);
|
|
|
|
|
|
+ if (firstForConfig) {
|
|
|
+ globalGen->GetByproductsForCleanTarget(config).push_back(targetOutputReal);
|
|
|
+ }
|
|
|
+ this->DeviceLinkObject = targetOutputReal;
|
|
|
+
|
|
|
+ // Write comments.
|
|
|
+ cmGlobalNinjaGenerator::WriteDivider(this->GetCommonFileStream());
|
|
|
+ this->GetCommonFileStream()
|
|
|
+ << "# Device Link build statements for "
|
|
|
+ << cmState::GetTargetTypeName(genTarget->GetType()) << " target "
|
|
|
+ << this->GetTargetName() << "\n\n";
|
|
|
+
|
|
|
+ if (this->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_ID") == "Clang") {
|
|
|
+ std::string architecturesStr =
|
|
|
+ this->GeneratorTarget->GetSafeProperty("CUDA_ARCHITECTURES");
|
|
|
+
|
|
|
+ if (cmIsOff(architecturesStr)) {
|
|
|
+ this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
|
|
|
+ "CUDA_SEPARABLE_COMPILATION on Clang "
|
|
|
+ "requires CUDA_ARCHITECTURES to be set.");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this->WriteDeviceLinkRules(config);
|
|
|
+ this->WriteDeviceLinkStatements(config, cmExpandedList(architecturesStr),
|
|
|
+ targetOutputReal);
|
|
|
+ } else {
|
|
|
+ this->WriteNvidiaDeviceLinkStatement(config, fileConfig, targetOutputDir,
|
|
|
+ targetOutputReal);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatements(
|
|
|
+ const std::string& config, const std::vector<std::string>& architectures,
|
|
|
+ const std::string& output)
|
|
|
+{
|
|
|
+ // Ensure there are no duplicates.
|
|
|
+ const cmNinjaDeps explicitDeps = [&]() -> std::vector<std::string> {
|
|
|
+ std::unordered_set<std::string> depsSet;
|
|
|
+ const cmNinjaDeps linkDeps =
|
|
|
+ this->ComputeLinkDeps(this->TargetLinkLanguage(config), config, true);
|
|
|
+ const cmNinjaDeps objects = this->GetObjects(config);
|
|
|
+ depsSet.insert(linkDeps.begin(), linkDeps.end());
|
|
|
+ depsSet.insert(objects.begin(), objects.end());
|
|
|
+
|
|
|
+ std::vector<std::string> deps;
|
|
|
+ std::copy(depsSet.begin(), depsSet.end(), std::back_inserter(deps));
|
|
|
+ return deps;
|
|
|
+ }();
|
|
|
+
|
|
|
+ const std::string objectDir =
|
|
|
+ cmStrCat(this->GeneratorTarget->GetSupportDirectory(),
|
|
|
+ this->GetGlobalGenerator()->ConfigDirectory(config));
|
|
|
+ const std::string ninjaOutputDir = this->ConvertToNinjaPath(objectDir);
|
|
|
+
|
|
|
+ cmNinjaBuild fatbinary(LanguageLinkerCudaFatbinaryRule(config));
|
|
|
+
|
|
|
+ // Link device code for each architecture.
|
|
|
+ for (const std::string& architectureKind : architectures) {
|
|
|
+ // Clang always generates real code, so strip the specifier.
|
|
|
+ const std::string architecture =
|
|
|
+ architectureKind.substr(0, architectureKind.find('-'));
|
|
|
+ const std::string cubin =
|
|
|
+ cmStrCat(ninjaOutputDir, "/sm_", architecture, ".cubin");
|
|
|
+
|
|
|
+ fatbinary.Variables["PROFILES"] +=
|
|
|
+ cmStrCat(" -im=profile=sm_", architecture, ",file=", cubin);
|
|
|
+ fatbinary.ExplicitDeps.emplace_back(cubin);
|
|
|
+
|
|
|
+ cmNinjaBuild dlink(LanguageLinkerCudaDeviceRule(config));
|
|
|
+ dlink.ExplicitDeps = explicitDeps;
|
|
|
+ dlink.Outputs = { cubin };
|
|
|
+ dlink.Variables["ARCH"] = cmStrCat("sm_", architecture);
|
|
|
+
|
|
|
+ // The generated register file contains macros that when expanded register
|
|
|
+ // the device routines. Because the routines are the same for all
|
|
|
+ // architectures the register file will be the same too. Thus generate it
|
|
|
+ // only on the first invocation to reduce overhead.
|
|
|
+ if (fatbinary.ExplicitDeps.size() == 1) {
|
|
|
+ dlink.Variables["REGISTER"] = cmStrCat(
|
|
|
+ "--register-link-binaries=", ninjaOutputDir, "/cmake_cuda_register.h");
|
|
|
+ }
|
|
|
+
|
|
|
+ this->GetGlobalGenerator()->WriteBuild(this->GetCommonFileStream(), dlink);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Combine all architectures into a single fatbinary.
|
|
|
+ fatbinary.Outputs = { cmStrCat(ninjaOutputDir, "/cmake_cuda_fatbin.h") };
|
|
|
+ this->GetGlobalGenerator()->WriteBuild(this->GetCommonFileStream(),
|
|
|
+ fatbinary);
|
|
|
+
|
|
|
+ // Compile the stub that registers the kernels and contains the fatbinaries.
|
|
|
+ cmNinjaBuild dcompile(LanguageLinkerCudaDeviceCompileRule(config));
|
|
|
+ dcompile.Outputs = { output };
|
|
|
+ dcompile.ExplicitDeps = { cmStrCat(ninjaOutputDir, "/cmake_cuda_fatbin.h") };
|
|
|
+ dcompile.Variables["FATBIN"] =
|
|
|
+ this->GetLocalGenerator()->ConvertToOutputFormat(
|
|
|
+ cmStrCat(objectDir, "/cmake_cuda_fatbin.h"), cmOutputConverter::SHELL);
|
|
|
+ dcompile.Variables["REGISTER"] =
|
|
|
+ this->GetLocalGenerator()->ConvertToOutputFormat(
|
|
|
+ cmStrCat(objectDir, "/cmake_cuda_register.h"), cmOutputConverter::SHELL);
|
|
|
+ this->GetGlobalGenerator()->WriteBuild(this->GetCommonFileStream(),
|
|
|
+ dcompile);
|
|
|
+}
|
|
|
+
|
|
|
+void cmNinjaNormalTargetGenerator::WriteNvidiaDeviceLinkStatement(
|
|
|
+ const std::string& config, const std::string& fileConfig,
|
|
|
+ const std::string& outputDir, const std::string& output)
|
|
|
+{
|
|
|
+ cmGeneratorTarget* genTarget = this->GetGeneratorTarget();
|
|
|
+ cmGlobalNinjaGenerator* globalGen = this->GetGlobalGenerator();
|
|
|
+
|
|
|
std::string targetOutputImplib = ConvertToNinjaPath(
|
|
|
genTarget->GetFullPath(config, cmStateEnums::ImportLibraryArtifact));
|
|
|
|
|
|
@@ -606,8 +795,8 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement(
|
|
|
cmStrCat(this->GetLocalGenerator()->GetTargetDirectory(genTarget),
|
|
|
globalGen->ConfigDirectory(fileConfig), "/");
|
|
|
targetOutputFileConfigDir =
|
|
|
- globalGen->ExpandCFGIntDir(targetOutputDir, fileConfig);
|
|
|
- if (targetOutputDir == targetOutputFileConfigDir) {
|
|
|
+ globalGen->ExpandCFGIntDir(outputDir, fileConfig);
|
|
|
+ if (outputDir == targetOutputFileConfigDir) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
@@ -623,27 +812,15 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement(
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (firstForConfig) {
|
|
|
- globalGen->GetByproductsForCleanTarget(config).push_back(targetOutputReal);
|
|
|
- }
|
|
|
- this->DeviceLinkObject = targetOutputReal;
|
|
|
-
|
|
|
- // Write comments.
|
|
|
- cmGlobalNinjaGenerator::WriteDivider(this->GetCommonFileStream());
|
|
|
- const cmStateEnums::TargetType targetType = genTarget->GetType();
|
|
|
- this->GetCommonFileStream() << "# Device Link build statements for "
|
|
|
- << cmState::GetTargetTypeName(targetType)
|
|
|
- << " target " << this->GetTargetName() << "\n\n";
|
|
|
-
|
|
|
// Compute the comment.
|
|
|
cmNinjaBuild build(this->LanguageLinkerDeviceRule(config));
|
|
|
build.Comment =
|
|
|
- cmStrCat("Link the ", this->GetVisibleTypeName(), ' ', targetOutputReal);
|
|
|
+ cmStrCat("Link the ", this->GetVisibleTypeName(), ' ', output);
|
|
|
|
|
|
cmNinjaVars& vars = build.Variables;
|
|
|
|
|
|
// Compute outputs.
|
|
|
- build.Outputs.push_back(targetOutputReal);
|
|
|
+ build.Outputs.push_back(output);
|
|
|
// Compute specific libraries to link with.
|
|
|
build.ExplicitDeps = this->GetObjects(config);
|
|
|
build.ImplicitDeps =
|
|
|
@@ -659,7 +836,7 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement(
|
|
|
cmLocalNinjaGenerator& localGen = *this->GetLocalGenerator();
|
|
|
|
|
|
vars["TARGET_FILE"] =
|
|
|
- localGen.ConvertToOutputFormat(targetOutputReal, cmOutputConverter::SHELL);
|
|
|
+ localGen.ConvertToOutputFormat(output, cmOutputConverter::SHELL);
|
|
|
|
|
|
std::unique_ptr<cmLinkLineComputer> linkLineComputer(
|
|
|
new cmNinjaLinkLineDeviceComputer(
|
|
|
@@ -683,8 +860,7 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement(
|
|
|
|
|
|
// Compute language specific link flags.
|
|
|
std::string langFlags;
|
|
|
- localGen.AddLanguageFlagsForLinking(langFlags, genTarget, cudaLinkLanguage,
|
|
|
- config);
|
|
|
+ localGen.AddLanguageFlagsForLinking(langFlags, genTarget, "CUDA", config);
|
|
|
vars["LANGUAGE_COMPILE_FLAGS"] = langFlags;
|
|
|
|
|
|
auto const tgtNames = this->TargetNames(config);
|
|
|
@@ -692,7 +868,7 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement(
|
|
|
vars["SONAME_FLAG"] =
|
|
|
this->GetMakefile()->GetSONameFlag(this->TargetLinkLanguage(config));
|
|
|
vars["SONAME"] = tgtNames.SharedObject;
|
|
|
- if (targetType == cmStateEnums::SHARED_LIBRARY) {
|
|
|
+ if (genTarget->GetType() == cmStateEnums::SHARED_LIBRARY) {
|
|
|
std::string install_dir =
|
|
|
this->GetGeneratorTarget()->GetInstallNameDirForBuildTree(config);
|
|
|
if (!install_dir.empty()) {
|
|
|
@@ -731,7 +907,7 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement(
|
|
|
// do not check if the user has explicitly forced a response file.
|
|
|
int const commandLineLengthLimit =
|
|
|
static_cast<int>(cmSystemTools::CalculateCommandLineLengthLimit()) -
|
|
|
- globalGen->GetRuleCmdLength(this->LanguageLinkerDeviceRule(config));
|
|
|
+ globalGen->GetRuleCmdLength(build.Rule);
|
|
|
|
|
|
build.RspFile = this->ConvertToNinjaPath(
|
|
|
cmStrCat("CMakeFiles/", genTarget->GetName(),
|
|
|
@@ -746,7 +922,7 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement(
|
|
|
bool usedResponseFile = false;
|
|
|
globalGen->WriteBuild(this->GetCommonFileStream(), build,
|
|
|
commandLineLengthLimit, &usedResponseFile);
|
|
|
- this->WriteDeviceLinkRule(usedResponseFile, config);
|
|
|
+ this->WriteNvidiaDeviceLinkRule(usedResponseFile, config);
|
|
|
}
|
|
|
|
|
|
void cmNinjaNormalTargetGenerator::WriteLinkStatement(
|