Index: include/clang/Driver/Action.h =================================================================== --- include/clang/Driver/Action.h +++ include/clang/Driver/Action.h @@ -41,6 +41,8 @@ enum ActionClass { InputClass = 0, BindArchClass, + CudaDeviceClass, + CudaHostClass, PreprocessJobClass, PrecompileJobClass, AnalyzeJobClass, @@ -133,6 +135,39 @@ } }; +class CudaDeviceAction : public Action { + virtual void anchor(); + /// GPU architecture to bind -- e.g sm_35 + const char *GpuArchName; + bool AtTopLevel; + +public: + CudaDeviceAction(std::unique_ptr Input, const char *ArchName, + bool AtTopLevel); + + const char *getGpuArchName() const { return GpuArchName; } + bool isAtTopLevel() const { return AtTopLevel; } + + static bool classof(const Action *A) { + return A->getKind() == CudaDeviceClass; + } +}; + +class CudaHostAction : public Action { + virtual void anchor(); + ActionList DeviceActions; + +public: + CudaHostAction(std::unique_ptr Input, + const ActionList &DeviceActions); + ~CudaHostAction() override; + + ActionList &getDeviceActions() { return DeviceActions; } + const ActionList &getDeviceActions() const { return DeviceActions; } + + static bool classof(const Action *A) { return A->getKind() == CudaHostClass; } +}; + class JobAction : public Action { virtual void anchor(); protected: Index: include/clang/Driver/Driver.h =================================================================== --- include/clang/Driver/Driver.h +++ include/clang/Driver/Driver.h @@ -409,6 +409,9 @@ /// /// Will cache ToolChains for the life of the driver object, and create them /// on-demand. + const ToolChain &getTargetToolChain(const llvm::opt::ArgList &Args, + llvm::Triple &Target) const; + const ToolChain &getToolChain(const llvm::opt::ArgList &Args, StringRef DarwinArchName = "") const; Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -351,6 +351,12 @@ MetaVarName<"">; def c : Flag<["-"], "c">, Flags<[DriverOption]>, HelpText<"Only run preprocess, compile, and assemble steps">; +def cuda_device_only : Flag<["--"], "cuda-device-only">, + HelpText<"Do device-side CUDA compilation only">; +def cuda_gpu_arch_EQ : Joined<["--"], "cuda-gpu-arch=">, + Flags<[DriverOption, HelpHidden]>, HelpText<"CUDA GPU architecture">; +def cuda_host_only : Flag<["--"], "cuda-host-only">, + HelpText<"Do host-side CUDA compilation only">; def dA : Flag<["-"], "dA">, Group; def dD : Flag<["-"], "dD">, Group, Flags<[CC1Option]>, HelpText<"Print macro definitions in -E mode in addition to normal output">; Index: include/clang/Driver/Types.h =================================================================== --- include/clang/Driver/Types.h +++ include/clang/Driver/Types.h @@ -63,6 +63,9 @@ /// isCXX - Is this a "C++" input (C++ and Obj-C++ sources and headers). bool isCXX(ID Id); + /// isCuda - Is this a CUDA input. + bool isCuda(ID Id); + /// isObjC - Is this an "ObjC" input (Obj-C and Obj-C++ sources and headers). bool isObjC(ID Id); Index: include/clang/Driver/Types.def =================================================================== --- include/clang/Driver/Types.def +++ include/clang/Driver/Types.def @@ -44,6 +44,7 @@ TYPE("cl", CL, PP_C, "cl", "u") TYPE("cuda-cpp-output", PP_CUDA, INVALID, "cui", "u") TYPE("cuda", CUDA, PP_CUDA, "cu", "u") +TYPE("cuda", CUDA_DEVICE, PP_CUDA, "cu", "") TYPE("objective-c-cpp-output", PP_ObjC, INVALID, "mi", "u") TYPE("objc-cpp-output", PP_ObjC_Alias, INVALID, "mi", "u") TYPE("objective-c", ObjC, PP_ObjC, "m", "u") Index: lib/Driver/Action.cpp =================================================================== --- lib/Driver/Action.cpp +++ lib/Driver/Action.cpp @@ -24,6 +24,8 @@ switch (AC) { case InputClass: return "input"; case BindArchClass: return "bind-arch"; + case CudaDeviceClass: return "cuda-device"; + case CudaHostClass: return "cuda-host"; case PreprocessJobClass: return "preprocessor"; case PrecompileJobClass: return "precompiler"; case AnalyzeJobClass: return "analyzer"; @@ -53,6 +55,25 @@ const char *_ArchName) : Action(BindArchClass, std::move(Input)), ArchName(_ArchName) {} +void CudaDeviceAction::anchor() {} + +CudaDeviceAction::CudaDeviceAction(std::unique_ptr Input, + const char *ArchName, bool AtTopLevel) + : Action(CudaDeviceClass, std::move(Input)), GpuArchName(ArchName), + AtTopLevel(AtTopLevel) {} + +void CudaHostAction::anchor() {} + +CudaHostAction::CudaHostAction(std::unique_ptr Input, + const ActionList &_DeviceActions) + : Action(CudaHostClass, std::move(Input)), DeviceActions(_DeviceActions) {} + +CudaHostAction::~CudaHostAction() { + for (iterator it = DeviceActions.begin(), ie = DeviceActions.end(); it != ie; + ++it) + delete *it; +} + void JobAction::anchor() {} JobAction::JobAction(ActionClass Kind, std::unique_ptr Input, Index: lib/Driver/Driver.cpp =================================================================== --- lib/Driver/Driver.cpp +++ lib/Driver/Driver.cpp @@ -180,10 +180,11 @@ } else if ((PhaseArg = DAL.getLastArg(options::OPT_S))) { FinalPhase = phases::Backend; - // -c only runs up to the assembler. - } else if ((PhaseArg = DAL.getLastArg(options::OPT_c))) { + // -c and partial CUDA compilations only run up to the assembler. + } else if ((PhaseArg = DAL.getLastArg(options::OPT_c)) || + (PhaseArg = DAL.getLastArg(options::OPT_cuda_device_only)) || + (PhaseArg = DAL.getLastArg(options::OPT_cuda_host_only))) { FinalPhase = phases::Assemble; - // Otherwise do everything. } else FinalPhase = phases::Link; @@ -819,7 +820,25 @@ } static unsigned PrintActions1(const Compilation &C, Action *A, - std::map &Ids) { + std::map &Ids); + +static std::string PrintActionList(const Compilation &C, ActionList &AL, + std::map &Ids) { + std::string str; + llvm::raw_string_ostream os(str); + os << "{"; + for (Action::iterator it = AL.begin(), ie = AL.end(); it != ie;) { + os << PrintActions1(C, *it, Ids); + ++it; + if (it != ie) + os << ", "; + } + os << "}"; + return str; +} + +static unsigned PrintActions1(const Compilation &C, Action *A, + std::map &Ids) { if (Ids.count(A)) return Ids[A]; @@ -832,15 +851,14 @@ } else if (BindArchAction *BIA = dyn_cast(A)) { os << '"' << BIA->getArchName() << '"' << ", {" << PrintActions1(C, *BIA->begin(), Ids) << "}"; + } else if (CudaDeviceAction *CDA = dyn_cast(A)) { + os << '"' << CDA->getGpuArchName() << '"' << ", {" + << PrintActions1(C, *CDA->begin(), Ids) << "}"; + } else if (CudaHostAction *CHA = dyn_cast(A)) { + os << "{" << PrintActions1(C, *CHA->begin(), Ids) << "}" + << ", gpu binaries " << PrintActionList(C, CHA->getDeviceActions(), Ids); } else { - os << "{"; - for (Action::iterator it = A->begin(), ie = A->end(); it != ie;) { - os << PrintActions1(C, *it, Ids); - ++it; - if (it != ie) - os << ", "; - } - os << "}"; + os << PrintActionList(C, A->getInputs(), Ids); } unsigned Id = Ids.size(); @@ -1150,6 +1168,77 @@ } } +// For eash unique --cuda-gpu-arch= argument creates a TY_CUDA_DEVICE input +// action and then wraps each in CudaDeviceAction paired with appropriate GPU +// arch name. If we're only building device-side code, each action remains +// independent. Otherwise we pass device-side actions as inputs to a new +// CudaHostAction which combines both host and device side actions. +static std::unique_ptr +BuildCudaActions(const Driver &D, const ToolChain &TC, DerivedArgList &Args, + const Arg *InputArg, const types::ID InputType, + std::unique_ptr Current, ActionList &Actions) { + + assert(InputType == types::TY_CUDA && + "CUDA Actions only apply to CUDA inputs."); + + SmallVector GpuArchList; + llvm::StringSet<> GpuArchNames; + for (Arg *A : Args) { + if (A->getOption().matches(options::OPT_cuda_gpu_arch_EQ)) { + A->claim(); + if (GpuArchNames.insert(A->getValue()).second) + GpuArchList.push_back(A->getValue()); + } + } + + // Default to sm_20 which is the lowest common denominator for supported GPUs. + // sm_20 code should work correctly, if suboptimally, on all newer GPUs. + if (GpuArchList.empty()) + GpuArchList.push_back("sm_20"); + + Driver::InputList CudaDeviceInputs; + for (unsigned i = 0, e = GpuArchList.size(); i != e; ++i) + CudaDeviceInputs.push_back(std::make_pair(types::TY_CUDA_DEVICE, InputArg)); + + ActionList CudaDeviceActions; + D.BuildActions(TC, Args, CudaDeviceInputs, CudaDeviceActions); + assert(GpuArchList.size() == CudaDeviceActions.size() && + "Failed to create actions for all devices"); + + bool PartialCompilation = false; + bool DeviceOnlyCompilation = Args.hasArg(options::OPT_cuda_device_only); + for (unsigned i = 0, e = GpuArchList.size(); i != e; ++i) { + if (CudaDeviceActions[i]->getKind() != Action::BackendJobClass) { + PartialCompilation = true; + break; + } + } + + if (PartialCompilation || DeviceOnlyCompilation) { + // If -o specified we can only work if it's device-only compilation for a + // single device. + if (Args.hasArg(options::OPT_o) && + (!DeviceOnlyCompilation || GpuArchList.size() > 1)) { + D.Diag(clang::diag::err_drv_output_argument_with_multiple_files); + return nullptr; + } + for (unsigned i = 0, e = GpuArchList.size(); i != e; ++i) + Actions.push_back(new CudaDeviceAction( + std::unique_ptr(CudaDeviceActions[i]), GpuArchList[i], true)); + if (DeviceOnlyCompilation) + Current.reset(nullptr); + return Current; + } else { + ActionList CudaDeviceJobActions; + for (unsigned i = 0, e = GpuArchList.size(); i != e; ++i) + CudaDeviceJobActions.push_back( + new CudaDeviceAction(std::unique_ptr(CudaDeviceActions[i]), + GpuArchList[i], false)); + return std::unique_ptr( + new CudaHostAction(std::move(Current), CudaDeviceJobActions)); + } +} + void Driver::BuildActions(const ToolChain &TC, DerivedArgList &Args, const InputList &Inputs, ActionList &Actions) const { llvm::PrettyStackTraceString CrashInfo("Building compilation actions"); @@ -1252,8 +1341,26 @@ // Build the pipeline for this file. std::unique_ptr Current(new InputAction(*InputArg, InputType)); - for (SmallVectorImpl::iterator - i = PL.begin(), e = PL.end(); i != e; ++i) { + phases::ID CudaInjectionPhase; + if (isSaveTempsEnabled()) { + // All phases are done independently, inject GPU blobs during compilation + // phase as that's where we generate glue code to init them. + CudaInjectionPhase = phases::Compile; + } else { + // Assumes that clang does everything up until linking phase, so we inject + // cuda device actions at the last step before linking. Otherwise CUDA + // host action forces preprocessor into a separate invocation. + if (FinalPhase == phases::Link) { + for (auto i = PL.begin(), e = PL.end(); i != e; ++i) { + auto next = i + 1; + if (next != e && *next == phases::Link) + CudaInjectionPhase = *i; + } + } else + CudaInjectionPhase = FinalPhase; + } + for (SmallVectorImpl::iterator i = PL.begin(), e = PL.end(); + i != e; ++i) { phases::ID Phase = *i; // We are done if this step is past what the user requested. @@ -1275,6 +1382,15 @@ // Otherwise construct the appropriate action. Current = ConstructPhaseAction(TC, Args, Phase, std::move(Current)); + + if (InputType == types::TY_CUDA && Phase == CudaInjectionPhase && + !Args.hasArg(options::OPT_cuda_host_only)) { + Current = BuildCudaActions(*this, TC, Args, InputArg, InputType, + std::move(Current), Actions); + if (!Current) + break; + } + if (Current->getType() == types::TY_Nothing) break; } @@ -1522,7 +1638,13 @@ if (isa(JA)) { // Check if the compiler supports emitting LLVM IR. assert(Inputs->size() == 1); - JobAction *CompileJA = cast(*Inputs->begin()); + JobAction *CompileJA; + // Extract real host action, if it's a CudaHostAction. + if (CudaHostAction *CudaHA = dyn_cast(*Inputs->begin())) + CompileJA = cast(*CudaHA->begin()); + else + CompileJA = cast(*Inputs->begin()); + const Tool *Compiler = TC->SelectTool(*CompileJA); if (!Compiler) return nullptr; @@ -1550,6 +1672,10 @@ return ToolForJob; } +static llvm::Triple computeTargetTriple(StringRef DefaultTargetTriple, + const ArgList &Args, + StringRef DarwinArchName); + void Driver::BuildJobsForAction(Compilation &C, const Action *A, const ToolChain *TC, @@ -1560,6 +1686,20 @@ InputInfo &Result) const { llvm::PrettyStackTraceString CrashInfo("Building compilation jobs"); + InputInfoList CudaDeviceInputInfos; + if (const CudaHostAction *CHA = dyn_cast(A)) { + InputInfo II; + // Append outputs of device jobs to the input list. + for (const Action *DA : CHA->getDeviceActions()) { + BuildJobsForAction(C, DA, TC, "", AtTopLevel, + /*MultipleArchs*/ false, LinkingOutput, II); + CudaDeviceInputInfos.push_back(II); + } + // Override current action with a real host compile action and continue + // processing it. + A = *CHA->begin(); + } + if (const InputAction *IA = dyn_cast(A)) { // FIXME: It would be nice to not claim this here; maybe the old scheme of // just using Args was better? @@ -1582,8 +1722,21 @@ else TC = &C.getDefaultToolChain(); - BuildJobsForAction(C, *BAA->begin(), TC, BAA->getArchName(), - AtTopLevel, MultipleArchs, LinkingOutput, Result); + BuildJobsForAction(C, *BAA->begin(), TC, ArchName, AtTopLevel, + MultipleArchs, LinkingOutput, Result); + return; + } + + if (const CudaDeviceAction *CDA = dyn_cast(A)) { + const ToolChain *TC; + const char *ArchName = CDA->getGpuArchName(); + llvm::Triple HostTriple = + computeTargetTriple(DefaultTargetTriple, C.getArgs(), ""); + llvm::Triple DeviceTriple(HostTriple.isArch64Bit() ? "nvptx64-nvidia-cuda" + : "nvptx-nvidia-cuda"); + TC = &getTargetToolChain(C.getArgs(), DeviceTriple); + BuildJobsForAction(C, *CDA->begin(), TC, ArchName, CDA->isAtTopLevel(), + /*MultipleArchs*/ true, LinkingOutput, Result); return; } @@ -1618,6 +1771,10 @@ if (JA->getType() == types::TY_dSYM) BaseInput = InputInfos[0].getFilename(); + // Append outputs of cuda device jobs to the input list + if (CudaDeviceInputInfos.size()) + InputInfos.append(CudaDeviceInputInfos.begin(), CudaDeviceInputInfos.end()); + // Determine the place to write output to, if any. if (JA->getType() == types::TY_Nothing) Result = InputInfo(A->getType(), BaseInput); @@ -2023,10 +2180,8 @@ return Target; } -const ToolChain &Driver::getToolChain(const ArgList &Args, - StringRef DarwinArchName) const { - llvm::Triple Target = computeTargetTriple(DefaultTargetTriple, Args, - DarwinArchName); +const ToolChain &Driver::getTargetToolChain(const ArgList &Args, + llvm::Triple &Target) const { ToolChain *&TC = ToolChains[Target.str()]; if (!TC) { @@ -2096,6 +2251,9 @@ break; } break; + case llvm::Triple::CUDA: + TC = new toolchains::Cuda(*this, Target, Args); + break; default: // TCE is an OSless target if (Target.getArchName() == "tce") { @@ -2126,6 +2284,13 @@ return *TC; } +const ToolChain &Driver::getToolChain(const ArgList &Args, + StringRef DarwinArchName) const { + llvm::Triple Target = + computeTargetTriple(DefaultTargetTriple, Args, DarwinArchName); + return getTargetToolChain(Args, Target); +} + bool Driver::ShouldUseClangCompiler(const JobAction &JA) const { // Check if user requested no clang, or clang doesn't understand this type (we // only handle single inputs for now). Index: lib/Driver/ToolChain.cpp =================================================================== --- lib/Driver/ToolChain.cpp +++ lib/Driver/ToolChain.cpp @@ -151,6 +151,8 @@ case Action::InputClass: case Action::BindArchClass: + case Action::CudaDeviceClass: + case Action::CudaHostClass: case Action::LipoJobClass: case Action::DsymutilJobClass: case Action::VerifyDebugInfoJobClass: Index: lib/Driver/ToolChains.h =================================================================== --- lib/Driver/ToolChains.h +++ lib/Driver/ToolChains.h @@ -689,6 +689,18 @@ std::string computeSysRoot() const; }; +class LLVM_LIBRARY_VISIBILITY Cuda : public Linux { +public: + Cuda(const Driver &D, const llvm::Triple &Triple, + const llvm::opt::ArgList &Args); + + llvm::opt::DerivedArgList * + TranslateArgs(const llvm::opt::DerivedArgList &Args, + const char *BoundArch) const override; + void addClangTargetOptions(const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args) const override; +}; + class LLVM_LIBRARY_VISIBILITY Hexagon_TC : public Linux { protected: GCCVersion GCCLibAndIncVersion; Index: lib/Driver/ToolChains.cpp =================================================================== --- lib/Driver/ToolChains.cpp +++ lib/Driver/ToolChains.cpp @@ -3613,6 +3613,62 @@ return new tools::dragonfly::Link(*this); } +/// Stub for CUDA toolchain. At the moment we don't have assembler or +/// linker and need toolchain mainly to propagate device-side options +/// to CC1. + +Cuda::Cuda(const Driver &D, const llvm::Triple &Triple, const ArgList &Args) + : Linux(D, Triple, Args) {} + +void Cuda::addClangTargetOptions(const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args) const { + Linux::addClangTargetOptions(DriverArgs, CC1Args); + CC1Args.push_back("-fcuda-is-device"); +} + +llvm::opt::DerivedArgList * +Cuda::TranslateArgs(const llvm::opt::DerivedArgList &Args, + const char *BoundArch) const { + DerivedArgList *DAL = new DerivedArgList(Args.getBaseArgs()); + const OptTable &Opts = getDriver().getOpts(); + + for (Arg *A : Args) { + if (A->getOption().matches(options::OPT_Xarch__)) { + // Skip this argument unless the architecture matches BoundArch + if (A->getValue(0) != StringRef(BoundArch)) + continue; + + unsigned Index = Args.getBaseArgs().MakeIndex(A->getValue(1)); + unsigned Prev = Index; + std::unique_ptr XarchArg(Opts.ParseOneArg(Args, Index)); + + // If the argument parsing failed or more than one argument was + // consumed, the -Xarch_ argument's parameter tried to consume + // extra arguments. Emit an error and ignore. + // + // We also want to disallow any options which would alter the + // driver behavior; that isn't going to work in our model. We + // use isDriverOption() as an approximation, although things + // like -O4 are going to slip through. + if (!XarchArg || Index > Prev + 1) { + getDriver().Diag(diag::err_drv_invalid_Xarch_argument_with_args) + << A->getAsString(Args); + continue; + } else if (XarchArg->getOption().hasFlag(options::DriverOption)) { + getDriver().Diag(diag::err_drv_invalid_Xarch_argument_isdriver) + << A->getAsString(Args); + continue; + } + XarchArg->setBaseArg(A); + A = XarchArg.release(); + DAL->AddSynthesizedArg(A); + } + DAL->append(A); + } + + DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_march_EQ), BoundArch); + return DAL; +} /// XCore tool chain XCore::XCore(const Driver &D, const llvm::Triple &Triple, Index: lib/Driver/Tools.cpp =================================================================== --- lib/Driver/Tools.cpp +++ lib/Driver/Tools.cpp @@ -1531,6 +1531,12 @@ return CPUName; } + case llvm::Triple::nvptx: + case llvm::Triple::nvptx64: + if (const Arg *A = Args.getLastArg(options::OPT_march_EQ)) + return A->getValue(); + return ""; + case llvm::Triple::ppc: case llvm::Triple::ppc64: case llvm::Triple::ppc64le: { @@ -2603,8 +2609,14 @@ getToolChain().getTriple().isWindowsCygwinEnvironment(); bool IsWindowsMSVC = getToolChain().getTriple().isWindowsMSVCEnvironment(); - assert(Inputs.size() == 1 && "Unable to handle multiple inputs."); + // Check number of inputs for sanity. We need at least one input. + assert(Inputs.size() >= 1 && "Must have at least one input."); const InputInfo &Input = Inputs[0]; + // CUDA compilation may have multiple inputs (source file + results of + // device-side compilations). All other jobs are expected to have exactly one + // input. + bool IsCuda = types::isCuda(Input.getType()); + assert((IsCuda || Inputs.size() == 1) && "Unable to handle multiple inputs."); // Invoke ourselves in -cc1 mode. // @@ -4703,6 +4715,16 @@ CmdArgs.push_back(SplitDwarfOut); } + // Host-side cuda compilation receives device-side outputs as Inputs[1...]. + // Include them with -fcuda-include-gpubinary. + if (IsCuda && Inputs.size() > 1) + for (InputInfoList::const_iterator it = std::next(Inputs.begin()), + ie = Inputs.end(); + it != ie; ++it) { + CmdArgs.push_back("-fcuda-include-gpubinary"); + CmdArgs.push_back(it->getFilename()); + } + // Finally add the compile command to the compilation. if (Args.hasArg(options::OPT__SLASH_fallback) && Output.getType() == types::TY_Object && Index: lib/Driver/Types.cpp =================================================================== --- lib/Driver/Types.cpp +++ lib/Driver/Types.cpp @@ -86,6 +86,7 @@ case TY_C: case TY_PP_C: case TY_CL: case TY_CUDA: case TY_PP_CUDA: + case TY_CUDA_DEVICE: case TY_ObjC: case TY_PP_ObjC: case TY_PP_ObjC_Alias: case TY_CXX: case TY_PP_CXX: case TY_ObjCXX: case TY_PP_ObjCXX: case TY_PP_ObjCXX_Alias: @@ -122,7 +123,19 @@ case TY_ObjCXX: case TY_PP_ObjCXX: case TY_PP_ObjCXX_Alias: case TY_CXXHeader: case TY_PP_CXXHeader: case TY_ObjCXXHeader: case TY_PP_ObjCXXHeader: - case TY_CUDA: case TY_PP_CUDA: + case TY_CUDA: case TY_PP_CUDA: case TY_CUDA_DEVICE: + return true; + } +} + +bool types::isCuda(ID Id) { + switch (Id) { + default: + return false; + + case TY_CUDA: + case TY_PP_CUDA: + case TY_CUDA_DEVICE: return true; } } @@ -206,10 +219,12 @@ P.push_back(phases::Compile); P.push_back(phases::Backend); } - P.push_back(phases::Assemble); + if (Id != TY_CUDA_DEVICE) + P.push_back(phases::Assemble); } } - if (!onlyPrecompileType(Id)) { + + if (!onlyPrecompileType(Id) && Id != TY_CUDA_DEVICE) { P.push_back(phases::Link); } assert(0 < P.size() && "Not enough phases in list"); Index: lib/Frontend/CreateInvocationFromCommandLine.cpp =================================================================== --- lib/Frontend/CreateInvocationFromCommandLine.cpp +++ lib/Frontend/CreateInvocationFromCommandLine.cpp @@ -15,6 +15,7 @@ #include "clang/Basic/DiagnosticOptions.h" #include "clang/Driver/Compilation.h" #include "clang/Driver/Driver.h" +#include "clang/Driver/Action.h" #include "clang/Driver/Options.h" #include "clang/Driver/Tool.h" #include "clang/Frontend/CompilerInstance.h" @@ -61,9 +62,21 @@ } // We expect to get back exactly one command job, if we didn't something - // failed. + // failed. CUDA compilation is an exception as it creates multiple jobs. If + // that's the case, we proceed with the first job. If caller needs particular + // CUDA job, it should be controlled via --cuda-{host|device}-only option + // passed to the driver. const driver::JobList &Jobs = C->getJobs(); - if (Jobs.size() != 1 || !isa(*Jobs.begin())) { + bool CudaCompilation = false; + if (Jobs.size() > 1) { + for (auto &A : C->getActions()) + if (isa(A)) { + CudaCompilation = true; + break; + } + } + if (Jobs.size() == 0 || !isa(*Jobs.begin()) || + (Jobs.size() > 1 && !CudaCompilation)) { SmallString<256> Msg; llvm::raw_svector_ostream OS(Msg); Jobs.Print(OS, "; ", true); Index: test/Driver/cuda-options.cu =================================================================== --- /dev/null +++ test/Driver/cuda-options.cu @@ -0,0 +1,108 @@ +// Tests CUDA compilation pipeline construction in Driver. + +// Simple compilation case: +// RUN: %clang -### -nocudainc -c %s 2>&1 \ +// Compile device-side to PTX assembly and make sure we use it on the host side. +// RUN: | FileCheck -check-prefix CUDA-D1 \ +// Then compile host side and incorporate device code. +// RUN: -check-prefix CUDA-H -check-prefix CUDA-H-I1 \ +// Make sure we don't link anything. +// RUN: -check-prefix CUDA-NL %s + +// Typical compilation + link case: +// RUN: %clang -### -nocudainc %s 2>&1 \ +// Compile device-side to PTX assembly and make sure we use it on the host side +// RUN: | FileCheck -check-prefix CUDA-D1 \ +// Then compile host side and incorporate device code. +// RUN: -check-prefix CUDA-H -check-prefix CUDA-H-I1 \ +// Then link things. +// RUN: -check-prefix CUDA-L %s + +// Verify that -cuda-no-device disables device-side compilation and linking +// RUN: %clang -### -nocudainc --cuda-host-only %s 2>&1 \ +// Make sure we didn't run device-side compilation. +// RUN: | FileCheck -check-prefix CUDA-ND \ +// Then compile host side and make sure we don't attempt to incorporate GPU code. +// RUN: -check-prefix CUDA-H -check-prefix CUDA-H-NI \ +// Make sure we don't link anything. +// RUN: -check-prefix CUDA-NL %s + +// Verify that -cuda-no-host disables host-side compilation and linking +// RUN: %clang -### -nocudainc --cuda-device-only %s 2>&1 \ +// Compile device-side to PTX assembly +// RUN: | FileCheck -check-prefix CUDA-D1 \ +// Make sure there are no host cmpilation or linking. +// RUN: -check-prefix CUDA-NH -check-prefix CUDA-NL %s + +// Verify that with -S we compile host and device sides to assembly +// and incorporate device code on the host side. +// RUN: %clang -### -nocudainc -S -c %s 2>&1 \ +// Compile device-side to PTX assembly +// RUN: | FileCheck -check-prefix CUDA-D1 \ +// Then compile host side and incorporate GPU code. +// RUN: -check-prefix CUDA-H -check-prefix CUDA-H-I1 \ +// Make sure we don't link anything. +// RUN: -check-prefix CUDA-NL %s + +// Verify that --cuda-gpu-arch option passes correct GPU +// archtecture info to device compilation. +// RUN: %clang -### -nocudainc --cuda-gpu-arch=sm_35 -c %s 2>&1 \ +// Compile device-side to PTX assembly. +// RUN: | FileCheck -check-prefix CUDA-D1 -check-prefix CUDA-D1-SM35 \ +// Then compile host side and incorporate GPU code. +// RUN: -check-prefix CUDA-H -check-prefix CUDA-H-I1 \ +// Make sure we don't link anything. +// RUN: -check-prefix CUDA-NL %s + +// Verify that there is device-side compilation per --cuda-gpu-arch args +// and that all results are included on the host side. +// RUN: %clang -### -nocudainc --cuda-gpu-arch=sm_35 --cuda-gpu-arch=sm_30 -c %s 2>&1 \ +// Compile both device-sides to PTX assembly +// RUN: | FileCheck \ +// RUN: -check-prefix CUDA-D1 -check-prefix CUDA-D1-SM35 \ +// RUN: -check-prefix CUDA-D2 -check-prefix CUDA-D2-SM30 \ +// Then compile host side and incorporate both device-side outputs +// RUN: -check-prefix CUDA-H -check-prefix CUDA-H-I1 -check-prefix CUDA-H-I2 \ +// Make sure we don't link anything. +// RUN: -check-prefix CUDA-NL %s + +// Match device-side compilation +// CUDA-D1: "-cc1" "-triple" "nvptx{{64?}}-nvidia-cuda" +// CUDA-D1-SAME: "-fcuda-is-device" +// CUDA-D1-SM35-SAME: "-target-cpu" "sm_35" +// CUDA-D1-SAME: "-o" "[[GPUBINARY1:[^"]*]]" +// CUDA-D1-SAME: "-x" "cuda" + +// Match anothe device-side compilation +// CUDA-D2: "-cc1" "-triple" "nvptx{{64?}}-nvidia-cuda" +// CUDA-D2-SAME: "-fcuda-is-device" +// CUDA-D2-SM30-SAME: "-target-cpu" "sm_30" +// CUDA-D2-SAME: "-o" "[[GPUBINARY2:[^"]*]]" +// CUDA-D2-SAME: "-x" "cuda" + +// Match no device-side compilation +// CUDA-ND-NOT: "-cc1" "-triple" "nvptx{{64?}}-nvidia-cuda" +// CUDA-ND-SAME-NOT: "-fcuda-is-device" + +// Match host-side compilation +// CUDA-H: "-cc1" "-triple" +// CUDA-H-SAME-NOT: "nvptx{{64?}}-nvidia-cuda" +// CUDA-H-SAME-NOT: "-fcuda-is-device" +// CUDA-H-SAME: "-o" "[[HOSTOBJ:[^"]*]]" +// CUDA-H-SAME: "-x" "cuda" +// CUDA-H-I1-SAME: "-fcuda-include-gpubinary" "[[GPUBINARY1]]" +// CUDA-H-I2-SAME: "-fcuda-include-gpubinary" "[[GPUBINARY2]]" + +// Match no GPU code inclusion. +// CUDA-H-NI-NOT: "-fcuda-include-gpubinary" + +// Match no CUDA compilation +// CUDA-NH-NOT: "-cc1" "-triple" +// CUDA-NH-SAME-NOT: "-x" "cuda" + +// Match linker +// CUDA-L: "{{.*}}ld{{(.exe)?}}" +// CUDA-L-SAME: "[[HOSTOBJ]]" + +// Match no linker +// CUDA-NL-NOT: "{{.*}}ld{{(.exe)?}}" Index: test/Index/attributes-cuda.cu =================================================================== --- test/Index/attributes-cuda.cu +++ test/Index/attributes-cuda.cu @@ -1,5 +1,5 @@ -// RUN: c-index-test -test-load-source all -x cuda %s | FileCheck %s - +// RUN: c-index-test -test-load-source all -x cuda -nocudainc --cuda-host-only %s | FileCheck %s +// RUN: c-index-test -test-load-source all -x cuda -nocudainc --cuda-device-only %s | FileCheck %s __attribute__((device)) void f_device(); __attribute__((global)) void f_global(); __attribute__((constant)) int* g_constant; Index: test/Index/index-file.cu =================================================================== --- /dev/null +++ test/Index/index-file.cu @@ -0,0 +1,9 @@ +// Make sure we can process CUDA file even if driver creates multiple jobs +// RUN: c-index-test -test-load-source all %s | FileCheck %s -check-prefix=CHECK-ANY +// Make sure we process correct side of cuda compilation +// RUN: c-index-test -test-load-source all --cuda-host-only %s | FileCheck %s -check-prefix=CHECK-HOST +// RUN: c-index-test -test-load-source all --cuda-device-only %s | FileCheck %s -check-prefix=CHECK-DEVICE + +// CHECK-ANY: macro definition=__cplusplus +// CHECK-HOST-NOT: macro definition=__CUDA_ARCH__ +// CHECK-DEVICE: macro definition=__CUDA_ARCH__ Index: tools/libclang/CIndex.cpp =================================================================== --- tools/libclang/CIndex.cpp +++ tools/libclang/CIndex.cpp @@ -3028,6 +3028,11 @@ /*AllowPCHWithCompilerErrors=*/true, SkipFunctionBodies, /*UserFilesAreVolatile=*/true, ForSerialization, &ErrUnit)); + if (!Unit && !ErrUnit) { + PTUI->result = CXError_ASTReadError; + return; + } + if (NumErrors != Diags->getClient()->getNumErrors()) { // Make sure to check that 'Unit' is non-NULL. if (CXXIdx->getDisplayDiagnostics()) Index: unittests/ASTMatchers/ASTMatchersTest.h =================================================================== --- unittests/ASTMatchers/ASTMatchersTest.h +++ unittests/ASTMatchers/ASTMatchersTest.h @@ -163,6 +163,7 @@ std::vector Args; Args.push_back("-xcuda"); Args.push_back("-fno-ms-extensions"); + Args.push_back("--cuda-host-only"); Args.push_back(CompileArg); if (!runToolOnCodeWithArgs(Factory->create(), CudaHeader + Code, Args)) {