[clang] [clang][HIP] Apply clang-cl optimization options to device code (PR #228096)
Yaxun Liu via cfe-commits
cfe-commits at lists.llvm.org
Sat Oct 3 04:18:35 PDT 2026
https://github.com/yxsamliu updated https://github.com/llvm/llvm-project/pull/228096
>From f4d578919ca8e4a0c9eebfb27f65ad637e4ffda1 Mon Sep 17 00:00:00 2001
From: "Yaxun (Sam) Liu" <yaxun.liu at amd.com>
Date: Thu, 1 Oct 2026 10:17:45 -0400
Subject: [PATCH 1/4] [clang][HIP] Apply clang-cl optimization options to
device code
clang-cl options such as /O1, /O2, and /Ox currently optimize only
host code in HIP builds, leaving device code unoptimized.
Translate these options before separating host and device arguments so
both compilations receive the requested optimization settings.
---
clang/docs/ReleaseNotes.md | 3 +
clang/include/clang/Driver/Driver.h | 3 +-
clang/lib/Driver/Driver.cpp | 130 +++++++++++++++++++++-
clang/lib/Driver/ToolChains/MSVC.cpp | 130 +---------------------
clang/test/Driver/cl-options.cu | 7 ++
clang/test/Driver/hip-cl-optimization.hip | 106 ++++++++++++++++++
6 files changed, 247 insertions(+), 132 deletions(-)
create mode 100644 clang/test/Driver/hip-cl-optimization.hip
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index d22874d4130eec..4fdefafccea14d 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -815,6 +815,9 @@ features cannot lower the translation-unit ABI level;
#### Miscellaneous Bug Fixes
+- Fixed `clang-cl` optimization options such as `/O1`, `/O2`, and
+ `/Ox` being ignored when compiling HIP device code.
+
#### Miscellaneous Clang Crashes Fixed
- Fixed a crash in CTAD for type alias templates when the aggregate deduction guide could not be resolved. (#GH206994)
diff --git a/clang/include/clang/Driver/Driver.h b/clang/include/clang/Driver/Driver.h
index 15b6fdcb8a5746..914ef62cd62fa9 100644
--- a/clang/include/clang/Driver/Driver.h
+++ b/clang/include/clang/Driver/Driver.h
@@ -360,7 +360,8 @@ class Driver {
/// TranslateInputArgs - Create a new derived argument list from the input
/// arguments, after applying the standard argument translations.
llvm::opt::DerivedArgList *
- TranslateInputArgs(const llvm::opt::InputArgList &Args) const;
+ TranslateInputArgs(const llvm::opt::InputArgList &Args,
+ const llvm::Triple &Triple) const;
// handleArguments - All code related to claiming and printing diagnostics
// related to arguments to the driver are done here.
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 7649941a68b1cc..07f70fbbe0128c 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -459,10 +459,128 @@ Arg *clang::driver::makeInputArg(DerivedArgList &Args, const OptTable &Opts,
return A;
}
-DerivedArgList *Driver::TranslateInputArgs(const InputArgList &Args) const {
+static void translateMSVCOptArg(Arg *A, llvm::opt::DerivedArgList &DAL,
+ bool SupportsForcingFramePointer,
+ const char *ExpandChar, const OptTable &Opts) {
+ assert(A->getOption().matches(options::OPT__SLASH_O));
+
+ StringRef OptStr = A->getValue();
+ for (size_t I = 0, E = OptStr.size(); I != E; ++I) {
+ const char &OptChar = *(OptStr.data() + I);
+ switch (OptChar) {
+ default:
+ break;
+ case '1':
+ case '2':
+ case 'x':
+ case 'd':
+ // Ignore /O[12xd] flags that aren't the last one on the command line.
+ // Only the last one gets expanded.
+ if (&OptChar != ExpandChar) {
+ A->claim();
+ break;
+ }
+ if (OptChar == 'd') {
+ DAL.AddFlagArg(A, Opts.getOption(options::OPT_O0));
+ } else {
+ if (OptChar == '1') {
+ DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "s");
+ } else if (OptChar == '2' || OptChar == 'x') {
+ DAL.AddFlagArg(A, Opts.getOption(options::OPT_fbuiltin));
+ DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "3");
+ }
+ if (SupportsForcingFramePointer &&
+ !DAL.hasArgNoClaim(options::OPT_fno_omit_frame_pointer))
+ DAL.AddFlagArg(A, Opts.getOption(options::OPT_fomit_frame_pointer));
+ if (OptChar == '1' || OptChar == '2')
+ DAL.AddFlagArg(A, Opts.getOption(options::OPT_ffunction_sections));
+ }
+ break;
+ case 'b':
+ if (I + 1 != E && isdigit(OptStr[I + 1])) {
+ switch (OptStr[I + 1]) {
+ case '0':
+ DAL.AddFlagArg(A, Opts.getOption(options::OPT_fno_inline));
+ break;
+ case '1':
+ DAL.AddFlagArg(A,
+ Opts.getOption(options::OPT_finline_hint_functions));
+ break;
+ case '2':
+ case '3':
+ DAL.AddFlagArg(A, Opts.getOption(options::OPT_finline_functions));
+ break;
+ }
+ ++I;
+ }
+ break;
+ case 'g':
+ A->claim();
+ break;
+ case 'i':
+ if (I + 1 != E && OptStr[I + 1] == '-') {
+ ++I;
+ DAL.AddFlagArg(A, Opts.getOption(options::OPT_fno_builtin));
+ } else {
+ DAL.AddFlagArg(A, Opts.getOption(options::OPT_fbuiltin));
+ }
+ break;
+ case 's':
+ DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "s");
+ break;
+ case 't':
+ DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "3");
+ break;
+ case 'y': {
+ bool OmitFramePointer = true;
+ if (I + 1 != E && OptStr[I + 1] == '-') {
+ OmitFramePointer = false;
+ ++I;
+ }
+ if (SupportsForcingFramePointer) {
+ if (OmitFramePointer)
+ DAL.AddFlagArg(A, Opts.getOption(options::OPT_fomit_frame_pointer));
+ else
+ DAL.AddFlagArg(A,
+ Opts.getOption(options::OPT_fno_omit_frame_pointer));
+ } else {
+ // Silently accept /Oy- on x86-64 for portable clang-cl build flags.
+ A->claim();
+ }
+ break;
+ }
+ }
+ }
+}
+
+DerivedArgList *Driver::TranslateInputArgs(const InputArgList &Args,
+ const llvm::Triple &Triple) const {
const llvm::opt::OptTable &Opts = getOpts();
DerivedArgList *DAL = new DerivedArgList(Args);
+ // Normalize MSVC optimization options before host and device arguments split.
+ bool TranslateMSVCOpts = Triple.isWindowsMSVCEnvironment();
+ // /Oy and /Oy- do not affect the x86-64 host.
+ bool SupportsForcingFramePointer = Triple.getArch() != llvm::Triple::x86_64;
+ // Expand only the last /O[12xd], preserving overrides such as /O2 /Oy-.
+ const char *ExpandChar = nullptr;
+ if (TranslateMSVCOpts) {
+ for (Arg *A : Args.filtered(options::OPT__SLASH_O)) {
+ StringRef OptStr = A->getValue();
+ for (size_t I = 0, E = OptStr.size(); I != E; ++I) {
+ char OptChar = OptStr[I];
+ char PrevChar = I > 0 ? OptStr[I - 1] : '0';
+ if (PrevChar == 'b') {
+ // OptChar does not expand; it's an argument to the previous char.
+ continue;
+ }
+ if (OptChar == '1' || OptChar == '2' || OptChar == 'x' ||
+ OptChar == 'd')
+ ExpandChar = OptStr.data() + I;
+ }
+ }
+ }
+
bool HasNostdlib = Args.hasArg(options::OPT_nostdlib);
bool HasNostdlibxx = Args.hasArg(options::OPT_nostdlibxx);
bool HasNodefaultlib = Args.hasArg(options::OPT_nodefaultlibs);
@@ -480,6 +598,14 @@ DerivedArgList *Driver::TranslateInputArgs(const InputArgList &Args) const {
continue;
}
+ if (TranslateMSVCOpts && A->getOption().matches(options::OPT__SLASH_O)) {
+ // Keep the original argument for unused-option diagnostics.
+ DAL->append(A);
+ translateMSVCOptArg(A, *DAL, SupportsForcingFramePointer, ExpandChar,
+ Opts);
+ continue;
+ }
+
// Unfortunately, we have to parse some forwarding options (-Xassembler,
// -Xlinker, -Xpreprocessor) because we either integrate their functionality
// (assembler and preprocessor), or bypass a previous driver ('collect2').
@@ -1783,7 +1909,7 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
}
// Perform the default argument translations.
- DerivedArgList *TranslatedArgs = TranslateInputArgs(*UArgs);
+ DerivedArgList *TranslatedArgs = TranslateInputArgs(*UArgs, TC.getTriple());
// Check if the environment version is valid except wasm case.
llvm::Triple Triple = TC.getTriple();
diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp
index 25ea64a4ba5055..d32d5b9a52fc94 100644
--- a/clang/lib/Driver/ToolChains/MSVC.cpp
+++ b/clang/lib/Driver/ToolChains/MSVC.cpp
@@ -1023,103 +1023,6 @@ SanitizerMask MSVCToolChain::getSupportedSanitizers(
return Res;
}
-static void TranslateOptArg(Arg *A, llvm::opt::DerivedArgList &DAL,
- bool SupportsForcingFramePointer,
- const char *ExpandChar, const OptTable &Opts) {
- assert(A->getOption().matches(options::OPT__SLASH_O));
-
- StringRef OptStr = A->getValue();
- for (size_t I = 0, E = OptStr.size(); I != E; ++I) {
- const char &OptChar = *(OptStr.data() + I);
- switch (OptChar) {
- default:
- break;
- case '1':
- case '2':
- case 'x':
- case 'd':
- // Ignore /O[12xd] flags that aren't the last one on the command line.
- // Only the last one gets expanded.
- if (&OptChar != ExpandChar) {
- A->claim();
- break;
- }
- if (OptChar == 'd') {
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_O0));
- } else {
- if (OptChar == '1') {
- DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "s");
- } else if (OptChar == '2' || OptChar == 'x') {
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_fbuiltin));
- DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "3");
- }
- if (SupportsForcingFramePointer &&
- !DAL.hasArgNoClaim(options::OPT_fno_omit_frame_pointer))
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_fomit_frame_pointer));
- if (OptChar == '1' || OptChar == '2')
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_ffunction_sections));
- }
- break;
- case 'b':
- if (I + 1 != E && isdigit(OptStr[I + 1])) {
- switch (OptStr[I + 1]) {
- case '0':
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_fno_inline));
- break;
- case '1':
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_finline_hint_functions));
- break;
- case '2':
- case '3':
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_finline_functions));
- break;
- }
- ++I;
- }
- break;
- case 'g':
- A->claim();
- break;
- case 'i':
- if (I + 1 != E && OptStr[I + 1] == '-') {
- ++I;
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_fno_builtin));
- } else {
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_fbuiltin));
- }
- break;
- case 's':
- DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "s");
- break;
- case 't':
- DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "3");
- break;
- case 'y': {
- bool OmitFramePointer = true;
- if (I + 1 != E && OptStr[I + 1] == '-') {
- OmitFramePointer = false;
- ++I;
- }
- if (SupportsForcingFramePointer) {
- if (OmitFramePointer)
- DAL.AddFlagArg(A,
- Opts.getOption(options::OPT_fomit_frame_pointer));
- else
- DAL.AddFlagArg(
- A, Opts.getOption(options::OPT_fno_omit_frame_pointer));
- } else {
- // Don't warn about /Oy- in x86-64 builds (where
- // SupportsForcingFramePointer is false). The flag having no effect
- // there is a compiler-internal optimization, and people shouldn't have
- // to special-case their build files for x86-64 clang-cl.
- A->claim();
- }
- break;
- }
- }
- }
-}
-
static void TranslateDArg(Arg *A, llvm::opt::DerivedArgList &DAL,
const OptTable &Opts) {
assert(A->getOption().matches(options::OPT_D));
@@ -1154,39 +1057,8 @@ MSVCToolChain::TranslateArgs(const llvm::opt::DerivedArgList &Args,
DerivedArgList *DAL = new DerivedArgList(Args.getBaseArgs());
const OptTable &Opts = getDriver().getOpts();
- // /Oy and /Oy- don't have an effect on X86-64
- bool SupportsForcingFramePointer = getArch() != llvm::Triple::x86_64;
-
- // The -O[12xd] flag actually expands to several flags. We must desugar the
- // flags so that options embedded can be negated. For example, the '-O2' flag
- // enables '-Oy'. Expanding '-O2' into its constituent flags allows us to
- // correctly handle '-O2 -Oy-' where the trailing '-Oy-' disables a single
- // aspect of '-O2'.
- //
- // Note that this expansion logic only applies to the *last* of '[12xd]'.
-
- // First step is to search for the character we'd like to expand.
- const char *ExpandChar = nullptr;
- for (Arg *A : Args.filtered(options::OPT__SLASH_O)) {
- StringRef OptStr = A->getValue();
- for (size_t I = 0, E = OptStr.size(); I != E; ++I) {
- char OptChar = OptStr[I];
- char PrevChar = I > 0 ? OptStr[I - 1] : '0';
- if (PrevChar == 'b') {
- // OptChar does not expand; it's an argument to the previous char.
- continue;
- }
- if (OptChar == '1' || OptChar == '2' || OptChar == 'x' || OptChar == 'd')
- ExpandChar = OptStr.data() + I;
- }
- }
-
for (Arg *A : Args) {
- if (A->getOption().matches(options::OPT__SLASH_O)) {
- // The -O flag actually takes an amalgam of other options. For example,
- // '/Ogyb2' is equivalent to '/Og' '/Oy' '/Ob2'.
- TranslateOptArg(A, *DAL, SupportsForcingFramePointer, ExpandChar, Opts);
- } else if (A->getOption().matches(options::OPT_D)) {
+ if (A->getOption().matches(options::OPT_D)) {
// Translate -Dfoo#bar into -Dfoo=bar.
TranslateDArg(A, *DAL, Opts);
} else if (A->getOption().matches(options::OPT__SLASH_permissive)) {
diff --git a/clang/test/Driver/cl-options.cu b/clang/test/Driver/cl-options.cu
index b241ec6672d851..2517e7d77d4a9e 100644
--- a/clang/test/Driver/cl-options.cu
+++ b/clang/test/Driver/cl-options.cu
@@ -25,3 +25,10 @@
// Gd-NOT: "-fdefault-calling-conv=cdecl"
// Gd: "-cc1" "-triple"
// Gd: "-fdefault-calling-conv=cdecl"
+
+// Optimization options must continue to apply to both CUDA compilation jobs.
+// RUN: not %clang_cl /c /O2 -### -nocudalib -nocudainc -- %s 2>&1 | FileCheck -check-prefix=O2 %s
+// O2: "-cc1" "-triple" "nvptx{{(64)?}}-nvidia-cuda"
+// O2-SAME: "-O3"
+// O2: "-cc1" "-triple"
+// O2-SAME: "-O3"
diff --git a/clang/test/Driver/hip-cl-optimization.hip b/clang/test/Driver/hip-cl-optimization.hip
new file mode 100644
index 00000000000000..3a3720f0a5c67f
--- /dev/null
+++ b/clang/test/Driver/hip-cl-optimization.hip
@@ -0,0 +1,106 @@
+// Check that clang-cl optimization options apply to both host and device.
+// Device-specific options must still override the shared optimization level.
+
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /O1 -- %s 2>&1 | FileCheck %s --check-prefix=OS
+
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /O2 -- %s 2>&1 | FileCheck %s --check-prefix=O3
+
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /Ox -- %s 2>&1 | FileCheck %s --check-prefix=O3
+
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /Od -- %s 2>&1 | FileCheck %s --check-prefix=O0
+
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /O2 /Od -- %s 2>&1 | FileCheck %s --check-prefix=O0
+
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /Od /O2 -- %s 2>&1 | FileCheck %s --check-prefix=O3
+
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /O2 -clang:-O1 -- %s 2>&1 | FileCheck %s --check-prefix=O1
+
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /O2 -clang:-Xarch_device -clang:-O1 -- %s 2>&1 | FileCheck %s --check-prefix=DEVICE-O1
+
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /O2 /Ob0 -- %s 2>&1 | FileCheck %s --check-prefix=NOINLINE
+
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --no-offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /O1 -- %s 2>&1 | FileCheck %s --check-prefix=OS
+
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --no-offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /O2 -- %s 2>&1 | FileCheck %s --check-prefix=O3
+
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --no-offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /Ox -- %s 2>&1 | FileCheck %s --check-prefix=O3
+
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --no-offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /Od -- %s 2>&1 | FileCheck %s --check-prefix=O0
+
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --no-offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /O2 /Od -- %s 2>&1 | FileCheck %s --check-prefix=O0
+
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --no-offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /Od /O2 -- %s 2>&1 | FileCheck %s --check-prefix=O3
+
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --no-offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /O2 -clang:-O1 -- %s 2>&1 | FileCheck %s --check-prefix=O1
+
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --no-offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /O2 -clang:-Xarch_device -clang:-O1 -- %s 2>&1 | FileCheck %s --check-prefix=DEVICE-O1
+
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --no-offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /O2 /Ob0 -- %s 2>&1 | FileCheck %s --check-prefix=NOINLINE
+
+// OS: "-cc1" "-triple" "amdgpu11.00-amd-amdhsa"
+// OS-SAME: "-Os"
+// OS: "-cc1" "-triple" "x86_64-pc-windows-msvc{{[^"]*}}"
+// OS-SAME: "-Os"
+
+// O3: "-cc1" "-triple" "amdgpu11.00-amd-amdhsa"
+// O3-SAME: "-O3"
+// O3: "-cc1" "-triple" "x86_64-pc-windows-msvc{{[^"]*}}"
+// O3-SAME: "-O3"
+
+// O0: "-cc1" "-triple" "amdgpu11.00-amd-amdhsa"
+// O0-SAME: "-O0"
+// O0: "-cc1" "-triple" "x86_64-pc-windows-msvc{{[^"]*}}"
+// O0-SAME: "-O0"
+
+// O1: "-cc1" "-triple" "amdgpu11.00-amd-amdhsa"
+// O1-SAME: "-O1"
+// O1: "-cc1" "-triple" "x86_64-pc-windows-msvc{{[^"]*}}"
+// O1-SAME: "-O1"
+
+// DEVICE-O1: "-cc1" "-triple" "amdgpu11.00-amd-amdhsa"
+// DEVICE-O1-SAME: "-O1"
+// DEVICE-O1: "-cc1" "-triple" "x86_64-pc-windows-msvc{{[^"]*}}"
+// DEVICE-O1-SAME: "-O3"
+
+// NOINLINE: "-cc1" "-triple" "amdgpu11.00-amd-amdhsa"
+// NOINLINE-SAME: "-O3"
+// NOINLINE-SAME: "-fno-inline"
+// NOINLINE: "-cc1" "-triple" "x86_64-pc-windows-msvc{{[^"]*}}"
+// NOINLINE-SAME: "-O3"
+// NOINLINE-SAME: "-fno-inline"
>From c437113e897dce7106e7a3fa7ca21dbd8e19b781 Mon Sep 17 00:00:00 2001
From: "Yaxun (Sam) Liu" <yaxun.liu at amd.com>
Date: Fri, 2 Oct 2026 10:21:12 -0400
Subject: [PATCH 2/4] [clang] Extract shared clang-cl optimization translation
Keep clang-cl optimization parsing in an internal helper while the driver
normalizes options before host and device arguments are split.
Select translation by clang-cl mode so Linux and MinGW host toolchains
also receive the canonical optimization options.
---
clang/docs/ReleaseNotes.md | 3 +-
clang/lib/Driver/CMakeLists.txt | 1 +
clang/lib/Driver/ClangCLArgs.cpp | 139 ++++++++++++++++++++++
clang/lib/Driver/ClangCLArgs.h | 41 +++++++
clang/lib/Driver/Driver.cpp | 127 +-------------------
clang/test/Driver/cl-options.c | 9 ++
clang/test/Driver/hip-cl-optimization.hip | 13 ++
7 files changed, 210 insertions(+), 123 deletions(-)
create mode 100644 clang/lib/Driver/ClangCLArgs.cpp
create mode 100644 clang/lib/Driver/ClangCLArgs.h
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 4fdefafccea14d..4d46ed3b646afc 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -816,7 +816,8 @@ features cannot lower the translation-unit ABI level;
#### Miscellaneous Bug Fixes
- Fixed `clang-cl` optimization options such as `/O1`, `/O2`, and
- `/Ox` being ignored when compiling HIP device code.
+ `/Ox` being ignored when compiling HIP device code or using a non-MSVC
+ host toolchain.
#### Miscellaneous Clang Crashes Fixed
diff --git a/clang/lib/Driver/CMakeLists.txt b/clang/lib/Driver/CMakeLists.txt
index 506536cdc04f5f..c33dfd2471f35a 100644
--- a/clang/lib/Driver/CMakeLists.txt
+++ b/clang/lib/Driver/CMakeLists.txt
@@ -24,6 +24,7 @@ endif()
add_clang_library(clangDriver
Action.cpp
+ ClangCLArgs.cpp
Compilation.cpp
CreateASTUnitFromArgs.cpp
CreateInvocationFromArgs.cpp
diff --git a/clang/lib/Driver/ClangCLArgs.cpp b/clang/lib/Driver/ClangCLArgs.cpp
new file mode 100644
index 00000000000000..64ff6adefd2567
--- /dev/null
+++ b/clang/lib/Driver/ClangCLArgs.cpp
@@ -0,0 +1,139 @@
+//===--- ClangCLArgs.cpp - clang-cl arguments -----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangCLArgs.h"
+#include "clang/Options/Options.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/OptTable.h"
+#include "llvm/Option/Option.h"
+#include "llvm/TargetParser/Triple.h"
+#include <cstddef>
+
+using namespace clang;
+using namespace clang::driver;
+using namespace llvm::opt;
+
+ClangCLArgs::ClangCLArgs(const ArgList &Args, const llvm::Triple &HostTriple)
+ : SupportsForcingFramePointer(HostTriple.getArch() !=
+ llvm::Triple::x86_64) {
+ // Expand only the last /O[12xd], preserving overrides such as /O2 /Oy-.
+ for (Arg *A : Args.filtered(options::OPT__SLASH_O)) {
+ llvm::StringRef OptStr = A->getValue();
+ for (size_t I = 0, E = OptStr.size(); I != E; ++I) {
+ char OptChar = OptStr[I];
+ char PrevChar = I > 0 ? OptStr[I - 1] : '0';
+ if (PrevChar == 'b') {
+ // OptChar does not expand; it's an argument to the previous char.
+ continue;
+ }
+ if (OptChar == '1' || OptChar == '2' || OptChar == 'x' || OptChar == 'd')
+ ExpandChar = OptStr.data() + I;
+ }
+ }
+}
+
+bool ClangCLArgs::translateArg(Arg *A, DerivedArgList &DAL) const {
+ if (!A->getOption().matches(options::OPT__SLASH_O))
+ return false;
+
+ // Keep the original argument for unused-option diagnostics.
+ DAL.append(A);
+ const OptTable &Opts = getDriverOptTable();
+
+ llvm::StringRef OptStr = A->getValue();
+ for (size_t I = 0, E = OptStr.size(); I != E; ++I) {
+ const char &OptChar = *(OptStr.data() + I);
+ switch (OptChar) {
+ default:
+ break;
+ case '1':
+ case '2':
+ case 'x':
+ case 'd':
+ // Ignore /O[12xd] flags that aren't the last one on the command line.
+ // Only the last one gets expanded.
+ if (&OptChar != ExpandChar) {
+ A->claim();
+ break;
+ }
+ if (OptChar == 'd') {
+ DAL.AddFlagArg(A, Opts.getOption(options::OPT_O0));
+ } else {
+ if (OptChar == '1') {
+ DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "s");
+ } else if (OptChar == '2' || OptChar == 'x') {
+ DAL.AddFlagArg(A, Opts.getOption(options::OPT_fbuiltin));
+ DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "3");
+ }
+ if (SupportsForcingFramePointer &&
+ !DAL.hasArgNoClaim(options::OPT_fno_omit_frame_pointer))
+ DAL.AddFlagArg(A, Opts.getOption(options::OPT_fomit_frame_pointer));
+ if (OptChar == '1' || OptChar == '2')
+ DAL.AddFlagArg(A, Opts.getOption(options::OPT_ffunction_sections));
+ }
+ break;
+ case 'b':
+ if (I + 1 != E && llvm::isDigit(OptStr[I + 1])) {
+ switch (OptStr[I + 1]) {
+ case '0':
+ DAL.AddFlagArg(A, Opts.getOption(options::OPT_fno_inline));
+ break;
+ case '1':
+ DAL.AddFlagArg(A,
+ Opts.getOption(options::OPT_finline_hint_functions));
+ break;
+ case '2':
+ case '3':
+ DAL.AddFlagArg(A, Opts.getOption(options::OPT_finline_functions));
+ break;
+ }
+ ++I;
+ }
+ break;
+ case 'g':
+ A->claim();
+ break;
+ case 'i':
+ if (I + 1 != E && OptStr[I + 1] == '-') {
+ ++I;
+ DAL.AddFlagArg(A, Opts.getOption(options::OPT_fno_builtin));
+ } else {
+ DAL.AddFlagArg(A, Opts.getOption(options::OPT_fbuiltin));
+ }
+ break;
+ case 's':
+ DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "s");
+ break;
+ case 't':
+ DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "3");
+ break;
+ case 'y': {
+ bool OmitFramePointer = true;
+ if (I + 1 != E && OptStr[I + 1] == '-') {
+ OmitFramePointer = false;
+ ++I;
+ }
+ if (SupportsForcingFramePointer) {
+ if (OmitFramePointer)
+ DAL.AddFlagArg(A, Opts.getOption(options::OPT_fomit_frame_pointer));
+ else
+ DAL.AddFlagArg(A,
+ Opts.getOption(options::OPT_fno_omit_frame_pointer));
+ } else {
+ // Silently accept /Oy- on x86-64 for portable clang-cl build flags.
+ A->claim();
+ }
+ break;
+ }
+ }
+ }
+ return true;
+}
diff --git a/clang/lib/Driver/ClangCLArgs.h b/clang/lib/Driver/ClangCLArgs.h
new file mode 100644
index 00000000000000..fbfd24064fa814
--- /dev/null
+++ b/clang/lib/Driver/ClangCLArgs.h
@@ -0,0 +1,41 @@
+//===--- ClangCLArgs.h - clang-cl arguments ---------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_DRIVER_CLANGCLARGS_H
+#define LLVM_CLANG_LIB_DRIVER_CLANGCLARGS_H
+
+#include "llvm/Support/Compiler.h"
+
+namespace llvm {
+class Triple;
+namespace opt {
+class Arg;
+class ArgList;
+class DerivedArgList;
+} // namespace opt
+} // namespace llvm
+
+namespace clang::driver {
+
+/// Translate clang-cl options before host and device arguments are split.
+class LLVM_LIBRARY_VISIBILITY ClangCLArgs {
+ const char *ExpandChar = nullptr;
+ bool SupportsForcingFramePointer;
+
+public:
+ /// The argument values must outlive this translator.
+ ClangCLArgs(const llvm::opt::ArgList &Args, const llvm::Triple &HostTriple);
+
+ /// Append A and its canonical expansion to DAL. Return false without
+ /// modifying DAL if A is not handled.
+ bool translateArg(llvm::opt::Arg *A, llvm::opt::DerivedArgList &DAL) const;
+};
+
+} // namespace clang::driver
+
+#endif // LLVM_CLANG_LIB_DRIVER_CLANGCLARGS_H
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 07f70fbbe0128c..20d5520fa03b3d 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "clang/Driver/Driver.h"
+#include "ClangCLArgs.h"
#include "ToolChains/AIX.h"
#include "ToolChains/AMDGPU.h"
#include "ToolChains/AVR.h"
@@ -459,127 +460,14 @@ Arg *clang::driver::makeInputArg(DerivedArgList &Args, const OptTable &Opts,
return A;
}
-static void translateMSVCOptArg(Arg *A, llvm::opt::DerivedArgList &DAL,
- bool SupportsForcingFramePointer,
- const char *ExpandChar, const OptTable &Opts) {
- assert(A->getOption().matches(options::OPT__SLASH_O));
-
- StringRef OptStr = A->getValue();
- for (size_t I = 0, E = OptStr.size(); I != E; ++I) {
- const char &OptChar = *(OptStr.data() + I);
- switch (OptChar) {
- default:
- break;
- case '1':
- case '2':
- case 'x':
- case 'd':
- // Ignore /O[12xd] flags that aren't the last one on the command line.
- // Only the last one gets expanded.
- if (&OptChar != ExpandChar) {
- A->claim();
- break;
- }
- if (OptChar == 'd') {
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_O0));
- } else {
- if (OptChar == '1') {
- DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "s");
- } else if (OptChar == '2' || OptChar == 'x') {
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_fbuiltin));
- DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "3");
- }
- if (SupportsForcingFramePointer &&
- !DAL.hasArgNoClaim(options::OPT_fno_omit_frame_pointer))
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_fomit_frame_pointer));
- if (OptChar == '1' || OptChar == '2')
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_ffunction_sections));
- }
- break;
- case 'b':
- if (I + 1 != E && isdigit(OptStr[I + 1])) {
- switch (OptStr[I + 1]) {
- case '0':
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_fno_inline));
- break;
- case '1':
- DAL.AddFlagArg(A,
- Opts.getOption(options::OPT_finline_hint_functions));
- break;
- case '2':
- case '3':
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_finline_functions));
- break;
- }
- ++I;
- }
- break;
- case 'g':
- A->claim();
- break;
- case 'i':
- if (I + 1 != E && OptStr[I + 1] == '-') {
- ++I;
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_fno_builtin));
- } else {
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_fbuiltin));
- }
- break;
- case 's':
- DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "s");
- break;
- case 't':
- DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "3");
- break;
- case 'y': {
- bool OmitFramePointer = true;
- if (I + 1 != E && OptStr[I + 1] == '-') {
- OmitFramePointer = false;
- ++I;
- }
- if (SupportsForcingFramePointer) {
- if (OmitFramePointer)
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_fomit_frame_pointer));
- else
- DAL.AddFlagArg(A,
- Opts.getOption(options::OPT_fno_omit_frame_pointer));
- } else {
- // Silently accept /Oy- on x86-64 for portable clang-cl build flags.
- A->claim();
- }
- break;
- }
- }
- }
-}
-
DerivedArgList *Driver::TranslateInputArgs(const InputArgList &Args,
const llvm::Triple &Triple) const {
const llvm::opt::OptTable &Opts = getOpts();
DerivedArgList *DAL = new DerivedArgList(Args);
- // Normalize MSVC optimization options before host and device arguments split.
- bool TranslateMSVCOpts = Triple.isWindowsMSVCEnvironment();
- // /Oy and /Oy- do not affect the x86-64 host.
- bool SupportsForcingFramePointer = Triple.getArch() != llvm::Triple::x86_64;
- // Expand only the last /O[12xd], preserving overrides such as /O2 /Oy-.
- const char *ExpandChar = nullptr;
- if (TranslateMSVCOpts) {
- for (Arg *A : Args.filtered(options::OPT__SLASH_O)) {
- StringRef OptStr = A->getValue();
- for (size_t I = 0, E = OptStr.size(); I != E; ++I) {
- char OptChar = OptStr[I];
- char PrevChar = I > 0 ? OptStr[I - 1] : '0';
- if (PrevChar == 'b') {
- // OptChar does not expand; it's an argument to the previous char.
- continue;
- }
- if (OptChar == '1' || OptChar == '2' || OptChar == 'x' ||
- OptChar == 'd')
- ExpandChar = OptStr.data() + I;
- }
- }
- }
+ std::optional<ClangCLArgs> CLArgs;
+ if (IsCLMode())
+ CLArgs.emplace(Args, Triple);
bool HasNostdlib = Args.hasArg(options::OPT_nostdlib);
bool HasNostdlibxx = Args.hasArg(options::OPT_nostdlibxx);
@@ -598,13 +486,8 @@ DerivedArgList *Driver::TranslateInputArgs(const InputArgList &Args,
continue;
}
- if (TranslateMSVCOpts && A->getOption().matches(options::OPT__SLASH_O)) {
- // Keep the original argument for unused-option diagnostics.
- DAL->append(A);
- translateMSVCOptArg(A, *DAL, SupportsForcingFramePointer, ExpandChar,
- Opts);
+ if (CLArgs && CLArgs->translateArg(A, *DAL))
continue;
- }
// Unfortunately, we have to parse some forwarding options (-Xassembler,
// -Xlinker, -Xpreprocessor) because we either integrate their functionality
diff --git a/clang/test/Driver/cl-options.c b/clang/test/Driver/cl-options.c
index 57d82622ef4de6..b1d0ccbf60f0de 100644
--- a/clang/test/Driver/cl-options.c
+++ b/clang/test/Driver/cl-options.c
@@ -184,6 +184,15 @@
// RUN: %clang_cl /Od -### -- %s 2>&1 | FileCheck -check-prefix=Od %s
// Od: -O0
+// clang-cl optimization options are independent of the host toolchain.
+// RUN: %clang_cl --target=x86_64-unknown-linux-gnu /O1 -### -- %s 2>&1 | FileCheck %s --check-prefix=CL-LINUX-OS
+// CL-LINUX-OS: "-cc1"
+// CL-LINUX-OS-SAME: "-Os"
+
+// RUN: %clang_cl --target=x86_64-w64-windows-gnu /O2 -### -- %s 2>&1 | FileCheck %s --check-prefix=CL-MINGW-O3
+// CL-MINGW-O3: "-cc1"
+// CL-MINGW-O3-SAME: "-O3"
+
// RUN: %clang_cl /Oi- /Oi -### -- %s 2>&1 | FileCheck -check-prefix=Oi %s
// Oi-NOT: -fno-builtin
diff --git a/clang/test/Driver/hip-cl-optimization.hip b/clang/test/Driver/hip-cl-optimization.hip
index 3a3720f0a5c67f..1fa80de854e566 100644
--- a/clang/test/Driver/hip-cl-optimization.hip
+++ b/clang/test/Driver/hip-cl-optimization.hip
@@ -5,6 +5,10 @@
// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
// RUN: /O1 -- %s 2>&1 | FileCheck %s --check-prefix=OS
+// RUN: %clang_cl --target=x86_64-unknown-linux-gnu -### /c --offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /O1 -- %s 2>&1 | FileCheck %s --check-prefix=LINUX
+
// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --offload-new-driver \
// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
// RUN: /O2 -- %s 2>&1 | FileCheck %s --check-prefix=O3
@@ -41,6 +45,10 @@
// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
// RUN: /O1 -- %s 2>&1 | FileCheck %s --check-prefix=OS
+// RUN: %clang_cl --target=x86_64-unknown-linux-gnu -### /c --no-offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /O1 -- %s 2>&1 | FileCheck %s --check-prefix=LINUX
+
// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --no-offload-new-driver \
// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
// RUN: /O2 -- %s 2>&1 | FileCheck %s --check-prefix=O3
@@ -78,6 +86,11 @@
// OS: "-cc1" "-triple" "x86_64-pc-windows-msvc{{[^"]*}}"
// OS-SAME: "-Os"
+// LINUX: "-cc1" "-triple" "amdgpu11.00-amd-amdhsa"
+// LINUX-SAME: "-Os"
+// LINUX: "-cc1" "-triple" "x86_64-unknown-linux-gnu"
+// LINUX-SAME: "-Os"
+
// O3: "-cc1" "-triple" "amdgpu11.00-amd-amdhsa"
// O3-SAME: "-O3"
// O3: "-cc1" "-triple" "x86_64-pc-windows-msvc{{[^"]*}}"
>From 21a546f5f4ea283212ebd9c708ede29e65fb4158 Mon Sep 17 00:00:00 2001
From: "Yaxun (Sam) Liu" <yaxun.liu at amd.com>
Date: Fri, 2 Oct 2026 12:34:42 -0400
Subject: [PATCH 3/4] [clang] Move remaining MSVC argument translation to
ClangCLArgs
Normalize macro separators and /permissive options for both host and
device compilations, and remove MSVCToolChain::TranslateArgs.
Preserve late OpenMP and Xarch arguments, optimization bundle precedence,
and synthesized argument ownership during per-toolchain filtering.
---
clang/docs/ReleaseNotes.md | 4 +-
clang/lib/Driver/ClangCLArgs.cpp | 99 +++++++++++++---
clang/lib/Driver/ClangCLArgs.h | 24 +++-
clang/lib/Driver/Compilation.cpp | 12 ++
clang/lib/Driver/Driver.cpp | 12 ++
clang/lib/Driver/ToolChain.cpp | 15 +++
clang/lib/Driver/ToolChains/MSVC.cpp | 53 ---------
clang/lib/Driver/ToolChains/MSVC.h | 4 -
clang/test/Driver/cl-argument-translation.c | 58 +++++++++
clang/test/Driver/cl-options.cu | 16 +++
.../Driver/hip-cl-argument-translation.hip | 107 +++++++++++++++++
.../Driver/openmp-cl-argument-translation.cpp | 111 ++++++++++++++++++
12 files changed, 434 insertions(+), 81 deletions(-)
create mode 100644 clang/test/Driver/cl-argument-translation.c
create mode 100644 clang/test/Driver/hip-cl-argument-translation.hip
create mode 100644 clang/test/Driver/openmp-cl-argument-translation.cpp
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 4d46ed3b646afc..f6f0e9bb88d639 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -817,7 +817,9 @@ features cannot lower the translation-unit ABI level;
- Fixed `clang-cl` optimization options such as `/O1`, `/O2`, and
`/Ox` being ignored when compiling HIP device code or using a non-MSVC
- host toolchain.
+ host toolchain. Macro definitions using `#` as the value separator and
+ `/permissive` options are also translated consistently for host and device
+ compilation.
#### Miscellaneous Clang Crashes Fixed
diff --git a/clang/lib/Driver/ClangCLArgs.cpp b/clang/lib/Driver/ClangCLArgs.cpp
index 64ff6adefd2567..a06d1983da3d54 100644
--- a/clang/lib/Driver/ClangCLArgs.cpp
+++ b/clang/lib/Driver/ClangCLArgs.cpp
@@ -16,6 +16,7 @@
#include "llvm/Option/Option.h"
#include "llvm/TargetParser/Triple.h"
#include <cstddef>
+#include <string>
using namespace clang;
using namespace clang::driver;
@@ -40,13 +41,33 @@ ClangCLArgs::ClangCLArgs(const ArgList &Args, const llvm::Triple &HostTriple)
}
}
-bool ClangCLArgs::translateArg(Arg *A, DerivedArgList &DAL) const {
+bool ClangCLArgs::translateArg(Arg *A, DerivedArgList &DAL,
+ const DerivedArgList *Owner) const {
+ const DerivedArgList &SynthesizedArgs = Owner ? *Owner : DAL;
+ const Arg *BaseArg = &A->getBaseArg();
+ const OptTable &Opts = getDriverOptTable();
+ switch (A->getOption().getID()) {
+ case options::OPT__SLASH_permissive:
+ DAL.append(SynthesizedArgs.MakeFlagArg(
+ BaseArg, Opts.getOption(options::OPT_fdelayed_template_parsing)));
+ DAL.append(SynthesizedArgs.MakeFlagArg(
+ BaseArg, Opts.getOption(options::OPT_fno_operator_names)));
+ return true;
+ case options::OPT__SLASH_permissive_:
+ DAL.append(SynthesizedArgs.MakeFlagArg(
+ BaseArg, Opts.getOption(options::OPT_fno_delayed_template_parsing)));
+ DAL.append(SynthesizedArgs.MakeFlagArg(
+ BaseArg, Opts.getOption(options::OPT_foperator_names)));
+ return true;
+ default:
+ break;
+ }
+
if (!A->getOption().matches(options::OPT__SLASH_O))
return false;
// Keep the original argument for unused-option diagnostics.
DAL.append(A);
- const OptTable &Opts = getDriverOptTable();
llvm::StringRef OptStr = A->getValue();
for (size_t I = 0, E = OptStr.size(); I != E; ++I) {
@@ -65,34 +86,42 @@ bool ClangCLArgs::translateArg(Arg *A, DerivedArgList &DAL) const {
break;
}
if (OptChar == 'd') {
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_O0));
+ DAL.append(SynthesizedArgs.MakeFlagArg(
+ BaseArg, Opts.getOption(options::OPT_O0)));
} else {
if (OptChar == '1') {
- DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "s");
+ DAL.append(SynthesizedArgs.MakeJoinedArg(
+ BaseArg, Opts.getOption(options::OPT_O), "s"));
} else if (OptChar == '2' || OptChar == 'x') {
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_fbuiltin));
- DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "3");
+ DAL.append(SynthesizedArgs.MakeFlagArg(
+ BaseArg, Opts.getOption(options::OPT_fbuiltin)));
+ DAL.append(SynthesizedArgs.MakeJoinedArg(
+ BaseArg, Opts.getOption(options::OPT_O), "3"));
}
if (SupportsForcingFramePointer &&
!DAL.hasArgNoClaim(options::OPT_fno_omit_frame_pointer))
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_fomit_frame_pointer));
+ DAL.append(SynthesizedArgs.MakeFlagArg(
+ BaseArg, Opts.getOption(options::OPT_fomit_frame_pointer)));
if (OptChar == '1' || OptChar == '2')
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_ffunction_sections));
+ DAL.append(SynthesizedArgs.MakeFlagArg(
+ BaseArg, Opts.getOption(options::OPT_ffunction_sections)));
}
break;
case 'b':
if (I + 1 != E && llvm::isDigit(OptStr[I + 1])) {
switch (OptStr[I + 1]) {
case '0':
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_fno_inline));
+ DAL.append(SynthesizedArgs.MakeFlagArg(
+ BaseArg, Opts.getOption(options::OPT_fno_inline)));
break;
case '1':
- DAL.AddFlagArg(A,
- Opts.getOption(options::OPT_finline_hint_functions));
+ DAL.append(SynthesizedArgs.MakeFlagArg(
+ BaseArg, Opts.getOption(options::OPT_finline_hint_functions)));
break;
case '2':
case '3':
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_finline_functions));
+ DAL.append(SynthesizedArgs.MakeFlagArg(
+ BaseArg, Opts.getOption(options::OPT_finline_functions)));
break;
}
++I;
@@ -104,16 +133,20 @@ bool ClangCLArgs::translateArg(Arg *A, DerivedArgList &DAL) const {
case 'i':
if (I + 1 != E && OptStr[I + 1] == '-') {
++I;
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_fno_builtin));
+ DAL.append(SynthesizedArgs.MakeFlagArg(
+ BaseArg, Opts.getOption(options::OPT_fno_builtin)));
} else {
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_fbuiltin));
+ DAL.append(SynthesizedArgs.MakeFlagArg(
+ BaseArg, Opts.getOption(options::OPT_fbuiltin)));
}
break;
case 's':
- DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "s");
+ DAL.append(SynthesizedArgs.MakeJoinedArg(
+ BaseArg, Opts.getOption(options::OPT_O), "s"));
break;
case 't':
- DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "3");
+ DAL.append(SynthesizedArgs.MakeJoinedArg(
+ BaseArg, Opts.getOption(options::OPT_O), "3"));
break;
case 'y': {
bool OmitFramePointer = true;
@@ -123,10 +156,11 @@ bool ClangCLArgs::translateArg(Arg *A, DerivedArgList &DAL) const {
}
if (SupportsForcingFramePointer) {
if (OmitFramePointer)
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_fomit_frame_pointer));
+ DAL.append(SynthesizedArgs.MakeFlagArg(
+ BaseArg, Opts.getOption(options::OPT_fomit_frame_pointer)));
else
- DAL.AddFlagArg(A,
- Opts.getOption(options::OPT_fno_omit_frame_pointer));
+ DAL.append(SynthesizedArgs.MakeFlagArg(
+ BaseArg, Opts.getOption(options::OPT_fno_omit_frame_pointer)));
} else {
// Silently accept /Oy- on x86-64 for portable clang-cl build flags.
A->claim();
@@ -137,3 +171,30 @@ bool ClangCLArgs::translateArg(Arg *A, DerivedArgList &DAL) const {
}
return true;
}
+
+DerivedArgList *ClangCLArgs::translateArgs(const DerivedArgList &Args,
+ const llvm::Triple &HostTriple,
+ const DerivedArgList &Owner) {
+ ClangCLArgs Translator(Args, HostTriple);
+ auto *DAL = new DerivedArgList(Args.getBaseArgs());
+ for (Arg *A : Args) {
+ if (!A->getOption().matches(options::OPT__SLASH_O) &&
+ A->getBaseArg().getOption().matches(options::OPT__SLASH_O))
+ continue;
+ if (!Translator.translateArg(A, *DAL, &Owner))
+ DAL->append(A);
+ }
+ return DAL;
+}
+
+const char *ClangCLArgs::translateMacroDefinition(const char *Value,
+ const ArgList &Args) {
+ llvm::StringRef Val = Value;
+ size_t Hash = Val.find('#');
+ if (Hash == llvm::StringRef::npos || Hash > Val.find('='))
+ return Value;
+
+ std::string NewVal = std::string(Val);
+ NewVal[Hash] = '=';
+ return Args.MakeArgString(NewVal);
+}
diff --git a/clang/lib/Driver/ClangCLArgs.h b/clang/lib/Driver/ClangCLArgs.h
index fbfd24064fa814..c713be83a31e13 100644
--- a/clang/lib/Driver/ClangCLArgs.h
+++ b/clang/lib/Driver/ClangCLArgs.h
@@ -22,7 +22,7 @@ class DerivedArgList;
namespace clang::driver {
-/// Translate clang-cl options before host and device arguments are split.
+/// Translate clang-cl options before and after offload argument filtering.
class LLVM_LIBRARY_VISIBILITY ClangCLArgs {
const char *ExpandChar = nullptr;
bool SupportsForcingFramePointer;
@@ -31,9 +31,25 @@ class LLVM_LIBRARY_VISIBILITY ClangCLArgs {
/// The argument values must outlive this translator.
ClangCLArgs(const llvm::opt::ArgList &Args, const llvm::Triple &HostTriple);
- /// Append A and its canonical expansion to DAL. Return false without
- /// modifying DAL if A is not handled.
- bool translateArg(llvm::opt::Arg *A, llvm::opt::DerivedArgList &DAL) const;
+ /// Append the canonical translation of A to DAL. Return false without
+ /// modifying DAL if A is not handled. If supplied, Owner owns synthesized
+ /// arguments instead of DAL and must outlive its consumers.
+ bool translateArg(llvm::opt::Arg *A, llvm::opt::DerivedArgList &DAL,
+ const llvm::opt::DerivedArgList *Owner = nullptr) const;
+
+ /// Translate a filtered list that may contain new clang-cl options, replacing
+ /// earlier /O expansions. The caller owns the returned list; Owner owns its
+ /// synthesized arguments and must outlive its consumers.
+ static llvm::opt::DerivedArgList *
+ translateArgs(const llvm::opt::DerivedArgList &Args,
+ const llvm::Triple &HostTriple,
+ const llvm::opt::DerivedArgList &Owner);
+
+ /// Normalize MSVC's '#' macro separator, also used by Clang targeting MSVC
+ /// and by arguments parsed after host/device filtering. Any replacement
+ /// string has the lifetime of Args; otherwise return Value unchanged.
+ static const char *translateMacroDefinition(const char *Value,
+ const llvm::opt::ArgList &Args);
};
} // namespace clang::driver
diff --git a/clang/lib/Driver/Compilation.cpp b/clang/lib/Driver/Compilation.cpp
index c81c4445a29f95..3ccfe60c520d92 100644
--- a/clang/lib/Driver/Compilation.cpp
+++ b/clang/lib/Driver/Compilation.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "clang/Driver/Compilation.h"
+#include "ClangCLArgs.h"
#include "clang/Basic/LLVM.h"
#include "clang/Driver/Action.h"
#include "clang/Driver/CommonArgs.h"
@@ -91,6 +92,17 @@ Compilation::getArgsForToolChain(const ToolChain *TC, BoundArch BA,
delete OpenMPArgs;
}
+ if (NewDAL && DeviceOffloadKind == Action::OFK_OpenMP &&
+ (TheDriver.IsCLMode() ||
+ DefaultToolChain.getTriple().isWindowsMSVCEnvironment())) {
+ // OpenMP forwarding can introduce clang-cl options after the shared
+ // translation. Keep synthesized arguments in the compilation-owned list.
+ auto *DAL = ClangCLArgs::translateArgs(
+ *NewDAL, DefaultToolChain.getTriple(), *TranslatedArgs);
+ delete NewDAL;
+ NewDAL = DAL;
+ }
+
if (!NewDAL) {
Entry = TC->TranslateArgs(*TranslatedArgs, BA, DeviceOffloadKind);
if (!Entry)
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 20d5520fa03b3d..6b901118f9c7b1 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -489,6 +489,18 @@ DerivedArgList *Driver::TranslateInputArgs(const InputArgList &Args,
if (CLArgs && CLArgs->translateArg(A, *DAL))
continue;
+ // Preserve MSVC macro syntax for Clang's other driver modes as well.
+ if ((CLArgs || Triple.isWindowsMSVCEnvironment()) &&
+ A->getOption().matches(options::OPT_D)) {
+ const char *Value =
+ ClangCLArgs::translateMacroDefinition(A->getValue(), Args);
+ if (Value == A->getValue())
+ DAL->append(A);
+ else
+ DAL->AddJoinedArg(A, Opts.getOption(options::OPT_D), Value);
+ continue;
+ }
+
// Unfortunately, we have to parse some forwarding options (-Xassembler,
// -Xlinker, -Xpreprocessor) because we either integrate their functionality
// (assembler and preprocessor), or bypass a previous driver ('collect2').
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index 255a9c9602b540..2d7dc6e63534b2 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "clang/Driver/ToolChain.h"
+#include "ClangCLArgs.h"
#include "ToolChains/Arch/AArch64.h"
#include "ToolChains/Arch/AMDGPU.h"
#include "ToolChains/Arch/ARM.h"
@@ -2074,6 +2075,12 @@ llvm::opt::DerivedArgList *ToolChain::TranslateOpenMPTargetArgs(
}
XOpenMPTargetArg->setBaseArg(A);
A = XOpenMPTargetArg.release();
+ if (A->getOption().matches(options::OPT_D) &&
+ (getDriver().IsCLMode() ||
+ llvm::Triple(llvm::Triple::normalize(getDriver().getTargetTriple()))
+ .isWindowsMSVCEnvironment()))
+ A->getValues()[0] =
+ ClangCLArgs::translateMacroDefinition(A->getValue(), Args);
AllocatedArgs.push_back(A);
DAL->append(A);
Modified = true;
@@ -2154,6 +2161,14 @@ void ToolChain::TranslateXarchArgs(
DAL->AddSynthesizedArg(A);
else
AllocatedArgs->push_back(A);
+
+ // Forwarded macros are parsed after the shared input translation.
+ if (A->getOption().matches(options::OPT_D) &&
+ (getDriver().IsCLMode() ||
+ llvm::Triple(llvm::Triple::normalize(getDriver().getTargetTriple()))
+ .isWindowsMSVCEnvironment()))
+ A->getValues()[0] =
+ ClangCLArgs::translateMacroDefinition(A->getValue(), Args);
}
/// Match any triple recognized arch aliases.
diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp
index d32d5b9a52fc94..8f4c8f89ce58f9 100644
--- a/clang/lib/Driver/ToolChains/MSVC.cpp
+++ b/clang/lib/Driver/ToolChains/MSVC.cpp
@@ -1023,59 +1023,6 @@ SanitizerMask MSVCToolChain::getSupportedSanitizers(
return Res;
}
-static void TranslateDArg(Arg *A, llvm::opt::DerivedArgList &DAL,
- const OptTable &Opts) {
- assert(A->getOption().matches(options::OPT_D));
-
- StringRef Val = A->getValue();
- size_t Hash = Val.find('#');
- if (Hash == StringRef::npos || Hash > Val.find('=')) {
- DAL.append(A);
- return;
- }
-
- std::string NewVal = std::string(Val);
- NewVal[Hash] = '=';
- DAL.AddJoinedArg(A, Opts.getOption(options::OPT_D), NewVal);
-}
-
-static void TranslatePermissive(Arg *A, llvm::opt::DerivedArgList &DAL,
- const OptTable &Opts) {
- DAL.AddFlagArg(A, Opts.getOption(options::OPT__SLASH_Zc_twoPhase_));
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_fno_operator_names));
-}
-
-static void TranslatePermissiveMinus(Arg *A, llvm::opt::DerivedArgList &DAL,
- const OptTable &Opts) {
- DAL.AddFlagArg(A, Opts.getOption(options::OPT__SLASH_Zc_twoPhase));
- DAL.AddFlagArg(A, Opts.getOption(options::OPT_foperator_names));
-}
-
-llvm::opt::DerivedArgList *
-MSVCToolChain::TranslateArgs(const llvm::opt::DerivedArgList &Args,
- BoundArch BA, Action::OffloadKind OFK) const {
- DerivedArgList *DAL = new DerivedArgList(Args.getBaseArgs());
- const OptTable &Opts = getDriver().getOpts();
-
- for (Arg *A : Args) {
- if (A->getOption().matches(options::OPT_D)) {
- // Translate -Dfoo#bar into -Dfoo=bar.
- TranslateDArg(A, *DAL, Opts);
- } else if (A->getOption().matches(options::OPT__SLASH_permissive)) {
- // Expand /permissive
- TranslatePermissive(A, *DAL, Opts);
- } else if (A->getOption().matches(options::OPT__SLASH_permissive_)) {
- // Expand /permissive-
- TranslatePermissiveMinus(A, *DAL, Opts);
- } else if (OFK != Action::OFK_HIP) {
- // HIP Toolchain translates input args by itself.
- DAL->append(A);
- }
- }
-
- return DAL;
-}
-
void MSVCToolChain::addClangTargetOptions(
const ArgList &DriverArgs, ArgStringList &CC1Args, BoundArch BA,
Action::OffloadKind DeviceOffloadKind) const {
diff --git a/clang/lib/Driver/ToolChains/MSVC.h b/clang/lib/Driver/ToolChains/MSVC.h
index 8ff8b2815d8df7..90461a1443da31 100644
--- a/clang/lib/Driver/ToolChains/MSVC.h
+++ b/clang/lib/Driver/ToolChains/MSVC.h
@@ -61,10 +61,6 @@ class LLVM_LIBRARY_VISIBILITY MSVCToolChain : public ToolChain {
MSVCToolChain(const Driver &D, const llvm::Triple &Triple,
const llvm::opt::ArgList &Args);
- llvm::opt::DerivedArgList *
- TranslateArgs(const llvm::opt::DerivedArgList &Args, BoundArch BA,
- Action::OffloadKind DeviceOffloadKind) const override;
-
UnwindTableLevel
getDefaultUnwindTableLevel(const llvm::opt::ArgList &Args) const override;
bool isPICDefault() const override;
diff --git a/clang/test/Driver/cl-argument-translation.c b/clang/test/Driver/cl-argument-translation.c
new file mode 100644
index 00000000000000..e1758265fa8890
--- /dev/null
+++ b/clang/test/Driver/cl-argument-translation.c
@@ -0,0 +1,58 @@
+// clang-cl syntax is normalized independently of the target toolchain.
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc /c -### /DFOO#7 /DBAR=a#b /DBAZ#a=b /DEMPTY# \
+// RUN: /DQUX#a#b -- %s 2>&1 | FileCheck %s --check-prefix=MACROS
+// RUN: %clang_cl --target=x86_64-unknown-linux-gnu /c -### /DFOO#7 /DBAR=a#b /DBAZ#a=b /DEMPTY# \
+// RUN: /DQUX#a#b -- %s 2>&1 | FileCheck %s --check-prefix=MACROS
+// RUN: %clang_cl --target=x86_64-w64-windows-gnu /c -### /DFOO#7 /DBAR=a#b /DBAZ#a=b /DEMPTY# \
+// RUN: /DQUX#a#b -- %s 2>&1 | FileCheck %s --check-prefix=MACROS
+// MACROS: "-D" "FOO=7" "-D" "BAR=a#b" "-D" "BAZ=a=b" "-D" "EMPTY=" "-D" "QUX=a#b"
+
+// Preserve GNU-mode Clang's existing MSVC macro compatibility.
+// RUN: %clang --target=x86_64-pc-windows-msvc -c -### -DFOO#7 -- %s 2>&1 | FileCheck %s --check-prefix=MSVC
+// RUN: %clang --target=x86_64-unknown-linux-gnu -c -### -DFOO#7 -- %s 2>&1 | FileCheck %s --check-prefix=GNU
+// MSVC: "-D" "FOO=7"
+// GNU: "-D" "FOO#7"
+
+// Last-option precedence must include both slash and canonical options.
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc /c -x c++ -### /permissive- /permissive -- %s 2>&1 | \
+// RUN: FileCheck %s --check-prefix=PERMISSIVE
+// RUN: %clang_cl --target=x86_64-unknown-linux-gnu /c -x c++ -### /permissive- /permissive -- %s 2>&1 \
+// RUN: | FileCheck %s --check-prefix=PERMISSIVE
+// RUN: %clang_cl --target=x86_64-w64-windows-gnu /c -x c++ -### /permissive -- %s 2>&1 | FileCheck %s \
+// RUN: --check-prefix=PERMISSIVE
+// PERMISSIVE: "-fno-operator-names"
+// PERMISSIVE-SAME: "-fdelayed-template-parsing"
+
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc /c -x c++ -### /permissive /permissive- -- %s 2>&1 | \
+// RUN: FileCheck %s --check-prefix=CONFORMING
+// RUN: %clang_cl --target=x86_64-unknown-linux-gnu /c -x c++ -### /permissive /permissive- -- %s 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CONFORMING
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc /c -x c++ -### /permissive /Zc:twoPhase \
+// RUN: -clang:-foperator-names -- %s 2>&1 | FileCheck %s --check-prefix=CONFORMING
+// /clang: arguments are appended after the other command-line arguments.
+// RUN: %clang_cl --target=x86_64-unknown-linux-gnu /c -x c++ -### \
+// RUN: -clang:-fno-delayed-template-parsing -clang:-foperator-names /permissive -- %s 2>&1 | \
+// RUN: FileCheck %s --check-prefix=CONFORMING
+// CONFORMING: "-cc1"
+// CONFORMING-NOT: "-fno-operator-names"
+// CONFORMING-NOT: "-fdelayed-template-parsing"
+
+// RUN: %clang --target=x86_64-pc-windows-msvc -c -### -Xarch_host -DFOO#7 -- %s 2>&1 | FileCheck %s \
+// RUN: --check-prefix=MSVC
+// RUN: %clang --target=x86_64-windows-msvc -c -### -Xarch_host -DFOO#7 -- %s 2>&1 | \
+// RUN: FileCheck %s --check-prefix=MSVC
+// RUN: %clang --target=x86_64-win32 -c -### -Xarch_host -DFOO#7 -- %s 2>&1 | \
+// RUN: FileCheck %s --check-prefix=MSVC
+// RUN: %clang --target=x86_64-unknown-linux-gnu -c -### -Xarch_host -DFOO#7 -- %s 2>&1 | FileCheck %s \
+// RUN: --check-prefix=GNU
+// RUN: %clang --target=x86_64-w64-windows-gnu -c -### -DFOO#7 -- %s 2>&1 | FileCheck %s --check-prefix=GNU
+
+// RUN: %clang_cl --target=x86_64-unknown-linux-gnu /EP /DFOO#7 /DTRANSLATION_CHECK -- %s | FileCheck \
+// RUN: %s --check-prefix=PREPROCESS
+// PREPROCESS: 7
+#ifdef TRANSLATION_CHECK
+#if FOO != 7
+#error macro separator was not translated
+#endif
+FOO
+#endif
diff --git a/clang/test/Driver/cl-options.cu b/clang/test/Driver/cl-options.cu
index 2517e7d77d4a9e..b373f4c7d0a031 100644
--- a/clang/test/Driver/cl-options.cu
+++ b/clang/test/Driver/cl-options.cu
@@ -32,3 +32,19 @@
// O2-SAME: "-O3"
// O2: "-cc1" "-triple"
// O2-SAME: "-O3"
+
+// Shared macros and language settings also reach CUDA without reintroducing
+// the original macro alongside its translated value.
+// RUN: not %clang_cl --target=x86_64-pc-windows-msvc -### /c \
+// RUN: --cuda-gpu-arch=sm_35 -nocudainc -nocudalib \
+// RUN: /DFOO#7 /permissive- -- %s 2>&1 | FileCheck %s --check-prefix=CL-COMMON
+// CL-COMMON: "-cc1" "-triple" "nvptx64-nvidia-cuda"
+// CL-COMMON-SAME: "-D" "FOO=7"
+// CL-COMMON-NOT: "FOO#7"
+// CL-COMMON-NOT: "-fno-operator-names"
+// CL-COMMON-NOT: "-fdelayed-template-parsing"
+// CL-COMMON: "-cc1" "-triple" "x86_64-pc-windows-msvc{{[^"]*}}"
+// CL-COMMON-SAME: "-D" "FOO=7"
+// CL-COMMON-NOT: "FOO#7"
+// CL-COMMON-NOT: "-fno-operator-names"
+// CL-COMMON-NOT: "-fdelayed-template-parsing"
diff --git a/clang/test/Driver/hip-cl-argument-translation.hip b/clang/test/Driver/hip-cl-argument-translation.hip
new file mode 100644
index 00000000000000..e35201d37c2351
--- /dev/null
+++ b/clang/test/Driver/hip-cl-argument-translation.hip
@@ -0,0 +1,107 @@
+// Shared source-language options must reach both HIP jobs.
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /DFOO#7 /permissive- -- %s 2>&1 | FileCheck %s --check-prefix=COMMON
+
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /permissive -- %s 2>&1 | FileCheck %s --check-prefix=PERMISSIVE
+
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /DFOO#1 -clang:-Xarch_host -clang:-DFOO#2 -clang:-Xarch_device -clang:-DDEV#3 -- %s 2>&1 | \
+// RUN: FileCheck %s --check-prefix=SCOPED
+
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /permissive -clang:-Xarch_device -clang:-fno-delayed-template-parsing -clang:-Xarch_device \
+// RUN: -clang:-foperator-names -- %s 2>&1 | FileCheck %s --check-prefix=DEVICE-CONFORMING
+
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --no-offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /DFOO#7 /permissive- -- %s 2>&1 | FileCheck %s --check-prefix=COMMON
+
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --no-offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /permissive -- %s 2>&1 | FileCheck %s --check-prefix=PERMISSIVE
+
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --no-offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /DFOO#1 -clang:-Xarch_host -clang:-DFOO#2 -clang:-Xarch_device -clang:-DDEV#3 -- %s 2>&1 | \
+// RUN: FileCheck %s --check-prefix=SCOPED
+
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --no-offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /permissive -clang:-Xarch_device -clang:-fno-delayed-template-parsing -clang:-Xarch_device \
+// RUN: -clang:-foperator-names -- %s 2>&1 | FileCheck %s --check-prefix=DEVICE-CONFORMING
+
+// RUN: %clang_cl --target=x86_64-unknown-linux-gnu -### /c --offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /DFOO#7 /permissive- -- %s 2>&1 | FileCheck %s --check-prefix=COMMON
+
+// RUN: %clang_cl --target=x86_64-unknown-linux-gnu -### /c --offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /permissive -- %s 2>&1 | FileCheck %s --check-prefix=PERMISSIVE
+
+// RUN: %clang_cl --target=x86_64-unknown-linux-gnu -### /c --offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /DFOO#1 -clang:-Xarch_host -clang:-DFOO#2 -clang:-Xarch_device -clang:-DDEV#3 -- %s 2>&1 | \
+// RUN: FileCheck %s --check-prefix=SCOPED
+
+// RUN: %clang_cl --target=x86_64-unknown-linux-gnu -### /c --offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /permissive -clang:-Xarch_device -clang:-fno-delayed-template-parsing -clang:-Xarch_device \
+// RUN: -clang:-foperator-names -- %s 2>&1 | FileCheck %s --check-prefix=DEVICE-CONFORMING
+
+// RUN: %clang_cl --target=x86_64-unknown-linux-gnu -### /c --no-offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /DFOO#7 /permissive- -- %s 2>&1 | FileCheck %s --check-prefix=COMMON
+
+// RUN: %clang_cl --target=x86_64-unknown-linux-gnu -### /c --no-offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /permissive -- %s 2>&1 | FileCheck %s --check-prefix=PERMISSIVE
+
+// RUN: %clang_cl --target=x86_64-unknown-linux-gnu -### /c --no-offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /DFOO#1 -clang:-Xarch_host -clang:-DFOO#2 -clang:-Xarch_device -clang:-DDEV#3 -- %s 2>&1 | \
+// RUN: FileCheck %s --check-prefix=SCOPED
+
+// RUN: %clang_cl --target=x86_64-unknown-linux-gnu -### /c --no-offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
+// RUN: /permissive -clang:-Xarch_device -clang:-fno-delayed-template-parsing -clang:-Xarch_device \
+// RUN: -clang:-foperator-names -- %s 2>&1 | FileCheck %s --check-prefix=DEVICE-CONFORMING
+
+// COMMON: "-cc1" "-triple" "amdgpu11.00-amd-amdhsa"
+// COMMON-SAME: "-D" "FOO=7"
+// COMMON-NOT: "-fdelayed-template-parsing"
+// COMMON-NOT: "-fno-operator-names"
+// COMMON: "-cc1" "-triple" "x86_64-{{[^"]*}}"
+// COMMON-SAME: "-D" "FOO=7"
+// COMMON-NOT: "-fdelayed-template-parsing"
+// COMMON-NOT: "-fno-operator-names"
+
+// PERMISSIVE: "-cc1" "-triple" "amdgpu11.00-amd-amdhsa"
+// PERMISSIVE-SAME: "-fno-operator-names"
+// PERMISSIVE-SAME: "-fdelayed-template-parsing"
+// PERMISSIVE: "-cc1" "-triple" "x86_64-{{[^"]*}}"
+// PERMISSIVE-SAME: "-fno-operator-names"
+// PERMISSIVE-SAME: "-fdelayed-template-parsing"
+
+// SCOPED: "-cc1" "-triple" "amdgpu11.00-amd-amdhsa"
+// SCOPED-SAME: "-D" "FOO=1" "-D" "DEV=3"
+// SCOPED-NOT: "FOO=2"
+// SCOPED: "-cc1" "-triple" "x86_64-{{[^"]*}}"
+// SCOPED-SAME: "-D" "FOO=1" "-D" "FOO=2"
+// SCOPED-NOT: "DEV=3"
+
+// DEVICE-CONFORMING: "-cc1" "-triple" "amdgpu11.00-amd-amdhsa"
+// DEVICE-CONFORMING-NOT: "-fno-operator-names"
+// DEVICE-CONFORMING-NOT: "-fdelayed-template-parsing"
+// DEVICE-CONFORMING: "-cc1" "-triple" "x86_64-{{[^"]*}}"
+// DEVICE-CONFORMING-SAME: "-fno-operator-names"
+// DEVICE-CONFORMING-SAME: "-fdelayed-template-parsing"
+
+// GNU-mode offloading retains MSVC macro syntax when selected by the host.
+// RUN: %clang --target=x86_64-pc-windows-msvc -### -c --offload-new-driver \
+// RUN: -x hip --offload-arch=gfx1100 -nogpuinc -nogpulib \
+// RUN: -DFOO#1 -Xarch_host -DFOO#2 -Xarch_device -DDEV#3 -- %s 2>&1 | FileCheck %s --check-prefix=SCOPED
diff --git a/clang/test/Driver/openmp-cl-argument-translation.cpp b/clang/test/Driver/openmp-cl-argument-translation.cpp
new file mode 100644
index 00000000000000..583db4718c9247
--- /dev/null
+++ b/clang/test/Driver/openmp-cl-argument-translation.cpp
@@ -0,0 +1,111 @@
+// OpenMP target arguments are parsed after the shared input translation.
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c \
+// RUN: -clang:-fopenmp -clang:-fopenmp-targets=amdgcn-amd-amdhsa \
+// RUN: -clang:--offload-arch=gfx1100 -clang:-nogpulib \
+// RUN: -clang:-Xopenmp-target=amdgcn-amd-amdhsa -clang:-DFOO#7 -- %s 2>&1 | FileCheck %s
+// RUN: %clang_cl --target=x86_64-unknown-linux-gnu -### /c \
+// RUN: -clang:-fopenmp -clang:-fopenmp-targets=amdgcn-amd-amdhsa \
+// RUN: -clang:--offload-arch=gfx1100 -clang:-nogpulib \
+// RUN: -clang:-Xopenmp-target=amdgcn-amd-amdhsa -clang:-DFOO#7 -- %s 2>&1 | FileCheck %s
+// RUN: %clang --target=x86_64-windows-msvc -### -c \
+// RUN: -fopenmp -fopenmp-targets=amdgcn-amd-amdhsa \
+// RUN: --offload-arch=gfx1100 -nogpulib \
+// RUN: -Xopenmp-target=amdgcn-amd-amdhsa -DFOO#7 -- %s 2>&1 | FileCheck %s
+// CHECK: "-cc1" "-triple" "x86_64-{{[^"]*}}"
+// CHECK-NOT: "FOO=7"
+// CHECK: "-cc1" "-triple" "amdgpu11.00-amd-amdhsa"
+// CHECK-SAME: "-D" "FOO=7"
+// CHECK: "-cc1" "-triple" "x86_64-{{[^"]*}}"
+// CHECK-NOT: "FOO=7"
+
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c \
+// RUN: -clang:-fopenmp -clang:-fopenmp-targets=x86_64-pc-windows-msvc \
+// RUN: -clang:-nogpulib \
+// RUN: -clang:-Xopenmp-target=x86_64-pc-windows-msvc -clang:/O2 \
+// RUN: -clang:-Xopenmp-target=x86_64-pc-windows-msvc -clang:/permissive -- %s 2>&1 | \
+// RUN: FileCheck %s --check-prefix=DEVICE-O3
+
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c \
+// RUN: -clang:-fopenmp -clang:-fopenmp-targets=amdgcn-amd-amdhsa \
+// RUN: -clang:--offload-arch=gfx1100 -clang:-nogpulib \
+// RUN: -clang:-Xopenmp-target=amdgcn-amd-amdhsa -clang:/O2 \
+// RUN: -clang:-Xopenmp-target=amdgcn-amd-amdhsa -clang:/permissive -- %s 2>&1 | \
+// RUN: FileCheck %s --check-prefix=DEVICE-O3
+
+// RUN: %clang_cl --target=x86_64-unknown-linux-gnu -### /c \
+// RUN: -clang:-fopenmp -clang:-fopenmp-targets=amdgcn-amd-amdhsa \
+// RUN: -clang:--offload-arch=gfx1100 -clang:-nogpulib \
+// RUN: -clang:-Xopenmp-target=amdgcn-amd-amdhsa -clang:/O2 \
+// RUN: -clang:-Xopenmp-target=amdgcn-amd-amdhsa -clang:/permissive -- %s 2>&1 | \
+// RUN: FileCheck %s --check-prefix=DEVICE-O3
+
+// DEVICE-O3: "-cc1" "-triple" "x86_64-{{[^"]*}}"
+// DEVICE-O3-NOT: "-O3"
+// DEVICE-O3-NOT: "-fno-operator-names"
+// DEVICE-O3: "-cc1" "-triple" "{{[^"]*}}"
+// DEVICE-O3-SAME: "-O3"
+// DEVICE-O3-SAME: "-fno-operator-names"
+// DEVICE-O3-SAME: "-fdelayed-template-parsing"
+// DEVICE-O3: "-cc1" "-triple" "x86_64-{{[^"]*}}"
+// DEVICE-O3-NOT: "-O3"
+// DEVICE-O3-NOT: "-fno-operator-names"
+// DEVICE-O3-NOT: argument unused during compilation
+
+// A forwarded /Od must remove the other flags implied by shared /O2.
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c /O2 /permissive \
+// RUN: -clang:-fopenmp -clang:-fopenmp-targets=amdgcn-amd-amdhsa \
+// RUN: -clang:--offload-arch=gfx1100 -clang:-nogpulib \
+// RUN: -clang:-Xopenmp-target=amdgcn-amd-amdhsa -clang:/Od \
+// RUN: -clang:-Xopenmp-target=amdgcn-amd-amdhsa -clang:/permissive- -- %s 2>&1 | \
+// RUN: FileCheck %s --check-prefix=DEVICE-OD
+// DEVICE-OD: "-cc1" "-triple" "x86_64-pc-windows-msvc{{[^"]*}}"
+// DEVICE-OD-SAME: "-O3"
+// DEVICE-OD-SAME: "-fno-operator-names"
+// DEVICE-OD: "-cc1" "-triple" "amdgpu11.00-amd-amdhsa"
+// DEVICE-OD-NOT: "-fbuiltin"
+// DEVICE-OD-NOT: "-ffunction-sections"
+// DEVICE-OD: "-O0"
+// DEVICE-OD-NOT: "-O3"
+// DEVICE-OD-NOT: "-fno-operator-names"
+// DEVICE-OD-NOT: "-fdelayed-template-parsing"
+// DEVICE-OD: "-cc1" "-triple" "x86_64-pc-windows-msvc{{[^"]*}}"
+
+// Repeated forwarded bundles use the last expandable option; explicit
+// suboptions and canonical options still retain their parsed-list order.
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c \
+// RUN: -clang:-fopenmp -clang:-fopenmp-targets=amdgcn-amd-amdhsa \
+// RUN: -clang:--offload-arch=gfx1100 -clang:-nogpulib \
+// RUN: -clang:-Xopenmp-target=amdgcn-amd-amdhsa -clang:/O2 \
+// RUN: -clang:-Xopenmp-target=amdgcn-amd-amdhsa -clang:/O1 \
+// RUN: -clang:-Xopenmp-target=amdgcn-amd-amdhsa -clang:/Ob0 \
+// RUN: -clang:-Xarch_device -clang:-O1 -- %s 2>&1 | \
+// RUN: FileCheck %s --check-prefix=DEVICE-NATIVE
+// DEVICE-NATIVE: "-cc1" "-triple" "x86_64-pc-windows-msvc{{[^"]*}}"
+// DEVICE-NATIVE: "-cc1" "-triple" "amdgpu11.00-amd-amdhsa"
+// DEVICE-NATIVE-NOT: "-Os"
+// DEVICE-NATIVE-NOT: "-O3"
+// DEVICE-NATIVE: "-O1"
+// DEVICE-NATIVE-SAME: "-fno-inline"
+// DEVICE-NATIVE: "-cc1" "-triple" "x86_64-pc-windows-msvc{{[^"]*}}"
+
+// GNU-mode Clang targeting MSVC also accepted these late slash options.
+// RUN: %clang --target=x86_64-windows-msvc -### -c \
+// RUN: -fopenmp -fopenmp-targets=x86_64-pc-windows-msvc -nogpulib \
+// RUN: -Xopenmp-target=x86_64-pc-windows-msvc /O2 \
+// RUN: -Xopenmp-target=x86_64-pc-windows-msvc /permissive -- %s 2>&1 | \
+// RUN: FileCheck %s --check-prefix=DEVICE-O3
+
+// Separate device architecture argument lists retain their own overrides.
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c /O2 \
+// RUN: -clang:-fopenmp -clang:-fopenmp-targets=amdgcn-amd-amdhsa \
+// RUN: -clang:--offload-arch=gfx1100,gfx1101 -clang:-nogpulib \
+// RUN: -clang:-Xopenmp-target=amdgcn-amd-amdhsa -clang:/O1 \
+// RUN: -clang:-Xarch_gfx1100 -clang:-O0 -- %s 2>&1 | \
+// RUN: FileCheck %s --check-prefix=MULTIARCH
+// MULTIARCH: "-cc1" "-triple" "x86_64-pc-windows-msvc{{[^"]*}}"
+// MULTIARCH-SAME: "-O3"
+// MULTIARCH: "-cc1" "-triple" "amdgpu11.00-amd-amdhsa"
+// MULTIARCH-SAME: "-O0"
+// MULTIARCH: "-cc1" "-triple" "amdgpu11.01-amd-amdhsa"
+// MULTIARCH-SAME: "-Os"
+// MULTIARCH: "-cc1" "-triple" "x86_64-pc-windows-msvc{{[^"]*}}"
>From 1551eb5afed5f37ba5575fc1bb2b1c7d905c6e29 Mon Sep 17 00:00:00 2001
From: "Yaxun (Sam) Liu" <yaxun.liu at amd.com>
Date: Sat, 3 Oct 2026 07:17:50 -0400
Subject: [PATCH 4/4] [clang] Use clang-cl mode for filtered argument
translation
Translate filtered argument lists in clang-cl mode regardless of the
host toolchain or offload kind. Keep GNU-mode MSVC forwarded-option
compatibility in the OpenMP parser using the same ClangCLArgs helper.
Pass the resolved host triple to that parser so frame-pointer options
respect -m32 and -m64. Add host-filter, MinGW, and architecture-override
regression coverage, and let HIP tests exercise the default driver.
---
clang/include/clang/Driver/ToolChain.h | 3 +-
clang/lib/Driver/Compilation.cpp | 9 ++--
clang/lib/Driver/ToolChain.cpp | 17 +++++--
clang/test/Driver/cl-argument-translation.c | 18 +++++++
.../Driver/hip-cl-argument-translation.hip | 18 +++----
clang/test/Driver/hip-cl-optimization.hip | 20 ++++----
.../Driver/openmp-cl-argument-translation.cpp | 48 +++++++++++++++++++
7 files changed, 102 insertions(+), 31 deletions(-)
diff --git a/clang/include/clang/Driver/ToolChain.h b/clang/include/clang/Driver/ToolChain.h
index 7304ddc43b63f7..b01dbb18c483cf 100644
--- a/clang/include/clang/Driver/ToolChain.h
+++ b/clang/include/clang/Driver/ToolChain.h
@@ -402,8 +402,9 @@ class ToolChain {
/// TranslateOpenMPTargetArgs - Create a new derived argument list for
/// that contains the OpenMP target specific flags passed via
/// -Xopenmp-target -opt=val OR -Xopenmp-target=<triple> -opt=val
+ /// HostTriple is the resolved host target, including architecture overrides.
virtual llvm::opt::DerivedArgList *TranslateOpenMPTargetArgs(
- const llvm::opt::DerivedArgList &Args, bool SameTripleAsHost,
+ const llvm::opt::DerivedArgList &Args, const llvm::Triple &HostTriple,
SmallVectorImpl<llvm::opt::Arg *> &AllocatedArgs) const;
/// Append the argument following \p A to \p DAL assuming \p A is an Xarch
diff --git a/clang/lib/Driver/Compilation.cpp b/clang/lib/Driver/Compilation.cpp
index 3ccfe60c520d92..c7ddf07bedf959 100644
--- a/clang/lib/Driver/Compilation.cpp
+++ b/clang/lib/Driver/Compilation.cpp
@@ -74,9 +74,8 @@ Compilation::getArgsForToolChain(const ToolChain *TC, BoundArch BA,
// Translate OpenMP toolchain arguments provided via the -Xopenmp-target flags.
if (DeviceOffloadKind == Action::OFK_OpenMP) {
const ToolChain *HostTC = getSingleOffloadToolChain<Action::OFK_Host>();
- bool SameTripleAsHost = (TC->getTriple() == HostTC->getTriple());
OpenMPArgs = TC->TranslateOpenMPTargetArgs(
- *TranslatedArgs, SameTripleAsHost, AllocatedArgs);
+ *TranslatedArgs, HostTC->getTriple(), AllocatedArgs);
}
DerivedArgList *NewDAL = nullptr;
@@ -92,10 +91,8 @@ Compilation::getArgsForToolChain(const ToolChain *TC, BoundArch BA,
delete OpenMPArgs;
}
- if (NewDAL && DeviceOffloadKind == Action::OFK_OpenMP &&
- (TheDriver.IsCLMode() ||
- DefaultToolChain.getTriple().isWindowsMSVCEnvironment())) {
- // OpenMP forwarding can introduce clang-cl options after the shared
+ if (NewDAL && TheDriver.IsCLMode()) {
+ // Forwarding can introduce clang-cl options after the shared
// translation. Keep synthesized arguments in the compilation-owned list.
auto *DAL = ClangCLArgs::translateArgs(
*NewDAL, DefaultToolChain.getTriple(), *TranslatedArgs);
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index 2d7dc6e63534b2..242f75af52d7b0 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -2009,11 +2009,12 @@ ToolChain::computeMSVCVersion(const Driver *D,
}
llvm::opt::DerivedArgList *ToolChain::TranslateOpenMPTargetArgs(
- const llvm::opt::DerivedArgList &Args, bool SameTripleAsHost,
+ const llvm::opt::DerivedArgList &Args, const llvm::Triple &HostTriple,
SmallVectorImpl<llvm::opt::Arg *> &AllocatedArgs) const {
DerivedArgList *DAL = new DerivedArgList(Args.getBaseArgs());
const OptTable &Opts = getDriver().getOpts();
bool Modified = false;
+ bool SameTripleAsHost = getTriple() == HostTriple;
// Handle -Xopenmp-target flags
for (auto *A : Args) {
@@ -2076,9 +2077,7 @@ llvm::opt::DerivedArgList *ToolChain::TranslateOpenMPTargetArgs(
XOpenMPTargetArg->setBaseArg(A);
A = XOpenMPTargetArg.release();
if (A->getOption().matches(options::OPT_D) &&
- (getDriver().IsCLMode() ||
- llvm::Triple(llvm::Triple::normalize(getDriver().getTargetTriple()))
- .isWindowsMSVCEnvironment()))
+ (getDriver().IsCLMode() || HostTriple.isWindowsMSVCEnvironment()))
A->getValues()[0] =
ClangCLArgs::translateMacroDefinition(A->getValue(), Args);
AllocatedArgs.push_back(A);
@@ -2086,8 +2085,16 @@ llvm::opt::DerivedArgList *ToolChain::TranslateOpenMPTargetArgs(
Modified = true;
}
- if (Modified)
+ if (Modified) {
+ // GNU-mode Clang targeting MSVC also accepts forwarded clang-cl options.
+ if (!getDriver().IsCLMode() && HostTriple.isWindowsMSVCEnvironment()) {
+ DerivedArgList *Translated =
+ ClangCLArgs::translateArgs(*DAL, HostTriple, Args);
+ delete DAL;
+ return Translated;
+ }
return DAL;
+ }
delete DAL;
return nullptr;
diff --git a/clang/test/Driver/cl-argument-translation.c b/clang/test/Driver/cl-argument-translation.c
index e1758265fa8890..a297ff88b7e2df 100644
--- a/clang/test/Driver/cl-argument-translation.c
+++ b/clang/test/Driver/cl-argument-translation.c
@@ -13,6 +13,24 @@
// MSVC: "-D" "FOO=7"
// GNU: "-D" "FOO#7"
+// Filtering host arguments preserves explicit overrides of shared /O options.
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc /c -### /O2 /Ob0 \
+// RUN: -clang:-Xarch_host -clang:-O1 -clang:-Xarch_host -clang:-fno-builtin \
+// RUN: -- %s 2>&1 | FileCheck %s --check-prefix=FILTERED
+// RUN: %clang_cl --target=x86_64-unknown-linux-gnu /c -### /O2 /Ob0 \
+// RUN: -clang:-Xarch_host -clang:-O1 -clang:-Xarch_host -clang:-fno-builtin \
+// RUN: -- %s 2>&1 | FileCheck %s --check-prefix=FILTERED
+// RUN: %clang_cl --target=x86_64-w64-windows-gnu /c -### /O2 /Ob0 \
+// RUN: -clang:-Xarch_host -clang:-O1 -clang:-Xarch_host -clang:-fno-builtin \
+// RUN: -- %s 2>&1 | FileCheck %s --check-prefix=FILTERED
+// FILTERED: "-cc1"
+// FILTERED-NOT: "-O3"
+// FILTERED: "-O1"
+// FILTERED-NOT: "-O3"
+// FILTERED: "-fno-builtin"
+// FILTERED-SAME: "-fno-inline"
+// FILTERED-NOT: argument unused during compilation
+
// Last-option precedence must include both slash and canonical options.
// RUN: %clang_cl --target=x86_64-pc-windows-msvc /c -x c++ -### /permissive- /permissive -- %s 2>&1 | \
// RUN: FileCheck %s --check-prefix=PERMISSIVE
diff --git a/clang/test/Driver/hip-cl-argument-translation.hip b/clang/test/Driver/hip-cl-argument-translation.hip
index e35201d37c2351..241d265cce3eca 100644
--- a/clang/test/Driver/hip-cl-argument-translation.hip
+++ b/clang/test/Driver/hip-cl-argument-translation.hip
@@ -1,18 +1,18 @@
// Shared source-language options must reach both HIP jobs.
-// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --offload-new-driver \
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c \
// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
// RUN: /DFOO#7 /permissive- -- %s 2>&1 | FileCheck %s --check-prefix=COMMON
-// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --offload-new-driver \
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c \
// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
// RUN: /permissive -- %s 2>&1 | FileCheck %s --check-prefix=PERMISSIVE
-// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --offload-new-driver \
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c \
// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
// RUN: /DFOO#1 -clang:-Xarch_host -clang:-DFOO#2 -clang:-Xarch_device -clang:-DDEV#3 -- %s 2>&1 | \
// RUN: FileCheck %s --check-prefix=SCOPED
-// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --offload-new-driver \
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c \
// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
// RUN: /permissive -clang:-Xarch_device -clang:-fno-delayed-template-parsing -clang:-Xarch_device \
// RUN: -clang:-foperator-names -- %s 2>&1 | FileCheck %s --check-prefix=DEVICE-CONFORMING
@@ -35,20 +35,20 @@
// RUN: /permissive -clang:-Xarch_device -clang:-fno-delayed-template-parsing -clang:-Xarch_device \
// RUN: -clang:-foperator-names -- %s 2>&1 | FileCheck %s --check-prefix=DEVICE-CONFORMING
-// RUN: %clang_cl --target=x86_64-unknown-linux-gnu -### /c --offload-new-driver \
+// RUN: %clang_cl --target=x86_64-unknown-linux-gnu -### /c \
// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
// RUN: /DFOO#7 /permissive- -- %s 2>&1 | FileCheck %s --check-prefix=COMMON
-// RUN: %clang_cl --target=x86_64-unknown-linux-gnu -### /c --offload-new-driver \
+// RUN: %clang_cl --target=x86_64-unknown-linux-gnu -### /c \
// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
// RUN: /permissive -- %s 2>&1 | FileCheck %s --check-prefix=PERMISSIVE
-// RUN: %clang_cl --target=x86_64-unknown-linux-gnu -### /c --offload-new-driver \
+// RUN: %clang_cl --target=x86_64-unknown-linux-gnu -### /c \
// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
// RUN: /DFOO#1 -clang:-Xarch_host -clang:-DFOO#2 -clang:-Xarch_device -clang:-DDEV#3 -- %s 2>&1 | \
// RUN: FileCheck %s --check-prefix=SCOPED
-// RUN: %clang_cl --target=x86_64-unknown-linux-gnu -### /c --offload-new-driver \
+// RUN: %clang_cl --target=x86_64-unknown-linux-gnu -### /c \
// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
// RUN: /permissive -clang:-Xarch_device -clang:-fno-delayed-template-parsing -clang:-Xarch_device \
// RUN: -clang:-foperator-names -- %s 2>&1 | FileCheck %s --check-prefix=DEVICE-CONFORMING
@@ -102,6 +102,6 @@
// DEVICE-CONFORMING-SAME: "-fdelayed-template-parsing"
// GNU-mode offloading retains MSVC macro syntax when selected by the host.
-// RUN: %clang --target=x86_64-pc-windows-msvc -### -c --offload-new-driver \
+// RUN: %clang --target=x86_64-pc-windows-msvc -### -c \
// RUN: -x hip --offload-arch=gfx1100 -nogpuinc -nogpulib \
// RUN: -DFOO#1 -Xarch_host -DFOO#2 -Xarch_device -DDEV#3 -- %s 2>&1 | FileCheck %s --check-prefix=SCOPED
diff --git a/clang/test/Driver/hip-cl-optimization.hip b/clang/test/Driver/hip-cl-optimization.hip
index 1fa80de854e566..0dc4cdd97e5924 100644
--- a/clang/test/Driver/hip-cl-optimization.hip
+++ b/clang/test/Driver/hip-cl-optimization.hip
@@ -1,43 +1,43 @@
// Check that clang-cl optimization options apply to both host and device.
// Device-specific options must still override the shared optimization level.
-// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --offload-new-driver \
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c \
// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
// RUN: /O1 -- %s 2>&1 | FileCheck %s --check-prefix=OS
-// RUN: %clang_cl --target=x86_64-unknown-linux-gnu -### /c --offload-new-driver \
+// RUN: %clang_cl --target=x86_64-unknown-linux-gnu -### /c \
// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
// RUN: /O1 -- %s 2>&1 | FileCheck %s --check-prefix=LINUX
-// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --offload-new-driver \
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c \
// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
// RUN: /O2 -- %s 2>&1 | FileCheck %s --check-prefix=O3
-// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --offload-new-driver \
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c \
// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
// RUN: /Ox -- %s 2>&1 | FileCheck %s --check-prefix=O3
-// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --offload-new-driver \
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c \
// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
// RUN: /Od -- %s 2>&1 | FileCheck %s --check-prefix=O0
-// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --offload-new-driver \
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c \
// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
// RUN: /O2 /Od -- %s 2>&1 | FileCheck %s --check-prefix=O0
-// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --offload-new-driver \
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c \
// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
// RUN: /Od /O2 -- %s 2>&1 | FileCheck %s --check-prefix=O3
-// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --offload-new-driver \
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c \
// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
// RUN: /O2 -clang:-O1 -- %s 2>&1 | FileCheck %s --check-prefix=O1
-// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --offload-new-driver \
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c \
// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
// RUN: /O2 -clang:-Xarch_device -clang:-O1 -- %s 2>&1 | FileCheck %s --check-prefix=DEVICE-O1
-// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c --offload-new-driver \
+// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c \
// RUN: -x hip --offload-arch=gfx1100 -clang:-nogpuinc -clang:-nogpulib \
// RUN: /O2 /Ob0 -- %s 2>&1 | FileCheck %s --check-prefix=NOINLINE
diff --git a/clang/test/Driver/openmp-cl-argument-translation.cpp b/clang/test/Driver/openmp-cl-argument-translation.cpp
index 583db4718c9247..5f357472377553 100644
--- a/clang/test/Driver/openmp-cl-argument-translation.cpp
+++ b/clang/test/Driver/openmp-cl-argument-translation.cpp
@@ -39,6 +39,13 @@
// RUN: -clang:-Xopenmp-target=amdgcn-amd-amdhsa -clang:/permissive -- %s 2>&1 | \
// RUN: FileCheck %s --check-prefix=DEVICE-O3
+// RUN: %clang_cl --target=x86_64-w64-windows-gnu -### /c \
+// RUN: -clang:-fopenmp -clang:-fopenmp-targets=amdgcn-amd-amdhsa \
+// RUN: -clang:--offload-arch=gfx1100 -clang:-nogpulib \
+// RUN: -clang:-Xopenmp-target=amdgcn-amd-amdhsa -clang:/O2 \
+// RUN: -clang:-Xopenmp-target=amdgcn-amd-amdhsa -clang:/permissive -- %s 2>&1 | \
+// RUN: FileCheck %s --check-prefix=DEVICE-O3
+
// DEVICE-O3: "-cc1" "-triple" "x86_64-{{[^"]*}}"
// DEVICE-O3-NOT: "-O3"
// DEVICE-O3-NOT: "-fno-operator-names"
@@ -95,6 +102,47 @@
// RUN: -Xopenmp-target=x86_64-pc-windows-msvc /permissive -- %s 2>&1 | \
// RUN: FileCheck %s --check-prefix=DEVICE-O3
+// Forwarded frame-pointer options use the host architecture after -m32/-m64.
+// /Oy- is silently accepted on x86-64, including when bundled with /O2.
+// RUN: %clang --target=i686-pc-windows-msvc -m64 -### -c -fopenmp \
+// RUN: -fopenmp-targets=x86_64-pc-windows-msvc -nogpulib \
+// RUN: -Xopenmp-target=x86_64-pc-windows-msvc /Oy- -- %s 2>&1 | \
+// RUN: FileCheck %s --check-prefix=FRAME64
+// RUN: %clang --target=i686-pc-windows-msvc -m64 -### -c -fopenmp \
+// RUN: -fopenmp-targets=x86_64-pc-windows-msvc -nogpulib \
+// RUN: -Xopenmp-target=x86_64-pc-windows-msvc /O2y- -- %s 2>&1 | \
+// RUN: FileCheck %s --check-prefix=FRAME64
+// FRAME64: "-cc1" "-triple" "x86_64-pc-windows-msvc{{[^"]*}}"
+// FRAME64: "-cc1" "-triple" "x86_64-pc-windows-msvc{{[^"]*}}"
+// FRAME64-SAME: "-mframe-pointer=none"
+
+// RUN: %clang --target=x86_64-pc-windows-msvc -m32 -### -c -fopenmp \
+// RUN: -fopenmp-targets=i386-pc-windows-msvc -nogpulib \
+// RUN: -Xopenmp-target=i386-pc-windows-msvc /Oy -- %s 2>&1 | \
+// RUN: FileCheck %s --check-prefix=FRAME32-OMIT
+// FRAME32-OMIT: "-cc1" "-triple" "i386-pc-windows-msvc{{[^"]*}}"
+// FRAME32-OMIT: "-cc1" "-triple" "i386-pc-windows-msvc{{[^"]*}}"
+// FRAME32-OMIT-SAME: "-mframe-pointer=none"
+
+// RUN: %clang --target=x86_64-pc-windows-msvc -m32 -### -c -fopenmp \
+// RUN: -fopenmp-targets=i386-pc-windows-msvc -nogpulib \
+// RUN: -Xopenmp-target=i386-pc-windows-msvc /O2y- -- %s 2>&1 | \
+// RUN: FileCheck %s --check-prefix=FRAME32-KEEP
+// FRAME32-KEEP: "-cc1" "-triple" "i386-pc-windows-msvc{{[^"]*}}"
+// FRAME32-KEEP: "-cc1" "-triple" "i386-pc-windows-msvc{{[^"]*}}"
+// FRAME32-KEEP-SAME: "-O3"
+// FRAME32-KEEP-SAME: "-mframe-pointer=all"
+
+// A different device architecture does not change the host's /Oy- policy.
+// RUN: %clang --target=i686-pc-windows-msvc -m64 -### -c -fopenmp \
+// RUN: -fopenmp-targets=i386-pc-windows-msvc -nogpulib \
+// RUN: -Xopenmp-target=i386-pc-windows-msvc /O2y- -- %s 2>&1 | \
+// RUN: FileCheck %s --check-prefix=HOST64-DEVICE32
+// HOST64-DEVICE32: "-cc1" "-triple" "x86_64-pc-windows-msvc{{[^"]*}}"
+// HOST64-DEVICE32: "-cc1" "-triple" "i386-pc-windows-msvc{{[^"]*}}"
+// HOST64-DEVICE32-SAME: "-O3"
+// HOST64-DEVICE32-SAME: "-mframe-pointer=none"
+
// Separate device architecture argument lists retain their own overrides.
// RUN: %clang_cl --target=x86_64-pc-windows-msvc -### /c /O2 \
// RUN: -clang:-fopenmp -clang:-fopenmp-targets=amdgcn-amd-amdhsa \
More information about the cfe-commits
mailing list