gftg created this revision.
gftg added reviewers: xiangzhangllvm, pengfei, erichkeane, joaomoreira.
Herald added subscribers: ormris, dexonsmith, dang, jdoerfert, steven_wu, 
martong, hiraditya.
Herald added a reviewer: aaron.ballman.
gftg requested review of this revision.
Herald added projects: clang, LLVM.
Herald added subscribers: llvm-commits, cfe-commits.

GCC has plans [1] to add a new switch that enables finer-grained control
of the insertion of CET stuff in generated code. This patch duplicates
their implementation within LLVM, in the hope that it can also be used
by Xen maintainers.

[1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102953

---8<---
With -fcf-protection=branch, clang automatically adds control-flow
protection to indirect calls and jumps. On X86, this translates to ENDBR
instructions being added to the prologues of functions.

This patch adds a new switch, '-mmanual-endbr', which tells the compiler
that, even though -fcf-protection is in use, functions should not get
the instrumentation automatically. Instead, it allows users to manually
add the new attribute, 'cf_check', to functions that require it.

When -mmanual-endbr is set, llvm refrains from automatically adding
ENDBR instructions to functions' prologues, which would have been
automatically added by -fcf-protection=branch. Although this works
correctly, missing ENDBR instructions where they are actually needed
could lead to broken binaries, which would fail only in running time.

Thus, when the backend detects that a function could be reached from an
indirect jump (e.g. when it has its address taken, or belongs to the
exported set of functions), a diagnostic warning is emitted, which
should help developers find missing occurrences of the 'cf_check'
attribute.

Depends on D118052 <https://reviews.llvm.org/D118052>.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D118355

Files:
  clang/include/clang/AST/Type.h
  clang/include/clang/AST/TypeProperties.td
  clang/include/clang/Basic/Attr.td
  clang/include/clang/Basic/AttrDocs.td
  clang/include/clang/Basic/CodeGenOptions.def
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/include/clang/CodeGen/CGFunctionInfo.h
  clang/include/clang/Driver/Options.td
  clang/lib/AST/ASTContext.cpp
  clang/lib/AST/ASTStructuralEquivalence.cpp
  clang/lib/AST/TypePrinter.cpp
  clang/lib/CodeGen/CGCall.cpp
  clang/lib/CodeGen/CodeGenModule.cpp
  clang/lib/Driver/ToolChains/Clang.cpp
  clang/lib/Frontend/CompilerInvocation.cpp
  clang/lib/Sema/SemaDeclAttr.cpp
  clang/lib/Sema/SemaType.cpp
  clang/lib/Serialization/ASTWriter.cpp
  clang/test/CodeGen/X86/x86-mmanual-endbr.c
  clang/test/CodeGen/attributes.c
  clang/test/Misc/pragma-attribute-supported-attributes-list.test
  clang/test/Sema/attr-cf_check.c
  clang/test/Sema/attr-cf_check.cpp
  clang/test/Sema/cf_check_attr_not_allowed.c
  llvm/bindings/go/llvm/ir_test.go
  llvm/docs/BitCodeFormat.rst
  llvm/docs/LangRef.rst
  llvm/include/llvm/AsmParser/LLToken.h
  llvm/include/llvm/Bitcode/LLVMBitCodes.h
  llvm/include/llvm/IR/Attributes.td
  llvm/include/llvm/IR/Function.h
  llvm/include/llvm/IR/InstrTypes.h
  llvm/lib/AsmParser/LLLexer.cpp
  llvm/lib/Bitcode/Reader/BitcodeReader.cpp
  llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
  llvm/lib/Target/X86/X86ISelLowering.cpp
  llvm/lib/Target/X86/X86IndirectBranchTracking.cpp
  llvm/lib/Transforms/Utils/CodeExtractor.cpp
  llvm/test/CodeGen/X86/cf_check.ll
  llvm/test/CodeGen/X86/missing_cf_check.ll

Index: llvm/test/CodeGen/X86/missing_cf_check.ll
===================================================================
--- /dev/null
+++ llvm/test/CodeGen/X86/missing_cf_check.ll
@@ -0,0 +1,71 @@
+; RUN: llc -mtriple=x86_64-unknown-unknown -x86-indirect-branch-tracking < %s 2>&1 | FileCheck %s
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Test that the compiler generates diagnostic messages for function pointer  ;;
+;; assignments in -mmanual-endbr mode.                                        ;;
+;; This program has been generated from the following source:                 ;;
+;;                                                                            ;;
+;; __attribute__((nocf_check)) void foo(void) {}                              ;;
+;; __attribute__((cf_check)) void bar(void) {}                                ;;
+;; void baz(void) {}                                                          ;;
+;;                                                                            ;;
+;; void foobar(void) {                                                        ;;
+;;   void (*fooptr)(void) = &foo; /* Should always warn. */                   ;;
+;;   void (*barptr)(void) = &bar; /* Should never warn. */                    ;;
+;;   void (*bazptr)(void) = &baz; /* Should warn with -mmanual-endbr. */      ;;
+;;   (*fooptr)();                                                             ;;
+;;   (*barptr)();                                                             ;;
+;;   (*bazptr)();                                                             ;;
+;; }                                                                          ;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+; Ensure that functions with 'nocf_check' attribute generate a
+; diagnostics message when assigned to function pointer.
+define dso_local void @foo() #0 {
+entry:
+  ret void
+}
+
+; No warning should be generated for this function
+define dso_local void @bar() #1 {
+entry:
+  ret void
+}
+
+; Ensure that functions without 'cf_check' attribute generate a
+; diagnostics message when assigned to function pointer in
+; -mmanual-endbr mode.
+define dso_local void @baz() #2 {
+entry:
+  ret void
+}
+
+; Function Attrs: noinline nounwind optnone uwtable
+define dso_local void @foobar() #2 {
+entry:
+  %0 = alloca void ()*, align 8
+  %1 = alloca void ()*, align 8
+  %2 = alloca void ()*, align 8
+  store void ()* @foo, void ()** %0, align 8
+  store void ()* @bar, void ()** %1, align 8
+  store void ()* @baz, void ()** %2, align 8
+  %3 = load void ()*, void ()** %0, align 8
+  call void %3()
+  %4 = load void ()*, void ()** %1, align 8
+  call void %4()
+  %5 = load void ()*, void ()** %2, align 8
+  call void %5()
+  ret void
+}
+
+attributes #0 = { nocf_check noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+attributes #1 = { cf_check noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+attributes #2 = { noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+
+!llvm.module.flags = !{!0, !1}
+
+!0 = !{i32 4, !"cf-protection-branch", i32 1}
+!1 = !{i32 4, !"manual-endbr", i32 1}
+
+; CHECK: warning: <unknown>:0:0: Function is possibly accessed from indirect branch, but has no 'cf_check' attribute
+; CHECK: warning: <unknown>:0:0: Function is possibly accessed from indirect branch, but has no 'cf_check' attribute
Index: llvm/test/CodeGen/X86/cf_check.ll
===================================================================
--- /dev/null
+++ llvm/test/CodeGen/X86/cf_check.ll
@@ -0,0 +1,62 @@
+; RUN: llc -mtriple=x86_64-unknown-unknown -x86-indirect-branch-tracking < %s | FileCheck %s
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; This test verifies the handling of ''cf_check'' attribute by the backend. ;;
+;; The file was generated using the following C code:                        ;;
+;;                                                                           ;;
+;; void NoCfCheckFunc(void) {}                                               ;;
+;; void __attribute__((cf_check)) CfCheckFunc(void) {}                       ;;
+;;                                                                           ;;
+;; typedef void(*FuncPointer)(void);                                         ;;
+;; void CfCheckCall(FuncPointer f) {                                         ;;
+;;   __attribute__((cf_check)) FuncPointer p = f;                            ;;
+;;   (*p)();                                                                 ;;
+;;   CfCheckFunc();                                                          ;;
+;; }                                                                         ;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+; Make sure that a function without ''*cf_check'' attributes is not instrumented
+; with endbr instruction when ''-mmanual-endbr'' is in effect
+define void @NoCfCheckFunc() #0 {
+; CHECK-LABEL: NoCfCheckFunc
+; CHECK-NOT:   endbr64
+entry:
+  ret void
+}
+
+; Make sure that a function with ''cf_check'' attribute is instrumented
+; with endbr instruction at the beginning.
+define void @CfCheckFunc() #1 {
+; CHECK-LABEL: CfCheckFunc
+; CHECK:       endbr64
+entry:
+  ret void
+}
+
+; Ensure the notrack prefix is not added before an indirect call using a pointer
+; with ''cf_check'' attribute. Also ensure a direct call to a function with
+; the ''cf_check'' attribute is correctly generated without notrack prefix.
+define void @CfCheckCall(void ()* %0) #0 {
+; CHECK-LABEL: CfCheckCall
+; CHECK-NOT:   notrack call
+; CHECK:       callq CfCheckFunc
+entry:
+  %1 = alloca void ()*, align 8
+  %2 = alloca void ()*, align 8
+  store void ()* %0, void ()** %1, align 8
+  %3 = load void ()*, void ()** %1, align 8
+  store void ()* %3, void ()** %2, align 8
+  %4 = load void ()*, void ()** %2, align 8
+  call void %4() #2
+  call void @CfCheckFunc() #2
+  ret void
+}
+
+attributes #0 = { noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+attributes #1 = { cf_check noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+attributes #2 = { cf_check }
+
+!llvm.module.flags = !{!0, !1}
+
+!0 = !{i32 4, !"cf-protection-branch", i32 1}
+!1 = !{i32 4, !"manual-endbr", i32 1}
Index: llvm/lib/Transforms/Utils/CodeExtractor.cpp
===================================================================
--- llvm/lib/Transforms/Utils/CodeExtractor.cpp
+++ llvm/lib/Transforms/Utils/CodeExtractor.cpp
@@ -958,6 +958,7 @@
       case Attribute::UWTable:
       case Attribute::VScaleRange:
       case Attribute::NoCfCheck:
+      case Attribute::CfCheck:
       case Attribute::MustProgress:
       case Attribute::NoProfile:
         break;
Index: llvm/lib/Target/X86/X86IndirectBranchTracking.cpp
===================================================================
--- llvm/lib/Target/X86/X86IndirectBranchTracking.cpp
+++ llvm/lib/Target/X86/X86IndirectBranchTracking.cpp
@@ -23,6 +23,7 @@
 #include "llvm/CodeGen/MachineFunctionPass.h"
 #include "llvm/CodeGen/MachineInstrBuilder.h"
 #include "llvm/CodeGen/MachineModuleInfo.h"
+#include "llvm/IR/DiagnosticInfo.h"
 
 using namespace llvm;
 
@@ -131,6 +132,7 @@
   const Module *M = MF.getMMI().getModule();
   // Check that the cf-protection-branch is enabled.
   Metadata *isCFProtectionSupported = M->getModuleFlag("cf-protection-branch");
+  Metadata *ManualENDBR = M->getModuleFlag("manual-endbr");
 
   //  NB: We need to enable IBT in jitted code if JIT compiler is CET
   //  enabled.
@@ -152,8 +154,25 @@
 
   // If function is reachable indirectly, mark the first BB with ENDBR.
   if (needsPrologueENDBR(MF, M)) {
-    auto MBB = MF.begin();
-    Changed |= addENDBR(*MBB, MBB->begin());
+    if (!ManualENDBR || MF.getFunction().doesCfCheck()) {
+      auto MBB = MF.begin();
+      Changed |= addENDBR(*MBB, MBB->begin());
+    } else {
+      // When -mmanual-endbr is in effect, the compiler does not
+      // automatically add ENDBR instructions at the entry points of any
+      // functions, unless the function has the 'cf_check' attribute.
+      // Thus, every function that would need it (i.e. functions that
+      // could be jumped into from a function pointer (e.g. address
+      // taken functions)) should have the 'cf_check' attribute manually
+      // added to it. When they don't, the following diagnostics message
+      // helps programmers find the missing attribute.
+      DiagnosticInfoOptimizationFailure Diag(
+          MF.getFunction(), NULL,
+          "Function is possibly accessed from indirect branch, "
+          "but has no 'cf_check' attribute");
+      LLVMContext &Ctx = MF.getFunction().getContext();
+      Ctx.diagnose(Diag);
+    }
   }
 
   for (auto &MBB : MF) {
Index: llvm/lib/Target/X86/X86ISelLowering.cpp
===================================================================
--- llvm/lib/Target/X86/X86ISelLowering.cpp
+++ llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -4164,6 +4164,7 @@
   bool HasNCSR = (CB && isa<CallInst>(CB) &&
                   CB->hasFnAttr("no_caller_saved_registers"));
   bool HasNoCfCheck = (CB && CB->doesNoCfCheck());
+  bool HasCfCheck = (CB && CB->doesCfCheck());
   bool IsIndirectCall = (CB && isa<CallInst>(CB) && CB->isIndirectCall());
   const Module *M = MF.getMMI().getModule();
   Metadata *IsCFProtectionSupported = M->getModuleFlag("cf-protection-branch");
@@ -4661,6 +4662,7 @@
   }
 
   if (HasNoCfCheck && IsCFProtectionSupported && IsIndirectCall) {
+    assert(!HasCfCheck && "cf_check and nocf_check used at the same time");
     Chain = DAG.getNode(X86ISD::NT_CALL, dl, NodeTys, Ops);
   } else if (CLI.CB && objcarc::hasAttachedCallOpBundle(CLI.CB)) {
     // Calls with a "clang.arc.attachedcall" bundle are special. They should be
Index: llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
===================================================================
--- llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -684,6 +684,8 @@
     return bitc::ATTR_KIND_NOSYNC;
   case Attribute::NoCfCheck:
     return bitc::ATTR_KIND_NOCF_CHECK;
+  case Attribute::CfCheck:
+    return bitc::ATTR_KIND_CF_CHECK;
   case Attribute::NoProfile:
     return bitc::ATTR_KIND_NO_PROFILE;
   case Attribute::NoUnwind:
Index: llvm/lib/Bitcode/Reader/BitcodeReader.cpp
===================================================================
--- llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -1447,6 +1447,8 @@
     return Attribute::NoSync;
   case bitc::ATTR_KIND_NOCF_CHECK:
     return Attribute::NoCfCheck;
+  case bitc::ATTR_KIND_CF_CHECK:
+    return Attribute::CfCheck;
   case bitc::ATTR_KIND_NO_PROFILE:
     return Attribute::NoProfile;
   case bitc::ATTR_KIND_NO_UNWIND:
Index: llvm/lib/AsmParser/LLLexer.cpp
===================================================================
--- llvm/lib/AsmParser/LLLexer.cpp
+++ llvm/lib/AsmParser/LLLexer.cpp
@@ -670,6 +670,7 @@
   KEYWORD(noreturn);
   KEYWORD(nosync);
   KEYWORD(nocf_check);
+  KEYWORD(cf_check);
   KEYWORD(noundef);
   KEYWORD(nounwind);
   KEYWORD(nosanitize_coverage);
Index: llvm/include/llvm/IR/InstrTypes.h
===================================================================
--- llvm/include/llvm/IR/InstrTypes.h
+++ llvm/include/llvm/IR/InstrTypes.h
@@ -1866,6 +1866,9 @@
   /// Determine if the call should not perform indirect branch tracking.
   bool doesNoCfCheck() const { return hasFnAttr(Attribute::NoCfCheck); }
 
+  /// Determine if the call could perform indirect branch tracking.
+  bool doesCfCheck() const { return hasFnAttr(Attribute::CfCheck); }
+
   /// Determine if the call cannot unwind.
   bool doesNotThrow() const { return hasFnAttr(Attribute::NoUnwind); }
   void setDoesNotThrow() { addFnAttr(Attribute::NoUnwind); }
Index: llvm/include/llvm/IR/Function.h
===================================================================
--- llvm/include/llvm/IR/Function.h
+++ llvm/include/llvm/IR/Function.h
@@ -552,6 +552,10 @@
   /// Determine if the function should not perform indirect branch tracking.
   bool doesNoCfCheck() const { return hasFnAttribute(Attribute::NoCfCheck); }
 
+  /// Determine if the function is manually set to perform indirect branch
+  /// tracking.
+  bool doesCfCheck() const { return hasFnAttribute(Attribute::CfCheck); }
+
   /// Determine if the function cannot unwind.
   bool doesNotThrow() const {
     return hasFnAttribute(Attribute::NoUnwind);
Index: llvm/include/llvm/IR/Attributes.td
===================================================================
--- llvm/include/llvm/IR/Attributes.td
+++ llvm/include/llvm/IR/Attributes.td
@@ -169,6 +169,9 @@
 /// Disable Indirect Branch Tracking.
 def NoCfCheck : EnumAttr<"nocf_check", [FnAttr]>;
 
+/// Enable Indirect Branch Tracking.
+def CfCheck : EnumAttr<"cf_check", [FnAttr]>;
+
 /// Function should not be instrumented.
 def NoProfile : EnumAttr<"noprofile", [FnAttr]>;
 
Index: llvm/include/llvm/Bitcode/LLVMBitCodes.h
===================================================================
--- llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -677,6 +677,7 @@
   ATTR_KIND_NO_SANITIZE_COVERAGE = 76,
   ATTR_KIND_ELEMENTTYPE = 77,
   ATTR_KIND_DISABLE_SANITIZER_INSTRUMENTATION = 78,
+  ATTR_KIND_CF_CHECK = 79,
 };
 
 enum ComdatSelectionKindCodes {
Index: llvm/include/llvm/AsmParser/LLToken.h
===================================================================
--- llvm/include/llvm/AsmParser/LLToken.h
+++ llvm/include/llvm/AsmParser/LLToken.h
@@ -218,6 +218,7 @@
   kw_noreturn,
   kw_nosync,
   kw_nocf_check,
+  kw_cf_check,
   kw_nounwind,
   kw_nosanitize_coverage,
   kw_null_pointer_is_valid,
Index: llvm/docs/LangRef.rst
===================================================================
--- llvm/docs/LangRef.rst
+++ llvm/docs/LangRef.rst
@@ -2115,6 +2115,13 @@
     entity to fine grain the HW control flow protection mechanism. The flag
     is target independent and currently appertains to a function or function
     pointer.
+``cf_check``
+    This attribute manually indicates that control-flow check will be performed
+    on the attributed entity. It only makes sense together with -mmanual-endbr,
+    which tells the compiler that -fcf-protection=<> should not guess which
+    functions need HW control flow protection. Instead, functions that need it
+    will have the ``cf_check`` attribute manually added to the source code. The
+    flag is target independent and appertains only to functions.
 ``shadowcallstack``
     This attribute indicates that the ShadowCallStack checks are enabled for
     the function. The instrumentation checks that the return address for the
Index: llvm/docs/BitCodeFormat.rst
===================================================================
--- llvm/docs/BitCodeFormat.rst
+++ llvm/docs/BitCodeFormat.rst
@@ -1078,6 +1078,7 @@
 * code 76: ``nosanitize_coverage``
 * code 77: ``elementtype``
 * code 78: ``disable_sanitizer_instrumentation``
+* code 79: ``cf_check``
 
 .. note::
   The ``allocsize`` attribute has a special encoding for its arguments. Its two
Index: llvm/bindings/go/llvm/ir_test.go
===================================================================
--- llvm/bindings/go/llvm/ir_test.go
+++ llvm/bindings/go/llvm/ir_test.go
@@ -87,6 +87,7 @@
 		"zeroext",
 		"cold",
 		"nocf_check",
+		"cf_check",
 	}
 
 	for _, name := range attrTests {
Index: clang/test/Sema/cf_check_attr_not_allowed.c
===================================================================
--- /dev/null
+++ clang/test/Sema/cf_check_attr_not_allowed.c
@@ -0,0 +1,5 @@
+// RUN: %clang_cc1 -triple powerpc-unknown-linux-gnu -fsyntax-only -verify -fcf-protection=branch %s
+// RUN: %clang_cc1 -triple arm-unknown-linux-gnu -fsyntax-only -verify -fcf-protection=branch %s
+// RUN: %clang_cc1 -triple arm-unknown-linux-gnu -fsyntax-only -verify %s
+
+void __attribute__((cf_check)) foo(); // expected-warning-re{{{{((unknown attribute 'cf_check' ignored)|('cf_check' attribute ignored; use -fcf-protection to enable the attribute))}}}}
Index: clang/test/Sema/attr-cf_check.cpp
===================================================================
--- /dev/null
+++ clang/test/Sema/attr-cf_check.cpp
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -triple=i386-unknown-unknown -verify -fcf-protection=branch -std=c++11 -fsyntax-only %s
+
+// Function pointer definition.
+[[gnu::cf_check]] typedef void (*FuncPointerWithCfCheck)(void); // no-warning
+typedef void (*FuncPointer)(void);
+
+// Dont allow function declaration and definition mismatch.
+[[gnu::cf_check]] void testCfCheck(); // expected-note {{previous declaration is here}}
+void testCfCheck(){};                 // expected-error {{conflicting types for 'testCfCheck'}}
+
+// No variable or parameter declaration
+int [[gnu::cf_check]] i;                            // expected-error {{'cf_check' attribute cannot be applied to types}}
+void testCfCheckImpl(double i [[gnu::cf_check]]) {} // expected-warning {{'cf_check' attribute only applies to functions and function pointers}}
+
+// Allow attributed function pointers as well as casting between attributed
+// and non-attributed function pointers.
+void testCfCheckMismatch(FuncPointer f) {
+  FuncPointerWithCfCheck fCfCheck = f; // expected-error {{cannot initialize a variable of type}}
+  (*fCfCheck)();                       // no-warning
+}
+
+// 'cf_check' Attribute has no parameters.
+[[gnu::cf_check(1)]] int testCfCheckParams(); // expected-error {{'cf_check' attribute takes no arguments}}
Index: clang/test/Sema/attr-cf_check.c
===================================================================
--- /dev/null
+++ clang/test/Sema/attr-cf_check.c
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -triple=x86_64-unknown-unknown -verify -fcf-protection=branch -fsyntax-only %s
+
+// Function pointer definition.
+typedef void (*FuncPointerWithCfCheck)(void) __attribute__((cf_check)); // no-warning
+typedef void (*FuncPointer)(void);
+
+// Dont allow function declaration and definition mismatch.
+void __attribute__((cf_check)) testCfCheck(); // expected-note {{previous declaration is here}}
+void testCfCheck(){};                         // expected-error {{conflicting types for 'testCfCheck'}}
+
+// No variable or parameter declaration
+__attribute__((cf_check)) int i;                            // expected-warning {{'cf_check' attribute only applies to function}}
+void testCfCheckImpl(double __attribute__((cf_check)) i) {} // expected-warning {{'cf_check' attribute only applies to function}}
+
+// Allow attributed function pointers as well as casting between attributed
+// and non-attributed function pointers.
+void testCfCheckMismatch(FuncPointer f) {
+  FuncPointerWithCfCheck fCfCheck = f; // expected-warning {{incompatible function pointer types}}
+  (*fCfCheck)();                       // no-warning
+}
+
+// 'cf_check' Attribute has no parameters.
+int testCfCheckParams() __attribute__((cf_check(1))); // expected-error {{'cf_check' attribute takes no arguments}}
Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test
===================================================================
--- clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -15,6 +15,7 @@
 // CHECK-NEXT: AlwaysDestroy (SubjectMatchRule_variable)
 // CHECK-NEXT: AlwaysInline (SubjectMatchRule_function)
 // CHECK-NEXT: Annotate ()
+// CHECK-NEXT: AnyX86CfCheck (SubjectMatchRule_hasType_functionType)
 // CHECK-NEXT: AnyX86NoCfCheck (SubjectMatchRule_hasType_functionType)
 // CHECK-NEXT: ArcWeakrefUnavailable (SubjectMatchRule_objc_interface)
 // CHECK-NEXT: ArmBuiltinAlias (SubjectMatchRule_function)
Index: clang/test/CodeGen/attributes.c
===================================================================
--- clang/test/CodeGen/attributes.c
+++ clang/test/CodeGen/attributes.c
@@ -117,6 +117,12 @@
   (*p)();
 }
 
+// CHECK: call void %{{[a-z0-9]+}}() [[CF_CHECK_CALL:#[0-9]+]]
+void t25(f_t f1) {
+  __attribute__((cf_check)) f_t p = f1;
+  (*p)();
+}
+
 // CHECK: attributes [[NUW]] = { noinline nounwind{{.*}} }
 // CHECK: attributes [[NR]] = { noinline noreturn nounwind{{.*}} }
 // CHECK: attributes [[COLDDEF]] = { cold {{.*}}}
@@ -127,3 +133,4 @@
 // CHECK: attributes [[COLDSITE]] = { cold {{.*}}}
 // CHECK: attributes [[HOTSITE]] = { hot {{.*}}}
 // CHECK: attributes [[NOCF_CHECK_CALL]] = { nocf_check }
+// CHECK: attributes [[CF_CHECK_CALL]] = { cf_check }
Index: clang/test/CodeGen/X86/x86-mmanual-endbr.c
===================================================================
--- /dev/null
+++ clang/test/CodeGen/X86/x86-mmanual-endbr.c
@@ -0,0 +1,4 @@
+// RUN: %clang -target i386-unknown-unknown -o - -emit-llvm -S -fcf-protection=branch -mmanual-endbr %s | FileCheck %s --check-prefix=MANUALENDBR
+
+// MANUALENDBR: "manual-endbr", i32 1
+__attribute__((cf_check)) void foo() {}
Index: clang/lib/Serialization/ASTWriter.cpp
===================================================================
--- clang/lib/Serialization/ASTWriter.cpp
+++ clang/lib/Serialization/ASTWriter.cpp
@@ -602,6 +602,7 @@
   Abv->Add(BitCodeAbbrevOp(0));                         // ProducesResult
   Abv->Add(BitCodeAbbrevOp(0));                         // NoCallerSavedRegs
   Abv->Add(BitCodeAbbrevOp(0));                         // NoCfCheck
+  Abv->Add(BitCodeAbbrevOp(0));                         // CfCheck
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // CmseNSCall
   // FunctionProtoType
   Abv->Add(BitCodeAbbrevOp(0));                         // IsVariadic
Index: clang/lib/Sema/SemaType.cpp
===================================================================
--- clang/lib/Sema/SemaType.cpp
+++ clang/lib/Sema/SemaType.cpp
@@ -135,6 +135,7 @@
   case ParsedAttr::AT_Regparm:                                                 \
   case ParsedAttr::AT_CmseNSCall:                                              \
   case ParsedAttr::AT_AnyX86NoCallerSavedRegisters:                            \
+  case ParsedAttr::AT_AnyX86CfCheck:                                           \
   case ParsedAttr::AT_AnyX86NoCfCheck:                                         \
     CALLING_CONV_ATTRS_CASELIST
 
@@ -7569,6 +7570,26 @@
     return true;
   }
 
+  if (attr.getKind() == ParsedAttr::AT_AnyX86CfCheck) {
+    if (!S.getLangOpts().CFProtectionBranch) {
+      S.Diag(attr.getLoc(), diag::warn_cf_check_attribute_ignored);
+      attr.setInvalid();
+      return true;
+    }
+
+    if (S.CheckAttrTarget(attr) || S.CheckAttrNoArgs(attr))
+      return true;
+
+    // If this is not a function type, warning will be asserted by subject
+    // check.
+    if (!unwrapped.isFunctionType())
+      return true;
+
+    FunctionType::ExtInfo EI = unwrapped.get()->getExtInfo().withCfCheck(true);
+    type = unwrapped.wrap(S, S.Context.adjustFunctionType(unwrapped.get(), EI));
+    return true;
+  }
+
   if (attr.getKind() == ParsedAttr::AT_Regparm) {
     unsigned value;
     if (S.CheckRegparmAttr(attr, value))
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -2173,6 +2173,13 @@
     handleSimpleAttribute<AnyX86NoCfCheckAttr>(S, D, Attrs);
 }
 
+static void handleCfCheckAttr(Sema &S, Decl *D, const ParsedAttr &Attrs) {
+  if (!S.getLangOpts().CFProtectionBranch)
+    S.Diag(Attrs.getLoc(), diag::warn_cf_check_attribute_ignored);
+  else
+    handleSimpleAttribute<AnyX86CfCheckAttr>(S, D, Attrs);
+}
+
 bool Sema::CheckAttrNoArgs(const ParsedAttr &Attrs) {
   if (!Attrs.checkExactlyNumArgs(*this, 0)) {
     Attrs.setInvalid();
@@ -8367,6 +8374,9 @@
   case ParsedAttr::AT_AnyX86NoCfCheck:
     handleNoCfCheckAttr(S, D, AL);
     break;
+  case ParsedAttr::AT_AnyX86CfCheck:
+    handleCfCheckAttr(S, D, AL);
+    break;
   case ParsedAttr::AT_NoThrow:
     if (!AL.isUsedAsTypeAttr())
       handleSimpleAttribute<NoThrowAttr>(S, D, AL);
Index: clang/lib/Frontend/CompilerInvocation.cpp
===================================================================
--- clang/lib/Frontend/CompilerInvocation.cpp
+++ clang/lib/Frontend/CompilerInvocation.cpp
@@ -1476,6 +1476,9 @@
   if (Opts.IBTSeal)
     GenerateArg(Args, OPT_mibt_seal, SA);
 
+  if (Opts.ManualENDBR)
+    GenerateArg(Args, OPT_mmanual_endbr, SA);
+
   for (const auto &F : Opts.LinkBitcodeFiles) {
     bool Builtint = F.LinkFlags == llvm::Linker::Flags::LinkOnlyNeeded &&
                     F.PropagateAttrs && F.Internalize;
@@ -1820,6 +1823,9 @@
   if (Opts.PrepareForLTO && Args.hasArg(OPT_mibt_seal))
     Opts.IBTSeal = 1;
 
+  if (Args.hasArg(OPT_mmanual_endbr))
+    Opts.ManualENDBR = 1;
+
   for (auto *A :
        Args.filtered(OPT_mlink_bitcode_file, OPT_mlink_builtin_bitcode)) {
     CodeGenOptions::BitcodeFileToLink F;
Index: clang/lib/Driver/ToolChains/Clang.cpp
===================================================================
--- clang/lib/Driver/ToolChains/Clang.cpp
+++ clang/lib/Driver/ToolChains/Clang.cpp
@@ -6172,6 +6172,8 @@
   if (IsUsingLTO)
     Args.AddLastArg(CmdArgs, options::OPT_mibt_seal);
 
+  Args.AddLastArg(CmdArgs, options::OPT_mmanual_endbr);
+
   // Forward -f options with positive and negative forms; we translate these by
   // hand.  Do not propagate PGO options to the GPU-side compilations as the
   // profile info is for the host-side compilation only.
Index: clang/lib/CodeGen/CodeGenModule.cpp
===================================================================
--- clang/lib/CodeGen/CodeGenModule.cpp
+++ clang/lib/CodeGen/CodeGenModule.cpp
@@ -715,6 +715,14 @@
   if (CodeGenOpts.IBTSeal)
     getModule().addModuleFlag(llvm::Module::Override, "ibt-seal", 1);
 
+  if (CodeGenOpts.ManualENDBR) {
+    // Indicate that the compiler should not automatically add ENDBR
+    // instructions to function prologues. Instead, each function that
+    // requires it must have the 'cf_check' attribute manually added in
+    // the source code.
+    getModule().addModuleFlag(llvm::Module::Override, "manual-endbr", 1);
+  }
+
   // Add module metadata for return address signing (ignoring
   // non-leaf/all) and stack tagging. These are actually turned on by function
   // attributes, but we use module metadata to emit build attributes. This is
Index: clang/lib/CodeGen/CGCall.cpp
===================================================================
--- clang/lib/CodeGen/CGCall.cpp
+++ clang/lib/CodeGen/CGCall.cpp
@@ -824,6 +824,7 @@
   FI->ReturnsRetained = info.getProducesResult();
   FI->NoCallerSavedRegs = info.getNoCallerSavedRegs();
   FI->NoCfCheck = info.getNoCfCheck();
+  FI->CfCheck = info.getCfCheck();
   FI->Required = required;
   FI->HasRegParm = info.getHasRegParm();
   FI->RegParm = info.getRegParm();
@@ -2108,6 +2109,8 @@
       FuncAttrs.addAttribute("no_caller_saved_registers");
     if (TargetDecl->hasAttr<AnyX86NoCfCheckAttr>())
       FuncAttrs.addAttribute(llvm::Attribute::NoCfCheck);
+    if (TargetDecl->hasAttr<AnyX86CfCheckAttr>())
+      FuncAttrs.addAttribute(llvm::Attribute::CfCheck);
     if (TargetDecl->hasAttr<LeafAttr>())
       FuncAttrs.addAttribute(llvm::Attribute::NoCallback);
 
Index: clang/lib/AST/TypePrinter.cpp
===================================================================
--- clang/lib/AST/TypePrinter.cpp
+++ clang/lib/AST/TypePrinter.cpp
@@ -1003,6 +1003,8 @@
     OS << " __attribute__((no_caller_saved_registers))";
   if (Info.getNoCfCheck())
     OS << " __attribute__((nocf_check))";
+  if (Info.getCfCheck())
+    OS << " __attribute__((cf_check))";
 }
 
 void TypePrinter::printFunctionNoProtoBefore(const FunctionNoProtoType *T,
@@ -1724,6 +1726,7 @@
   // FIXME: When Sema learns to form this AttributedType, avoid printing the
   // attribute again in printFunctionProtoAfter.
   case attr::AnyX86NoCfCheck: OS << "nocf_check"; break;
+  case attr::AnyX86CfCheck: OS << "cf_check"; break;
   case attr::CDecl: OS << "cdecl"; break;
   case attr::FastCall: OS << "fastcall"; break;
   case attr::StdCall: OS << "stdcall"; break;
Index: clang/lib/AST/ASTStructuralEquivalence.cpp
===================================================================
--- clang/lib/AST/ASTStructuralEquivalence.cpp
+++ clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -619,6 +619,8 @@
     return false;
   if (EI1.getNoCfCheck() != EI2.getNoCfCheck())
     return false;
+  if (EI1.getCfCheck() != EI2.getCfCheck())
+    return false;
 
   return true;
 }
Index: clang/lib/AST/ASTContext.cpp
===================================================================
--- clang/lib/AST/ASTContext.cpp
+++ clang/lib/AST/ASTContext.cpp
@@ -9670,8 +9670,24 @@
     return {};
   if (lbaseInfo.getNoCallerSavedRegs() != rbaseInfo.getNoCallerSavedRegs())
     return {};
+
+  // The following checks generate incompatible function pointer type
+  // warning when functions have cf_check/nocf_check attributes and get
+  // assigned to function pointers without them or vice-versa.
+  // For instance:
+  //
+  //   int __attribute__((cf_check)) foo(void) { return 0 };
+  //   int (*fooptr)(void) = &foo;
+  //
+  // produces:
+  //
+  //   warning: incompatible function pointer types initializing 'int (*)(void)'
+  //   with an expression of type 'int (*)(void) __attribute__((cf_check))'
+  //   [-Wincompatible-function-pointer-types]
   if (lbaseInfo.getNoCfCheck() != rbaseInfo.getNoCfCheck())
     return {};
+  if (lbaseInfo.getCfCheck() != rbaseInfo.getCfCheck())
+    return {};
 
   // FIXME: some uses, e.g. conditional exprs, really want this to be 'both'.
   bool NoReturn = lbaseInfo.getNoReturn() || rbaseInfo.getNoReturn();
Index: clang/include/clang/Driver/Options.td
===================================================================
--- clang/include/clang/Driver/Options.td
+++ clang/include/clang/Driver/Options.td
@@ -1931,6 +1931,8 @@
   HelpText<"Enable cf-protection in 'full' mode">;
 def mibt_seal : Flag<["-"], "mibt-seal">, Group<m_Group>, Flags<[CoreOption, CC1Option]>,
   HelpText<"Optimize fcf-protection=branch/full (requires LTO).">;
+def mmanual_endbr : Flag<["-"], "mmanual-endbr">, Group<m_Group>, Flags<[CoreOption, CC1Option]>,
+  HelpText<"Used with -fcf-protection, but tells the compiler to refrain from automatically adding ENDBR instructions to function prologues">;
 
 defm xray_instrument : BoolFOption<"xray-instrument",
   LangOpts<"XRayInstrument">, DefaultFalse,
Index: clang/include/clang/CodeGen/CGFunctionInfo.h
===================================================================
--- clang/include/clang/CodeGen/CGFunctionInfo.h
+++ clang/include/clang/CodeGen/CGFunctionInfo.h
@@ -586,6 +586,9 @@
   /// Whether this function has nocf_check attribute.
   unsigned NoCfCheck : 1;
 
+  /// Whether this function has cf_check attribute.
+  unsigned CfCheck : 1;
+
   RequiredArgs Required;
 
   /// The struct representing all arguments passed in memory.  Only used when
@@ -674,6 +677,9 @@
   /// Whether this function has nocf_check attribute.
   bool isNoCfCheck() const { return NoCfCheck; }
 
+  /// Whether this function has cf_check attribute.
+  bool isCfCheck() const { return CfCheck; }
+
   /// getASTCallingConvention() - Return the AST-specified calling
   /// convention.
   CallingConv getASTCallingConvention() const {
@@ -700,7 +706,7 @@
     return FunctionType::ExtInfo(isNoReturn(), getHasRegParm(), getRegParm(),
                                  getASTCallingConvention(), isReturnsRetained(),
                                  isNoCallerSavedRegs(), isNoCfCheck(),
-                                 isCmseNSCall());
+                                 isCfCheck(), isCmseNSCall());
   }
 
   CanQualType getReturnType() const { return getArgsBuffer()[0].type; }
@@ -741,6 +747,7 @@
     ID.AddBoolean(HasRegParm);
     ID.AddInteger(RegParm);
     ID.AddBoolean(NoCfCheck);
+    ID.AddBoolean(CfCheck);
     ID.AddBoolean(CmseNSCall);
     ID.AddInteger(Required.getOpaqueData());
     ID.AddBoolean(HasExtParameterInfos);
@@ -769,6 +776,7 @@
     ID.AddBoolean(info.getHasRegParm());
     ID.AddInteger(info.getRegParm());
     ID.AddBoolean(info.getNoCfCheck());
+    ID.AddBoolean(info.getCfCheck());
     ID.AddBoolean(info.getCmseNSCall());
     ID.AddInteger(required.getOpaqueData());
     ID.AddBoolean(!paramInfos.empty());
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3323,6 +3323,9 @@
 def warn_nocf_check_attribute_ignored :
   Warning<"'nocf_check' attribute ignored; use -fcf-protection to enable the attribute">,
   InGroup<IgnoredAttributes>;
+def warn_cf_check_attribute_ignored :
+  Warning<"'cf_check' attribute ignored; use -fcf-protection to enable the attribute">,
+  InGroup<IgnoredAttributes>;
 def warn_attribute_after_definition_ignored : Warning<
   "attribute %0 after definition is ignored">,
    InGroup<IgnoredAttributes>;
Index: clang/include/clang/Basic/CodeGenOptions.def
===================================================================
--- clang/include/clang/Basic/CodeGenOptions.def
+++ clang/include/clang/Basic/CodeGenOptions.def
@@ -108,7 +108,9 @@
 CODEGENOPT(CFProtectionBranch , 1, 0) ///< if -fcf-protection is
                                       ///< set to full or branch.
 CODEGENOPT(IBTSeal, 1, 0)             ///< set to optimize CFProtectionBranch.
-
+CODEGENOPT(ManualENDBR , 1, 0) ///< if -mmanual-endbr is set, which tells the
+                               ///< compiler not to add ENDBR instructions to
+                               ///< functions prologues automatically.
 CODEGENOPT(XRayInstrumentFunctions , 1, 0) ///< Set when -fxray-instrument is
                                            ///< enabled.
 CODEGENOPT(StackSizeSection  , 1, 0) ///< Set when -fstack-size-section is enabled.
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ clang/include/clang/Basic/AttrDocs.td
@@ -4599,6 +4599,10 @@
 the function.
 2. Appertains to a function pointer - do not track the target function of this
 pointer (by adding nocf_check prefix to the indirect-call instruction).
+Conversely, when ``-mmannual-endbr`` is in effect, clang will refrain from
+adding ENDBR instructions at the beginnings of functions; instead, users can
+manually select which functions get the instruction with the use of the
+``cf_check`` attribute.
 }];
 }
 
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -2956,6 +2956,12 @@
   let Documentation = [AnyX86NoCfCheckDocs];
 }
 
+def AnyX86CfCheck : DeclOrTypeAttr, TargetSpecificAttr<TargetAnyX86>{
+  let Spellings = [GCC<"cf_check">];
+  let Subjects = SubjectList<[FunctionLike]>;
+  let Documentation = [AnyX86NoCfCheckDocs];
+}
+
 def X86ForceAlignArgPointer : InheritableAttr, TargetSpecificAttr<TargetAnyX86> {
   let Spellings = [GCC<"force_align_arg_pointer">];
   // Technically, this appertains to a FunctionDecl, but the target-specific
Index: clang/include/clang/AST/TypeProperties.td
===================================================================
--- clang/include/clang/AST/TypeProperties.td
+++ clang/include/clang/AST/TypeProperties.td
@@ -284,6 +284,9 @@
   def : Property<"noCfCheck", Bool> {
     let Read = [{ node->getExtInfo().getNoCfCheck() }];
   }
+  def : Property<"cfCheck", Bool> {
+    let Read = [{ node->getExtInfo().getCfCheck() }];
+  }
   def : Property<"cmseNSCall", Bool> {
     let Read = [{ node->getExtInfo().getCmseNSCall() }];
   }
@@ -294,7 +297,7 @@
     auto extInfo = FunctionType::ExtInfo(noReturn, hasRegParm, regParm,
                                          callingConvention, producesResult,
                                          noCallerSavedRegs, noCfCheck,
-                                         cmseNSCall);
+                                         cfCheck, cmseNSCall);
     return ctx.getFunctionNoProtoType(returnType, extInfo);
   }]>;
 }
@@ -328,7 +331,7 @@
     auto extInfo = FunctionType::ExtInfo(noReturn, hasRegParm, regParm,
                                          callingConvention, producesResult,
                                          noCallerSavedRegs, noCfCheck,
-                                         cmseNSCall);
+                                         cfCheck, cmseNSCall);
     FunctionProtoType::ExtProtoInfo epi;
     epi.ExtInfo = extInfo;
     epi.Variadic = variadic;
Index: clang/include/clang/AST/Type.h
===================================================================
--- clang/include/clang/AST/Type.h
+++ clang/include/clang/AST/Type.h
@@ -1586,7 +1586,7 @@
 
     /// Extra information which affects how the function is called, like
     /// regparm and the calling convention.
-    unsigned ExtInfo : 13;
+    unsigned ExtInfo : 14;
 
     /// The ref-qualifier associated with a \c FunctionProtoType.
     ///
@@ -3662,8 +3662,8 @@
     // adjust the Bits field below, and if you add bits, you'll need to adjust
     // Type::FunctionTypeBitfields::ExtInfo as well.
 
-    // |  CC  |noreturn|produces|nocallersavedregs|regparm|nocfcheck|cmsenscall|
-    // |0 .. 4|   5    |    6   |       7         |8 .. 10|    11   |    12    |
+    // |  CC  |noreturn|produces|nocallersavedregs|regparm|nocfcheck|cfcheck|cmsenscall|
+    // |0 .. 4|   5    |    6   |       7         |8 .. 10|    11   |   12  |    13    |
     //
     // regparm is either 0 (no regparm attribute) or the regparm value+1.
     enum { CallConvMask = 0x1F };
@@ -3675,7 +3675,8 @@
       RegParmOffset = 8
     };
     enum { NoCfCheckMask = 0x800 };
-    enum { CmseNSCallMask = 0x1000 };
+    enum { CfCheckMask = 0x1000 };
+    enum { CmseNSCallMask = 0x2000 };
     uint16_t Bits = CC_C;
 
     ExtInfo(unsigned Bits) : Bits(static_cast<uint16_t>(Bits)) {}
@@ -3685,13 +3686,14 @@
     // have all the elements (when reading an AST file for example).
     ExtInfo(bool noReturn, bool hasRegParm, unsigned regParm, CallingConv cc,
             bool producesResult, bool noCallerSavedRegs, bool NoCfCheck,
-            bool cmseNSCall) {
+            bool CfCheck, bool cmseNSCall) {
       assert((!hasRegParm || regParm < 7) && "Invalid regparm value");
       Bits = ((unsigned)cc) | (noReturn ? NoReturnMask : 0) |
              (producesResult ? ProducesResultMask : 0) |
              (noCallerSavedRegs ? NoCallerSavedRegsMask : 0) |
              (hasRegParm ? ((regParm + 1) << RegParmOffset) : 0) |
              (NoCfCheck ? NoCfCheckMask : 0) |
+             (CfCheck ? CfCheckMask : 0) |
              (cmseNSCall ? CmseNSCallMask : 0);
     }
 
@@ -3708,6 +3710,7 @@
     bool getCmseNSCall() const { return Bits & CmseNSCallMask; }
     bool getNoCallerSavedRegs() const { return Bits & NoCallerSavedRegsMask; }
     bool getNoCfCheck() const { return Bits & NoCfCheckMask; }
+    bool getCfCheck() const { return Bits & CfCheckMask; }
     bool getHasRegParm() const { return ((Bits & RegParmMask) >> RegParmOffset) != 0; }
 
     unsigned getRegParm() const {
@@ -3764,6 +3767,13 @@
         return ExtInfo(Bits & ~NoCfCheckMask);
     }
 
+    ExtInfo withCfCheck(bool cfCheck) const {
+      if (cfCheck)
+        return ExtInfo(Bits | CfCheckMask);
+      else
+        return ExtInfo(Bits & ~CfCheckMask);
+    }
+
     ExtInfo withRegParm(unsigned RegParm) const {
       assert(RegParm < 7 && "Invalid regparm value");
       return ExtInfo((Bits & ~RegParmMask) |
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D118355: Add -... Gabriel F. T. Gomes via Phabricator via cfe-commits

Reply via email to