From 09ceae6c4b97c7acc0d05eeb3a38a4396d7c72d8 Mon Sep 17 00:00:00 2001 From: Samuel Antao <sfantao@us.ibm.com> Date: Thu, 27 Oct 2016 16:38:05 +0000 Subject: [PATCH] [Driver][OpenMP] Create tool chains for OpenMP offloading kind. Summary: This patch adds new logic to create the necessary tool chains to support offloading for OpenMP. The OpenMP related options are checked and the tool chains created accordingly. Diagnostics are emitted in case the options are illegal or express unknown targets. Reviewers: echristo, tra, jlebar, rsmith, ABataev, hfinkel Subscribers: whchung, mkuron, mehdi_amini, cfe-commits, Hahnfeld, arpith-jacob, carlo.bertolli, caomhin Differential Revision: https://reviews.llvm.org/D21843 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@285311 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticDriverKinds.td | 5 ++ include/clang/Driver/Action.h | 1 + include/clang/Driver/Driver.h | 23 ++++++ lib/Driver/Action.cpp | 4 + lib/Driver/Driver.cpp | 79 ++++++++++++++++++ lib/Driver/Tools.cpp | 85 +++++--------------- test/Driver/openmp-offload.c | 37 +++++++++ 7 files changed, 167 insertions(+), 67 deletions(-) create mode 100644 test/Driver/openmp-offload.c diff --git a/include/clang/Basic/DiagnosticDriverKinds.td b/include/clang/Basic/DiagnosticDriverKinds.td index 064919fa58c..c28c92e7a78 100644 --- a/include/clang/Basic/DiagnosticDriverKinds.td +++ b/include/clang/Basic/DiagnosticDriverKinds.td @@ -159,6 +159,11 @@ 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 warn_drv_omp_offload_target_duplicate : Warning< + "The OpenMP offloading target '%0' is similar to target '%1' already specified - will be ignored.">, + InGroup<OpenMPTarget>; def err_drv_bitcode_unsupported_on_toolchain : Error< "-fembed-bitcode is not supported on versions of iOS prior to 6.0">; diff --git a/include/clang/Driver/Action.h b/include/clang/Driver/Action.h index f947632dfc1..c913c4c8eb4 100644 --- a/include/clang/Driver/Action.h +++ b/include/clang/Driver/Action.h @@ -80,6 +80,7 @@ public: 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); diff --git a/include/clang/Driver/Driver.h b/include/clang/Driver/Driver.h index f4bd7e9f612..af3a84675cc 100644 --- a/include/clang/Driver/Driver.h +++ b/include/clang/Driver/Driver.h @@ -91,6 +91,26 @@ class Driver { 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); @@ -272,6 +292,9 @@ public: 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 /// @{ diff --git a/lib/Driver/Action.cpp b/lib/Driver/Action.cpp index d30f2cf6569..a351429ad7b 100644 --- a/lib/Driver/Action.cpp +++ b/lib/Driver/Action.cpp @@ -87,6 +87,8 @@ std::string Action::getOffloadingKindPrefix() const { 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 Action::getOffloadingKindPrefix() const { std::string Res("host"); if (ActiveOffloadKindMask & OFK_Cuda) Res += "-cuda"; + if (ActiveOffloadKindMask & OFK_OpenMP) + Res += "-openmp"; // TODO: Add other programming models here. diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 956cceadae1..bdf3e0ebe85 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -434,6 +434,32 @@ void Driver::setLTOMode(const llvm::opt::ArgList &Args) { } } +/// 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<OpenMPRuntimeKind>(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) { @@ -454,6 +480,59 @@ void Driver::CreateOffloadingDeviceToolChains(Compilation &C, C.addOffloadDeviceToolChain(&TC, Action::OFK_Cuda); } + // + // 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<const char *> 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. // diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index 36b4c950ae4..848ea5bf1e8 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -3009,72 +3009,23 @@ static void addClangRT(const ToolChain &TC, const ArgList &Args, 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<OpenMPRuntimeKind>(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; } @@ -5188,9 +5139,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, 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"); @@ -9854,21 +9805,21 @@ void gnutools::Linker::ConstructJob(Compilation &C, const JobAction &JA, 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; } @@ -10546,16 +10497,16 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA, 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; } diff --git a/test/Driver/openmp-offload.c b/test/Driver/openmp-offload.c new file mode 100644 index 00000000000..8faa18b309b --- /dev/null +++ b/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, please use -fopenmp=libomp or -fopenmp=libiomp5. + +/// ########################################################################### + +/// 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. -- GitLab