[clang] [llvm] [win] Add a flag to control the Control Flow Guard mechanism on Windows (PR #176276)
Daniel Paoliello via cfe-commits
cfe-commits at lists.llvm.org
Fri Feb 27 10:52:52 PST 2026
https://github.com/dpaoliello updated https://github.com/llvm/llvm-project/pull/176276
>From 3fc58627440b243bd266ce51e11ef0a0adf615b4 Mon Sep 17 00:00:00 2001
From: Daniel Paoliello <danpao at microsoft.com>
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/docs/ReleaseNotes.rst | 10 ++++
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 | 48 +++++++++++++----
llvm/test/CodeGen/ARM/cfguard-module-flag.ll | 36 +++++++++----
llvm/test/CodeGen/X86/cfguard-module-flag.ll | 50 +++++++++++++-----
llvm/test/Linker/cfguard.ll | 39 ++++++++++++++
25 files changed, 299 insertions(+), 104 deletions(-)
create mode 100644 clang/test/CodeGen/cfguard-mechanism.c
create mode 100644 llvm/test/Linker/cfguard.ll
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index af124fce2bc64..7b79215e8da7b 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -172,6 +172,16 @@ New Compiler Flags
Control Flow Guard (CFG) is enabled by other options, it will instruct Clang
to emit the CFG metadata, but disable adding checks.
+- New option ``-fwin-cfg-mechanism=`` added to control the mechanism used by
+ Control Flow Guard on Windows. Accepted values are ``automatic`` (default),
+ ``dispatch``, and ``check``. The ``dispatch`` mechanism uses the dispatch
+ function to perform indirect call checks and can improve performance, while
+ ``check`` uses the traditional check mechanism.
+- New ``-cl`` option ``/d2guardcfgdispatch`` added to match MSVC. This acts as a
+ shorthand for ``-fwin-cfg-mechanism=dispatch``.
+- New ``-cl`` option ``/d2guardcfgdispatch-`` added to match MSVC. This acts as a
+ shorthand for ``-fwin-cfg-mechanism=check``.
+
Deprecated Compiler Flags
-------------------------
diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index 5e174b21be466..053682094fa24 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -509,6 +509,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)
+
/// Adds attributes that prevent outlining (`-mno-outline`)
CODEGENOPT(DisableOutlining, 1, 0, Benign)
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 c8a1e478122e1..e25962c5e4365 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -2283,6 +2283,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 "
@@ -9517,6 +9525,10 @@ def _SLASH_d2epilogunwindrequirev2 : CLFlag<"d2epilogunwindrequirev2">,
HelpText<"Require generation of unwind v2 (epilog) information for x64 Windows">;
def _SLASH_d2guardnochecks : CLFlag<"d2guardnochecks">,
HelpText<"When used with /guard:cf, emits Windows Control Flow Guard tables only (no checks)">;
+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 c31bcabe49016..ffa06c5a509ac 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -1152,6 +1152,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 0aa93e2e46814..4518c4f0be0c4 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7500,6 +7500,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);
@@ -8638,6 +8641,12 @@ void Clang::AddClangCLArgs(const ArgList &Args, types::ID InputType,
else if (HasCFGuardNoChecks)
CmdArgs.push_back("-cfguard-no-checks");
+ // 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 b98a0a017a2ba..9580bf9be4f72 100644
--- a/clang/test/Driver/cl-options.c
+++ b/clang/test/Driver/cl-options.c
@@ -860,4 +860,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 2f5ba6c22ddab..674c6203f0515 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -9034,6 +9034,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 78e4c73ef0d2b..64133b910ad08 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -872,26 +872,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 879713f4d6e57..3bcdfd61f1be0 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -412,6 +412,7 @@ FUNCTION_PASS("atomic-expand", AtomicExpandPass(*TM))
FUNCTION_PASS("bdce", BDCEPass())
FUNCTION_PASS("break-crit-edges", BreakCriticalEdgesPass())
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 4255ebd4cc557..c5fdbf89b73e2 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 d74d7737995a1..4f9d56b9730b6 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 53932675c68b1..7eae698709a6d 100644
--- a/llvm/lib/Target/X86/X86CodeGenPassBuilder.cpp
+++ b/llvm/lib/Target/X86/X86CodeGenPassBuilder.cpp
@@ -87,9 +87,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 a71ec74ebc9b8..baa1dc87bc99d 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 7cd9cf1ed2e5c..f279aad41ade5 100644
--- a/llvm/test/CodeGen/AArch64/cfguard-module-flag.ll
+++ b/llvm/test/CodeGen/AArch64/cfguard-module-flag.ll
@@ -1,13 +1,22 @@
-
-; 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).
+; NOECWARN-NOT: warning:
+; ECWARN: warning: only the Check Control Flow Guard mechanism is supported for Arm64EC
; If no checks were inserted then the GuardCF bit shouldn't be set in @feat.00.
-; CHECK: "@feat.00" = 0
+; TABLEONLY: "@feat.00" = 0
+; USECHECK: "@feat.00" = 2048
+; USEDISPATCH: "@feat.00" = 2048
+; EC: "@feat.00" = 2048
declare void @target_func()
@@ -20,9 +29,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 bf0120781292f..0b3006231663b 100644
--- a/llvm/test/CodeGen/X86/cfguard-module-flag.ll
+++ b/llvm/test/CodeGen/X86/cfguard-module-flag.ll
@@ -1,18 +1,23 @@
-; 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,X86NOFEAT
+; RUN: sed -e s/.tableonly:// %s | llc -mtriple=x86_64-pc-windows-msvc | FileCheck %s --check-prefixes=CHECK,TABLEONLY,X64NOFEAT
+; RUN: sed -e s/.tableonly:// %s | llc -mtriple=i686-w64-windows-gnu | FileCheck %s --check-prefixes=CHECK,TABLEONLY,X86NOFEAT
+; RUN: sed -e s/.tableonly:// %s | llc -mtriple=x86_64-w64-windows-gnu | FileCheck %s --check-prefixes=CHECK,TABLEONLY,X64NOFEAT
+; RUN: sed -e s/.normal:// %s | llc -mtriple=i686-pc-windows-msvc | FileCheck %s --check-prefixes=CHECK,USECHECK,X86FEAT
+; RUN: sed -e s/.normal:// %s | llc -mtriple=x86_64-pc-windows-msvc | FileCheck %s --check-prefixes=CHECK,USEDISPATCH,X64FEAT
+; RUN: sed -e s/.normal:// %s | llc -mtriple=i686-w64-windows-gnu | FileCheck %s --check-prefixes=CHECK,USECHECK,X86FEAT
+; 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,X86FEAT
+; RUN: sed -e s/.check:// %s | llc -mtriple=x86_64-pc-windows-msvc | FileCheck %s --check-prefixes=CHECK,USECHECK,X64FEAT
+; RUN: sed -e s/.dispatch:// %s | llc -mtriple=i686-pc-windows-msvc | FileCheck %s --check-prefixes=CHECK,USEDISPATCH,X86FEAT
+; RUN: sed -e s/.dispatch:// %s | llc -mtriple=x86_64-pc-windows-msvc | FileCheck %s --check-prefixes=CHECK,USEDISPATCH,X64FEAT
; 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).
-
-; If no checks were inserted then the GuardCF bit shouldn't be set in @feat.00.
-; CHECK: "@feat.00" = 0
; i686 has SafeSEH (0x1) but should NOT have GuardCF (0x800).
-; X86: @feat.00 = 1
+; X86NOFEAT: @feat.00 = 1
+; X86FEAT: @feat.00 = 2049
; x86_64 has no SafeSEH and should NOT have GuardCF.
-; X64: @feat.00 = 0
+; X64NOFEAT: @feat.00 = 0
+; X64FEAT: @feat.00 = 2048
declare void @target_func()
@@ -25,9 +30,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}
More information about the cfe-commits
mailing list