https://github.com/PiJoules updated 
https://github.com/llvm/llvm-project/pull/135836

>From fc0a5f17e670818c08ceefc68887902dc7bc7984 Mon Sep 17 00:00:00 2001
From: Leonard Chan <leonardc...@google.com>
Date: Wed, 9 Apr 2025 14:21:00 -0700
Subject: [PATCH] [clang] Function type attribute to prevent CFI
 instrumentation

This introduces the attribute discussed in
https://discourse.llvm.org/t/rfc-function-type-attribute-to-prevent-cfi-instrumentation/85458.

The proposed name has been changed from `no_cfi` to
`cfi_unchecked_callee` to help differentiate from `no_sanitize("cfi")`
more easily. The proposed attribute has the following semantics:

1. Indirect calls to a function type with this attribute will not be
   instrumented with CFI. That is, the indirect call will not be
   checked. Note that this only changes the behavior for indirect calls
   on pointers to function types having this attribute. It does not
   prevent all indirect function calls for a given type from being checked.
2. All direct references to a function whose type has this attribute will
   always reference the true function definition rather than an entry
   in the CFI jump table.
3. When a pointer to a function with this attribute is implicitly cast
   to a pointer to a function without this attribute, the compiler
   will give a warning saying this attribute is discarded. This warning
   can be silenced with an explicit C-style cast or C++ static_cast.
---
 clang/include/clang/AST/Type.h                |  39 ++-
 clang/include/clang/AST/TypeProperties.td     |   7 +-
 clang/include/clang/Basic/Attr.td             |   5 +
 clang/include/clang/Basic/AttrDocs.td         |  48 ++++
 clang/include/clang/Basic/DiagnosticGroups.td |   2 +
 .../clang/Basic/DiagnosticSemaKinds.td        |   9 +
 clang/include/clang/CodeGen/CGFunctionInfo.h  |  10 +-
 clang/include/clang/Sema/Sema.h               |  27 +-
 clang/lib/AST/TypePrinter.cpp                 |   5 +
 clang/lib/CodeGen/CGExpr.cpp                  |   8 +-
 clang/lib/CodeGen/CGExprConstant.cpp          |   8 +-
 clang/lib/CodeGen/CGPointerAuth.cpp           |   5 +
 clang/lib/CodeGen/ItaniumCXXABI.cpp           |  12 +
 clang/lib/Sema/SemaChecking.cpp               |  45 ++++
 clang/lib/Sema/SemaDeclAttr.cpp               |  18 ++
 clang/lib/Sema/SemaExceptionSpec.cpp          |   3 +-
 clang/lib/Sema/SemaExpr.cpp                   |  22 +-
 clang/lib/Sema/SemaExprCXX.cpp                |   5 +
 clang/lib/Sema/SemaOverload.cpp               |  57 +++--
 clang/lib/Sema/SemaTemplate.cpp               |   3 +-
 clang/lib/Sema/SemaTemplateDeduction.cpp      |   8 +-
 clang/lib/Sema/SemaType.cpp                   |  19 ++
 ...ecked-callee-attribute-member-function.cpp |  53 ++++
 .../cfi-unchecked-callee-attribute.cpp        |  77 ++++++
 .../Frontend/cfi-unchecked-callee-attribute.c |  70 ++++++
 .../cfi-unchecked-callee-attribute.cpp        | 237 ++++++++++++++++++
 26 files changed, 761 insertions(+), 41 deletions(-)
 create mode 100644 
clang/test/CodeGen/cfi-unchecked-callee-attribute-member-function.cpp
 create mode 100644 clang/test/CodeGen/cfi-unchecked-callee-attribute.cpp
 create mode 100644 clang/test/Frontend/cfi-unchecked-callee-attribute.c
 create mode 100644 clang/test/Frontend/cfi-unchecked-callee-attribute.cpp

diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 02a6fb5333538..a829a3b83a979 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -1991,7 +1991,7 @@ class alignas(TypeAlignment) Type : public 
ExtQualsTypeCommonBase {
     /// Extra information which affects how the function is called, like
     /// regparm and the calling convention.
     LLVM_PREFERRED_TYPE(CallingConv)
-    unsigned ExtInfo : 14;
+    unsigned ExtInfo : 15;
 
     /// The number of parameters this function has, not counting '...'.
     /// According to [implimits] 8 bits should be enough here but this is
@@ -2566,6 +2566,8 @@ class alignas(TypeAlignment) Type : public 
ExtQualsTypeCommonBase {
   bool isSignableType() const;
   bool isAnyPointerType() const;   // Any C pointer or ObjC object pointer
   bool isCountAttributedType() const;
+  bool isCFIUncheckedCalleeFunctionType() const;
+  bool isPointerToCFIUncheckedCalleeFunctionType() const;
   bool isBlockPointerType() const;
   bool isVoidPointerType() const;
   bool isReferenceType() const;
@@ -4496,8 +4498,8 @@ class FunctionType : public Type {
     // 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 .. 5|   6    |    7   |       8         |9 .. 11|    12   |    13    
|
+    // |  CC  
|noreturn|produces|nocallersavedregs|regparm|nocfcheck|cmsenscall|cfiuncheckedcallee|
+    // |0 .. 5|   6    |    7   |       8         |9 .. 11|    12   |    13    
|        14        |
     //
     // regparm is either 0 (no regparm attribute) or the regparm value+1.
     enum { CallConvMask = 0x3F };
@@ -4507,6 +4509,7 @@ class FunctionType : public Type {
     enum { RegParmMask = 0xe00, RegParmOffset = 9 };
     enum { NoCfCheckMask = 0x1000 };
     enum { CmseNSCallMask = 0x2000 };
+    enum { CFIUncheckedCalleeMask = 0x4000 };
     uint16_t Bits = CC_C;
 
     ExtInfo(unsigned Bits) : Bits(static_cast<uint16_t>(Bits)) {}
@@ -4516,14 +4519,15 @@ class FunctionType : public Type {
     // 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 cmseNSCall, bool cfiUncheckedCallee) {
       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) |
-             (cmseNSCall ? CmseNSCallMask : 0);
+             (cmseNSCall ? CmseNSCallMask : 0) |
+             (cfiUncheckedCallee ? CFIUncheckedCalleeMask : 0);
     }
 
     // Constructor with all defaults. Use when for example creating a
@@ -4540,6 +4544,7 @@ class FunctionType : public Type {
     bool getNoCallerSavedRegs() const { return Bits & NoCallerSavedRegsMask; }
     bool getNoCfCheck() const { return Bits & NoCfCheckMask; }
     bool getHasRegParm() const { return ((Bits & RegParmMask) >> 
RegParmOffset) != 0; }
+    bool getCFIUncheckedCallee() const { return Bits & CFIUncheckedCalleeMask; 
}
 
     unsigned getRegParm() const {
       unsigned RegParm = (Bits & RegParmMask) >> RegParmOffset;
@@ -4560,6 +4565,13 @@ class FunctionType : public Type {
     // Note that we don't have setters. That is by design, use
     // the following with methods instead of mutating these objects.
 
+    ExtInfo withCFIUncheckedCallee(bool cfiUncheckedCallee) const {
+      if (cfiUncheckedCallee)
+        return ExtInfo(Bits | CFIUncheckedCalleeMask);
+      else
+        return ExtInfo(Bits & ~CFIUncheckedCalleeMask);
+    }
+
     ExtInfo withNoReturn(bool noReturn) const {
       if (noReturn)
         return ExtInfo(Bits | NoReturnMask);
@@ -4712,6 +4724,10 @@ class FunctionType : public Type {
   /// type.
   bool getNoReturnAttr() const { return getExtInfo().getNoReturn(); }
 
+  bool getCFIUncheckedCalleeAttr() const {
+    return getExtInfo().getCFIUncheckedCallee();
+  }
+
   bool getCmseNSCallAttr() const { return getExtInfo().getCmseNSCall(); }
   CallingConv getCallConv() const { return getExtInfo().getCC(); }
   ExtInfo getExtInfo() const { return ExtInfo(FunctionTypeBits.ExtInfo); }
@@ -8253,6 +8269,19 @@ inline bool Type::isObjectPointerType() const {
     return false;
 }
 
+inline bool Type::isCFIUncheckedCalleeFunctionType() const {
+  if (const auto *Fn = getAs<FunctionType>())
+    return Fn->getCFIUncheckedCalleeAttr();
+  return false;
+}
+
+inline bool Type::isPointerToCFIUncheckedCalleeFunctionType() const {
+  QualType Pointee = getPointeeType();
+  if (Pointee.isNull())
+    return false;
+  return Pointee->isCFIUncheckedCalleeFunctionType();
+}
+
 inline bool Type::isFunctionPointerType() const {
   if (const auto *T = getAs<PointerType>())
     return T->getPointeeType()->isFunctionType();
diff --git a/clang/include/clang/AST/TypeProperties.td 
b/clang/include/clang/AST/TypeProperties.td
index f4b8ce0994ba8..d520ec3d904d0 100644
--- a/clang/include/clang/AST/TypeProperties.td
+++ b/clang/include/clang/AST/TypeProperties.td
@@ -298,6 +298,9 @@ let Class = FunctionType in {
   def : Property<"cmseNSCall", Bool> {
     let Read = [{ node->getExtInfo().getCmseNSCall() }];
   }
+  def : Property<"cfiUncheckedCallee", Bool> {
+    let Read = [{ node->getExtInfo().getCFIUncheckedCallee() }];
+  }
 }
 
 let Class = FunctionNoProtoType in {
@@ -305,7 +308,7 @@ let Class = FunctionNoProtoType in {
     auto extInfo = FunctionType::ExtInfo(noReturn, hasRegParm, regParm,
                                          callingConvention, producesResult,
                                          noCallerSavedRegs, noCfCheck,
-                                         cmseNSCall);
+                                         cmseNSCall, cfiUncheckedCallee);
     return ctx.getFunctionNoProtoType(returnType, extInfo);
   }]>;
 }
@@ -348,7 +351,7 @@ let Class = FunctionProtoType in {
     auto extInfo = FunctionType::ExtInfo(noReturn, hasRegParm, regParm,
                                          callingConvention, producesResult,
                                          noCallerSavedRegs, noCfCheck,
-                                         cmseNSCall);
+                                         cmseNSCall, cfiUncheckedCallee);
     FunctionProtoType::ExtProtoInfo epi;
     epi.ExtInfo = extInfo;
     epi.Variadic = variadic;
diff --git a/clang/include/clang/Basic/Attr.td 
b/clang/include/clang/Basic/Attr.td
index a734eb6658c3d..40537f2666667 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -3082,6 +3082,11 @@ def NoDeref : TypeAttr {
   let Documentation = [NoDerefDocs];
 }
 
+def CFIUncheckedCallee : TypeAttr {
+  let Spellings = [Clang<"cfi_unchecked_callee">];
+  let Documentation = [CFIUncheckedCalleeDocs];
+}
+
 def ReqdWorkGroupSize : InheritableAttr {
   // Does not have a [[]] spelling because it is an OpenCL-related attribute.
   let Spellings = [GNU<"reqd_work_group_size">];
diff --git a/clang/include/clang/Basic/AttrDocs.td 
b/clang/include/clang/Basic/AttrDocs.td
index cbb397cb31dfb..60e833311b66d 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -6869,6 +6869,54 @@ for references or Objective-C object pointers.
 }];
 }
 
+def CFIUncheckedCalleeDocs : Documentation {
+  let Category = DocCatType;
+  let Content = [{
+``cfi_unchecked_callee`` is a function type attribute which prevents the 
compiler from instrumenting
+`Control Flow Integrity 
<https://clang.llvm.org/docs/ControlFlowIntegrity.html>`_ checks on indirect
+function calls. Specifically, the attribute has the following semantics:
+
+1. Indirect calls to a function type with this attribute will not be 
instrumented with CFI. That is,
+   the indirect call will not be checked. Note that this only changes the 
behavior for indirect calls
+   on pointers to function types having this attribute. It does not prevent 
all indirect function calls
+   for a given type from being checked.
+2. All direct references to a function whose type has this attribute will 
always reference the
+   function definition rather than an entry in the CFI jump table.
+3. When a pointer to a function with this attribute is implicitly cast to a 
pointer to a function
+   without this attribute, the compiler will give a warning saying this 
attribute is discarded. This
+   warning can be silenced with an explicit cast. Note an explicit cast just 
disables the warning, so
+   direct references to a function with a ``cfi_unchecked_callee`` attribute 
will still reference the
+   function definition rather than the CFI jump table.
+
+.. code-block:: c
+
+  #define CFI_UNCHECKED_CALLEE __attribute__((cfi_unchecked_callee))
+
+  void no_cfi() CFI_UNCHECKED_CALLEE {}
+
+  void (*with_cfi)() = no_cfi;  // warning: implicit conversion discards 
`cfi_unchecked_callee` attribute.
+                                // `with_cfi` also points to the actual 
definition of `no_cfi` rather than
+                                // its jump table entry.
+
+  void invoke(void (CFI_UNCHECKED_CALLEE *func)()) {
+    func();  // CFI will not instrument this indirect call.
+
+    void (*func2)() = func;  // warning: implicit conversion discards 
`cfi_unchecked_callee` attribute.
+
+    func2();  // CFI will instrument this indirect call. Users should be 
careful however because if this
+              // references a function with type `cfi_unchecked_callee`, then 
the CFI check may incorrectly
+              // fail because the reference will be to the function definition 
rather than the CFI jump
+              // table entry.
+  }
+
+This attribute can only be applied on functions or member functions. This 
attribute can be a good
+alternative to ``no_sanitize("cfi")`` if you only want to disable 
innstrumentation for specific indirect
+calls rather than applying ``no_sanitize("cfi")`` on the whole function 
containing indirect call. Note
+that ``cfi_unchecked_attribute`` is a type attribute doesn't disable CFI 
instrumentation on a function
+body.
+}];
+}
+
 def ReinitializesDocs : Documentation {
   let Category = DocCatFunction;
   let Content = [{
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td 
b/clang/include/clang/Basic/DiagnosticGroups.td
index 1faf8508121f4..727186ed1efcd 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1576,6 +1576,8 @@ def FunctionMultiVersioning
 
 def NoDeref : DiagGroup<"noderef">;
 
+def CFIUncheckedCallee : DiagGroup<"cfi-unchecked-callee">;
+
 // -fbounds-safety and bounds annotation related warnings
 def BoundsSafetyCountedByEltTyUnknownSize :
   DiagGroup<"bounds-safety-counted-by-elt-type-unknown-size">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index e5a7cdc14a737..b3828169c85aa 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12628,6 +12628,15 @@ def warn_noderef_on_non_pointer_or_array : Warning<
 def warn_noderef_to_dereferenceable_pointer : Warning<
   "casting to dereferenceable pointer removes 'noderef' attribute">, 
InGroup<NoDeref>;
 
+def warn_cfi_unchecked_callee_on_non_function
+    : Warning<"use of `cfi_unchecked_callee` on %0; can only be used on "
+              "function types">,
+      InGroup<CFIUncheckedCallee>;
+def warn_cast_discards_cfi_unchecked_callee
+    : Warning<"implicit conversion from %0 to %1 discards "
+              "`cfi_unchecked_callee` attribute">,
+      InGroup<CFIUncheckedCallee>;
+
 def err_builtin_launder_invalid_arg : Error<
   "%select{non-pointer|function pointer|void pointer}0 argument to "
   "'__builtin_launder' is not allowed">;
diff --git a/clang/include/clang/CodeGen/CGFunctionInfo.h 
b/clang/include/clang/CodeGen/CGFunctionInfo.h
index 040ee025afaa8..457c7905a37b0 100644
--- a/clang/include/clang/CodeGen/CGFunctionInfo.h
+++ b/clang/include/clang/CodeGen/CGFunctionInfo.h
@@ -602,6 +602,10 @@ class CGFunctionInfo final
   LLVM_PREFERRED_TYPE(bool)
   unsigned CmseNSCall : 1;
 
+  /// Whether this function is a CFI unchecked callee
+  LLVM_PREFERRED_TYPE(bool)
+  unsigned CFIUncheckedCallee : 1;
+
   /// Whether this function is noreturn.
   LLVM_PREFERRED_TYPE(bool)
   unsigned NoReturn : 1;
@@ -704,6 +708,8 @@ class CGFunctionInfo final
 
   bool isNoReturn() const { return NoReturn; }
 
+  bool isCFIUncheckedCallee() const { return CFIUncheckedCallee; }
+
   /// In ARC, whether this function retains its return value.  This
   /// is not always reliable for call sites.
   bool isReturnsRetained() const { return ReturnsRetained; }
@@ -740,7 +746,7 @@ class CGFunctionInfo final
     return FunctionType::ExtInfo(isNoReturn(), getHasRegParm(), getRegParm(),
                                  getASTCallingConvention(), 
isReturnsRetained(),
                                  isNoCallerSavedRegs(), isNoCfCheck(),
-                                 isCmseNSCall());
+                                 isCmseNSCall(), isCFIUncheckedCallee());
   }
 
   CanQualType getReturnType() const { return getArgsBuffer()[0].type; }
@@ -794,6 +800,7 @@ class CGFunctionInfo final
     ID.AddInteger(RegParm);
     ID.AddBoolean(NoCfCheck);
     ID.AddBoolean(CmseNSCall);
+    ID.AddBoolean(CFIUncheckedCallee);
     ID.AddInteger(Required.getOpaqueData());
     ID.AddBoolean(HasExtParameterInfos);
     if (HasExtParameterInfos) {
@@ -821,6 +828,7 @@ class CGFunctionInfo final
     ID.AddInteger(info.getRegParm());
     ID.AddBoolean(info.getNoCfCheck());
     ID.AddBoolean(info.getCmseNSCall());
+    ID.AddBoolean(info.getCFIUncheckedCallee());
     ID.AddInteger(required.getOpaqueData());
     ID.AddBoolean(!paramInfos.empty());
     if (!paramInfos.empty()) {
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index dd3c7b487aa29..b680cb3133e8f 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2645,6 +2645,16 @@ class Sema final : public SemaBase {
   /// void*).
   void DiscardMisalignedMemberAddress(const Type *T, Expr *E);
 
+  /// Returns true if `From` is a function or pointer to a function with the
+  /// `cfi_unchecked_callee` attribute but `To` is a function or pointer to
+  /// function without this attribute.
+  bool DiscardingCFIUncheckedCallee(QualType From, QualType To) const;
+
+  /// Returns true if `From` is a function or pointer to a function without the
+  /// `cfi_unchecked_callee` attribute but `To` is a function or pointer to
+  /// function with this attribute.
+  bool AddingCFIUncheckedCallee(QualType From, QualType To) const;
+
   /// This function calls Action when it determines that E designates a
   /// misaligned member due to the packed attribute. This is used to emit
   /// local diagnostics like in reference binding.
@@ -10219,10 +10229,21 @@ class Sema final : public SemaBase {
                                  bool CStyle, bool &ObjCLifetimeConversion);
 
   /// Determine whether the conversion from FromType to ToType is a valid
-  /// conversion that strips "noexcept" or "noreturn" off the nested function
-  /// type.
+  /// conversion that strips "noexcept" or "noreturn" or "cfi_unchecked_callee"
+  /// off the nested function type. This also checks if "cfi_unchecked_callee"
+  /// was added to the function type. If "cfi_unchecked_callee" is added and
+  /// `AddingCFIUncheckedCallee` is provided, it will be set to true. The same
+  /// thing applies for `DiscardingCFIUncheckedCallee` if the attribute is
+  /// discarded.
   bool IsFunctionConversion(QualType FromType, QualType ToType,
-                            QualType &ResultTy);
+                            bool *DiscardingCFIUncheckedCallee = nullptr,
+                            bool *AddingCFIUncheckedCallee = nullptr) const;
+
+  /// Returns the same result as `IsFunctionConversion`, but if there was a
+  /// change (ie. the function returns true), then `ResultTy` is set to
+  /// `ToType`.
+  bool TryFunctionConversion(QualType FromType, QualType ToType,
+                             QualType &ResultTy);
   bool DiagnoseMultipleUserDefinedConversion(Expr *From, QualType ToType);
   void DiagnoseUseOfDeletedFunction(SourceLocation Loc, SourceRange Range,
                                     DeclarationName Name,
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index cba1a2d98d660..5a191b8ca1cad 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -1158,6 +1158,8 @@ void TypePrinter::printFunctionAfter(const 
FunctionType::ExtInfo &Info,
 
   if (Info.getNoReturn())
     OS << " __attribute__((noreturn))";
+  if (Info.getCFIUncheckedCallee())
+    OS << " __attribute__((cfi_unchecked_callee))";
   if (Info.getCmseNSCall())
     OS << " __attribute__((cmse_nonsecure_call))";
   if (Info.getProducesResult())
@@ -2089,6 +2091,9 @@ void TypePrinter::printAttributedAfter(const 
AttributedType *T,
   case attr::NoDeref:
     OS << "noderef";
     break;
+  case attr::CFIUncheckedCallee:
+    OS << "cfi_unchecked_callee";
+    break;
   case attr::AcquireHandle:
     OS << "acquire_handle";
     break;
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 2e01adc51fdf0..3aa91f2990321 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -3003,6 +3003,10 @@ static LValue EmitFunctionDeclLValue(CodeGenFunction 
&CGF, const Expr *E,
                                      GlobalDecl GD) {
   const FunctionDecl *FD = cast<FunctionDecl>(GD.getDecl());
   llvm::Constant *V = CGF.CGM.getFunctionPointer(GD);
+  if (E->getType()->isCFIUncheckedCalleeFunctionType()) {
+    if (auto *GV = dyn_cast<llvm::GlobalValue>(V))
+      V = llvm::NoCFIValue::get(GV);
+  }
   CharUnits Alignment = CGF.getContext().getDeclAlign(FD);
   return CGF.MakeAddrLValue(V, E->getType(), Alignment,
                             AlignmentSource::Decl);
@@ -6238,10 +6242,12 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType,
       FD && FD->hasAttr<OpenCLKernelAttr>())
     CGM.getTargetCodeGenInfo().setOCLKernelStubCallingConvention(FnType);
 
+  bool CFIUnchecked = CalleeType->isPointerToCFIUncheckedCalleeFunctionType();
+
   // If we are checking indirect calls and this call is indirect, check that 
the
   // function pointer is a member of the bit set for the function type.
   if (SanOpts.has(SanitizerKind::CFIICall) &&
-      (!TargetDecl || !isa<FunctionDecl>(TargetDecl))) {
+      (!TargetDecl || !isa<FunctionDecl>(TargetDecl)) && !CFIUnchecked) {
     SanitizerScope SanScope(this);
     EmitSanitizerStatReport(llvm::SanStat_CFI_ICall);
 
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp 
b/clang/lib/CodeGen/CGExprConstant.cpp
index b21ebeee4bed1..1e9db799f247a 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -2236,8 +2236,12 @@ ConstantLValueEmitter::tryEmitBase(const 
APValue::LValueBase &base) {
       return ConstantLValue(C);
     };
 
-    if (const auto *FD = dyn_cast<FunctionDecl>(D))
-      return PtrAuthSign(CGM.getRawFunctionPointer(FD));
+    if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+      llvm::Constant *C = CGM.getRawFunctionPointer(FD);
+      if (FD->getType()->isCFIUncheckedCalleeFunctionType())
+        C = llvm::NoCFIValue::get(cast<llvm::GlobalValue>(C));
+      return PtrAuthSign(C);
+    }
 
     if (const auto *VD = dyn_cast<VarDecl>(D)) {
       // We can never refer to a variable with local storage.
diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp 
b/clang/lib/CodeGen/CGPointerAuth.cpp
index 0a183a8524c17..a61a97fd94ecd 100644
--- a/clang/lib/CodeGen/CGPointerAuth.cpp
+++ b/clang/lib/CodeGen/CGPointerAuth.cpp
@@ -517,6 +517,11 @@ llvm::Constant 
*CodeGenModule::getMemberFunctionPointer(llvm::Constant *Pointer,
         Pointer, PointerAuth.getKey(), nullptr,
         cast_or_null<llvm::ConstantInt>(PointerAuth.getDiscriminator()));
 
+  if (const auto *MFT = dyn_cast<MemberPointerType>(FT.getTypePtr())) {
+    if (MFT->isPointerToCFIUncheckedCalleeFunctionType())
+      Pointer = llvm::NoCFIValue::get(cast<llvm::GlobalValue>(Pointer));
+  }
+
   return Pointer;
 }
 
diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp 
b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index 70b53be7e77a3..00da800e1d368 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -693,6 +693,18 @@ CGCallee ItaniumCXXABI::EmitLoadOfMemberFunctionPointer(
   llvm::Constant *CheckTypeDesc;
   bool ShouldEmitCFICheck = CGF.SanOpts.has(SanitizerKind::CFIMFCall) &&
                             CGM.HasHiddenLTOVisibility(RD);
+
+  if (ShouldEmitCFICheck) {
+    if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) {
+      if (BinOp->isPtrMemOp()) {
+        if (BinOp->getRHS()
+                ->getType()
+                ->isPointerToCFIUncheckedCalleeFunctionType())
+          ShouldEmitCFICheck = false;
+      }
+    }
+  }
+
   bool ShouldEmitVFEInfo = CGM.getCodeGenOpts().VirtualFunctionElimination &&
                            CGM.HasHiddenLTOVisibility(RD);
   bool ShouldEmitWPDInfo =
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 97f623f61a405..f1927c11481d5 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -11810,6 +11810,46 @@ static void DiagnoseIntInBoolContext(Sema &S, Expr *E) 
{
   }
 }
 
+enum CFIUncheckedCalleeChange {
+  None,
+  Adding,
+  Discarding,
+};
+
+CFIUncheckedCalleeChange DiscardingCFIUncheckedCallee(QualType From,
+                                                      QualType To) {
+  QualType MaybePointee = From->getPointeeType();
+  if (!MaybePointee.isNull() && MaybePointee->getAs<FunctionType>())
+    From = MaybePointee;
+  MaybePointee = To->getPointeeType();
+  if (!MaybePointee.isNull() && MaybePointee->getAs<FunctionType>())
+    To = MaybePointee;
+
+  if (const auto *FromFn = From->getAs<FunctionType>()) {
+    if (const auto *ToFn = To->getAs<FunctionType>()) {
+      if (FromFn->getCFIUncheckedCalleeAttr() &&
+          !ToFn->getCFIUncheckedCalleeAttr())
+        return Discarding;
+      if (!FromFn->getCFIUncheckedCalleeAttr() &&
+          ToFn->getCFIUncheckedCalleeAttr())
+        return Adding;
+    }
+  }
+  return None;
+}
+
+bool Sema::DiscardingCFIUncheckedCallee(QualType From, QualType To) const {
+  From = Context.getCanonicalType(From);
+  To = Context.getCanonicalType(To);
+  return ::DiscardingCFIUncheckedCallee(From, To) == Discarding;
+}
+
+bool Sema::AddingCFIUncheckedCallee(QualType From, QualType To) const {
+  From = Context.getCanonicalType(From);
+  To = Context.getCanonicalType(To);
+  return ::DiscardingCFIUncheckedCallee(From, To) == Adding;
+}
+
 void Sema::CheckImplicitConversion(Expr *E, QualType T, SourceLocation CC,
                                    bool *ICContext, bool IsListInit) {
   if (E->isTypeDependent() || E->isValueDependent()) return;
@@ -12150,6 +12190,11 @@ void Sema::CheckImplicitConversion(Expr *E, QualType 
T, SourceLocation CC,
   if (Target->isBooleanType())
     DiagnoseIntInBoolContext(*this, E);
 
+  if (DiscardingCFIUncheckedCallee(QualType(Source, 0), QualType(Target, 0))) {
+    Diag(CC, diag::warn_cast_discards_cfi_unchecked_callee)
+        << QualType(Source, 0) << QualType(Target, 0);
+  }
+
   if (!Source->isIntegerType() || !Target->isIntegerType())
     return;
 
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index bfb3ee9dcbd16..d54d7e3b0f002 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -872,6 +872,21 @@ static void handleDiagnoseIfAttr(Sema &S, Decl *D, const 
ParsedAttr &AL) {
       cast<NamedDecl>(D)));
 }
 
+static void handleCFIUncheckedCalleeAttr(Sema &S, Decl *D,
+                                         const ParsedAttr &Attrs) {
+  if (hasDeclarator(D))
+    return;
+
+  if (!isa<ObjCMethodDecl>(D)) {
+    S.Diag(Attrs.getLoc(), diag::warn_attribute_wrong_decl_type)
+        << Attrs << Attrs.isRegularKeywordAttribute()
+        << ExpectedFunctionOrMethod;
+    return;
+  }
+
+  D->addAttr(::new (S.Context) CFIUncheckedCalleeAttr(S.Context, Attrs));
+}
+
 static void handleNoBuiltinAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   static constexpr const StringRef kWildcard = "*";
 
@@ -7091,6 +7106,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, 
const ParsedAttr &AL,
   case ParsedAttr::AT_NoBuiltin:
     handleNoBuiltinAttr(S, D, AL);
     break;
+  case ParsedAttr::AT_CFIUncheckedCallee:
+    handleCFIUncheckedCalleeAttr(S, D, AL);
+    break;
   case ParsedAttr::AT_ExtVectorType:
     handleExtVectorTypeAttr(S, D, AL);
     break;
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp 
b/clang/lib/Sema/SemaExceptionSpec.cpp
index aaa2bb22565e4..c83eab53891ca 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -700,12 +700,11 @@ bool Sema::handlerCanCatch(QualType HandlerType, QualType 
ExceptionType) {
     //    -- a qualification conversion
     //    -- a function pointer conversion
     bool LifetimeConv;
-    QualType Result;
     // FIXME: Should we treat the exception as catchable if a lifetime
     // conversion is required?
     if (IsQualificationConversion(ExceptionType, HandlerType, false,
                                   LifetimeConv) ||
-        IsFunctionConversion(ExceptionType, HandlerType, Result))
+        IsFunctionConversion(ExceptionType, HandlerType))
       return true;
 
     //    -- a standard pointer conversion [...]
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index be3f145f3c5f1..5de62b7544157 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -9097,8 +9097,9 @@ static AssignConvertType 
checkPointerTypesForAssignment(Sema &S,
           diag::warn_typecheck_convert_incompatible_function_pointer_strict,
           Loc) &&
       RHSType->isFunctionPointerType() && LHSType->isFunctionPointerType() &&
-      !S.IsFunctionConversion(RHSType, LHSType, RHSType))
+      !S.TryFunctionConversion(RHSType, LHSType, RHSType)) {
     return AssignConvertType::IncompatibleFunctionPointerStrict;
+  }
 
   // C99 6.5.16.1p1 (constraint 3): both operands are pointers to qualified or
   // unqualified versions of compatible types, ...
@@ -9161,9 +9162,14 @@ static AssignConvertType 
checkPointerTypesForAssignment(Sema &S,
       return AssignConvertType::IncompatibleFunctionPointer;
     return AssignConvertType::IncompatiblePointer;
   }
+  bool DiscardingCFIUncheckedCallee, AddingCFIUncheckedCallee;
   if (!S.getLangOpts().CPlusPlus &&
-      S.IsFunctionConversion(ltrans, rtrans, ltrans))
-    return AssignConvertType::IncompatibleFunctionPointer;
+      S.IsFunctionConversion(ltrans, rtrans, &DiscardingCFIUncheckedCallee,
+                             &AddingCFIUncheckedCallee)) {
+    // Allow conversions between CFIUncheckedCallee-ness.
+    if (!DiscardingCFIUncheckedCallee && !AddingCFIUncheckedCallee)
+      return AssignConvertType::IncompatibleFunctionPointer;
+  }
   if (IsInvalidCmseNSCallConversion(S, ltrans, rtrans))
     return AssignConvertType::IncompatibleFunctionPointer;
   if (S.IsInvalidSMECallConversion(rtrans, ltrans))
@@ -12614,9 +12620,17 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, 
ExprResult &RHS,
       LangAS AddrSpaceR = RCanPointeeTy.getAddressSpace();
       CastKind Kind = AddrSpaceL != AddrSpaceR ? CK_AddressSpaceConversion
                                                : CK_BitCast;
+
+      const FunctionType *LFn = LCanPointeeTy->getAs<FunctionType>();
+      const FunctionType *RFn = RCanPointeeTy->getAs<FunctionType>();
+      bool LHSHasCFIUncheckedCallee = LFn && LFn->getCFIUncheckedCalleeAttr();
+      bool RHSHasCFIUncheckedCallee = RFn && RFn->getCFIUncheckedCalleeAttr();
+      bool DiscardingCFIUncheckedCallee =
+          LHSHasCFIUncheckedCallee != RHSHasCFIUncheckedCallee;
+
       if (LHSIsNull && !RHSIsNull)
         LHS = ImpCastExprToType(LHS.get(), RHSType, Kind);
-      else
+      else if (!DiscardingCFIUncheckedCallee)
         RHS = ImpCastExprToType(RHS.get(), LHSType, Kind);
     }
     return computeResultTy();
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 235ea1529b0b8..e7c724836d780 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -7926,6 +7926,11 @@ QualType Sema::FindCompositePointerType(SourceLocation 
Loc,
         EPI1.ExtInfo = EPI1.ExtInfo.withNoReturn(Noreturn);
         EPI2.ExtInfo = EPI2.ExtInfo.withNoReturn(Noreturn);
 
+        bool CFIUncheckedCallee = EPI1.ExtInfo.getCFIUncheckedCallee() ||
+                                  EPI2.ExtInfo.getCFIUncheckedCallee();
+        EPI1.ExtInfo = EPI1.ExtInfo.withCFIUncheckedCallee(CFIUncheckedCallee);
+        EPI2.ExtInfo = EPI2.ExtInfo.withCFIUncheckedCallee(CFIUncheckedCallee);
+
         // The result is nothrow if both operands are.
         SmallVector<QualType, 8> ExceptionTypeStorage;
         EPI1.ExceptionSpec = EPI2.ExceptionSpec = Context.mergeExceptionSpecs(
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index d3ee9989c73ed..03c0d374c47ce 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -1881,8 +1881,22 @@ ExprResult Sema::PerformImplicitConversion(Expr *From, 
QualType ToType,
   return PerformImplicitConversion(From, ToType, ICS, Action);
 }
 
+bool Sema::TryFunctionConversion(QualType FromType, QualType ToType,
+                                 QualType &ResultTy) {
+  bool Res = IsFunctionConversion(FromType, ToType);
+  if (Res)
+    ResultTy = ToType;
+  return Res;
+}
+
 bool Sema::IsFunctionConversion(QualType FromType, QualType ToType,
-                                QualType &ResultTy) {
+                                bool *DiscardingCFIUncheckedCallee,
+                                bool *AddingCFIUncheckedCallee) const {
+  if (DiscardingCFIUncheckedCallee)
+    *DiscardingCFIUncheckedCallee = false;
+  if (AddingCFIUncheckedCallee)
+    *AddingCFIUncheckedCallee = false;
+
   if (Context.hasSameUnqualifiedType(FromType, ToType))
     return false;
 
@@ -1937,6 +1951,21 @@ bool Sema::IsFunctionConversion(QualType FromType, 
QualType ToType,
     Changed = true;
   }
 
+  if (FromEInfo.getCFIUncheckedCallee() && !ToEInfo.getCFIUncheckedCallee()) {
+    FromFn = Context.adjustFunctionType(
+        FromFn, FromEInfo.withCFIUncheckedCallee(false));
+    Changed = true;
+    if (DiscardingCFIUncheckedCallee)
+      *DiscardingCFIUncheckedCallee = true;
+  } else if (!FromEInfo.getCFIUncheckedCallee() &&
+             ToEInfo.getCFIUncheckedCallee()) {
+    FromFn = Context.adjustFunctionType(FromFn,
+                                        
FromEInfo.withCFIUncheckedCallee(true));
+    Changed = true;
+    if (AddingCFIUncheckedCallee)
+      *AddingCFIUncheckedCallee = true;
+  }
+
   // Drop 'noexcept' if not present in target type.
   if (const auto *FromFPT = dyn_cast<FunctionProtoType>(FromFn)) {
     const auto *ToFPT = cast<FunctionProtoType>(ToFn);
@@ -1993,7 +2022,6 @@ bool Sema::IsFunctionConversion(QualType FromType, 
QualType ToType,
   assert(QualType(FromFn, 0).isCanonical());
   if (QualType(FromFn, 0) != CanTo) return false;
 
-  ResultTy = ToType;
   return true;
 }
 
@@ -2232,14 +2260,14 @@ static bool IsStandardConversion(Sema &S, Expr* From, 
QualType ToType,
       // we can sometimes resolve &foo<int> regardless of ToType, so check
       // if the type matches (identity) or we are converting to bool
       if (!S.Context.hasSameUnqualifiedType(
-                      S.ExtractUnqualifiedFunctionType(ToType), FromType)) {
-        QualType resultTy;
+              S.ExtractUnqualifiedFunctionType(ToType), FromType)) {
         // if the function type matches except for [[noreturn]], it's ok
         if (!S.IsFunctionConversion(FromType,
-              S.ExtractUnqualifiedFunctionType(ToType), resultTy))
+                                    S.ExtractUnqualifiedFunctionType(ToType))) 
{
           // otherwise, only a boolean conversion is standard
           if (!ToType->isBooleanType())
             return false;
+        }
       }
 
       // Check if the "from" expression is taking the address of an overloaded
@@ -2476,7 +2504,7 @@ static bool IsStandardConversion(Sema &S, Expr* From, 
QualType ToType,
   // The third conversion can be a function pointer conversion or a
   // qualification conversion (C++ [conv.fctptr], [conv.qual]).
   bool ObjCLifetimeConversion;
-  if (S.IsFunctionConversion(FromType, ToType, FromType)) {
+  if (S.TryFunctionConversion(FromType, ToType, FromType)) {
     // Function pointer conversions (removing 'noexcept') including removal of
     // 'noreturn' (Clang extension).
     SCS.Third = ICK_Function_Conversion;
@@ -2505,12 +2533,15 @@ static bool IsStandardConversion(Sema &S, Expr* From, 
QualType ToType,
 
   SCS.setToType(2, FromType);
 
-  if (CanonFrom == CanonTo)
-    return true;
-
   // If we have not converted the argument type to the parameter type,
   // this is a bad conversion sequence, unless we're resolving an overload in 
C.
-  if (S.getLangOpts().CPlusPlus || !InOverloadResolution)
+  //
+  // Permit conversions from a function without `cfi_unchecked_callee` to a
+  // function with `cfi_unchecked_callee`.
+  if (CanonFrom == CanonTo || S.AddingCFIUncheckedCallee(CanonFrom, CanonTo))
+    return true;
+
+  if ((S.getLangOpts().CPlusPlus || !InOverloadResolution))
     return false;
 
   ExprResult ER = ExprResult{From};
@@ -5033,7 +5064,6 @@ Sema::CompareReferenceRelationship(SourceLocation Loc,
   // Check for standard conversions we can apply to pointers: derived-to-base
   // conversions, ObjC pointer conversions, and function pointer conversions.
   // (Qualification conversions are checked last.)
-  QualType ConvertedT2;
   if (UnqualT1 == UnqualT2) {
     // Nothing to do.
   } else if (isCompleteType(Loc, OrigT2) &&
@@ -5044,7 +5074,7 @@ Sema::CompareReferenceRelationship(SourceLocation Loc,
            Context.canBindObjCObjectType(UnqualT1, UnqualT2))
     Conv |= ReferenceConversions::ObjC;
   else if (UnqualT2->isFunctionType() &&
-           IsFunctionConversion(UnqualT2, UnqualT1, ConvertedT2)) {
+           IsFunctionConversion(UnqualT2, UnqualT1)) {
     Conv |= ReferenceConversions::Function;
     // No need to check qualifiers; function types don't have them.
     return Ref_Compatible;
@@ -13426,9 +13456,8 @@ class AddressOfFunctionResolver {
 
 private:
   bool candidateHasExactlyCorrectType(const FunctionDecl *FD) {
-    QualType Discard;
     return Context.hasSameUnqualifiedType(TargetFunctionType, FD->getType()) ||
-           S.IsFunctionConversion(FD->getType(), TargetFunctionType, Discard);
+           S.IsFunctionConversion(FD->getType(), TargetFunctionType);
   }
 
   /// \return true if A is considered a better overload candidate for the
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 95c7b6f25ddc6..3f1e3ee70dca8 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -7618,9 +7618,8 @@ ExprResult Sema::BuildExpressionFromDeclTemplateArgument(
   QualType DestExprType = ParamType.getNonLValueExprType(Context);
   if (!Context.hasSameType(RefExpr.get()->getType(), DestExprType)) {
     CastKind CK;
-    QualType Ignored;
     if (Context.hasSimilarType(RefExpr.get()->getType(), DestExprType) ||
-        IsFunctionConversion(RefExpr.get()->getType(), DestExprType, Ignored)) 
{
+        IsFunctionConversion(RefExpr.get()->getType(), DestExprType)) {
       CK = CK_NoOp;
     } else if (ParamType->isVoidPointerType() &&
                RefExpr.get()->getType()->isPointerType()) {
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp 
b/clang/lib/Sema/SemaTemplateDeduction.cpp
index c9de36383d334..b3505bad281f2 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -1354,7 +1354,7 @@ bool Sema::isSameOrCompatibleFunctionType(QualType P, 
QualType A) {
     return Context.hasSameType(P, A);
 
   // Noreturn and noexcept adjustment.
-  if (QualType AdjustedParam; IsFunctionConversion(P, A, AdjustedParam))
+  if (QualType AdjustedParam; TryFunctionConversion(P, A, AdjustedParam))
     P = AdjustedParam;
 
   // FIXME: Compatible calling conventions.
@@ -3809,8 +3809,7 @@ CheckOriginalCallArgDeduction(Sema &S, 
TemplateDeductionInfo &Info,
     // FIXME: Resolve core issue (no number yet): if the original P is a
     // reference type and the transformed A is function type "noexcept F",
     // the deduced A can be F.
-    QualType Tmp;
-    if (A->isFunctionType() && S.IsFunctionConversion(A, DeducedA, Tmp))
+    if (A->isFunctionType() && S.IsFunctionConversion(A, DeducedA))
       return TemplateDeductionResult::Success;
 
     Qualifiers AQuals = A.getQualifiers();
@@ -3847,11 +3846,10 @@ CheckOriginalCallArgDeduction(Sema &S, 
TemplateDeductionInfo &Info,
   // Also allow conversions which merely strip __attribute__((noreturn)) from
   // function types (recursively).
   bool ObjCLifetimeConversion = false;
-  QualType ResultTy;
   if ((A->isAnyPointerType() || A->isMemberPointerType()) &&
       (S.IsQualificationConversion(A, DeducedA, false,
                                    ObjCLifetimeConversion) ||
-       S.IsFunctionConversion(A, DeducedA, ResultTy)))
+       S.IsFunctionConversion(A, DeducedA)))
     return TemplateDeductionResult::Success;
 
   //    - If P is a class and P has the form simple-template-id, then the
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 294daef70c339..50858fffee315 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -155,6 +155,7 @@ static void diagnoseBadTypeAttribute(Sema &S, const 
ParsedAttr &attr,
   case ParsedAttr::AT_Blocking:                                                
\
   case ParsedAttr::AT_Allocating:                                              
\
   case ParsedAttr::AT_Regparm:                                                 
\
+  case ParsedAttr::AT_CFIUncheckedCallee:                                      
\
   case ParsedAttr::AT_CmseNSCall:                                              
\
   case ParsedAttr::AT_ArmStreaming:                                            
\
   case ParsedAttr::AT_ArmStreamingCompatible:                                  
\
@@ -7813,6 +7814,24 @@ static bool handleFunctionTypeAttr(TypeProcessingState 
&state, ParsedAttr &attr,
     return true;
   }
 
+  if (attr.getKind() == ParsedAttr::AT_CFIUncheckedCallee) {
+    // Delay if this is not a function type.
+    if (!unwrapped.isFunctionType()) {
+      return false;
+    }
+
+    if (!type->isFunctionType() && !type->isDependentType()) {
+      state.getSema().Diag(attr.getLoc(),
+                           diag::warn_cfi_unchecked_callee_on_non_function)
+          << type;
+    }
+
+    FunctionType::ExtInfo EI =
+        unwrapped.get()->getExtInfo().withCFIUncheckedCallee(true);
+    type = unwrapped.wrap(S, S.Context.adjustFunctionType(unwrapped.get(), 
EI));
+    return true;
+  }
+
   if (attr.getKind() == ParsedAttr::AT_CmseNSCall) {
     // Delay if this is not a function type.
     if (!unwrapped.isFunctionType())
diff --git 
a/clang/test/CodeGen/cfi-unchecked-callee-attribute-member-function.cpp 
b/clang/test/CodeGen/cfi-unchecked-callee-attribute-member-function.cpp
new file mode 100644
index 0000000000000..637b5201ad6b4
--- /dev/null
+++ b/clang/test/CodeGen/cfi-unchecked-callee-attribute-member-function.cpp
@@ -0,0 +1,53 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm 
-fsanitize=cfi-mfcall -o - %s -fvisibility=hidden | FileCheck %s
+
+#define CFI_UNCHECKED_CALLEE __attribute__((cfi_unchecked_callee))
+
+class A {};
+
+// CHECK-LABEL: _Z14MemberFuncCallP1AMS_FvvE
+void MemberFuncCall(A *s, void (A::*p)()) {
+  // CHECK:      memptr.virtual:
+  // CHECK-NEXT:   [[VTABLE:%.*]] = load ptr, ptr {{.*}}, align 8
+  // CHECK-NEXT:   [[OFFSET:%.*]] = sub i64 %memptr.ptr, 1
+  // CHECK-NEXT:   [[FUNC:%.*]] = getelementptr i8, ptr [[VTABLE]], i64 
[[OFFSET]]
+  // CHECK-NEXT:   [[VALID:%.*]] = call i1 @llvm.type.test(ptr [[FUNC]], 
metadata !"_ZTSM1AFvvE.virtual")
+  // CHECK-NEXT:   [[FUNC:%.*]] = getelementptr i8, ptr [[VTABLE]], i64 
[[OFFSET]]
+  // CHECK-NEXT:   %memptr.virtualfn = load ptr, ptr [[FUNC]], align 8
+  // CHECK-NEXT:   {{.*}}= call i1 @llvm.type.test(ptr [[VTABLE]], metadata 
!"all-vtables")
+  // CHECK-NEXT:   br i1 [[VALID]], label %[[CONT:.*]], label 
%handler.cfi_check_fail{{.*}}
+
+  // CHECK:      [[CONT]]:
+  // CHECK-NEXT:   br label %memptr.end
+
+  // CHECK:      memptr.nonvirtual:
+  // CHECK-NEXT:   %memptr.nonvirtualfn = inttoptr i64 %memptr.ptr to ptr
+  // CHECK-NEXT:   [[VALID:%.*]] = call i1 @llvm.type.test(ptr 
%memptr.nonvirtualfn, metadata !"_ZTSM1AFvvE")
+  // CHECK-NEXT:   [[VALID2:%.*]] = or i1 false, [[VALID]]
+  // CHECK-NEXT:   br i1 [[VALID2]], label %[[CONT2:.*]], label 
%handler.cfi_check_fail{{.*}}
+
+  // CHECK:      [[CONT2]]:
+  // CHECK-NEXT:   br label %memptr.end
+
+  // CHECK:      memptr.end:
+  // CHECK-NEXT:   {{.*}} = phi ptr [ %memptr.virtualfn, %[[CONT]] ], [ 
%memptr.nonvirtualfn, %[[CONT2]] ]
+  (s->*p)();
+}
+
+// CHECK-LABEL: _Z19MemberFuncCallNoCFIP1AMS_FvvE
+// CHECK-NOT: llvm.type.test
+void MemberFuncCallNoCFI(A *s, void (CFI_UNCHECKED_CALLEE A::*p)()) {
+  // CHECK:      memptr.virtual:
+  // CHECK-NEXT:   [[VTABLE:%.*]] = load ptr, ptr {{.*}}, align 8
+  // CHECK-NEXT:   [[OFFSET:%.*]] = sub i64 %memptr.ptr, 1
+  // CHECK-NEXT:   [[FUNC:%.*]] = getelementptr i8, ptr [[VTABLE]], i64 
[[OFFSET]]
+  // CHECK-NEXT:   %memptr.virtualfn = load ptr, ptr [[FUNC]], align 8
+  // CHECK-NEXT:   br label %memptr.end
+
+  // CHECK:      memptr.nonvirtual:
+  // CHECK-NEXT:   %memptr.nonvirtualfn = inttoptr i64 %memptr.ptr to ptr
+  // CHECK-NEXT:   br label %memptr.end
+
+  // CHECK:      memptr.end:
+  // CHECK-NEXT:   {{.*}} = phi ptr [ %memptr.virtualfn, %memptr.virtual ], [ 
%memptr.nonvirtualfn, %memptr.nonvirtual ]
+  (s->*p)();
+}
diff --git a/clang/test/CodeGen/cfi-unchecked-callee-attribute.cpp 
b/clang/test/CodeGen/cfi-unchecked-callee-attribute.cpp
new file mode 100644
index 0000000000000..feb7c9c30a219
--- /dev/null
+++ b/clang/test/CodeGen/cfi-unchecked-callee-attribute.cpp
@@ -0,0 +1,77 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm 
-fsanitize=cfi-icall -o - %s | FileCheck %s
+
+#define CFI_UNCHECKED_CALLEE __attribute__((cfi_unchecked_callee))
+
+void unchecked(void) CFI_UNCHECKED_CALLEE {}
+
+/// All references to unchecked function with `cfi_unchecked_callee` should 
have the `cfi_unchecked_callee` wrapper.
+// CHECK: @checked = global ptr no_cfi @_Z9uncheckedv
+void (*checked)(void) = unchecked;
+
+// CHECK: @unchecked2 = global ptr no_cfi @_Z9uncheckedv
+void (CFI_UNCHECKED_CALLEE *unchecked2)(void) = unchecked;
+
+// CHECK: @checked2 = global ptr no_cfi @_Z9uncheckedv
+constexpr void (CFI_UNCHECKED_CALLEE *unchecked_constexpr)(void) = unchecked;
+void (*checked2)(void) = unchecked_constexpr;
+
+/// Note we still reference the `no_cfi` function rather than the jump table 
entry.
+/// The explicit cast will only silence the warning.
+// CHECK: @checked_explicit_cast = global ptr no_cfi @_Z9uncheckedv
+void (*checked_explicit_cast)(void) = (void (*)(void))unchecked;
+
+// CHECK: @checked_array = global [3 x ptr] [ptr no_cfi @_Z9uncheckedv, ptr 
no_cfi @_Z9uncheckedv, ptr no_cfi @_Z9uncheckedv]
+void (*checked_array[])(void) = {
+  unchecked,
+  (void (*)(void))unchecked,
+  reinterpret_cast<void (*)(void)>(unchecked),
+};
+
+void func_accepting_checked(void (*p)(void)) {}
+
+// CHECK-LABEL: _Z9InvokeCFIv
+void InvokeCFI() {
+  // CHECK: %0 = load ptr, ptr @checked, align 8
+  // CHECK: %1 = call i1 @llvm.type.test(ptr %0, metadata !"_ZTSFvvE")
+  checked();
+}
+
+// CHECK-LABEL: _Z11InvokeNoCFIv
+void InvokeNoCFI() {
+  // CHECK:  %0 = load ptr, ptr @unchecked2, align 8
+  // CHECK:  call void %0()
+  unchecked2();
+}
+
+struct A {
+  void unchecked_method() CFI_UNCHECKED_CALLEE {}
+  virtual void unchecked_virtual_method() CFI_UNCHECKED_CALLEE {}
+  static void unchecked_static_method() CFI_UNCHECKED_CALLEE {}
+  int unchecked_const_method() const CFI_UNCHECKED_CALLEE { return 0; }
+  int unchecked_const_method_int_arg(int n) const CFI_UNCHECKED_CALLEE { 
return 0; }
+};
+
+void h(void) {
+  // CHECK: store ptr no_cfi @_Z9uncheckedv, ptr %unchecked_local
+  void (*unchecked_local)(void) = unchecked;
+
+  // CHECK: call void @_Z22func_accepting_checkedPFvvE(ptr noundef no_cfi 
@_Z9uncheckedv)
+  func_accepting_checked(unchecked);
+
+  // CHECK:      [[B:%.*]] = load ptr, ptr @checked
+  // CHECK-NEXT: call void @_Z22func_accepting_checkedPFvvE(ptr noundef [[B]]) 
+  func_accepting_checked(checked);
+
+  // CHECK: store { i64, i64 } { i64 ptrtoint (ptr no_cfi 
@_ZN1A16unchecked_methodEv to i64), i64 0 }, ptr %A1
+  auto A1 = &A::unchecked_method;
+  /// Storing unchecked virtual function pointer stores an offset instead. 
This is part of the
+  /// normal Itanium C++ ABI, but let's make sure we don't change anything.
+  // CHECK: store { i64, i64 } { i64 1, i64 0 }, ptr %A2
+  auto A2 = &A::unchecked_virtual_method;
+  // CHECK: store ptr no_cfi @_ZN1A23unchecked_static_methodEv, ptr %A3
+  auto A3 = &A::unchecked_static_method;
+  // CHECK: store { i64, i64 } { i64 ptrtoint (ptr no_cfi 
@_ZNK1A22unchecked_const_methodEv to i64), i64 0 }, ptr %A4
+  auto A4 = (int(CFI_UNCHECKED_CALLEE A::*)() 
const)(&A::unchecked_const_method);
+  // CHECK: store { i64, i64 } { i64 ptrtoint (ptr no_cfi 
@_ZNK1A30unchecked_const_method_int_argEi to i64), i64 0 }, ptr %A5
+  auto A5 = (int(CFI_UNCHECKED_CALLEE A::*)(int) 
const)(&A::unchecked_const_method_int_arg);
+}
diff --git a/clang/test/Frontend/cfi-unchecked-callee-attribute.c 
b/clang/test/Frontend/cfi-unchecked-callee-attribute.c
new file mode 100644
index 0000000000000..b56dc9a04379c
--- /dev/null
+++ b/clang/test/Frontend/cfi-unchecked-callee-attribute.c
@@ -0,0 +1,70 @@
+// RUN: %clang_cc1 -Wall -Wno-unused -Wno-uninitialized -verify %s
+
+#define CFI_UNCHECKED_CALLEE __attribute__((cfi_unchecked_callee))
+
+void unchecked() CFI_UNCHECKED_CALLEE {}
+void checked() {}
+
+void (*checked_ptr)() = unchecked;  // expected-warning{{implicit conversion 
from 'void () __attribute__((cfi_unchecked_callee))' to 'void (*)()' discards 
`cfi_unchecked_callee` attribute}}
+void (CFI_UNCHECKED_CALLEE *unchecked_ptr)() = unchecked;
+void (CFI_UNCHECKED_CALLEE *from_normal)() = checked;
+void (CFI_UNCHECKED_CALLEE *c_no_function_decay)() = &unchecked;
+
+typedef void (CFI_UNCHECKED_CALLEE unchecked_func_t)();
+typedef void (checked_func_t)();
+typedef void (CFI_UNCHECKED_CALLEE *cfi_unchecked_func_ptr_t)();
+typedef void (*checked_func_ptr_t)();
+checked_func_t *cfi_func = unchecked;  // expected-warning{{implicit 
conversion from 'void () __attribute__((cfi_unchecked_callee))' to 'void (*)()' 
discards `cfi_unchecked_callee` attribute}}
+unchecked_func_t *unchecked_func = unchecked;
+
+void CFI_UNCHECKED_CALLEE after_ret_type();
+CFI_UNCHECKED_CALLEE void before_ret_type();
+
+void UsageOnImproperTypes() {
+  int CFI_UNCHECKED_CALLEE i;  // expected-warning{{'cfi_unchecked_callee' 
only applies to function types; type here is 'int'}}
+  
+  /// Here `cfi_unchecked_callee` is applied to the pointer here rather than 
the actual function.
+  void (* CFI_UNCHECKED_CALLEE func_ptr)();  // expected-warning{{use of 
`cfi_unchecked_callee` on 'void (*)()'; can only be used on function types}}
+}
+
+/// Explicit casts suppress the warning.
+void CheckCasts() {
+  void (*should_warn)() = unchecked;  // expected-warning{{implicit conversion 
from 'void () __attribute__((cfi_unchecked_callee))' to 'void (*)()' discards 
`cfi_unchecked_callee` attribute}}
+
+  void (*no_warn_c_style_cast)() = (void (*)())unchecked;
+
+  struct B {} CFI_UNCHECKED_CALLEE b;  // 
expected-warning{{'cfi_unchecked_callee' attribute only applies to functions 
and methods}}
+  struct CFI_UNCHECKED_CALLEE C {} c;  // 
expected-warning{{'cfi_unchecked_callee' attribute only applies to functions 
and methods}}
+  CFI_UNCHECKED_CALLEE struct D {} d;  // 
expected-warning{{'cfi_unchecked_callee' only applies to function types; type 
here is 'struct D'}}
+
+  void *ptr2 = (void *)unchecked;
+}
+
+int checked_arg_func(checked_func_t *checked_func);
+
+void CheckDifferentConstructions() {
+  void (CFI_UNCHECKED_CALLEE *arr[10])();
+  void (*cfi_elem)() = arr[1];  // expected-warning{{implicit conversion from 
'void (*)() __attribute__((cfi_unchecked_callee))' to 'void (*)()' discards 
`cfi_unchecked_callee` attribute}}
+  void (CFI_UNCHECKED_CALLEE *cfi_unchecked_elem)() = arr[1];
+
+  int invoke = checked_arg_func(unchecked);  // expected-warning{{implicit 
conversion from 'void () __attribute__((cfi_unchecked_callee))' to 'void (*)()' 
discards `cfi_unchecked_callee` attribute}}
+}
+
+checked_func_t *returning_checked_func() {
+  return unchecked;  // expected-warning{{implicit conversion from 'void () 
__attribute__((cfi_unchecked_callee))' to 'void (*)()' discards 
`cfi_unchecked_callee` attribute}}
+}
+
+void no_args() __attribute__((cfi_unchecked_callee(10)));  // 
expected-error{{'cfi_unchecked_callee' attribute takes no arguments}}
+
+void Comparisons() {
+  /// Let's be able to compare checked and unchecked pointers without warnings.
+  unchecked == checked_ptr;
+  checked_ptr == unchecked;
+  unchecked == unchecked_ptr;
+  unchecked != checked_ptr;
+  checked_ptr != unchecked;
+  unchecked != unchecked_ptr;
+
+  (void (*)())unchecked == checked_ptr;
+  checked_ptr == (void (*)())unchecked;
+}
diff --git a/clang/test/Frontend/cfi-unchecked-callee-attribute.cpp 
b/clang/test/Frontend/cfi-unchecked-callee-attribute.cpp
new file mode 100644
index 0000000000000..eff675d1d7454
--- /dev/null
+++ b/clang/test/Frontend/cfi-unchecked-callee-attribute.cpp
@@ -0,0 +1,237 @@
+// RUN: %clang_cc1 -Wall -Wno-unused -Wno-uninitialized -std=c++2b -verify %s
+
+#define CFI_UNCHECKED_CALLEE __attribute__((cfi_unchecked_callee))
+
+void unchecked(void) CFI_UNCHECKED_CALLEE {}
+void checked(void) {}
+
+void (*checked_ptr)(void) = unchecked;  // expected-warning{{implicit 
conversion from 'void () __attribute__((cfi_unchecked_callee))' to 'void (*)()' 
discards `cfi_unchecked_callee` attribute}}
+void (CFI_UNCHECKED_CALLEE *unchecked_ptr)(void) = unchecked;
+void (CFI_UNCHECKED_CALLEE *from_normal)(void) = checked;
+void (CFI_UNCHECKED_CALLEE *c_no_function_decay)(void) = &unchecked;
+void (CFI_UNCHECKED_CALLEE *arr[10])(void);
+void (*cfi_elem)(void) = arr[1];  // expected-warning{{implicit conversion 
from 'void (*)() __attribute__((cfi_unchecked_callee))' to 'void (*)()' 
discards `cfi_unchecked_callee` attribute}}
+void (CFI_UNCHECKED_CALLEE *cfi_unchecked_elem)(void) = arr[1];
+void (CFI_UNCHECKED_CALLEE &ref)(void) = unchecked;
+void (CFI_UNCHECKED_CALLEE &ref2)(void) = *unchecked;
+void (&ref_cfi_checked)(void) = unchecked;  // expected-warning{{implicit 
conversion from 'void () __attribute__((cfi_unchecked_callee))' to 'void ()' 
discards `cfi_unchecked_callee` attribute}}
+void (&ref_cfi_checked2)(void) = *unchecked;  // expected-warning{{implicit 
conversion from 'void () __attribute__((cfi_unchecked_callee))' to 'void ()' 
discards `cfi_unchecked_callee` attribute}}
+
+void (CFI_UNCHECKED_CALLEE *unchecked_from_deref)(void) = &*unchecked;
+void (*checked_from_deref)(void) = &*unchecked;  // expected-warning{{implicit 
conversion from 'void (*)() __attribute__((cfi_unchecked_callee))' to 'void 
(*)()' discards `cfi_unchecked_callee` attribute}}
+
+typedef void (CFI_UNCHECKED_CALLEE unchecked_func_t)(void);
+typedef void (checked_func_t)(void);
+typedef void (CFI_UNCHECKED_CALLEE *unchecked_func_ptr_t)(void);
+typedef void (*checked_func_ptr_t)(void);
+checked_func_t *checked_func = unchecked;  // expected-warning{{implicit 
conversion from 'void () __attribute__((cfi_unchecked_callee))' to 'void (*)()' 
discards `cfi_unchecked_callee` attribute}}
+unchecked_func_t *unchecked_func = unchecked;
+
+void CFI_UNCHECKED_CALLEE before_func(void);
+CFI_UNCHECKED_CALLEE void before_return_type(void);
+
+void UsageOnImproperTypes() {
+  int CFI_UNCHECKED_CALLEE i;  // expected-warning{{'cfi_unchecked_callee' 
only applies to function types; type here is 'int'}}
+
+  /// Here `cfi_unchecked_callee` is applied to the pointer here rather than 
the actual function.
+  void (* CFI_UNCHECKED_CALLEE func)(void);  // expected-warning{{use of 
`cfi_unchecked_callee` on 'void (*)()'; can only be used on function types}}
+}
+
+/// Explicit casts suppress the warning.
+void CheckCasts() {
+  void (*should_warn)(void) = unchecked;  // expected-warning{{implicit 
conversion from 'void () __attribute__((cfi_unchecked_callee))' to 'void (*)()' 
discards `cfi_unchecked_callee` attribute}}
+
+  void (*no_warn_c_style_cast)(void) = (void (*)(void))unchecked;
+  void (*no_warn_static_cast)(void) = static_cast<void (*)(void)>(unchecked);
+  void (*no_warn_reinterpret_cast)(void) = reinterpret_cast<void 
(*)(void)>(unchecked);
+  unsigned long long ull = (unsigned long long)unchecked;
+
+  struct A {};
+  void (CFI_UNCHECKED_CALLEE A::*cfi_unchecked_member_ptr)(void);
+  void (A::*member_ptr)(void) = cfi_unchecked_member_ptr;  // 
expected-warning{{implicit conversion from 'void (A::*)() 
__attribute__((cfi_unchecked_callee))' to 'void (A::*)()' discards 
`cfi_unchecked_callee` attribute}}
+
+  struct B {} CFI_UNCHECKED_CALLEE b;  // 
expected-warning{{'cfi_unchecked_callee' attribute only applies to functions 
and methods}}
+  struct CFI_UNCHECKED_CALLEE C {} c;  // 
expected-warning{{'cfi_unchecked_callee' attribute only applies to functions 
and methods}}
+  CFI_UNCHECKED_CALLEE struct D {} d;  // 
expected-warning{{'cfi_unchecked_callee' only applies to function types; type 
here is 'struct D'}}
+
+  void *ptr2 = (void *)unchecked;
+}
+
+void CheckDifferentConstructions() {
+  checked_func_t *checked_func(unchecked_func);  // expected-warning{{implicit 
conversion from 'void (*)() __attribute__((cfi_unchecked_callee))' to 'void 
(*)()' discards `cfi_unchecked_callee` attribute}}
+  new (checked_func_t *)(unchecked_func);  // expected-warning{{implicit 
conversion from 'void (*)() __attribute__((cfi_unchecked_callee))' to 'void 
(*)()' discards `cfi_unchecked_callee` attribute}}
+  struct S {
+    checked_func_t *checked_func;
+
+    S(unchecked_func_t *unchecked_func) : checked_func(unchecked_func) {}  // 
expected-warning{{implicit conversion from 'void (*)() 
__attribute__((cfi_unchecked_callee))' to 'void (*)()' discards 
`cfi_unchecked_callee` attribute}}
+  };
+
+  checked_func_t *checked_func2{unchecked_func};  // 
expected-warning{{implicit conversion from 'void (*)() 
__attribute__((cfi_unchecked_callee))' to 'void (*)()' discards 
`cfi_unchecked_callee` attribute}}
+  checked_ptr = checked_func_ptr_t(unchecked);
+
+  auto checked_auto = checked;
+  auto unchecked_auto = unchecked;
+  unchecked_ptr = checked_auto;
+  unchecked_ptr = unchecked_auto;
+  checked_ptr = checked_auto;
+  checked_ptr = unchecked_auto;  // expected-warning{{implicit conversion from 
'void (*)() __attribute__((cfi_unchecked_callee))' to 'void (*)()' discards 
`cfi_unchecked_callee` attribute}}
+}
+
+checked_func_t *returning_checked_func() {
+  return unchecked;  // expected-warning{{implicit conversion from 'void () 
__attribute__((cfi_unchecked_callee))' to 'void (*)()' discards 
`cfi_unchecked_callee` attribute}}
+}
+
+int checked_arg_func(checked_func_t *checked_func);
+int invoke = checked_arg_func(unchecked);  // expected-warning{{implicit 
conversion from 'void () __attribute__((cfi_unchecked_callee))' to 'void (*)()' 
discards `cfi_unchecked_callee` attribute}}
+
+template <typename T>
+struct S {
+  S(T *ptr) {}
+};
+S<unchecked_func_t> s(checked);
+S<unchecked_func_t> s2(unchecked);
+S<checked_func_t> s3(checked);
+S<checked_func_t> s4(unchecked);  // expected-warning{{implicit conversion 
from 'void () __attribute__((cfi_unchecked_callee))' to 'void (*)()' discards 
`cfi_unchecked_callee` attribute}}
+S s5(checked);
+S s6(unchecked);
+
+template <typename T, typename U>
+struct is_same {
+  static constexpr bool value = false;
+};
+template <typename T>
+struct is_same<T,T> {
+  static constexpr bool value = true;
+};
+
+template <typename T>
+struct ExpectingCFIUncheckedCallee {
+  static_assert(is_same<T, unchecked_func_t>::value);
+  ExpectingCFIUncheckedCallee(T *) {}
+  ExpectingCFIUncheckedCallee() = default;
+};
+ExpectingCFIUncheckedCallee<unchecked_func_t> expecting;
+ExpectingCFIUncheckedCallee expecting2(unchecked);
+
+void no_args() __attribute__((cfi_unchecked_callee(10)));  // 
expected-error{{'cfi_unchecked_callee' attribute takes no arguments}}
+
+void bracket_cfi_unchecked(void) [[clang::cfi_unchecked_callee]] {}
+
+void BracketNotation() {
+  checked_ptr = bracket_cfi_unchecked;  // expected-warning{{implicit 
conversion from 'void () __attribute__((cfi_unchecked_callee))' to 'void (*)()' 
discards `cfi_unchecked_callee` attribute}}
+}
+
+void Comparisons() {
+  /// Let's be able to compare checked and unchecked pointers without warnings.
+  unchecked == checked_ptr;
+  checked_ptr == unchecked;
+  unchecked == unchecked_ptr;
+  unchecked != checked_ptr;
+  checked_ptr != unchecked;
+  unchecked != unchecked_ptr;
+
+  (void (*)(void))unchecked == checked_ptr;
+  checked_ptr == (void (*)(void))unchecked;
+
+  struct S {
+    typedef void CB() CFI_UNCHECKED_CALLEE;
+    constexpr bool operator==(const S &other) const {
+      return cb == other.cb;
+    }
+    CB *cb;
+  };
+}
+
+/// Type aliasing
+typedef void (BaseType)(void);
+using WithoutAttr = BaseType;
+using WithAttr = __attribute__((cfi_unchecked_callee)) BaseType;
+
+WithoutAttr *checked_func_alias = unchecked;  // expected-warning{{implicit 
conversion from 'void () __attribute__((cfi_unchecked_callee))' to 'void (*)()' 
discards `cfi_unchecked_callee` attribute}}
+WithAttr *unchecked_func_allias = unchecked;
+WithoutAttr *checked_func_alias2 = checked;
+WithAttr *unchecked_func_alias2 = checked;
+
+using MyType = WithAttr;  // expected-note{{previous definition is here}}
+using MyType = WithoutAttr;  // expected-error{{type alias redefinition with 
different types ('WithoutAttr' (aka 'void ()') vs 'WithAttr' (aka 'void () 
__attribute__((cfi_unchecked_callee))'))}}
+
+void MemberFunctionPointer() {
+  struct A {
+    void unchecked() CFI_UNCHECKED_CALLEE {}
+    virtual void unchecked_virtual() CFI_UNCHECKED_CALLEE {}
+    static void unchecked_static() CFI_UNCHECKED_CALLEE {}
+    void unchecked_explicit_this(this A&) CFI_UNCHECKED_CALLEE {}
+    int operator+=(int i) CFI_UNCHECKED_CALLEE { return i; }
+
+    void checked() {}
+    virtual void checked_virtual() {}
+    static void checked_static() {}
+    void checked_explicit_this(this A&) {}
+    int operator-=(int i) { return i; }
+  };
+
+  void (CFI_UNCHECKED_CALLEE A::*unchecked)() = &A::unchecked;
+  unchecked = &A::unchecked_virtual;
+  void (CFI_UNCHECKED_CALLEE *unchecked_explicit_this)(A&) = 
&A::unchecked_explicit_this;
+  void (CFI_UNCHECKED_CALLEE *unchecked_static)() = &A::unchecked_static;
+  int (CFI_UNCHECKED_CALLEE A::*unchecked_overloaded)(int) = &A::operator+=;
+
+  void (A::*checked)() = &A::unchecked;  // expected-warning{{implicit 
conversion from 'void (A::*)() __attribute__((cfi_unchecked_callee))' to 'void 
(A::*)()' discards `cfi_unchecked_callee` attribute}}
+  checked = &A::unchecked_virtual;  // expected-warning{{implicit conversion 
from 'void (A::*)() __attribute__((cfi_unchecked_callee))' to 'void (A::*)()' 
discards `cfi_unchecked_callee` attribute}}
+  void (*checked_explicit_this)(A&) = &A::unchecked_explicit_this;  // 
expected-warning{{implicit conversion from 'void (*)(A &) 
__attribute__((cfi_unchecked_callee))' to 'void (*)(A &)' discards 
`cfi_unchecked_callee` attribute}}
+  void (*checked_static)() = &A::unchecked_static;  // 
expected-warning{{implicit conversion from 'void (*)() 
__attribute__((cfi_unchecked_callee))' to 'void (*)()' discards 
`cfi_unchecked_callee` attribute}}
+  int (A::*checked_overloaded)(int) = &A::operator+=;  // 
expected-warning{{implicit conversion from 'int (A::*)(int) 
__attribute__((cfi_unchecked_callee))' to 'int (A::*)(int)' discards 
`cfi_unchecked_callee` attribute}}
+
+  unchecked = &A::checked;
+  unchecked = &A::checked_virtual;
+  unchecked_explicit_this = &A::checked_explicit_this;
+  unchecked_static = &A::checked_static;
+  unchecked_overloaded = &A::operator-=;
+
+  checked = &A::checked;
+  checked = &A::checked_virtual;
+  checked_explicit_this = &A::checked_explicit_this;
+  checked_static = &A::checked_static;
+  checked_overloaded = &A::operator-=;
+
+  typedef void (CFI_UNCHECKED_CALLEE A::*WithAttr)();
+  typedef void (CFI_UNCHECKED_CALLEE A::*WithoutAttr)();
+  using WithoutAttr = decltype(unchecked);
+}
+
+void lambdas() {
+  auto unchecked_lambda = [](void) CFI_UNCHECKED_CALLEE -> void {};
+  auto checked_lambda = [](void) -> void {};
+  void (CFI_UNCHECKED_CALLEE *unchecked_func)(void) = unchecked_lambda;
+  unchecked_func = checked_lambda;
+  void (*checked_func)(void) = unchecked_lambda;  // 
expected-warning{{implicit conversion from 'void (*)() 
__attribute__((cfi_unchecked_callee))' to 'void (*)()' discards 
`cfi_unchecked_callee` attribute}}
+  checked_func = checked_lambda;
+
+  auto capture_by_value = [unchecked_lambda, checked_lambda]() {
+    void (CFI_UNCHECKED_CALLEE *unchecked_func)(void) = unchecked_lambda;
+    unchecked_func = checked_lambda;
+    void (*checked_func)(void) = unchecked_lambda;  // 
expected-warning{{implicit conversion from 'void (*)() 
__attribute__((cfi_unchecked_callee))' to 'void (*)()' discards 
`cfi_unchecked_callee` attribute}}
+    checked_func = checked_lambda;
+  };
+
+  auto capture_by_ref = [&unchecked_lambda, &checked_lambda]() {
+    void (CFI_UNCHECKED_CALLEE *unchecked_func)(void) = unchecked_lambda;
+    unchecked_func = checked_lambda;
+    void (*checked_func)(void) = unchecked_lambda;  // 
expected-warning{{implicit conversion from 'void (*)() 
__attribute__((cfi_unchecked_callee))' to 'void (*)()' discards 
`cfi_unchecked_callee` attribute}}
+    checked_func = checked_lambda;
+  };
+
+  auto capture_all_by_value = [=]() {
+    void (CFI_UNCHECKED_CALLEE *unchecked_func)(void) = unchecked_lambda;
+    unchecked_func = checked_lambda;
+    void (*checked_func)(void) = unchecked_lambda;  // 
expected-warning{{implicit conversion from 'void (*)() 
__attribute__((cfi_unchecked_callee))' to 'void (*)()' discards 
`cfi_unchecked_callee` attribute}}
+    checked_func = checked_lambda;
+  };
+
+  auto capture_all_by_ref = [&]() {
+    void (CFI_UNCHECKED_CALLEE *unchecked_func)(void) = unchecked_lambda;
+    unchecked_func = checked_lambda;
+    void (*checked_func)(void) = unchecked_lambda;  // 
expected-warning{{implicit conversion from 'void (*)() 
__attribute__((cfi_unchecked_callee))' to 'void (*)()' discards 
`cfi_unchecked_callee` attribute}}
+    checked_func = checked_lambda;
+  };
+}

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to