Index: include/clang/Basic/DiagnosticDriverKinds.td =================================================================== --- include/clang/Basic/DiagnosticDriverKinds.td +++ include/clang/Basic/DiagnosticDriverKinds.td @@ -155,6 +155,11 @@ "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.">; +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 err_drv_bitcode_unsupported_on_toolchain : Error< "-fembed-bitcode is not supported on versions of iOS prior to 6.0">; Index: include/clang/Driver/Action.h =================================================================== --- include/clang/Driver/Action.h +++ include/clang/Driver/Action.h @@ -80,6 +80,7 @@ OFK_Host = 0x01, // The device offloading tool chains - one bit for each programming model. OFK_Cuda = 0x02, + OFK_OpenMP = 0x04, }; static const char *getClassName(ActionClass AC); Index: include/clang/Driver/Driver.h =================================================================== --- include/clang/Driver/Driver.h +++ include/clang/Driver/Driver.h @@ -91,6 +91,26 @@ LTOKind LTOMode; public: + enum OpenMPRuntimeKind { + /// An unknown OpenMP runtime. We can't generate effective OpenMP code + /// without knowing what runtime to target. + OMPRT_Unknown, + + /// The LLVM OpenMP runtime. When completed and integrated, this will become + /// the default for Clang. + OMPRT_OMP, + + /// The GNU OpenMP runtime. Clang doesn't support generating OpenMP code for + /// this runtime but can swallow the pragmas, and find and link against the + /// runtime library itself. + OMPRT_GOMP, + + /// The legacy name for the LLVM OpenMP runtime from when it was the Intel + /// OpenMP runtime. We support this mode for users with existing + /// dependencies on this runtime library name. + OMPRT_IOMP5 + }; + // Diag - Forwarding function for diagnostics. DiagnosticBuilder Diag(unsigned DiagID) const { return Diags.Report(DiagID); @@ -269,6 +289,9 @@ bool embedBitcodeEnabled() const { return BitcodeEmbed == EmbedBitcode; } bool embedBitcodeMarkerOnly() const { return BitcodeEmbed == EmbedMarker; } + /// Compute the desired OpenMP runtime from the flags provided. + OpenMPRuntimeKind getOpenMPRuntime(const llvm::opt::ArgList &Args) const; + /// @} /// @name Primary Functionality /// @{ Index: lib/Driver/Action.cpp =================================================================== --- lib/Driver/Action.cpp +++ lib/Driver/Action.cpp @@ -87,6 +87,8 @@ break; case OFK_Cuda: return "device-cuda"; + case OFK_OpenMP: + return "device-openmp"; // TODO: Add other programming models here. } @@ -97,6 +99,8 @@ std::string Res("host"); if (ActiveOffloadKindMask & OFK_Cuda) Res += "-cuda"; + if (ActiveOffloadKindMask & OFK_OpenMP) + Res += "-openmp"; // TODO: Add other programming models here. Index: lib/Driver/Driver.cpp =================================================================== --- lib/Driver/Driver.cpp +++ lib/Driver/Driver.cpp @@ -422,6 +422,32 @@ } } +/// Compute the desired OpenMP runtime from the flags provided. +Driver::OpenMPRuntimeKind Driver::getOpenMPRuntime(const ArgList &Args) const { + StringRef RuntimeName(CLANG_DEFAULT_OPENMP_RUNTIME); + + const Arg *A = Args.getLastArg(options::OPT_fopenmp_EQ); + if (A) + RuntimeName = A->getValue(); + + auto RT = llvm::StringSwitch(RuntimeName) + .Case("libomp", OMPRT_OMP) + .Case("libgomp", OMPRT_GOMP) + .Case("libiomp5", OMPRT_IOMP5) + .Default(OMPRT_Unknown); + + if (RT == OMPRT_Unknown) { + if (A) + Diag(diag::err_drv_unsupported_option_argument) + << A->getOption().getName() << A->getValue(); + else + // FIXME: We could use a nicer diagnostic here. + Diag(diag::err_drv_unsupported_opt) << "-fopenmp"; + } + + return RT; +} + void Driver::CreateOffloadingDeviceToolChains(Compilation &C, InputList &Inputs) { @@ -443,6 +469,59 @@ } // + // OpenMP + // + // We need to generate an OpenMP toolchain if the user specified targets with + // the -fopenmp-targets option. + if (Arg *OpenMPTargets = + C.getInputArgs().getLastArg(options::OPT_fopenmp_targets_EQ)) { + if (OpenMPTargets->getNumValues()) { + // We expect that -fopenmp-targets is always used in conjunction with the + // option -fopenmp specifying a valid runtime with offloading support, + // i.e. libomp or libiomp. + bool HasValidOpenMPRuntime = C.getInputArgs().hasFlag( + options::OPT_fopenmp, options::OPT_fopenmp_EQ, + options::OPT_fno_openmp, false); + if (HasValidOpenMPRuntime) { + OpenMPRuntimeKind OpenMPKind = getOpenMPRuntime(C.getInputArgs()); + HasValidOpenMPRuntime = + OpenMPKind == OMPRT_OMP || OpenMPKind == OMPRT_IOMP5; + } + + if (HasValidOpenMPRuntime) { + llvm::StringMap FoundNormalizedTriples; + for (const char *Val : OpenMPTargets->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_omp_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_omp_target) << Val; + else { + const ToolChain &TC = getToolChain(C.getInputArgs(), TT); + C.addOffloadDeviceToolChain(&TC, Action::OFK_OpenMP); + } + } + } else + Diag(clang::diag::err_drv_expecting_fopenmp_with_fopenmp_targets); + } else + Diag(clang::diag::warn_drv_empty_joined_argument) + << OpenMPTargets->getAsString(C.getInputArgs()); + } + + // // TODO: Add support for other offloading programming models here. // Index: lib/Driver/Tools.cpp =================================================================== --- lib/Driver/Tools.cpp +++ lib/Driver/Tools.cpp @@ -2988,72 +2988,23 @@ CmdArgs.push_back(TC.getCompilerRTArgString(Args, "builtins")); } -namespace { -enum OpenMPRuntimeKind { - /// An unknown OpenMP runtime. We can't generate effective OpenMP code - /// without knowing what runtime to target. - OMPRT_Unknown, - - /// The LLVM OpenMP runtime. When completed and integrated, this will become - /// the default for Clang. - OMPRT_OMP, - - /// The GNU OpenMP runtime. Clang doesn't support generating OpenMP code for - /// this runtime but can swallow the pragmas, and find and link against the - /// runtime library itself. - OMPRT_GOMP, - - /// The legacy name for the LLVM OpenMP runtime from when it was the Intel - /// OpenMP runtime. We support this mode for users with existing dependencies - /// on this runtime library name. - OMPRT_IOMP5 -}; -} - -/// Compute the desired OpenMP runtime from the flag provided. -static OpenMPRuntimeKind getOpenMPRuntime(const ToolChain &TC, - const ArgList &Args) { - StringRef RuntimeName(CLANG_DEFAULT_OPENMP_RUNTIME); - - const Arg *A = Args.getLastArg(options::OPT_fopenmp_EQ); - if (A) - RuntimeName = A->getValue(); - - auto RT = llvm::StringSwitch(RuntimeName) - .Case("libomp", OMPRT_OMP) - .Case("libgomp", OMPRT_GOMP) - .Case("libiomp5", OMPRT_IOMP5) - .Default(OMPRT_Unknown); - - if (RT == OMPRT_Unknown) { - if (A) - TC.getDriver().Diag(diag::err_drv_unsupported_option_argument) - << A->getOption().getName() << A->getValue(); - else - // FIXME: We could use a nicer diagnostic here. - TC.getDriver().Diag(diag::err_drv_unsupported_opt) << "-fopenmp"; - } - - return RT; -} - static void addOpenMPRuntime(ArgStringList &CmdArgs, const ToolChain &TC, const ArgList &Args) { if (!Args.hasFlag(options::OPT_fopenmp, options::OPT_fopenmp_EQ, options::OPT_fno_openmp, false)) return; - switch (getOpenMPRuntime(TC, Args)) { - case OMPRT_OMP: + switch (TC.getDriver().getOpenMPRuntime(Args)) { + case Driver::OMPRT_OMP: CmdArgs.push_back("-lomp"); break; - case OMPRT_GOMP: + case Driver::OMPRT_GOMP: CmdArgs.push_back("-lgomp"); break; - case OMPRT_IOMP5: + case Driver::OMPRT_IOMP5: CmdArgs.push_back("-liomp5"); break; - case OMPRT_Unknown: + case Driver::OMPRT_Unknown: // Already diagnosed. break; } @@ -5015,9 +4966,9 @@ if (Args.hasFlag(options::OPT_fopenmp, options::OPT_fopenmp_EQ, options::OPT_fno_openmp, false) && JA.isDeviceOffloading(Action::OFK_None)) { - switch (getOpenMPRuntime(getToolChain(), Args)) { - case OMPRT_OMP: - case OMPRT_IOMP5: + switch (getToolChain().getDriver().getOpenMPRuntime(Args)) { + case Driver::OMPRT_OMP: + case Driver::OMPRT_IOMP5: // Clang can generate useful OpenMP code for these two runtime libraries. CmdArgs.push_back("-fopenmp"); @@ -9522,21 +9473,21 @@ WantPthread = true; // Also link the particular OpenMP runtimes. - switch (getOpenMPRuntime(ToolChain, Args)) { - case OMPRT_OMP: + switch (ToolChain.getDriver().getOpenMPRuntime(Args)) { + case Driver::OMPRT_OMP: CmdArgs.push_back("-lomp"); break; - case OMPRT_GOMP: + case Driver::OMPRT_GOMP: CmdArgs.push_back("-lgomp"); // FIXME: Exclude this for platforms with libgomp that don't require // librt. Most modern Linux platforms require it, but some may not. CmdArgs.push_back("-lrt"); break; - case OMPRT_IOMP5: + case Driver::OMPRT_IOMP5: CmdArgs.push_back("-liomp5"); break; - case OMPRT_Unknown: + case Driver::OMPRT_Unknown: // Already diagnosed. break; } @@ -10105,16 +10056,16 @@ CmdArgs.push_back("-nodefaultlib:vcompd.lib"); CmdArgs.push_back(Args.MakeArgString(std::string("-libpath:") + TC.getDriver().Dir + "/../lib")); - switch (getOpenMPRuntime(getToolChain(), Args)) { - case OMPRT_OMP: + switch (TC.getDriver().getOpenMPRuntime(Args)) { + case Driver::OMPRT_OMP: CmdArgs.push_back("-defaultlib:libomp.lib"); break; - case OMPRT_IOMP5: + case Driver::OMPRT_IOMP5: CmdArgs.push_back("-defaultlib:libiomp5md.lib"); break; - case OMPRT_GOMP: + case Driver::OMPRT_GOMP: break; - case OMPRT_Unknown: + case Driver::OMPRT_Unknown: // Already diagnosed. break; } Index: test/Driver/openmp-offload.c =================================================================== --- /dev/null +++ test/Driver/openmp-offload.c @@ -0,0 +1,37 @@ +/// +/// Perform several driver tests for OpenMP offloading +/// + +/// ########################################################################### + +/// Check whether an invalid OpenMP target is specified: +// RUN: %clang -### -fopenmp=libomp -fopenmp-targets=aaa-bbb-ccc-ddd %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-INVALID-TARGET %s +// RUN: %clang -### -fopenmp -fopenmp-targets=aaa-bbb-ccc-ddd %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-INVALID-TARGET %s +// CHK-INVALID-TARGET: error: OpenMP target is invalid: 'aaa-bbb-ccc-ddd' + +/// ########################################################################### + +/// Check warning for empty -fopenmp-targets +// RUN: %clang -### -fopenmp=libomp -fopenmp-targets= %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-EMPTY-OMPTARGETS %s +// RUN: %clang -### -fopenmp -fopenmp-targets= %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-EMPTY-OMPTARGETS %s +// CHK-EMPTY-OMPTARGETS: warning: joined argument expects additional value: '-fopenmp-targets=' + +/// ########################################################################### + +/// Check error for no -fopenmp option +// RUN: %clang -### -fopenmp-targets=powerpc64le-ibm-linux-gnu %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-NO-FOPENMP %s +// RUN: %clang -### -fopenmp=libgomp -fopenmp-targets=powerpc64le-ibm-linux-gnu %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-NO-FOPENMP %s +// CHK-NO-FOPENMP: error: The option -fopenmp-targets must be used in conjunction with a -fopenmp option compatible with offloading. + +/// ########################################################################### + +/// Check warning for duplicate offloading targets. +// RUN: %clang -### -ccc-print-phases -fopenmp -fopenmp-targets=powerpc64le-ibm-linux-gnu,powerpc64le-ibm-linux-gnu %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-DUPLICATES %s +// CHK-DUPLICATES: warning: The OpenMP offloading target 'powerpc64le-ibm-linux-gnu' is similar to target 'powerpc64le-ibm-linux-gnu' already specified - will be ignored.