https://github.com/dpaoliello updated 
https://github.com/llvm/llvm-project/pull/176276

>From 4e948b6e89cc52f300508e28f1f269a5657689f9 Mon Sep 17 00:00:00 2001
From: Daniel Paoliello <[email protected]>
Date: Thu, 15 Jan 2026 16:11:36 -0800
Subject: [PATCH] [win] Add a flag to control the Control Flow Guard mechanism
 on Windows

---
 clang/include/clang/Basic/CodeGenOptions.def  |  4 ++
 clang/include/clang/Basic/CodeGenOptions.h    |  1 +
 clang/include/clang/Options/Options.td        | 12 +++++
 clang/lib/CodeGen/CodeGenModule.cpp           |  7 +++
 clang/lib/Driver/ToolChains/Clang.cpp         |  9 ++++
 clang/test/CodeGen/cfguard-mechanism.c        | 11 ++++
 clang/test/Driver/cl-options.c                |  6 +++
 llvm/docs/LangRef.rst                         | 30 +++++++++++
 llvm/include/llvm/Support/CodeGen.h           |  7 +++
 llvm/include/llvm/Transforms/CFGuard.h        | 12 ++---
 llvm/lib/Passes/PassBuilder.cpp               | 20 -------
 llvm/lib/Passes/PassRegistry.def              |  5 +-
 .../AArch64/AArch64Arm64ECCallLowering.cpp    | 16 ++++++
 .../Target/AArch64/AArch64TargetMachine.cpp   |  2 +-
 llvm/lib/Target/ARM/ARMTargetMachine.cpp      |  2 +-
 llvm/lib/Target/X86/X86CallingConv.td         |  4 +-
 llvm/lib/Target/X86/X86CodeGenPassBuilder.cpp |  4 +-
 llvm/lib/Target/X86/X86RegisterInfo.cpp       | 10 ++--
 llvm/lib/Target/X86/X86TargetMachine.cpp      |  6 +--
 llvm/lib/Transforms/CFGuard/CFGuard.cpp       | 52 ++++++++++---------
 .../CodeGen/AArch64/cfguard-module-flag.ll    | 46 ++++++++++++----
 llvm/test/CodeGen/ARM/cfguard-module-flag.ll  | 36 +++++++++----
 llvm/test/CodeGen/X86/cfguard-module-flag.ll  | 43 +++++++++++----
 llvm/test/Linker/cfguard.ll                   | 39 ++++++++++++++
 24 files changed, 282 insertions(+), 102 deletions(-)
 create mode 100644 clang/test/CodeGen/cfguard-mechanism.c
 create mode 100644 llvm/test/Linker/cfguard.ll

diff --git a/clang/include/clang/Basic/CodeGenOptions.def 
b/clang/include/clang/Basic/CodeGenOptions.def
index baf8b093c10e6..3dad7dd1d551f 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -505,6 +505,10 @@ CODEGENOPT(AllResourcesBound, 1, 0, Benign)
 ENUM_CODEGENOPT(WinX64EHUnwindV2, WinX64EHUnwindV2Mode,
                 2, WinX64EHUnwindV2Mode::Disabled, Benign)
 
+/// Controls the mechanism used for Control Flow Guard (CFG) on Windows.
+ENUM_CODEGENOPT(WinControlFlowGuardMechanism, ControlFlowGuardMechanism,
+                2, ControlFlowGuardMechanism::Automatic, Benign)
+
 /// FIXME: Make DebugOptions its own top-level .def file.
 #include "DebugOptions.def"
 
diff --git a/clang/include/clang/Basic/CodeGenOptions.h 
b/clang/include/clang/Basic/CodeGenOptions.h
index 8ef0d87faaeaf..0a3d5beca5e96 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -66,6 +66,7 @@ class CodeGenOptionsBase {
   using VectorLibrary = llvm::driver::VectorLibrary;
   using ZeroCallUsedRegsKind = llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind;
   using WinX64EHUnwindV2Mode = llvm::WinX64EHUnwindV2Mode;
+  using ControlFlowGuardMechanism = llvm::ControlFlowGuardMechanism;
 
   using DebugCompressionType = llvm::DebugCompressionType;
   using EmitDwarfUnwindType = llvm::EmitDwarfUnwindType;
diff --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index 5ecb516c2cf68..65ac6fa06a880 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -2271,6 +2271,14 @@ def winx64_eh_unwindv2
       NormalizedValues<["Disabled", "BestEffort", "Required"]>,
       NormalizedValuesScope<"llvm::WinX64EHUnwindV2Mode">,
       MarshallingInfoEnum<CodeGenOpts<"WinX64EHUnwindV2">, "Disabled">;
+def win_cfg_mechanism
+    : Joined<["-"], "fwin-cfg-mechanism=">, Group<f_Group>,
+    Visibility<[ClangOption, CC1Option]>,
+      HelpText<"Sets the mechanism to use for Control Flow Guard on Windows">,
+      Values<"automatic,dispatch,check">,
+      NormalizedValues<["Automatic", "Dispatch", "Check"]>,
+      NormalizedValuesScope<"llvm::ControlFlowGuardMechanism">,
+      MarshallingInfoEnum<CodeGenOpts<"WinControlFlowGuardMechanism">, 
"Automatic">;
 def fexcess_precision_EQ : Joined<["-"], "fexcess-precision=">, Group<f_Group>,
   Visibility<[ClangOption, CLOption]>,
   HelpText<"Allows control over excess precision on targets where native "
@@ -9411,6 +9419,10 @@ def _SLASH_d2epilogunwind : CLFlag<"d2epilogunwind">,
   HelpText<"Best effort generate unwind v2 (epilog) information for x64 
Windows">;
 def _SLASH_d2epilogunwindrequirev2 : CLFlag<"d2epilogunwindrequirev2">,
   HelpText<"Require generation of unwind v2 (epilog) information for x64 
Windows">;
+def _SLASH_d2guardcfgdispatch : CLFlag<"d2guardcfgdispatch">,
+  HelpText<"Use the dispatch mechanism to call the Control Flow Guard 
checker">;
+def _SLASH_d2guardcfgdispatch_ : CLFlag<"d2guardcfgdispatch-">,
+  HelpText<"Use the check mechanism to call the Control Flow Guard checker">;
 def _SLASH_EH : CLJoined<"EH">, HelpText<"Set exception handling model">;
 def _SLASH_EP : CLFlag<"EP">,
   HelpText<"Disable linemarker output and preprocess to stdout">;
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp 
b/clang/lib/CodeGen/CodeGenModule.cpp
index d50c9605a30b3..67c54ceb74d75 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -1148,6 +1148,13 @@ void CodeGenModule::Release() {
         llvm::Module::Warning, "cfguard",
         static_cast<unsigned>(llvm::ControlFlowGuardMode::TableOnly));
   }
+  if (CodeGenOpts.getWinControlFlowGuardMechanism() !=
+      llvm::ControlFlowGuardMechanism::Automatic) {
+    // Specify the Control Flow Guard mechanism to use on Windows.
+    getModule().addModuleFlag(
+        llvm::Module::Warning, "cfguard-mechanism",
+        static_cast<unsigned>(CodeGenOpts.getWinControlFlowGuardMechanism()));
+  }
   if (CodeGenOpts.EHContGuard) {
     // Function ID tables for EH Continuation Guard.
     getModule().addModuleFlag(llvm::Module::Warning, "ehcontguard", 1);
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp 
b/clang/lib/Driver/ToolChains/Clang.cpp
index 0293b04217673..37fe39cd9a92c 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7457,6 +7457,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction 
&JA,
   // Unwind v2 (epilog) information for x64 Windows.
   Args.AddLastArg(CmdArgs, options::OPT_winx64_eh_unwindv2);
 
+  // Control Flow Guard mechanism for Windows.
+  Args.AddLastArg(CmdArgs, options::OPT_win_cfg_mechanism);
+
   // C++ "sane" operator new.
   Args.addOptOutFlag(CmdArgs, options::OPT_fassume_sane_operator_new,
                      options::OPT_fno_assume_sane_operator_new);
@@ -8557,6 +8560,12 @@ void Clang::AddClangCLArgs(const ArgList &Args, 
types::ID InputType,
     A->claim();
   }
 
+  // Control Flow Guard mechanism for Windows.
+  if (Args.hasArg(options::OPT__SLASH_d2guardcfgdispatch_))
+    CmdArgs.push_back("-fwin-cfg-mechanism=check");
+  else if (Args.hasArg(options::OPT__SLASH_d2guardcfgdispatch))
+    CmdArgs.push_back("-fwin-cfg-mechanism=dispatch");
+
   for (const auto &FuncOverride :
        Args.getAllArgValues(options::OPT__SLASH_funcoverride)) {
     CmdArgs.push_back(Args.MakeArgString(
diff --git a/clang/test/CodeGen/cfguard-mechanism.c 
b/clang/test/CodeGen/cfguard-mechanism.c
new file mode 100644
index 0000000000000..e3b3a6eae2d7f
--- /dev/null
+++ b/clang/test/CodeGen/cfguard-mechanism.c
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -emit-llvm %s -o - | FileCheck %s -check-prefix=AUTOMATIC
+// RUN: %clang_cc1 -fwin-cfg-mechanism=automatic -emit-llvm %s -o - | 
FileCheck %s -check-prefix=AUTOMATIC
+// RUN: %clang_cc1 -fwin-cfg-mechanism=dispatch -emit-llvm %s -o - | FileCheck 
%s -check-prefix=DISPATCH
+// RUN: %clang_cc1 -fwin-cfg-mechanism=check -emit-llvm %s -o - | FileCheck %s 
-check-prefix=CHECK
+// RUN: %clang -fwin-cfg-mechanism=dispatch -S -emit-llvm %s -o - | FileCheck 
%s -check-prefix=DISPATCH
+
+void f(void) {}
+
+// CHECK: !"cfguard-mechanism", i32 1}
+// DISPATCH: !"cfguard-mechanism", i32 2}
+// AUTOMATIC-NOT: "cfguard-mechanism"
diff --git a/clang/test/Driver/cl-options.c b/clang/test/Driver/cl-options.c
index 9fd3ff6326dcc..54f92c98a3739 100644
--- a/clang/test/Driver/cl-options.c
+++ b/clang/test/Driver/cl-options.c
@@ -847,4 +847,10 @@
 // FUNCOVERRIDE: -loader-replaceable-function=override_me1
 // FUNCOVERRIDE-SAME: -loader-replaceable-function=override_me2
 
+// RUN: %clang_cl /d2guardcfgdispatch /c -### -- %s 2>&1 | FileCheck %s 
--check-prefix=GUARDCFGDISPATCH
+// GUARDCFGDISPATCH: -fwin-cfg-mechanism=dispatch
+
+// RUN: %clang_cl /d2guardcfgdispatch- /c -### -- %s 2>&1 | FileCheck %s 
--check-prefix=GUARDCFGDISPATCHNEG
+// GUARDCFGDISPATCHNEG: -fwin-cfg-mechanism=check
+
 void f(void) { }
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 9c3ffb396649b..bc776f7a1e1b7 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -9011,6 +9011,36 @@ For example:
 
 This will change the stack alignment to 8B.
 
+Windows Control Flow Guard Metadata
+-----------------------------------
+
+Controls what Control Flow Guard (CFG) checks are performed, how they are
+performed, and what metadata is emitted. There are multiple flags that can be
+used to control different aspects of CFG. Using two different values for the
+same flag will raise a warning when linking.
+
+To pass this information to the backend, these options are encoded in module
+flags metadata, using the following key-value pairs:
+
+.. list-table::
+   :header-rows: 1
+   :widths: 30 70
+
+   * - Key
+     - Value
+
+   * - cfguard
+     - * 0 --- CFG is completely disabled.
+       * 1 --- The CFG table is emitted, but no checks are performed.
+       * 2 --- The CFG table is emitted and checks are performed.
+
+   * - cfguard-mechanism
+     - * 0 --- CFG uses the default mechanism for the architecture.
+       * 1 --- CFG uses the "check" mechanism. This will result in a separate
+         call to the checker function and then one to the target.
+       * 2 --- CFG uses the "dispatch" mechanism. This calls a dispatcher
+         function which both checks and then calls the target.
+
 Embedded Objects Names Metadata
 ===============================
 
diff --git a/llvm/include/llvm/Support/CodeGen.h 
b/llvm/include/llvm/Support/CodeGen.h
index 65d262a087378..52f00c3258c0f 100644
--- a/llvm/include/llvm/Support/CodeGen.h
+++ b/llvm/include/llvm/Support/CodeGen.h
@@ -183,6 +183,13 @@ namespace llvm {
     Enabled = 2,
   };
 
+  enum class ControlFlowGuardMechanism {
+    // Choose the mechanism automatically based on the target.
+    Automatic = 0,
+    Check = 1,
+    Dispatch = 2,
+  };
+
   } // namespace llvm
 
 #endif
diff --git a/llvm/include/llvm/Transforms/CFGuard.h 
b/llvm/include/llvm/Transforms/CFGuard.h
index df5385718becc..175d62851f363 100644
--- a/llvm/include/llvm/Transforms/CFGuard.h
+++ b/llvm/include/llvm/Transforms/CFGuard.h
@@ -24,18 +24,12 @@ class CFGuardPass : public PassInfoMixin<CFGuardPass> {
 public:
   enum class Mechanism { Check, Dispatch };
 
-  CFGuardPass(Mechanism M = Mechanism::Check) : GuardMechanism(M) {}
+  CFGuardPass() {}
   LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM);
-
-private:
-  Mechanism GuardMechanism;
 };
 
-/// Insert Control FLow Guard checks on indirect function calls.
-LLVM_ABI FunctionPass *createCFGuardCheckPass();
-
-/// Insert Control FLow Guard dispatches on indirect function calls.
-LLVM_ABI FunctionPass *createCFGuardDispatchPass();
+/// Insert Control Flow Guard checks on indirect function calls.
+LLVM_ABI FunctionPass *createCFGuardPass();
 
 LLVM_ABI bool isCFGuardCall(const CallBase *CB);
 LLVM_ABI bool isCFGuardFunction(const GlobalValue *GV);
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 45955426d66a0..0acf3a1e7146c 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -871,26 +871,6 @@ Expected<bool> 
parsePostOrderFunctionAttrsPassOptions(StringRef Params) {
       Params, "skip-non-recursive-function-attrs", "PostOrderFunctionAttrs");
 }
 
-Expected<CFGuardPass::Mechanism> parseCFGuardPassOptions(StringRef Params) {
-  if (Params.empty())
-    return CFGuardPass::Mechanism::Check;
-
-  auto [Param, RHS] = Params.split(';');
-  if (!RHS.empty())
-    return make_error<StringError>(
-        formatv("too many CFGuardPass parameters '{}'", Params).str(),
-        inconvertibleErrorCode());
-
-  if (Param == "check")
-    return CFGuardPass::Mechanism::Check;
-  if (Param == "dispatch")
-    return CFGuardPass::Mechanism::Dispatch;
-
-  return make_error<StringError>(
-      formatv("invalid CFGuardPass mechanism: '{}'", Param).str(),
-      inconvertibleErrorCode());
-}
-
 Expected<bool> parseEarlyCSEPassOptions(StringRef Params) {
   return PassBuilder::parseSinglePassOption(Params, "memssa", "EarlyCSE");
 }
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 2cfb5b2592601..02ada05519433 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -413,6 +413,7 @@ FUNCTION_PASS("bdce", BDCEPass())
 FUNCTION_PASS("break-crit-edges", BreakCriticalEdgesPass())
 FUNCTION_PASS("callbr-prepare", CallBrPreparePass())
 FUNCTION_PASS("callsite-splitting", CallSiteSplittingPass())
+FUNCTION_PASS("cfguard", CFGuardPass())
 FUNCTION_PASS("chr", ControlHeightReductionPass())
 FUNCTION_PASS("codegenprepare", CodeGenPreparePass(*TM))
 FUNCTION_PASS("complex-deinterleaving", ComplexDeinterleavingPass(*TM))
@@ -573,10 +574,6 @@ FUNCTION_PASS("wasm-eh-prepare", WasmEHPreparePass())
 #ifndef FUNCTION_PASS_WITH_PARAMS
 #define FUNCTION_PASS_WITH_PARAMS(NAME, CLASS, CREATE_PASS, PARSER, PARAMS)
 #endif
-FUNCTION_PASS_WITH_PARAMS(
-    "cfguard", "CFGuardPass",
-    [](CFGuardPass::Mechanism M) { return CFGuardPass(M); },
-    parseCFGuardPassOptions, "check;dispatch")
 FUNCTION_PASS_WITH_PARAMS(
     "early-cse", "EarlyCSEPass",
     [](bool UseMemorySSA) { return EarlyCSEPass(UseMemorySSA); },
diff --git a/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp 
b/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp
index c27a693ceecc1..c8acd9a383574 100644
--- a/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp
@@ -21,6 +21,7 @@
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/Statistic.h"
 #include "llvm/IR/CallingConv.h"
+#include "llvm/IR/DiagnosticInfo.h"
 #include "llvm/IR/GlobalAlias.h"
 #include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/Instruction.h"
@@ -797,6 +798,21 @@ bool AArch64Arm64ECCallLowering::runOnModule(Module &Mod) {
   // Check if this module has the cfguard flag and read its value.
   CFGuardModuleFlag = M->getControlFlowGuardMode();
 
+  // Warn if the module flag requests an unsupported CFGuard mechanism.
+  if (CFGuardModuleFlag == ControlFlowGuardMode::Enabled) {
+    if (auto *CI = mdconst::dyn_extract_or_null<ConstantInt>(
+            Mod.getModuleFlag("cfguard-mechanism"))) {
+      auto MechanismOverride =
+          static_cast<ControlFlowGuardMechanism>(CI->getZExtValue());
+      if (MechanismOverride != ControlFlowGuardMechanism::Automatic &&
+          MechanismOverride != ControlFlowGuardMechanism::Check)
+        Mod.getContext().diagnose(
+            DiagnosticInfoGeneric("only the Check Control Flow Guard mechanism 
"
+                                  "is supported for Arm64EC",
+                                  DS_Warning));
+    }
+  }
+
   PtrTy = PointerType::getUnqual(M->getContext());
   I64Ty = Type::getInt64Ty(M->getContext());
   VoidTy = Type::getVoidTy(M->getContext());
diff --git a/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp 
b/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp
index 652844e9dc591..b7a2bf58a2c45 100644
--- a/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp
+++ b/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp
@@ -703,7 +703,7 @@ void AArch64PassConfig::addIRPasses() {
     if (TM->getTargetTriple().isWindowsArm64EC())
       addPass(createAArch64Arm64ECCallLoweringPass());
     else
-      addPass(createCFGuardCheckPass());
+      addPass(createCFGuardPass());
   }
 
   if (TM->Options.JMCInstrument)
diff --git a/llvm/lib/Target/ARM/ARMTargetMachine.cpp 
b/llvm/lib/Target/ARM/ARMTargetMachine.cpp
index 590d4c70592f8..aadf8624c7c8c 100644
--- a/llvm/lib/Target/ARM/ARMTargetMachine.cpp
+++ b/llvm/lib/Target/ARM/ARMTargetMachine.cpp
@@ -384,7 +384,7 @@ void ARMPassConfig::addIRPasses() {
 
   // Add Control Flow Guard checks.
   if (TM->getTargetTriple().isOSWindows())
-    addPass(createCFGuardCheckPass());
+    addPass(createCFGuardPass());
 
   if (TM->Options.JMCInstrument)
     addPass(createJMCInstrumenterPass());
diff --git a/llvm/lib/Target/X86/X86CallingConv.td 
b/llvm/lib/Target/X86/X86CallingConv.td
index f020e0b55141c..7d24126f820f8 100644
--- a/llvm/lib/Target/X86/X86CallingConv.td
+++ b/llvm/lib/Target/X86/X86CallingConv.td
@@ -1217,8 +1217,10 @@ def CSR_Win32_CFGuard_Check_NoSSE : CalleeSavedRegs<(add 
CSR_32_RegCall_NoSSE, E
 def CSR_Win32_CFGuard_Check       : CalleeSavedRegs<(add CSR_32_RegCall, ECX)>;
 def CSR_Win64_RegCall_NoSSE : CalleeSavedRegs<(add RBX, RBP,
                                               (sequence "R%u", 10, 15))>;
-def CSR_Win64_RegCall       : CalleeSavedRegs<(add CSR_Win64_RegCall_NoSSE,    
                              
+def CSR_Win64_RegCall       : CalleeSavedRegs<(add CSR_Win64_RegCall_NoSSE,
                                               (sequence "XMM%u", 8, 15))>;
+def CSR_Win64_CFGuard_Check_NoSSE : CalleeSavedRegs<(add 
CSR_Win64_RegCall_NoSSE, RCX)>;
+def CSR_Win64_CFGuard_Check       : CalleeSavedRegs<(add CSR_Win64_RegCall, 
RCX)>;
 def CSR_SysV64_RegCall_NoSSE : CalleeSavedRegs<(add RBX, RBP,
                                                (sequence "R%u", 12, 15))>;
 def CSR_SysV64_RegCall       : CalleeSavedRegs<(add CSR_SysV64_RegCall_NoSSE,  
             
diff --git a/llvm/lib/Target/X86/X86CodeGenPassBuilder.cpp 
b/llvm/lib/Target/X86/X86CodeGenPassBuilder.cpp
index 74256b13ecf6b..8950e44307305 100644
--- a/llvm/lib/Target/X86/X86CodeGenPassBuilder.cpp
+++ b/llvm/lib/Target/X86/X86CodeGenPassBuilder.cpp
@@ -81,9 +81,7 @@ void X86CodeGenPassBuilder::addIRPasses(PassManagerWrapper 
&PMW) const {
   // Add Control Flow Guard checks.
   const Triple &TT = TM.getTargetTriple();
   if (TT.isOSWindows())
-    addFunctionPass(CFGuardPass(TT.isX86_64() ? 
CFGuardPass::Mechanism::Dispatch
-                                              : CFGuardPass::Mechanism::Check),
-                    PMW);
+    addFunctionPass(CFGuardPass(), PMW);
 
   if (TM.Options.JMCInstrument) {
     flushFPMsToMPM(PMW);
diff --git a/llvm/lib/Target/X86/X86RegisterInfo.cpp 
b/llvm/lib/Target/X86/X86RegisterInfo.cpp
index 72f38133e21ff..635c06440c873 100644
--- a/llvm/lib/Target/X86/X86RegisterInfo.cpp
+++ b/llvm/lib/Target/X86/X86RegisterInfo.cpp
@@ -434,9 +434,13 @@ X86RegisterInfo::getCallPreservedMask(const 
MachineFunction &MF,
                        CSR_32_RegCall_NoSSE_RegMask);
     }
   case CallingConv::CFGuard_Check:
-    assert(!Is64Bit && "CFGuard check mechanism only used on 32-bit X86");
-    return (HasSSE ? CSR_Win32_CFGuard_Check_RegMask
-                   : CSR_Win32_CFGuard_Check_NoSSE_RegMask);
+    if (Is64Bit) {
+      return (HasSSE ? CSR_Win64_CFGuard_Check_RegMask
+                     : CSR_Win64_CFGuard_Check_NoSSE_RegMask);
+    } else {
+      return (HasSSE ? CSR_Win32_CFGuard_Check_RegMask
+                     : CSR_Win32_CFGuard_Check_NoSSE_RegMask);
+    }
   case CallingConv::Cold:
     if (Is64Bit)
       return CSR_64_MostRegs_RegMask;
diff --git a/llvm/lib/Target/X86/X86TargetMachine.cpp 
b/llvm/lib/Target/X86/X86TargetMachine.cpp
index 96afcbf21c78f..767b4395273a2 100644
--- a/llvm/lib/Target/X86/X86TargetMachine.cpp
+++ b/llvm/lib/Target/X86/X86TargetMachine.cpp
@@ -442,11 +442,7 @@ void X86PassConfig::addIRPasses() {
   // Add Control Flow Guard checks.
   const Triple &TT = TM->getTargetTriple();
   if (TT.isOSWindows()) {
-    if (TT.isX86_64()) {
-      addPass(createCFGuardDispatchPass());
-    } else {
-      addPass(createCFGuardCheckPass());
-    }
+    addPass(createCFGuardPass());
   }
 
   if (TM->Options.JMCInstrument)
diff --git a/llvm/lib/Transforms/CFGuard/CFGuard.cpp 
b/llvm/lib/Transforms/CFGuard/CFGuard.cpp
index 28d0eddca7ce2..a9819a54d20e5 100644
--- a/llvm/lib/Transforms/CFGuard/CFGuard.cpp
+++ b/llvm/lib/Transforms/CFGuard/CFGuard.cpp
@@ -38,24 +38,11 @@ namespace {
 
 /// Adds Control Flow Guard (CFG) checks on indirect function calls/invokes.
 /// These checks ensure that the target address corresponds to the start of an
-/// address-taken function. X86_64 targets use the Mechanism::Dispatch
-/// mechanism. X86, ARM, and AArch64 targets use the Mechanism::Check 
machanism.
+/// address-taken function.
 class CFGuardImpl {
 public:
   using Mechanism = CFGuardPass::Mechanism;
 
-  CFGuardImpl(Mechanism M) : GuardMechanism(M) {
-    // Get or insert the guard check or dispatch global symbols.
-    switch (GuardMechanism) {
-    case Mechanism::Check:
-      GuardFnName = GuardCheckFunctionName;
-      break;
-    case Mechanism::Dispatch:
-      GuardFnName = GuardDispatchFunctionName;
-      break;
-    }
-  }
-
   /// Inserts a Control Flow Guard (CFG) check on an indirect call using the 
CFG
   /// check mechanism. When the image is loaded, the loader puts the 
appropriate
   /// guard check function pointer in the __guard_check_icall_fptr global
@@ -148,7 +135,6 @@ class CFGuardImpl {
 private:
   // Only add checks if the module has them enabled.
   ControlFlowGuardMode CFGuardModuleFlag = ControlFlowGuardMode::Disabled;
-  StringRef GuardFnName;
   Mechanism GuardMechanism = Mechanism::Check;
   FunctionType *GuardFnType = nullptr;
   PointerType *GuardFnPtrType = nullptr;
@@ -162,7 +148,7 @@ class CFGuard : public FunctionPass {
   static char ID;
 
   // Default constructor required for the INITIALIZE_PASS macro.
-  CFGuard(CFGuardImpl::Mechanism M) : FunctionPass(ID), Impl(M) {}
+  CFGuard() : FunctionPass(ID) {}
 
   bool doInitialization(Module &M) override { return Impl.doInitialization(M); 
}
   bool runOnFunction(Function &F) override { return Impl.runOnFunction(F); }
@@ -239,12 +225,36 @@ bool CFGuardImpl::doInitialization(Module &M) {
   if (CFGuardModuleFlag != ControlFlowGuardMode::Enabled)
     return false;
 
+  // Determine the guard mechanism to use.
+  ControlFlowGuardMechanism MechanismOverride =
+      ControlFlowGuardMechanism::Automatic;
+  if (auto *CI = mdconst::dyn_extract_or_null<ConstantInt>(
+          M.getModuleFlag("cfguard-mechanism")))
+    MechanismOverride =
+        static_cast<ControlFlowGuardMechanism>(CI->getZExtValue());
+  switch (MechanismOverride) {
+  case ControlFlowGuardMechanism::Check:
+    GuardMechanism = Mechanism::Check;
+    break;
+  case ControlFlowGuardMechanism::Dispatch:
+    GuardMechanism = Mechanism::Dispatch;
+    break;
+  default:
+    // X86_64 uses dispatch; all other architectures use check.
+    GuardMechanism =
+        M.getTargetTriple().isX86_64() ? Mechanism::Dispatch : 
Mechanism::Check;
+    break;
+  }
+
   // Set up prototypes for the guard check and dispatch functions.
   GuardFnType =
       FunctionType::get(Type::getVoidTy(M.getContext()),
                         {PointerType::getUnqual(M.getContext())}, false);
   GuardFnPtrType = PointerType::get(M.getContext(), 0);
 
+  StringRef GuardFnName = GuardMechanism == Mechanism::Check
+                              ? GuardCheckFunctionName
+                              : GuardDispatchFunctionName;
   GuardFnGlobal = M.getOrInsertGlobal(GuardFnName, GuardFnPtrType, [&] {
     auto *Var = new GlobalVariable(M, GuardFnPtrType, false,
                                    GlobalVariable::ExternalLinkage, nullptr,
@@ -294,7 +304,7 @@ bool CFGuardImpl::runOnFunction(Function &F) {
 }
 
 PreservedAnalyses CFGuardPass::run(Function &F, FunctionAnalysisManager &FAM) {
-  CFGuardImpl Impl(GuardMechanism);
+  CFGuardImpl Impl;
   bool Changed = Impl.doInitialization(*F.getParent());
   Changed |= Impl.runOnFunction(F);
   return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all();
@@ -303,13 +313,7 @@ PreservedAnalyses CFGuardPass::run(Function &F, 
FunctionAnalysisManager &FAM) {
 char CFGuard::ID = 0;
 INITIALIZE_PASS(CFGuard, "CFGuard", "CFGuard", false, false)
 
-FunctionPass *llvm::createCFGuardCheckPass() {
-  return new CFGuard(CFGuardPass::Mechanism::Check);
-}
-
-FunctionPass *llvm::createCFGuardDispatchPass() {
-  return new CFGuard(CFGuardPass::Mechanism::Dispatch);
-}
+FunctionPass *llvm::createCFGuardPass() { return new CFGuard(); }
 
 bool llvm::isCFGuardCall(const CallBase *CB) {
   return CB->getCallingConv() == CallingConv::CFGuard_Check ||
diff --git a/llvm/test/CodeGen/AArch64/cfguard-module-flag.ll 
b/llvm/test/CodeGen/AArch64/cfguard-module-flag.ll
index 317ad2c42a3e1..e47fa5b59c837 100644
--- a/llvm/test/CodeGen/AArch64/cfguard-module-flag.ll
+++ b/llvm/test/CodeGen/AArch64/cfguard-module-flag.ll
@@ -1,14 +1,19 @@
-
-; RUN: llc < %s -mtriple=aarch64-pc-windows-msvc | FileCheck %s
-; RUN: llc < %s -mtriple=aarch64-w64-windows-gnu | FileCheck %s
+; RUN: sed -e s/.tableonly:// %s | llc -mtriple=aarch64-pc-windows-msvc | 
FileCheck %s --check-prefixes=CHECK,TABLEONLY
+; RUN: sed -e s/.tableonly:// %s | llc -mtriple=aarch64-w64-windows-gnu | 
FileCheck %s --check-prefixes=CHECK,TABLEONLY
+; RUN: sed -e s/.normal:// %s | llc -mtriple=aarch64-pc-windows-msvc | 
FileCheck %s --check-prefixes=CHECK,USECHECK
+; RUN: sed -e s/.normal:// %s | llc -mtriple=aarch64-w64-windows-gnu | 
FileCheck %s --check-prefixes=CHECK,USECHECK
+; RUN: sed -e s/.normal:// %s | llc -mtriple=arm64ec-pc-windows-msvc 2>&1 | 
FileCheck %s --check-prefixes=CHECK,EC,NOECWARN
+; RUN: sed -e s/.check:// %s | llc -mtriple=aarch64-pc-windows-msvc | 
FileCheck %s --check-prefixes=CHECK,USECHECK
+; RUN: sed -e s/.check:// %s | llc -mtriple=arm64ec-pc-windows-msvc 2>&1 | 
FileCheck %s --check-prefixes=CHECK,EC,NOECWARN
+; RUN: sed -e s/.dispatch:// %s | llc -mtriple=aarch64-pc-windows-msvc | 
FileCheck %s --check-prefixes=CHECK,USEDISPATCH
+; RUN: sed -e s/.dispatch:// %s | llc -mtriple=arm64ec-pc-windows-msvc 2>&1 | 
FileCheck %s --check-prefixes=CHECK,EC,ECWARN
 ; Control Flow Guard is currently only available on Windows
 
-; Test that Control Flow Guard checks are not added in modules with the
-; cfguard=1 flag (emit tables but no checks).
-
-
 declare void @target_func()
 
+; NOECWARN-NOT: warning:
+; ECWARN: warning: only the Check Control Flow Guard mechanism is supported 
for Arm64EC
+
 define void @func_in_module_without_cfguard() #0 {
 entry:
   %func_ptr = alloca ptr, align 8
@@ -18,9 +23,30 @@ entry:
   call void %0()
   ret void
 
-  ; CHECK-NOT: __guard_check_icall_fptr
-  ; CHECK-NOT: __guard_dispatch_icall_fptr
+  ; CHECK:            adrp
+
+  ; USECHECK-SAME:    __guard_check_icall_fptr
+  ; USECHECK-NOT:     __guard_dispatch_icall_fptr
+
+  ; USEDISPATCH-SAME: __guard_dispatch_icall_fptr
+  ; USEDISPATCH-NOT:  __guard_check_icall_fptr
+
+  ; TABLEONLY-SAME:   target_func
+  ; TABLEONLY-NOT:    __guard_dispatch_icall_fptr
+  ; TABLEONLY-NOT:    __guard_check_icall_fptr
+
+  ; Arm64EC Always uses check
+  ; EC-SAME:    __os_arm64x_check_icall_cfg
+  ; EC-NOT:     _dispatch_icall_
 }
 
-!llvm.module.flags = !{!0}
+; CHECK: .section        .gfids$y,"dr"
+
 !0 = !{i32 2, !"cfguard", i32 1}
+!1 = !{i32 2, !"cfguard", i32 2}
+!2 = !{i32 2, !"cfguard-mechanism", i32 1}
+!3 = !{i32 2, !"cfguard-mechanism", i32 2}
+;tableonly: !llvm.module.flags = !{!0}
+;normal:    !llvm.module.flags = !{!1}
+;check:     !llvm.module.flags = !{!1, !2}
+;dispatch:  !llvm.module.flags = !{!1, !3}
diff --git a/llvm/test/CodeGen/ARM/cfguard-module-flag.ll 
b/llvm/test/CodeGen/ARM/cfguard-module-flag.ll
index bb3c04a54caff..e17730fb3e777 100644
--- a/llvm/test/CodeGen/ARM/cfguard-module-flag.ll
+++ b/llvm/test/CodeGen/ARM/cfguard-module-flag.ll
@@ -1,12 +1,11 @@
-
-; RUN: llc < %s -mtriple=arm-pc-windows-msvc | FileCheck %s
-; RUN: llc < %s -mtriple=arm-w64-windows-gnu | FileCheck %s
+; RUN: sed -e s/.tableonly:// %s | llc -mtriple=arm-pc-windows-msvc | 
FileCheck %s --check-prefixes=CHECK,TABLEONLY
+; RUN: sed -e s/.tableonly:// %s | llc -mtriple=arm-w64-windows-gnu | 
FileCheck %s --check-prefixes=CHECK,TABLEONLY
+; RUN: sed -e s/.normal:// %s | llc -mtriple=arm-pc-windows-msvc | FileCheck 
%s --check-prefixes=CHECK,USECHECK
+; RUN: sed -e s/.normal:// %s | llc -mtriple=arm-w64-windows-gnu | FileCheck 
%s --check-prefixes=CHECK,USECHECK
+; RUN: sed -e s/.check:// %s | llc -mtriple=arm-pc-windows-msvc | FileCheck %s 
--check-prefixes=CHECK,USECHECK
+; RUN: sed -e s/.dispatch:// %s | llc -mtriple=arm-pc-windows-msvc | FileCheck 
%s --check-prefixes=CHECK,USEDISPATCH
 ; Control Flow Guard is currently only available on Windows
 
-; Test that Control Flow Guard checks are not added in modules with the
-; cfguard=1 flag (emit tables but no checks).
-
-
 declare void @target_func()
 
 define void @func_in_module_without_cfguard() #0 {
@@ -18,10 +17,27 @@ entry:
   call void %0()
   ret void
 
-  ; CHECK-NOT: __guard_check_icall_fptr
-  ; CHECK-NOT: __guard_dispatch_icall_fptr
+  ; CHECK:            movw
+
+  ; USECHECK-SAME:    __guard_check_icall_fptr
+  ; USECHECK-NOT:     __guard_dispatch_icall_fptr
+
+  ; USEDISPATCH-SAME: __guard_dispatch_icall_fptr
+  ; USEDISPATCH-NOT:  __guard_check_icall_fptr
+
+  ; TABLEONLY-SAME:   target_func
+  ; TABLEONLY-NOT:    __guard_dispatch_icall_fptr
+  ; TABLEONLY-NOT:    __guard_check_icall_fptr
 }
 attributes #0 = { "correctly-rounded-divide-sqrt-fp-math"="false" 
"disable-tail-calls"="false" "less-precise-fpmad"="false" "frame-pointer"="all" 
"no-infs-fp-math"="false" "no-nans-fp-math"="false" 
"no-signed-zeros-fp-math"="false" "no-trapping-math"="false" 
"stack-protector-buffer-size"="8" "target-cpu"="cortex-a9" 
"target-features"="+armv7-a,+dsp,+fp16,+neon,+strict-align,+thumb-mode,+vfp3" 
"use-soft-float"="false"}
 
-!llvm.module.flags = !{!0}
+; CHECK: .section        .gfids$y,"dr"
+
 !0 = !{i32 2, !"cfguard", i32 1}
+!1 = !{i32 2, !"cfguard", i32 2}
+!2 = !{i32 2, !"cfguard-mechanism", i32 1}
+!3 = !{i32 2, !"cfguard-mechanism", i32 2}
+;tableonly: !llvm.module.flags = !{!0}
+;normal:    !llvm.module.flags = !{!1}
+;check:     !llvm.module.flags = !{!1, !2}
+;dispatch:  !llvm.module.flags = !{!1, !3}
diff --git a/llvm/test/CodeGen/X86/cfguard-module-flag.ll 
b/llvm/test/CodeGen/X86/cfguard-module-flag.ll
index f69eccfc3d282..cb2ba290dba31 100644
--- a/llvm/test/CodeGen/X86/cfguard-module-flag.ll
+++ b/llvm/test/CodeGen/X86/cfguard-module-flag.ll
@@ -1,13 +1,17 @@
-; RUN: llc < %s -mtriple=i686-pc-windows-msvc | FileCheck %s -check-prefix=X86
-; RUN: llc < %s -mtriple=x86_64-pc-windows-msvc | FileCheck %s 
-check-prefix=X64
-; RUN: llc < %s -mtriple=i686-w64-windows-gnu | FileCheck %s -check-prefix=X86
-; RUN: llc < %s -mtriple=x86_64-w64-windows-gnu | FileCheck %s 
-check-prefix=X64
+; RUN: sed -e s/.tableonly:// %s | llc -mtriple=i686-pc-windows-msvc | 
FileCheck %s --check-prefixes=CHECK,TABLEONLY
+; RUN: sed -e s/.tableonly:// %s | llc -mtriple=x86_64-pc-windows-msvc | 
FileCheck %s --check-prefixes=CHECK,TABLEONLY
+; RUN: sed -e s/.tableonly:// %s | llc -mtriple=i686-w64-windows-gnu | 
FileCheck %s --check-prefixes=CHECK,TABLEONLY
+; RUN: sed -e s/.tableonly:// %s | llc -mtriple=x86_64-w64-windows-gnu | 
FileCheck %s --check-prefixes=CHECK,TABLEONLY
+; RUN: sed -e s/.normal:// %s | llc -mtriple=i686-pc-windows-msvc | FileCheck 
%s --check-prefixes=CHECK,USECHECK
+; RUN: sed -e s/.normal:// %s | llc -mtriple=x86_64-pc-windows-msvc | 
FileCheck %s --check-prefixes=CHECK,USEDISPATCH
+; RUN: sed -e s/.normal:// %s | llc -mtriple=i686-w64-windows-gnu | FileCheck 
%s --check-prefixes=CHECK,USECHECK
+; RUN: sed -e s/.normal:// %s | llc -mtriple=x86_64-w64-windows-gnu | 
FileCheck %s --check-prefixes=CHECK,USEDISPATCH
+; RUN: sed -e s/.check:// %s | llc -mtriple=i686-pc-windows-msvc | FileCheck 
%s --check-prefixes=CHECK,USECHECK
+; RUN: sed -e s/.check:// %s | llc -mtriple=x86_64-pc-windows-msvc | FileCheck 
%s --check-prefixes=CHECK,USECHECK
+; RUN: sed -e s/.dispatch:// %s | llc -mtriple=i686-pc-windows-msvc | 
FileCheck %s --check-prefixes=CHECK,USEDISPATCH
+; RUN: sed -e s/.dispatch:// %s | llc -mtriple=x86_64-pc-windows-msvc | 
FileCheck %s --check-prefixes=CHECK,USEDISPATCH
 ; Control Flow Guard is currently only available on Windows
 
-; Test that Control Flow Guard checks are not added in modules with the
-; cfguard=1 flag (emit tables but no checks).
-
-
 declare void @target_func()
 
 define void @func_in_module_without_cfguard() #0 {
@@ -19,9 +23,26 @@ entry:
   call void %0()
   ret void
 
-  ; X86-NOT: __guard_check_icall_fptr
-  ; X64-NOT: __guard_dispatch_icall_fptr
+  ; CHECK:            call
+
+  ; USECHECK-SAME:    __guard_check_icall_fptr
+  ; USECHECK-NOT:     __guard_dispatch_icall_fptr
+
+  ; USEDISPATCH-SAME: __guard_dispatch_icall_fptr
+  ; USEDISPATCH-NOT:  __guard_check_icall_fptr
+
+  ; TABLEONLY-SAME:   *%
+  ; TABLEONLY-NOT:    __guard_dispatch_icall_fptr
+  ; TABLEONLY-NOT:    __guard_check_icall_fptr
 }
 
-!llvm.module.flags = !{!0}
+; CHECK: .section        .gfids$y,"dr"
+
 !0 = !{i32 2, !"cfguard", i32 1}
+!1 = !{i32 2, !"cfguard", i32 2}
+!2 = !{i32 2, !"cfguard-mechanism", i32 1}
+!3 = !{i32 2, !"cfguard-mechanism", i32 2}
+;tableonly: !llvm.module.flags = !{!0}
+;normal:    !llvm.module.flags = !{!1}
+;check:     !llvm.module.flags = !{!1, !2}
+;dispatch:  !llvm.module.flags = !{!1, !3}
diff --git a/llvm/test/Linker/cfguard.ll b/llvm/test/Linker/cfguard.ll
new file mode 100644
index 0000000000000..2ffdb6ed612ab
--- /dev/null
+++ b/llvm/test/Linker/cfguard.ll
@@ -0,0 +1,39 @@
+; RUN: split-file %s %t
+; RUN: llvm-link %t/base.ll %t/mode.ll 2>&1 | FileCheck --check-prefix=MODE %s
+; RUN: llvm-link %t/base.ll %t/mechanism.ll 2>&1 | FileCheck 
--check-prefix=MECHANISM %s
+; RUN: llvm-link %t/base.ll %t/same.ll
+
+; MODE: warning: linking module flags 'cfguard': IDs have conflicting values
+; MECHANISM: warning: linking module flags 'cfguard-mechanism': IDs have 
conflicting values
+
+;--- base.ll
+define void @foo() {
+  ret void
+}
+!llvm.module.flags = !{!0,!1}
+!0 = !{i32 2, !"cfguard", i32 1}
+!1 = !{i32 2, !"cfguard-mechanism", i32 1}
+
+;--- mode.ll
+define void @bar() {
+  ret void
+}
+!llvm.module.flags = !{!0,!1}
+!0 = !{i32 2, !"cfguard", i32 2}
+!1 = !{i32 2, !"cfguard-mechanism", i32 1}
+
+;--- mechanism.ll
+define void @bar() {
+  ret void
+}
+!llvm.module.flags = !{!0,!1}
+!0 = !{i32 2, !"cfguard", i32 1}
+!1 = !{i32 2, !"cfguard-mechanism", i32 2}
+
+;--- same.ll
+define void @bar() {
+  ret void
+}
+!llvm.module.flags = !{!0,!1}
+!0 = !{i32 2, !"cfguard", i32 1}
+!1 = !{i32 2, !"cfguard-mechanism", i32 1}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to