diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td index 2d6db287cd9e8..247143fde4825 100644 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -81,6 +81,10 @@ def err_drv_Xopenmp_target_missing_triple : Error< "cannot deduce implicit triple value for -Xopenmp-target, specify triple using -Xopenmp-target=">; def err_drv_invalid_Xopenmp_target_with_args : Error< "invalid -Xopenmp-target argument: '%0', options requiring arguments are unsupported">; +def err_drv_Xsycl_target_missing_triple : Error< + "cannot deduce implicit triple value for -Xsycl-target, specify triple using -Xsycl-target=">; +def err_drv_invalid_Xsycl_target_with_args : Error< + "invalid -Xsycl-target argument: '%0', options requiring arguments are unsupported">; def err_drv_argument_only_allowed_with : Error< "invalid argument '%0' only allowed with '%1'">; def err_drv_argument_not_allowed_with : Error< @@ -204,15 +208,21 @@ def err_drv_optimization_remark_pattern : Error< "%0 in '%1'">; def err_drv_no_neon_modifier : Error<"[no]neon is not accepted as modifier, please use [no]simd instead">; def err_drv_invalid_omp_target : Error<"OpenMP target is invalid: '%0'">; +def err_drv_invalid_sycl_target : Error<"SYCL target is invalid: '%0'">; def err_drv_omp_host_ir_file_not_found : Error< "The provided host compiler IR file '%0' is required to generate code for OpenMP target regions but cannot be found.">; def err_drv_omp_host_target_not_supported : Error< "The target '%0' is not a supported OpenMP host target.">; def err_drv_expecting_fopenmp_with_fopenmp_targets : Error< "The option -fopenmp-targets must be used in conjunction with a -fopenmp option compatible with offloading, please use -fopenmp=libomp or -fopenmp=libiomp5.">; +def err_drv_expecting_fsycl_with_fsycl_targets : Error< + "The option -fsycl-targets must be used in conjunction with -fsycl to enable offloading.">; def warn_drv_omp_offload_target_duplicate : Warning< "The OpenMP offloading target '%0' is similar to target '%1' already specified - will be ignored.">, InGroup; +def warn_drv_sycl_offload_target_duplicate : Warning< + "The SYCL offloading target '%0' is similar to target '%1' already specified - will be ignored.">, + InGroup; def warn_drv_omp_offload_target_missingbcruntime : Warning< "No library '%0' found in the default clang lib directory or in LIBRARY_PATH. Expect degraded performance due to no inlining of runtime functions on target devices.">, InGroup; diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 6ba6c19a28c2c..5ad5ec3c1e21d 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -995,6 +995,9 @@ def OpenMPClauses : DiagGroup<"openmp-clauses">; def OpenMPLoopForm : DiagGroup<"openmp-loop-form">; def OpenMPTarget : DiagGroup<"openmp-target">; +// SYCL warnings +def SyclTarget : DiagGroup<"sycl-target">; + // Backend warnings. def BackendInlineAsm : DiagGroup<"inline-asm">; def BackendFrameLargerThanEQ : DiagGroup<"frame-larger-than=">; diff --git a/clang/include/clang/Driver/Action.h b/clang/include/clang/Driver/Action.h index f4aaa6c544ac2..0e63a7fcf45eb 100644 --- a/clang/include/clang/Driver/Action.h +++ b/clang/include/clang/Driver/Action.h @@ -90,6 +90,7 @@ class Action { OFK_Cuda = 0x02, OFK_OpenMP = 0x04, OFK_HIP = 0x08, + OFK_SYCL = 0x10 }; static const char *getClassName(ActionClass AC); diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index a5e4cca0a203e..9e37da2620585 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -473,6 +473,11 @@ def Xopenmp_target : Separate<["-"], "Xopenmp-target">, def Xopenmp_target_EQ : JoinedAndSeparate<["-"], "Xopenmp-target=">, HelpText<"Pass to the target offloading toolchain identified by .">, MetaVarName<" ">; +def Xsycl_target : Separate<["-"], "Xsycl-target">, + HelpText<"Pass to the SYCL based target offloading toolchain.">, MetaVarName<"">; +def Xsycl_target_EQ : JoinedAndSeparate<["-"], "Xsycl-target=">, + HelpText<"Pass to the SYCL based target offloading toolchain identified by .">, + MetaVarName<" ">; def z : Separate<["-"], "z">, Flags<[LinkerInput, RenderAsInput]>, HelpText<"Pass -z to the linker">, MetaVarName<"">, Group; @@ -1677,6 +1682,11 @@ def fstrict_vtable_pointers: Flag<["-"], "fstrict-vtable-pointers">, HelpText<"Enable optimizations based on the strict rules for overwriting " "polymorphic C++ objects">; def fstrict_overflow : Flag<["-"], "fstrict-overflow">, Group; +def fsycl : Flag<["-"], "fsycl">, Group, Flags<[CC1Option, NoArgumentUnused]>, + HelpText<"generate SYCL code.">; +def fno_sycl : Flag<["-"], "fno-sycl">, Group, Flags<[NoArgumentUnused]>; +def fsycl_targets_EQ : CommaJoined<["-"], "fsycl-targets=">, Flags<[DriverOption, CC1Option]>, + HelpText<"Specify comma-separated list of triples SYCL offloading targets to be supported">; def fsyntax_only : Flag<["-"], "fsyntax-only">, Flags<[DriverOption,CoreOption,CC1Option]>, Group; def ftabstop_EQ : Joined<["-"], "ftabstop=">, Group; diff --git a/clang/include/clang/Driver/ToolChain.h b/clang/include/clang/Driver/ToolChain.h index d5f75b8271101..46edca161f6e6 100644 --- a/clang/include/clang/Driver/ToolChain.h +++ b/clang/include/clang/Driver/ToolChain.h @@ -273,12 +273,14 @@ class ToolChain { return nullptr; } - /// TranslateOpenMPTargetArgs - Create a new derived argument list for - /// that contains the OpenMP target specific flags passed via + /// TranslateOffloadTargetArgs - Create a new derived argument list for + /// that contains the Offloat target specific flags passed via /// -Xopenmp-target -opt=val OR -Xopenmp-target= -opt=val - virtual llvm::opt::DerivedArgList *TranslateOpenMPTargetArgs( + /// Also handles -Xsycl-target OR -Xsycl-target= + virtual llvm::opt::DerivedArgList *TranslateOffloadTargetArgs( const llvm::opt::DerivedArgList &Args, bool SameTripleAsHost, - SmallVectorImpl &AllocatedArgs) const; + SmallVectorImpl &AllocatedArgs, + Action::OffloadKind DeviceOffloadKind) const; /// Choose a tool to use to handle the action \p JA. /// diff --git a/clang/include/clang/Driver/Types.def b/clang/include/clang/Driver/Types.def index 8c727ba69e07e..ac330207a0123 100644 --- a/clang/include/clang/Driver/Types.def +++ b/clang/include/clang/Driver/Types.def @@ -102,5 +102,6 @@ TYPE("dSYM", dSYM, INVALID, "dSYM", "A") TYPE("dependencies", Dependencies, INVALID, "d", "") TYPE("cuda-fatbin", CUDA_FATBIN, INVALID, "fatbin","A") TYPE("spirv", SPIRV, INVALID, "spv", "") +TYPE("sycl-header", SYCL_Header, INVALID, "h", "") TYPE("hip-fatbin", HIP_FATBIN, INVALID, "hipfb", "A") TYPE("none", Nothing, INVALID, nullptr, "u") diff --git a/clang/lib/Driver/Action.cpp b/clang/lib/Driver/Action.cpp index d4c7040a233c7..7130be04222be 100644 --- a/clang/lib/Driver/Action.cpp +++ b/clang/lib/Driver/Action.cpp @@ -99,6 +99,8 @@ std::string Action::getOffloadingKindPrefix() const { return "device-openmp"; case OFK_HIP: return "device-hip"; + case OFK_SYCL: + return "device-sycl"; // TODO: Add other programming models here. } @@ -116,6 +118,8 @@ std::string Action::getOffloadingKindPrefix() const { Res += "-hip"; if (ActiveOffloadKindMask & OFK_OpenMP) Res += "-openmp"; + if (ActiveOffloadKindMask & OFK_SYCL) + Res += "-sycl"; // TODO: Add other programming models here. @@ -152,6 +156,8 @@ StringRef Action::GetOffloadKindName(OffloadKind Kind) { return "openmp"; case OFK_HIP: return "hip"; + case OFK_SYCL: + return "sycl"; // TODO: Add other programming models here. } diff --git a/clang/lib/Driver/CMakeLists.txt b/clang/lib/Driver/CMakeLists.txt index 084176b52464e..43a3135fd6f22 100644 --- a/clang/lib/Driver/CMakeLists.txt +++ b/clang/lib/Driver/CMakeLists.txt @@ -64,6 +64,7 @@ add_clang_library(clangDriver ToolChains/TCE.cpp ToolChains/WebAssembly.cpp ToolChains/XCore.cpp + ToolChains/SYCL.cpp Types.cpp XRayArgs.cpp diff --git a/clang/lib/Driver/Compilation.cpp b/clang/lib/Driver/Compilation.cpp index 982d7ecad9620..d09252a2526b8 100644 --- a/clang/lib/Driver/Compilation.cpp +++ b/clang/lib/Driver/Compilation.cpp @@ -69,12 +69,14 @@ Compilation::getArgsForToolChain(const ToolChain *TC, StringRef BoundArch, if (!Entry) { SmallVector AllocatedArgs; DerivedArgList *OpenMPArgs = nullptr; - // Translate OpenMP toolchain arguments provided via the -Xopenmp-target flags. - if (DeviceOffloadKind == Action::OFK_OpenMP) { + // Translate OpenMP toolchain arguments provided via the -Xopenmp-target + // or -Xsycl-target flags. + if (DeviceOffloadKind == Action::OFK_OpenMP || + DeviceOffloadKind == Action::OFK_SYCL) { const ToolChain *HostTC = getSingleOffloadToolChain(); bool SameTripleAsHost = (TC->getTriple() == HostTC->getTriple()); - OpenMPArgs = TC->TranslateOpenMPTargetArgs( - *TranslatedArgs, SameTripleAsHost, AllocatedArgs); + OpenMPArgs = TC->TranslateOffloadTargetArgs( + *TranslatedArgs, SameTripleAsHost, AllocatedArgs, DeviceOffloadKind); } if (!OpenMPArgs) { diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index 07509e5548a6e..1a4f843c3143f 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -43,6 +43,7 @@ #include "ToolChains/TCE.h" #include "ToolChains/WebAssembly.h" #include "ToolChains/XCore.h" +#include "ToolChains/SYCL.h" #include "clang/Basic/Version.h" #include "clang/Config/config.h" #include "clang/Driver/Action.h" @@ -693,6 +694,61 @@ void Driver::CreateOffloadingDeviceToolChains(Compilation &C, << OpenMPTargets->getAsString(C.getInputArgs()); } + // + // SYCL + // + // We need to generate a SYCL toolchain if the user specified targets with + // the -fsycl-targets option. + if (Arg *SYCLTargets = + C.getInputArgs().getLastArg(options::OPT_fsycl_targets_EQ)) { + if (SYCLTargets->getNumValues()) { + // We expect that -fsycl-targets is always used in conjunction with the + // -fsycl option + bool HasValidSYCLRuntime = C.getInputArgs().hasFlag( + options::OPT_fsycl, options::OPT_fno_sycl, false); + + if (HasValidSYCLRuntime) { + llvm::StringMap FoundNormalizedTriples; + for (const char *Val : SYCLTargets->getValues()) { + llvm::Triple TT(Val); + std::string NormalizedName = TT.normalize(); + + // Make sure we don't have a duplicate triple. + auto Duplicate = FoundNormalizedTriples.find(NormalizedName); + if (Duplicate != FoundNormalizedTriples.end()) { + Diag(clang::diag::warn_drv_sycl_offload_target_duplicate) + << Val << Duplicate->second; + continue; + } + + // Store the current triple so that we can check for duplicates in the + // following iterations. + FoundNormalizedTriples[NormalizedName] = Val; + + // If the specified target is invalid, emit a diagnostic. + if (TT.getArch() == llvm::Triple::UnknownArch) + Diag(clang::diag::err_drv_invalid_sycl_target) << Val; + else { + const ToolChain *HostTC = + C.getSingleOffloadToolChain(); + const llvm::Triple &HostTriple = HostTC->getTriple(); + // Use the SYCL and host triples as the key into the ToolChains map, + // because the device toolchain we create depends on both. + auto &SYCLTC = ToolChains[TT.str() + "/" + HostTriple.str()]; + if (!SYCLTC) { + SYCLTC = llvm::make_unique( + *this, TT, *HostTC, C.getInputArgs()); + } + C.addOffloadDeviceToolChain(SYCLTC.get(), Action::OFK_SYCL); + } + } + } else + Diag(clang::diag::err_drv_expecting_fsycl_with_fsycl_targets); + } else + Diag(clang::diag::warn_drv_empty_joined_argument) + << SYCLTargets->getAsString(C.getInputArgs()); + } + // // TODO: Add support for other offloading programming models here. // @@ -2852,6 +2908,154 @@ class OffloadingActionBuilder final { } }; + /// SYCL action builder. The host bitcode is passed to the device frontend + /// and all the device linked images are passed to the host link phase. + /// SPIR related are wrapped before added to the fat binary + class SYCLActionBuilder final : public DeviceActionBuilder { + /// The SYCL actions for the current input. + ActionList SYCLDeviceActions; + + /// The linker inputs obtained for each toolchain. + SmallVector DeviceLinkerInputs; + + /// The compiler inputs obtained for each toolchain + Action * DeviceCompilerInput = nullptr; + + public: + SYCLActionBuilder(Compilation &C, DerivedArgList &Args, + const Driver::InputList &Inputs) + : DeviceActionBuilder(C, Args, Inputs, Action::OFK_SYCL) {} + + ActionBuilderReturnCode + getDeviceDependences(OffloadAction::DeviceDependences &DA, + phases::ID CurPhase, phases::ID FinalPhase, + PhasesTy &Phases) override { + + // We should always have an action for each input. + assert(SYCLDeviceActions.size() == ToolChains.size() && + "Number of SYCL actions and toolchains do not match."); + + // FIXME: This adds the integrated header generation pass before the + // Host compilation pass so the Host can use the header generated. This + // can be improved upon to where the header generation and spv generation + // is done in the same step. Currently, its not too efficient. + // The host depends on the generated integrated header from the device + // compilation. + if (CurPhase == phases::Compile) { + for (Action *&A : SYCLDeviceActions) { + DeviceCompilerInput = + C.MakeAction(A, types::TY_SYCL_Header); + } + DA.add(*DeviceCompilerInput, *ToolChains.front(), /*BoundArch=*/nullptr, + Action::OFK_SYCL); + // Clear the input file, it is already a dependence to a host + // action. + DeviceCompilerInput = nullptr; + } + + // The host only depends on device action in the linking phase, when all + // the device images have to be embedded in the host image. + if (CurPhase == phases::Link) { + assert(ToolChains.size() == DeviceLinkerInputs.size() && + "Toolchains and linker inputs sizes do not match."); + auto LI = DeviceLinkerInputs.begin(); + for (auto *A : SYCLDeviceActions) { + LI->push_back(A); + ++LI; + } + + // We passed the device action as a host dependence, so we don't need to + // do anything else with them. + SYCLDeviceActions.clear(); + return ABRT_Success; + } + + // By default, we produce an action for each device arch. + for (Action *&A : SYCLDeviceActions) { + A = C.getDriver().ConstructPhaseAction(C, Args, CurPhase, A, + AssociatedOffloadKind); + } + + return ABRT_Success; + } + + ActionBuilderReturnCode addDeviceDepences(Action *HostAction) override { + + // If this is an input action replicate it for each SYCL toolchain. + if (auto *IA = dyn_cast(HostAction)) { + SYCLDeviceActions.clear(); + for (unsigned I = 0; I < ToolChains.size(); ++I) + SYCLDeviceActions.push_back( + C.MakeAction(IA->getInputArg(), IA->getType())); + return ABRT_Success; + } + + // If this is an unbundling action use it as is for each SYCL toolchain. + if (auto *UA = dyn_cast(HostAction)) { + SYCLDeviceActions.clear(); + for (unsigned I = 0; I < ToolChains.size(); ++I) { + SYCLDeviceActions.push_back(UA); + UA->registerDependentActionInfo( + ToolChains[I], /*BoundArch=*/StringRef(), Action::OFK_SYCL); + } + return ABRT_Success; + } + return ABRT_Success; + } + + void appendTopLevelActions(ActionList &AL) override { + if (SYCLDeviceActions.empty()) + return; + + // We should always have an action for each input. + assert(SYCLDeviceActions.size() == ToolChains.size() && + "Number of SYCL actions and toolchains do not match."); + + // Append all device actions followed by the proper offload action. + auto TI = ToolChains.begin(); + for (auto *A : SYCLDeviceActions) { + OffloadAction::DeviceDependences Dep; + Dep.add(*A, **TI, /*BoundArch=*/nullptr, Action::OFK_SYCL); + AL.push_back(C.MakeAction(Dep, A->getType())); + ++TI; + } + // We no longer need the action stored in this builder. + SYCLDeviceActions.clear(); + } + + void appendLinkDependences(OffloadAction::DeviceDependences &DA) override { + assert(ToolChains.size() == DeviceLinkerInputs.size() && + "Toolchains and linker inputs sizes do not match."); + + // Append a new link action for each device. + auto TC = ToolChains.begin(); + for (auto &LI : DeviceLinkerInputs) { + auto *DeviceLinkAction = + C.MakeAction(LI, types::TY_Image); + DA.add(*DeviceLinkAction, **TC, /*BoundArch=*/nullptr, + Action::OFK_SYCL); + ++TC; + } + } + + bool initialize() override { + // Get the SYCL toolchains. If we don't get any, the action builder will + // know there is nothing to do related to SYCL offloading. + auto SYCLTCRange = C.getOffloadToolChains(); + for (auto TI = SYCLTCRange.first, TE = SYCLTCRange.second; TI != TE; + ++TI) + ToolChains.push_back(TI->second); + + DeviceLinkerInputs.resize(ToolChains.size()); + return false; + } + + bool canUseBundlerUnbundler() const override { + // SYCL should use bundled files whenever possible. + return true; + } + }; + /// /// TODO: Add the implementation for other specialized builders here. /// @@ -2879,6 +3083,9 @@ class OffloadingActionBuilder final { // Create a specialized builder for OpenMP. SpecializedBuilders.push_back(new OpenMPActionBuilder(C, Args, Inputs)); + // Create a specialized builder for SYCL. + SpecializedBuilders.push_back(new SYCLActionBuilder(C, Args, Inputs)); + // // TODO: Build other specialized builders here. // diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp index 88a627eab6de1..e20361f91dc5c 100644 --- a/clang/lib/Driver/ToolChain.cpp +++ b/clang/lib/Driver/ToolChain.cpp @@ -897,14 +897,18 @@ ToolChain::computeMSVCVersion(const Driver *D, return VersionTuple(); } -llvm::opt::DerivedArgList *ToolChain::TranslateOpenMPTargetArgs( +llvm::opt::DerivedArgList *ToolChain::TranslateOffloadTargetArgs( const llvm::opt::DerivedArgList &Args, bool SameTripleAsHost, - SmallVectorImpl &AllocatedArgs) const { + SmallVectorImpl &AllocatedArgs, + Action::OffloadKind DeviceOffloadKind) const { + assert((DeviceOffloadKind == Action::OFK_OpenMP || + DeviceOffloadKind == Action::OFK_SYCL) && + "requires OpenMP or SYCL offload kind"); DerivedArgList *DAL = new DerivedArgList(Args.getBaseArgs()); const OptTable &Opts = getDriver().getOpts(); bool Modified = false; - // Handle -Xopenmp-target flags + // Handle -Xopenmp-target and -Xsycl-target flags for (auto *A : Args) { // Exclude flags which may only apply to the host toolchain. // Do not exclude flags when the host triple (AuxTriple) @@ -918,40 +922,77 @@ llvm::opt::DerivedArgList *ToolChain::TranslateOpenMPTargetArgs( continue; } + // Exclude -fsycl + if (A->getOption().matches(options::OPT_fsycl)) { + Modified = true; + continue; + } + unsigned Index; unsigned Prev; - bool XOpenMPTargetNoTriple = + bool XOffloadTargetNoTriple; + + if (DeviceOffloadKind == Action::OFK_OpenMP) { + XOffloadTargetNoTriple = A->getOption().matches(options::OPT_Xopenmp_target); - - if (A->getOption().matches(options::OPT_Xopenmp_target_EQ)) { - // Passing device args: -Xopenmp-target= -opt=val. - if (A->getValue(0) == getTripleString()) - Index = Args.getBaseArgs().MakeIndex(A->getValue(1)); - else + if (A->getOption().matches(options::OPT_Xopenmp_target_EQ)) { + // Passing device args: -Xopenmp-target= -opt=val. + if (A->getValue(0) == getTripleString()) + Index = Args.getBaseArgs().MakeIndex(A->getValue(1)); + else + continue; + } else if (XOffloadTargetNoTriple) { + // Passing device args: -Xopenmp-target -opt=val. + Index = Args.getBaseArgs().MakeIndex(A->getValue(0)); + } else { + DAL->append(A); continue; - } else if (XOpenMPTargetNoTriple) { - // Passing device args: -Xopenmp-target -opt=val. - Index = Args.getBaseArgs().MakeIndex(A->getValue(0)); - } else { - DAL->append(A); - continue; + } + } else if (DeviceOffloadKind == Action::OFK_SYCL) { + XOffloadTargetNoTriple = + A->getOption().matches(options::OPT_Xsycl_target); + if (A->getOption().matches(options::OPT_Xsycl_target_EQ)) { + // Passing device args: -Xsycl-target= -opt=val. + if (A->getValue(0) == getTripleString()) + Index = Args.getBaseArgs().MakeIndex(A->getValue(1)); + else + continue; + } else if (XOffloadTargetNoTriple) { + // Passing device args: -Xsycl-target -opt=val. + Index = Args.getBaseArgs().MakeIndex(A->getValue(0)); + } else { + DAL->append(A); + continue; + } } + // Parse the argument to -Xopenmp-target. Prev = Index; - std::unique_ptr XOpenMPTargetArg(Opts.ParseOneArg(Args, Index)); - if (!XOpenMPTargetArg || Index > Prev + 1) { - getDriver().Diag(diag::err_drv_invalid_Xopenmp_target_with_args) - << A->getAsString(Args); + std::unique_ptr XOffloadTargetArg(Opts.ParseOneArg(Args, Index)); + if (!XOffloadTargetArg || Index > Prev + 1) { + if (DeviceOffloadKind == Action::OFK_OpenMP) { + getDriver().Diag(diag::err_drv_invalid_Xopenmp_target_with_args) + << A->getAsString(Args); + } else { + getDriver().Diag(diag::err_drv_invalid_Xsycl_target_with_args) + << A->getAsString(Args); + } continue; } - if (XOpenMPTargetNoTriple && XOpenMPTargetArg && - Args.getAllArgValues(options::OPT_fopenmp_targets_EQ).size() != 1) { - getDriver().Diag(diag::err_drv_Xopenmp_target_missing_triple); - continue; + if (XOffloadTargetNoTriple && XOffloadTargetArg) { + if (DeviceOffloadKind == Action::OFK_OpenMP && + Args.getAllArgValues(options::OPT_fopenmp_targets_EQ).size() != 1) { + getDriver().Diag(diag::err_drv_Xopenmp_target_missing_triple); + continue; + } else if (DeviceOffloadKind == Action::OFK_SYCL && + Args.getAllArgValues(options::OPT_fsycl_targets_EQ).size() != 1) { + getDriver().Diag(diag::err_drv_Xsycl_target_missing_triple); + continue; + } } - XOpenMPTargetArg->setBaseArg(A); - A = XOpenMPTargetArg.release(); + XOffloadTargetArg->setBaseArg(A); + A = XOffloadTargetArg.release(); AllocatedArgs.push_back(A); DAL->append(A); Modified = true; diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 21ef861ede411..c332c10de764e 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -144,6 +144,13 @@ forAllAssociatedToolChains(Compilation &C, const JobAction &JA, } else if (JA.isDeviceOffloading(Action::OFK_OpenMP)) Work(*C.getSingleOffloadToolChain()); + if (JA.isHostOffloading(Action::OFK_SYCL)) { + auto TCs = C.getOffloadToolChains(); + for (auto II = TCs.first, IE = TCs.second; II != IE; ++II) + Work(*II->second); + } else if (JA.isDeviceOffloading(Action::OFK_SYCL)) + Work(*C.getSingleOffloadToolChain()); + // // TODO: Add support for other offloading programming models here. // @@ -3378,14 +3385,18 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // Check number of inputs for sanity. We need at least one input. assert(Inputs.size() >= 1 && "Must have at least one input."); // CUDA/HIP compilation may have multiple inputs (source file + results of - // device-side compilations). OpenMP device jobs also take the host IR as a - // second input. Module precompilation accepts a list of header files to + // device-side compilations). OpenMP and SYCL device jobs also take the host + // IR as a second input. All other jobs are expected to have exactly one // include as part of the module. All other jobs are expected to have exactly // one input. bool IsCuda = JA.isOffloading(Action::OFK_Cuda); bool IsHIP = JA.isOffloading(Action::OFK_HIP); bool IsOpenMPDevice = JA.isDeviceOffloading(Action::OFK_OpenMP); + bool IsSYCLOffloadDevice = JA.isDeviceOffloading(Action::OFK_SYCL); + bool IsSYCL = JA.isOffloading(Action::OFK_SYCL); bool IsHeaderModulePrecompile = isa(JA); + assert((IsCuda || IsHIP || (IsOpenMPDevice && Inputs.size() == 2) || + IsSYCL || Inputs.size() == 1) && "Unable to handle multiple inputs."); // A header module compilation doesn't have a main input file, so invent a // fake one as a placeholder. @@ -3478,7 +3489,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, if (IsSYCLDevice) { // We want to compile sycl kernels. - CmdArgs.push_back("-std=c++11"); + if (types::isCXX(Input.getType())) + CmdArgs.push_back("-std=c++11"); CmdArgs.push_back("-fsycl-is-device"); // Pass the triple of host when doing SYCL std::string NormalizedTriple = @@ -3530,9 +3542,13 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("-P"); } } else if (isa(JA)) { - CmdArgs.push_back("-emit-obj"); - - CollectArgsForIntegratedAssembler(C, Args, CmdArgs, D); + if (IsSYCLOffloadDevice && IsSYCLDevice) { + CmdArgs.push_back("-emit-spirv"); + } + else { + CmdArgs.push_back("-emit-obj"); + CollectArgsForIntegratedAssembler(C, Args, CmdArgs, D); + } // Also ignore explicit -force_cpusubtype_ALL option. (void)Args.hasArg(options::OPT_force__cpusubtype__ALL); @@ -3550,7 +3566,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, } else { assert((isa(JA) || isa(JA)) && "Invalid action for clang tool."); - if (JA.getType() == types::TY_Nothing) { + if (JA.getType() == types::TY_Nothing || + JA.getType() == types::TY_SYCL_Header) { CmdArgs.push_back("-fsyntax-only"); } else if (JA.getType() == types::TY_LLVM_IR || JA.getType() == types::TY_LTO_IR) { @@ -5203,6 +5220,21 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("-fcuda-short-ptr"); } + if (IsSYCL) { + // Host-side SYCL compilation receives the integrated header file as + // Inputs[1]. Include the header with -include + if (!IsSYCLOffloadDevice && Inputs.size() > 1) { + CmdArgs.push_back("-include"); + CmdArgs.push_back(Inputs[1].getFilename()); + } + if (IsSYCLOffloadDevice && JA.getType() == types::TY_SYCL_Header) { + // Generating a SYCL Header + SmallString<128> HeaderOpt("-fsycl-int-header="); + HeaderOpt += Output.getFilename(); + CmdArgs.push_back(Args.MakeArgString(HeaderOpt)); + } + } + // OpenMP offloading device jobs take the argument -fopenmp-host-ir-file-path // to specify the result of the compile phase on the host, so the meaningful // device declarations can be identified. Also, -fopenmp-is-device is passed @@ -5235,6 +5267,25 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back(Args.MakeArgString(TargetInfo.str())); } + // For all the host SYCL offloading compile jobs we need to pass the targets + // information using -fsycl-targets= option. + if (isa(JA) && JA.isHostOffloading(Action::OFK_SYCL)) { + SmallString<128> TargetInfo("-fsycl-targets="); + + Arg *Tgts = Args.getLastArg(options::OPT_fsycl_targets_EQ); + assert(Tgts && Tgts->getNumValues() && + "SYCL offloading has to have targets specified."); + for (unsigned i = 0; i < Tgts->getNumValues(); ++i) { + if (i) + TargetInfo += ','; + // We need to get the string from the triple because it may be not exactly + // the same as the one we get directly from the arguments. + llvm::Triple T(Tgts->getValue(i)); + TargetInfo += T.getTriple(); + } + CmdArgs.push_back(Args.MakeArgString(TargetInfo.str())); + } + bool WholeProgramVTables = Args.hasFlag(options::OPT_fwhole_program_vtables, options::OPT_fno_whole_program_vtables, false); diff --git a/clang/lib/Driver/ToolChains/SYCL.cpp b/clang/lib/Driver/ToolChains/SYCL.cpp new file mode 100644 index 0000000000000..620987f52aa59 --- /dev/null +++ b/clang/lib/Driver/ToolChains/SYCL.cpp @@ -0,0 +1,225 @@ +//===--- SYCL.cpp - SYCL Tool and ToolChain Implementations -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SYCL.h" +#include "CommonArgs.h" +#include "InputInfo.h" +#include "clang/Driver/Compilation.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/Options.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" + +using namespace clang::driver; +using namespace clang::driver::toolchains; +using namespace clang::driver::tools; +using namespace clang; +using namespace llvm::opt; + +const char *SYCL::Linker::constructLLVMSpirvCommand(Compilation &C, + const JobAction &JA, StringRef OutputFilePrefix, bool toBc, + const char *InputFileName) const { + // Construct llvm-spirv command. + // The output is a bc file or vice versa depending on the -r option usage + // llvm-spirv -r -o a_kernel.bc a_kernel.spv + // llvm-spirv -o a_kernel.spv a_kernel.bc + ArgStringList CmdArgs; + if (toBc) + CmdArgs.push_back("-r"); + std::string TmpName = + C.getDriver().GetTemporaryPath(OutputFilePrefix.str() + "-spirv", + toBc ? "bc" : "spv"); + const char *OutputFileName = + C.addTempFile(C.getArgs().MakeArgString(TmpName)); + //return OutputFileName; + CmdArgs.push_back("-o"); + CmdArgs.push_back(OutputFileName); + CmdArgs.push_back(InputFileName); + SmallString<128> LLVMSpirvPath(C.getDriver().Dir); + llvm::sys::path::append(LLVMSpirvPath, "llvm-spirv"); + const char *LLVMSpirv = C.getArgs().MakeArgString(LLVMSpirvPath); + C.addCommand(llvm::make_unique(JA, *this, LLVMSpirv, CmdArgs, None)); + return OutputFileName; +} + +const char *SYCL::Linker::constructLLVMLinkCommand( + Compilation &C, const JobAction &JA, StringRef SubArchName, + StringRef OutputFilePrefix, + const llvm::opt::ArgStringList &InputFiles) const { + ArgStringList CmdArgs; + // Add the input bc's created by compile step. + for (const auto &II : InputFiles) + CmdArgs.push_back(II); + // Add an intermediate output file. + CmdArgs.push_back("-o"); + SmallString<128> TmpName(C.getDriver().GetTemporaryPath( + OutputFilePrefix.str() + "-linked", "bc")); + const char *OutputFileName = + C.addTempFile(C.getArgs().MakeArgString(TmpName)); + CmdArgs.push_back(OutputFileName); + SmallString<128> ExecPath(C.getDriver().Dir); + llvm::sys::path::append(ExecPath, "llvm-link"); + const char *Exec = C.getArgs().MakeArgString(ExecPath); + C.addCommand(llvm::make_unique(JA, *this, Exec, CmdArgs, None)); + return OutputFileName; +} + +const char *SYCL::Linker::constructOffloadWrapperCommand( + Compilation &C, const JobAction &JA, StringRef OutputFilePrefix, + const char *InputFileName) const { + // Construct offload-wrapper command. + // The output is bc wrapped spirv file + std::string TmpName = + C.getDriver().GetTemporaryPath(OutputFilePrefix.str() + "-wrapped", "bc"); + const char *OutputFileName = + C.addTempFile(C.getArgs().MakeArgString(TmpName)); + ArgStringList WrapperArgs; + + SmallString<128> TmpOutOpt("-o="); + TmpOutOpt += OutputFileName; + WrapperArgs.push_back(C.getArgs().MakeArgString(TmpOutOpt)); + WrapperArgs.push_back(InputFileName); + + SmallString<128> TmpTargetOpt("-target="); + TmpTargetOpt += Action::GetOffloadKindName(JA.getOffloadingDeviceKind()); + TmpTargetOpt += '-'; + TmpTargetOpt += getToolChain().getAuxTriple()->str(); + WrapperArgs.push_back(C.getArgs().MakeArgString(TmpTargetOpt)); + + // For SYCL, do not emit entry tables + WrapperArgs.push_back("-emit-reg-funcs=0"); + WrapperArgs.push_back("-emit-entry-table=0"); + + SmallString<128> WrapperPath(C.getDriver().Dir); + llvm::sys::path::append(WrapperPath, "clang-offload-wrapper"); + const char *Wrapper = C.getArgs().MakeArgString(WrapperPath); + C.addCommand(llvm::make_unique(JA, *this, Wrapper, WrapperArgs, + None)); + return OutputFileName; +} + +void SYCL::Linker::constructLlcCommand(Compilation &C, const JobAction &JA, + const InputInfo &Output, const char *InputFileName) const { + // Construct llc command. + // The output is an object file + ArgStringList LlcArgs{"-filetype=obj", "-o", Output.getFilename(), + InputFileName}; + SmallString<128> LlcPath(C.getDriver().Dir); + llvm::sys::path::append(LlcPath, "llc"); + const char *Llc = C.getArgs().MakeArgString(LlcPath); + C.addCommand(llvm::make_unique(JA, *this, Llc, LlcArgs, None)); +} + +// For SYCL the inputs of the linker job are SPIR-V binaries and output is +// object file. It calls llvm-spirv, llvm-link, llvm-spirv, offload-wrapper +// and llc +void SYCL::Linker::ConstructJob(Compilation &C, const JobAction &JA, + const InputInfo &Output, + const InputInfoList &Inputs, + const ArgList &Args, + const char *LinkingOutput) const { + + assert((getToolChain().getTriple().getArch() == llvm::Triple::spir || + getToolChain().getTriple().getArch() == llvm::Triple::spir64) && + "Unsupported target"); + + std::string SubArchName = getToolChain().getTriple().getArchName(); + + // Prefix for temporary file name. + std::string Prefix = llvm::sys::path::stem(SubArchName); + + // We want to use llvm-spirv linker to link spirv binaries before putting + // them into the fat object. + // Each command outputs different files. + ArgStringList SpirvInputs; + for (const auto &II : Inputs) { + if (!II.isFilename()) + continue; + const char *LLVMSpirvOutputFile = + constructLLVMSpirvCommand(C, JA, Prefix, true, II.getFilename()); + SpirvInputs.push_back(LLVMSpirvOutputFile); + } + const char *LLVMLinkOutputFile = + constructLLVMLinkCommand(C, JA, SubArchName, Prefix, SpirvInputs); + const char *LLVMSpirvOutputFile = + constructLLVMSpirvCommand(C, JA, Prefix, false, LLVMLinkOutputFile); + const char *OffloadWrapperOutputFile = + constructOffloadWrapperCommand(C, JA, Prefix, LLVMSpirvOutputFile); + constructLlcCommand(C, JA, Output, OffloadWrapperOutputFile); +} + +SYCLToolChain::SYCLToolChain(const Driver &D, const llvm::Triple &Triple, + const ToolChain &HostTC, const ArgList &Args) + : ToolChain(D, Triple, Args), HostTC(HostTC) { + // Lookup binaries into the driver directory, this is used to + // discover the clang-offload-bundler executable. + getProgramPaths().push_back(getDriver().Dir); +} + +void SYCLToolChain::addClangTargetOptions( + const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args, + Action::OffloadKind DeviceOffloadingKind) const { + HostTC.addClangTargetOptions(DriverArgs, CC1Args, DeviceOffloadingKind); + + assert(DeviceOffloadingKind == Action::OFK_SYCL && + "Only SYCL offloading kinds are supported"); + + CC1Args.push_back("-fsycl-is-device"); +} + +llvm::opt::DerivedArgList * +SYCLToolChain::TranslateArgs(const llvm::opt::DerivedArgList &Args, + StringRef BoundArch, + Action::OffloadKind DeviceOffloadKind) const { + DerivedArgList *DAL = + HostTC.TranslateArgs(Args, BoundArch, DeviceOffloadKind); + if (!DAL) + DAL = new DerivedArgList(Args.getBaseArgs()); + + const OptTable &Opts = getDriver().getOpts(); + + for (Arg *A : Args) { + DAL->append(A); + } + + if (!BoundArch.empty()) { + DAL->eraseArg(options::OPT_march_EQ); + DAL->AddJoinedArg(nullptr, Opts.getOption(options::OPT_march_EQ), + BoundArch); + } + return DAL; +} + +Tool *SYCLToolChain::buildLinker() const { + assert(getTriple().getArch() == llvm::Triple::spir || + getTriple().getArch() == llvm::Triple::spir64); + return new tools::SYCL::Linker(*this); +} + +void SYCLToolChain::addClangWarningOptions(ArgStringList &CC1Args) const { + HostTC.addClangWarningOptions(CC1Args); +} + +ToolChain::CXXStdlibType +SYCLToolChain::GetCXXStdlibType(const ArgList &Args) const { + return HostTC.GetCXXStdlibType(Args); +} + +void SYCLToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs, + ArgStringList &CC1Args) const { + HostTC.AddClangSystemIncludeArgs(DriverArgs, CC1Args); +} + +void SYCLToolChain::AddClangCXXStdlibIncludeArgs(const ArgList &Args, + ArgStringList &CC1Args) const { + HostTC.AddClangCXXStdlibIncludeArgs(Args, CC1Args); +} + diff --git a/clang/lib/Driver/ToolChains/SYCL.h b/clang/lib/Driver/ToolChains/SYCL.h new file mode 100644 index 0000000000000..b4f6fca94b728 --- /dev/null +++ b/clang/lib/Driver/ToolChains/SYCL.h @@ -0,0 +1,99 @@ +//===--- SYCL.h - SYCL ToolChain Implementations -----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_SYCL_H +#define LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_SYCL_H + +#include "clang/Driver/ToolChain.h" +#include "clang/Driver/Tool.h" + +namespace clang { +namespace driver { + +namespace tools { +namespace SYCL { +// Runs llvm-spirv to convert spirv to bc, llvm-link, which links multiple LLVM +// bitcode. Converts generated bc back to spirv using llvm-spirv, wraps with +// offloading information. Finally compiles to object using llc +class LLVM_LIBRARY_VISIBILITY Linker : public Tool { +public: + Linker(const ToolChain &TC) : Tool("SYCL::Linker", "sycl-link", TC) {} + + bool hasIntegratedCPP() const override { return false; } + + void ConstructJob(Compilation &C, const JobAction &JA, + const InputInfo &Output, const InputInfoList &Inputs, + const llvm::opt::ArgList &TCArgs, + const char *LinkingOutput) const override; + +private: + /// \return llvm-spirv output file name. + const char *constructLLVMSpirvCommand(Compilation &C, const JobAction &JA, + llvm::StringRef OutputFilePrefix, + bool isBc, const char *InputFile) const; + /// \return llvm-link output file name. + const char *constructLLVMLinkCommand(Compilation &C, const JobAction &JA, + llvm::StringRef SubArchName, + llvm::StringRef OutputFilePrefix, + const llvm::opt::ArgStringList &InputFiles) const; + /// \return clang-offload-wrapper output file name. + const char *constructOffloadWrapperCommand(Compilation &C, + const JobAction &JA, + llvm::StringRef OutputFilePrefix, + const char *InputFile) const; + void constructLlcCommand(Compilation &C, const JobAction &JA, + const InputInfo &Output, + const char *InputFile) const; +}; + +} // end namespace SYCL +} // end namespace tools + +namespace toolchains { + +class LLVM_LIBRARY_VISIBILITY SYCLToolChain : public ToolChain { +public: + SYCLToolChain(const Driver &D, const llvm::Triple &Triple, + const ToolChain &HostTC, const llvm::opt::ArgList &Args); + + const llvm::Triple *getAuxTriple() const override { + return &HostTC.getTriple(); + } + + llvm::opt::DerivedArgList * + TranslateArgs(const llvm::opt::DerivedArgList &Args, StringRef BoundArch, + Action::OffloadKind DeviceOffloadKind) const override; + void addClangTargetOptions(const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args, + Action::OffloadKind DeviceOffloadKind) const override; + + bool useIntegratedAs() const override { return true; } + bool isPICDefault() const override { return false; } + bool isPIEDefault() const override { return false; } + bool isPICDefaultForced() const override { return false; } + + void addClangWarningOptions(llvm::opt::ArgStringList &CC1Args) const override; + CXXStdlibType GetCXXStdlibType(const llvm::opt::ArgList &Args) const override; + void AddClangSystemIncludeArgs(const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args) const override; + void AddClangCXXStdlibIncludeArgs( + const llvm::opt::ArgList &Args, + llvm::opt::ArgStringList &CC1Args) const override; + + const ToolChain &HostTC; + +protected: + Tool *buildLinker() const override; +}; + +} // end namespace toolchains +} // end namespace driver +} // end namespace clang + +#endif // LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_SYCL_H diff --git a/clang/lib/Driver/Types.cpp b/clang/lib/Driver/Types.cpp index 9d2737bbc7196..8e9070fec2ec3 100644 --- a/clang/lib/Driver/Types.cpp +++ b/clang/lib/Driver/Types.cpp @@ -116,6 +116,7 @@ bool types::isAcceptedByClang(ID Id) { case TY_CXXModule: case TY_PP_CXXModule: case TY_AST: case TY_ModuleFile: case TY_LLVM_IR: case TY_LLVM_BC: + case TY_SPIRV: return true; } } diff --git a/clang/test/Driver/sycl-offload.c b/clang/test/Driver/sycl-offload.c new file mode 100644 index 0000000000000..97b124bf06a36 --- /dev/null +++ b/clang/test/Driver/sycl-offload.c @@ -0,0 +1,203 @@ +/// +/// Perform several driver tests for SYCL offloading +/// + +// REQUIRES: clang-driver +// REQUIRES: x86-registered-target + +/// ########################################################################### + +/// Check whether an invalid SYCL target is specified: +// RUN: %clang -### -fsycl -fsycl-targets=aaa-bbb-ccc-ddd %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-INVALID-TARGET %s +// CHK-INVALID-TARGET: error: SYCL target is invalid: 'aaa-bbb-ccc-ddd' + +/// ########################################################################### + +/// Check warning for empty -fsycl-targets +// RUN: %clang -### -fsycl -fsycl-targets= %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-EMPTY-SYCLTARGETS %s +// CHK-EMPTY-SYCLTARGETS: warning: joined argument expects additional value: '-fsycl-targets=' + +/// ########################################################################### + +/// Check error for no -fsycl option +// RUN: %clang -### -fsycl-targets=spir64-unknown-linux-sycldevice %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-NO-FSYCL %s +// CHK-NO-FSYCL: error: The option -fsycl-targets must be used in conjunction with -fsycl to enable offloading. + +/// ########################################################################### + +/// Check warning for duplicate offloading targets. +// RUN: %clang -### -ccc-print-phases -fsycl -fsycl-targets=spir64-unknown-linux-sycldevice,spir64-unknown-linux-sycldevice %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-DUPLICATES %s +// CHK-DUPLICATES: warning: The SYCL offloading target 'spir64-unknown-linux-sycldevice' is similar to target 'spir64-unknown-linux-sycldevice' already specified - will be ignored. + +/// ########################################################################### + +/// Check -Xsycl-target triggers error when multiple triples are used. +// RUN: %clang -### -no-canonical-prefixes -fsycl -fsycl-targets=spir64-unknown-linux-sycldevice,spir-unknown-linux-sycldevice -Xsycl-target -mcpu=pentium4 %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-FSYCL-TARGET-AMBIGUOUS-ERROR %s + +// CHK-FSYCL-TARGET-AMBIGUOUS-ERROR: clang{{.*}} error: cannot deduce implicit triple value for -Xsycl-target, specify triple using -Xsycl-target= + +/// ########################################################################### + +/// Check -Xsycl-target triggers error when an option requiring arguments is passed to it. +// RUN: %clang -### -no-canonical-prefixes -fsycl -fsycl-targets=spir64-unknown-linux-sycldevice -Xsycl-target -Xsycl-target -mcpu=none %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-FSYCL-TARGET-NESTED-ERROR %s + +// CHK-FSYCL-TARGET-NESTED-ERROR: clang{{.*}} error: invalid -Xsycl-target argument: '-Xsycl-target -Xsycl-target', options requiring arguments are unsupported + +/// ########################################################################### + +/// Check the phases graph when using a single target, different from the host. +/// We should have an offload action joining the host compile and device +/// preprocessor and another one joining the device linking outputs to the host +/// action. +// RUN: %clang -ccc-print-phases -target x86_64-unknown-linux-gnu -fsycl -fsycl-targets=spir64-unknown-linux-sycldevice %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-PHASES %s +// CHK-PHASES: 0: input, "[[INPUT:.+\.c]]", c, (host-sycl) +// CHK-PHASES: 1: preprocessor, {0}, cpp-output, (host-sycl) +// CHK-PHASES: 2: input, "[[INPUT]]", c, (device-sycl) +// CHK-PHASES: 3: preprocessor, {2}, cpp-output, (device-sycl) +// CHK-PHASES: 4: compiler, {3}, sycl-header, (device-sycl) +// CHK-PHASES: 5: offload, "host-sycl (x86_64-unknown-linux-gnu)" {1}, "device-sycl (spir64-unknown-linux-sycldevice)" {4}, cpp-output +// CHK-PHASES: 6: compiler, {5}, ir, (host-sycl) +// CHK-PHASES: 7: backend, {6}, assembler, (host-sycl) +// CHK-PHASES: 8: assembler, {7}, object, (host-sycl) +// CHK-PHASES: 9: linker, {8}, image, (host-sycl) +// CHK-PHASES: 10: compiler, {3}, ir, (device-sycl) +// CHK-PHASES: 11: backend, {10}, assembler, (device-sycl) +// CHK-PHASES: 12: assembler, {11}, object, (device-sycl) +// CHK-PHASES: 13: linker, {12}, image, (device-sycl) +// CHK-PHASES: 14: offload, "host-sycl (x86_64-unknown-linux-gnu)" {9}, "device-sycl (spir64-unknown-linux-sycldevice)" {13}, image + +/// ########################################################################### + +/// Check the phases also add a library to make sure it is treated as input by +/// the device. +// RUN: %clang -ccc-print-phases -target x86_64-unknown-linux-gnu -lsomelib -fsycl -fsycl-targets=spir64-unknown-linux-sycldevice %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-PHASES-LIB %s +// CHK-PHASES-LIB: 0: input, "somelib", object, (host-sycl) +// CHK-PHASES-LIB: 1: input, "[[INPUT:.+\.c]]", c, (host-sycl) +// CHK-PHASES-LIB: 2: preprocessor, {1}, cpp-output, (host-sycl) +// CHK-PHASES-LIB: 3: input, "[[INPUT]]", c, (device-sycl) +// CHK-PHASES-LIB: 4: preprocessor, {3}, cpp-output, (device-sycl) +// CHK-PHASES-LIB: 5: compiler, {4}, sycl-header, (device-sycl) +// CHK-PHASES-LIB: 6: offload, "host-sycl (x86_64-unknown-linux-gnu)" {2}, "device-sycl (spir64-unknown-linux-sycldevice)" {5}, cpp-output +// CHK-PHASES-LIB: 7: compiler, {6}, ir, (host-sycl) +// CHK-PHASES-LIB: 8: backend, {7}, assembler, (host-sycl) +// CHK-PHASES-LIB: 9: assembler, {8}, object, (host-sycl) +// CHK-PHASES-LIB: 10: linker, {0, 9}, image, (host-sycl) +// CHK-PHASES-LIB: 11: input, "somelib", object, (device-sycl) +// CHK-PHASES-LIB: 12: compiler, {4}, ir, (device-sycl) +// CHK-PHASES-LIB: 13: backend, {12}, assembler, (device-sycl) +// CHK-PHASES-LIB: 14: assembler, {13}, object, (device-sycl) +// CHK-PHASES-LIB: 15: linker, {11, 14}, image, (device-sycl) +// CHK-PHASES-LIB: 16: offload, "host-sycl (x86_64-unknown-linux-gnu)" {10}, "device-sycl (spir64-unknown-linux-sycldevice)" {15}, image + +/// ########################################################################### + +/// Check the phases when using and multiple source files +// RUN: echo " " > %t.c +// RUN: %clang -ccc-print-phases -lsomelib -target x86_64-unknown-linux-gnu -fsycl -fsycl-targets=spir64-unknown-linux-sycldevice %s %t.c 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-PHASES-FILES %s + +// CHK-PHASES-FILES: 0: input, "somelib", object, (host-sycl) +// CHK-PHASES-FILES: 1: input, "[[INPUT1:.+\.c]]", c, (host-sycl) +// CHK-PHASES-FILES: 2: preprocessor, {1}, cpp-output, (host-sycl) +// CHK-PHASES-FILES: 3: input, "[[INPUT1]]", c, (device-sycl) +// CHK-PHASES-FILES: 4: preprocessor, {3}, cpp-output, (device-sycl) +// CHK-PHASES-FILES: 5: compiler, {4}, sycl-header, (device-sycl) +// CHK-PHASES-FILES: 6: offload, "host-sycl (x86_64-unknown-linux-gnu)" {2}, "device-sycl (spir64-unknown-linux-sycldevice)" {5}, cpp-output +// CHK-PHASES-FILES: 7: compiler, {6}, ir, (host-sycl) +// CHK-PHASES-FILES: 8: backend, {7}, assembler, (host-sycl) +// CHK-PHASES-FILES: 9: assembler, {8}, object, (host-sycl) +// CHK-PHASES-FILES: 10: input, "[[INPUT2:.+\.c]]", c, (host-sycl) +// CHK-PHASES-FILES: 11: preprocessor, {10}, cpp-output, (host-sycl) +// CHK-PHASES-FILES: 12: input, "[[INPUT2]]", c, (device-sycl) +// CHK-PHASES-FILES: 13: preprocessor, {12}, cpp-output, (device-sycl) +// CHK-PHASES-FILES: 14: compiler, {13}, sycl-header, (device-sycl) +// CHK-PHASES-FILES: 15: offload, "host-sycl (x86_64-unknown-linux-gnu)" {11}, "device-sycl (spir64-unknown-linux-sycldevice)" {14}, cpp-output +// CHK-PHASES-FILES: 16: compiler, {15}, ir, (host-sycl) +// CHK-PHASES-FILES: 17: backend, {16}, assembler, (host-sycl) +// CHK-PHASES-FILES: 18: assembler, {17}, object, (host-sycl) +// CHK-PHASES-FILES: 19: linker, {0, 9, 18}, image, (host-sycl) +// CHK-PHASES-FILES: 20: input, "somelib", object, (device-sycl) +// CHK-PHASES-FILES: 21: compiler, {4}, ir, (device-sycl) +// CHK-PHASES-FILES: 22: backend, {21}, assembler, (device-sycl) +// CHK-PHASES-FILES: 23: assembler, {22}, object, (device-sycl) +// CHK-PHASES-FILES: 24: compiler, {13}, ir, (device-sycl) +// CHK-PHASES-FILES: 25: backend, {24}, assembler, (device-sycl) +// CHK-PHASES-FILES: 26: assembler, {25}, object, (device-sycl) +// CHK-PHASES-FILES: 27: linker, {20, 23, 26}, image, (device-sycl) +// CHK-PHASES-FILES: 28: offload, "host-sycl (x86_64-unknown-linux-gnu)" {19}, "device-sycl (spir64-unknown-linux-sycldevice)" {27}, image + +/// ########################################################################### + +/// Check separate compilation with offloading - bundling actions +// RUN: %clang -### -ccc-print-phases -target x86_64-unknown-linux-gnu -fsycl -c -o %t.o -lsomelib -fsycl-targets=spir64-unknown-linux-sycldevice %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-BUACTIONS %s +// CHK-BUACTIONS: 0: input, "[[INPUT:.+\.c]]", c, (device-sycl) +// CHK-BUACTIONS: 1: preprocessor, {0}, cpp-output, (device-sycl) +// CHK-BUACTIONS: 2: compiler, {1}, ir, (device-sycl) +// CHK-BUACTIONS: 3: backend, {2}, assembler, (device-sycl) +// CHK-BUACTIONS: 4: assembler, {3}, object, (device-sycl) +// CHK-BUACTIONS: 5: offload, "device-sycl (spir64-unknown-linux-sycldevice)" {4}, object +// CHK-BUACTIONS: 6: input, "[[INPUT]]", c, (host-sycl) +// CHK-BUACTIONS: 7: preprocessor, {6}, cpp-output, (host-sycl) +// CHK-BUACTIONS: 8: compiler, {1}, sycl-header, (device-sycl) +// CHK-BUACTIONS: 9: offload, "host-sycl (x86_64-unknown-linux-gnu)" {7}, "device-sycl (spir64-unknown-linux-sycldevice)" {8}, cpp-output +// CHK-BUACTIONS: 10: compiler, {9}, ir, (host-sycl) +// CHK-BUACTIONS: 11: backend, {10}, assembler, (host-sycl) +// CHK-BUACTIONS: 12: assembler, {11}, object, (host-sycl) +// CHK-BUACTIONS: 13: clang-offload-bundler, {5, 12}, object, (host-sycl) + +/// ########################################################################### + +/// Check separate compilation with offloading - unbundling actions +// RUN: touch %t.o +// RUN: %clang -### -ccc-print-phases -target x86_64-unknown-linux-gnu -fsycl -o %t.out -lsomelib -fsycl-targets=spir64-unknown-linux-sycldevice %t.o 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-UBACTIONS %s +// CHK-UBACTIONS: 0: input, "somelib", object, (host-sycl) +// CHK-UBACTIONS: 1: input, "[[INPUT:.+\.o]]", object, (host-sycl) +// CHK-UBACTIONS: 2: clang-offload-unbundler, {1}, object, (host-sycl) +// CHK-UBACTIONS: 3: linker, {0, 2}, image, (host-sycl) +// CHK-UBACTIONS: 4: input, "somelib", object, (device-sycl) +// CHK-UBACTIONS: 5: linker, {4, 2}, image, (device-sycl) +// CHK-UBACTIONS: 6: offload, "host-sycl (x86_64-unknown-linux-gnu)" {3}, "device-sycl (spir64-unknown-linux-sycldevice)" {5}, image + +/// ########################################################################### + +/// Check separate compilation with offloading - unbundling with source +// RUN: touch %t.o +// RUN: %clang -### -ccc-print-phases -target x86_64-unknown-linux-gnu -lsomelib -fsycl %t.o -fsycl-targets=spir64-unknown-linux-sycldevice %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-UBUACTIONS %s +// CHK-UBUACTIONS: 0: input, "somelib", object, (host-sycl) +// CHK-UBUACTIONS: 1: input, "[[INPUT1:.+\.o]]", object, (host-sycl) +// CHK-UBUACTIONS: 2: clang-offload-unbundler, {1}, object, (host-sycl) +// CHK-UBUACTIONS: 3: input, "[[INPUT2:.+\.c]]", c, (host-sycl) +// CHK-UBUACTIONS: 4: preprocessor, {3}, cpp-output, (host-sycl) +// CHK-UBUACTIONS: 5: input, "[[INPUT2]]", c, (device-sycl) +// CHK-UBUACTIONS: 6: preprocessor, {5}, cpp-output, (device-sycl) +// CHK-UBUACTIONS: 7: compiler, {6}, sycl-header, (device-sycl) +// CHK-UBUACTIONS: 8: offload, "host-sycl (x86_64-unknown-linux-gnu)" {4}, "device-sycl (spir64-unknown-linux-sycldevice)" {7}, cpp-output +// CHK-UBUACTIONS: 9: compiler, {8}, ir, (host-sycl) +// CHK-UBUACTIONS: 10: backend, {9}, assembler, (host-sycl) +// CHK-UBUACTIONS: 11: assembler, {10}, object, (host-sycl) +// CHK-UBUACTIONS: 12: linker, {0, 2, 11}, image, (host-sycl) +// CHK-UBUACTIONS: 13: input, "somelib", object, (device-sycl) +// CHK-UBUACTIONS: 14: compiler, {6}, ir, (device-sycl) +// CHK-UBUACTIONS: 15: backend, {14}, assembler, (device-sycl) +// CHK-UBUACTIONS: 16: assembler, {15}, object, (device-sycl) +// CHK-UBUACTIONS: 17: linker, {13, 2, 16}, image, (device-sycl) +// CHK-UBUACTIONS: 18: offload, "host-sycl (x86_64-unknown-linux-gnu)" {12}, "device-sycl (spir64-unknown-linux-sycldevice)" {17}, image + +/// ########################################################################### + +/// Check -fsycl-is-device is passed when compiling for the device. +// RUN: %clang -### -no-canonical-prefixes -fsycl -fsycl-targets=spir64-unknown-linux-sycldevice %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-FSYCL-IS-DEVICE %s + +// CHK-FSYCL-IS-DEVICE: clang{{.*}} "-fsycl-is-device" {{.*}}.c diff --git a/clang/tools/clang-offload-bundler/ClangOffloadBundler.cpp b/clang/tools/clang-offload-bundler/ClangOffloadBundler.cpp index 29cd9848d1115..761788a75b7b8 100644 --- a/clang/tools/clang-offload-bundler/ClangOffloadBundler.cpp +++ b/clang/tools/clang-offload-bundler/ClangOffloadBundler.cpp @@ -972,6 +972,7 @@ int main(int argc, const char **argv) { .Case("host", true) .Case("openmp", true) .Case("hip", true) + .Case("sycl", true) .Default(false); bool TripleIsValid = !Triple.empty();