[clang] [llvm] [clang][RISCV] Introduce CodeGenModule::calcRISCVZicfilpFuncSigLabel() (PR #111661)

Ming-Yi Lai via llvm-commits llvm-commits at lists.llvm.org
Fri Oct 11 04:28:13 PDT 2024


https://github.com/mylai-mtk updated https://github.com/llvm/llvm-project/pull/111661

>From 9041af3df59b6f6f2f6c2ff335cc697dfd41ac73 Mon Sep 17 00:00:00 2001
From: Ming-Yi Lai <ming-yi.lai at mediatek.com>
Date: Fri, 31 May 2024 17:03:04 +0800
Subject: [PATCH] [clang][RISCV] Emit RISCV function-signature-based CFI label
 in llvm::Function metadata

This patch emits the RISC-V Zicfilp func-sig CFI label in the metadata of
generated `llvm::Function`s.

It introduces CodeGenModule::calcRISCVZicfilpFuncSigLabel(), which calculates a
CFI label used in the RISC-V Zicfilp func-sig CFI scheme for a given function
type/declaration. The scheme, according to psABI, encodes the label based on
function signature, and the rules are modified from the Itanium C++ ABI mangling
rule to allow functions (callees) that are called indirectly to have the
expected label as indicated by the function pointer type seen at the call site
(caller).
---
 clang/include/clang/AST/ASTContext.h          |   7 +
 clang/include/clang/AST/Mangle.h              |   5 +
 clang/lib/AST/ASTContext.cpp                  |   6 +
 clang/lib/AST/ItaniumMangle.cpp               | 137 +++++++++++++++++-
 clang/lib/CodeGen/CodeGenModule.cpp           |  84 +++++++++++
 clang/lib/CodeGen/CodeGenModule.h             |  19 +++
 .../mangle-class-covariant-virtual-method.cpp |  33 +++++
 .../mangle-class-destructor.cpp               |  17 +++
 ...angle-class-incovariant-virtual-method.cpp |  18 +++
 .../zicfilp-func-sig/mangle-class-method.cpp  |  18 +++
 .../mangle-class-static-method.cpp            |  21 +++
 .../mangle-func-empty-param-list.c            |  15 ++
 .../mangle-func-empty-param-list.cpp          |  15 ++
 .../mangle-ignore-exception-spec.cpp          |  15 ++
 .../RISCV/zicfilp-func-sig/mangle-main.cpp    |  39 +++++
 .../RISCV/zicfilp-func-sig/mangle-wchar-t.cpp |  12 ++
 llvm/include/llvm/Support/RISCVISAUtils.h     |   6 +-
 llvm/lib/Support/RISCVISAUtils.cpp            |  22 ++-
 18 files changed, 485 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-class-covariant-virtual-method.cpp
 create mode 100644 clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-class-destructor.cpp
 create mode 100644 clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-class-incovariant-virtual-method.cpp
 create mode 100644 clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-class-method.cpp
 create mode 100644 clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-class-static-method.cpp
 create mode 100644 clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-func-empty-param-list.c
 create mode 100644 clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-func-empty-param-list.cpp
 create mode 100644 clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-ignore-exception-spec.cpp
 create mode 100644 clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-main.cpp
 create mode 100644 clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-wchar-t.cpp

diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index a4d36f2eacd5d1..4812e0bac2cfc3 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -1876,6 +1876,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// (struct/union/class/enum) decl.
   QualType getTagDeclType(const TagDecl *Decl) const;
 
+  /// Return the type for "void *"
+  QualType getVoidPtrType() const { return VoidPtrTy; }
+
   /// Return the unique type for "size_t" (C99 7.17), defined in
   /// <stddef.h>.
   ///
@@ -1903,6 +1906,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// defined in <stddef.h> as defined by the target.
   QualType getWideCharType() const { return WideCharTy; }
 
+  /// Return the type of wide characters in C context, no matter whether it's C
+  /// or C++ being compiled.
+  QualType getWCharTypeInC() const;
+
   /// Return the type of "signed wchar_t".
   ///
   /// Used when in C++, as a GCC extension.
diff --git a/clang/include/clang/AST/Mangle.h b/clang/include/clang/AST/Mangle.h
index d5f6c0f6cc67df..16cbd802177ba4 100644
--- a/clang/include/clang/AST/Mangle.h
+++ b/clang/include/clang/AST/Mangle.h
@@ -212,6 +212,11 @@ class ItaniumMangleContext : public MangleContext {
 
   virtual void mangleModuleInitializer(const Module *Module, raw_ostream &) = 0;
 
+  virtual void mangleForRISCVZicfilpFuncSigLabel(const FunctionType &FT,
+                                                 const bool IsCXXInstanceMethod,
+                                                 const bool IsCXXVirtualMethod,
+                                                 raw_ostream &) = 0;
+
   // This has to live here, otherwise the CXXNameMangler won't have access to
   // it.
   virtual DiscriminatorOverrideTy getDiscriminatorOverride() const = 0;
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 034fbbe0bc7829..ce8688a489cb43 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -6442,6 +6442,12 @@ CanQualType ASTContext::getUIntMaxType() const {
   return getFromTargetType(Target->getUIntMaxType());
 }
 
+/// Return the type of wide characters in C context, no matter whether it's C
+/// or C++ being compiled.
+QualType ASTContext::getWCharTypeInC() const {
+  return getFromTargetType(Target->getWCharType());
+}
+
 /// getSignedWCharType - Return the type of "signed wchar_t".
 /// Used when in C++, as a GCC extension.
 QualType ASTContext::getSignedWCharType() const {
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 777cdca1a0c0d7..9eff94b0844092 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -43,6 +43,14 @@ using namespace clang;
 
 namespace {
 
+static bool mayBeCovariant(const Type &Ty) {
+  if (auto *const PT = Ty.getAs<PointerType>())
+    return PT->getPointeeType()->isStructureOrClassType();
+  if (auto *const RT = Ty.getAs<ReferenceType>())
+    return RT->getPointeeType()->isStructureOrClassType();
+  return false;
+}
+
 static bool isLocalContainerContext(const DeclContext *DC) {
   return isa<FunctionDecl>(DC) || isa<ObjCMethodDecl>(DC) || isa<BlockDecl>(DC);
 }
@@ -136,6 +144,11 @@ class ItaniumMangleContextImpl : public ItaniumMangleContext {
 
   void mangleModuleInitializer(const Module *Module, raw_ostream &) override;
 
+  void mangleForRISCVZicfilpFuncSigLabel(const FunctionType &,
+                                         const bool IsCXXInstanceMethod,
+                                         const bool IsCXXVirtualMethod,
+                                         raw_ostream &) override;
+
   bool getNextDiscriminator(const NamedDecl *ND, unsigned &disc) {
     // Lambda closure types are already numbered.
     if (isLambda(ND))
@@ -395,8 +408,10 @@ class CXXNameMangler {
   llvm::DenseMap<uintptr_t, unsigned> Substitutions;
   llvm::DenseMap<StringRef, unsigned> ModuleSubstitutions;
 
+protected:
   ASTContext &getASTContext() const { return Context.getASTContext(); }
 
+private:
   bool isCompatibleWith(LangOptions::ClangABI Ver) {
     return Context.getASTContext().getLangOpts().getClangABICompat() <= Ver;
   }
@@ -443,6 +458,8 @@ class CXXNameMangler {
     NullOut = true;
   }
 
+  virtual ~CXXNameMangler() = default;
+
   struct WithTemplateDepthOffset { unsigned Offset; };
   CXXNameMangler(ItaniumMangleContextImpl &C, raw_ostream &Out,
                  WithTemplateDepthOffset Offset)
@@ -559,9 +576,12 @@ class CXXNameMangler {
                                       StringRef Prefix = "");
   void mangleOperatorName(DeclarationName Name, unsigned Arity);
   void mangleOperatorName(OverloadedOperatorKind OO, unsigned Arity);
+
+protected:
   void mangleQualifiers(Qualifiers Quals, const DependentAddressSpaceType *DAST = nullptr);
   void mangleRefQualifier(RefQualifierKind RefQualifier);
 
+private:
   void mangleObjCMethodName(const ObjCMethodDecl *MD);
 
   // Declare manglers for every type class.
@@ -572,11 +592,24 @@ class CXXNameMangler {
 
   void mangleType(const TagType*);
   void mangleType(TemplateName);
+
+protected:
+  // Use the `Impl` scheme instead of directly virtualizing `mangleType`s since
+  // `mangleType`s are declared by tables
+  virtual void mangleTypeImpl(const BuiltinType *T);
+  virtual void mangleTypeImpl(const FunctionProtoType *T);
+  virtual void mangleTypeImpl(const FunctionNoProtoType *T);
+
+private:
   static StringRef getCallingConvQualifierName(CallingConv CC);
   void mangleExtParameterInfo(FunctionProtoType::ExtParameterInfo info);
   void mangleExtFunctionInfo(const FunctionType *T);
+
+protected:
   void mangleBareFunctionType(const FunctionProtoType *T, bool MangleReturnType,
                               const FunctionDecl *FD = nullptr);
+
+private:
   void mangleNeonVectorType(const VectorType *T);
   void mangleNeonVectorType(const DependentVectorType *T);
   void mangleAArch64NeonVectorType(const VectorType *T);
@@ -3058,7 +3091,9 @@ void CXXNameMangler::mangleCXXRecordDecl(const CXXRecordDecl *Record) {
   addSubstitution(Record);
 }
 
-void CXXNameMangler::mangleType(const BuiltinType *T) {
+void CXXNameMangler::mangleType(const BuiltinType *T) { mangleTypeImpl(T); }
+
+void CXXNameMangler::mangleTypeImpl(const BuiltinType *T) {
   //  <type>         ::= <builtin-type>
   //  <builtin-type> ::= v  # void
   //                 ::= w  # wchar_t
@@ -3563,10 +3598,14 @@ CXXNameMangler::mangleExtParameterInfo(FunctionProtoType::ExtParameterInfo PI) {
     mangleVendorQualifier("noescape");
 }
 
+void CXXNameMangler::mangleType(const FunctionProtoType *T) {
+  return mangleTypeImpl(T);
+}
+
 // <type>          ::= <function-type>
 // <function-type> ::= [<CV-qualifiers>] F [Y]
 //                      <bare-function-type> [<ref-qualifier>] E
-void CXXNameMangler::mangleType(const FunctionProtoType *T) {
+void CXXNameMangler::mangleTypeImpl(const FunctionProtoType *T) {
   mangleExtFunctionInfo(T);
 
   // Mangle CV-qualifiers, if present.  These are 'this' qualifiers,
@@ -3604,6 +3643,10 @@ void CXXNameMangler::mangleType(const FunctionProtoType *T) {
 }
 
 void CXXNameMangler::mangleType(const FunctionNoProtoType *T) {
+  return mangleTypeImpl(T);
+}
+
+void CXXNameMangler::mangleTypeImpl(const FunctionNoProtoType *T) {
   // Function types without prototypes can arise when mangling a function type
   // within an overloadable function in C. We mangle these as the absence of any
   // parameter types (not even an empty parameter list).
@@ -7074,6 +7117,85 @@ bool CXXNameMangler::shouldHaveAbiTags(ItaniumMangleContextImpl &C,
   return TrackAbiTags.AbiTagsRoot.getUsedAbiTags().size();
 }
 
+namespace {
+
+class RISCVZicfilpFuncSigLabelMangler : public CXXNameMangler {
+  bool IsTopLevelAndCXXVirtualMethod;
+
+public:
+  RISCVZicfilpFuncSigLabelMangler(ItaniumMangleContextImpl &C, raw_ostream &Out,
+                                  const bool IsCXXVirtualMethod)
+      : CXXNameMangler(C, Out),
+        IsTopLevelAndCXXVirtualMethod(/*IsTopLevel=*/true &&
+                                      IsCXXVirtualMethod) {}
+
+  void mangleTypeImpl(const BuiltinType *T) override {
+    if (T->getKind() == BuiltinType::WChar_S ||
+        T->getKind() == BuiltinType::WChar_U) {
+      const Type *const OverrideT =
+          getASTContext().getWCharTypeInC().getTypePtr();
+      assert(isa<BuiltinType>(OverrideT) &&
+             "`wchar_t' in C is expected to be defined to a built-in type");
+      T = static_cast<const BuiltinType *>(OverrideT);
+    }
+    return CXXNameMangler::mangleTypeImpl(T);
+  }
+
+  // This <function-type> is the RISC-V psABI modified version
+  // <function-type> ::= [<CV-qualifiers>] [Dx] F <bare-function-type>
+  //                     [<ref-qualifier>] E
+  void mangleTypeImpl(const FunctionProtoType *T) override {
+    // Mangle CV-qualifiers, if present.  These are 'this' qualifiers,
+    // e.g. "const" in "int (A::*)() const".
+    mangleQualifiers(T->getMethodQuals());
+
+    getStream() << 'F';
+
+    bool MangleReturnType = true;
+    if (const Type &RetT = *T->getReturnType().getTypePtr();
+        IsTopLevelAndCXXVirtualMethod && mayBeCovariant(RetT)) {
+      // Possible covariant types mangle dummy cv-unqualified `class v` as its
+      // class type
+      if (RetT.isPointerType())
+        getStream() << "P1v";
+      else if (RetT.isLValueReferenceType())
+        getStream() << "R1v";
+      else {
+        assert(RetT.isRValueReferenceType() &&
+               "Expect an r-value ref for covariant return type that is not a "
+               "pointer or an l-value ref");
+        getStream() << "O1v";
+      }
+
+      IsTopLevelAndCXXVirtualMethod = false; // Not top-level anymore
+      MangleReturnType = false;
+    }
+    mangleBareFunctionType(T, MangleReturnType);
+
+    // Mangle the ref-qualifier, if present.
+    mangleRefQualifier(T->getRefQualifier());
+
+    getStream() << 'E';
+  }
+
+  void mangleTypeImpl(const FunctionNoProtoType *T) override {
+    return CXXNameMangler::mangleTypeImpl(toFunctionProtoType(T));
+  }
+
+private:
+  const FunctionProtoType *
+  toFunctionProtoType(const FunctionNoProtoType *const T) {
+    FunctionProtoType::ExtProtoInfo EPI;
+    EPI.ExtInfo = T->getExtInfo();
+    const Type *const NewT = getASTContext()
+                                 .getFunctionType(T->getReturnType(), {}, EPI)
+                                 .getTypePtr();
+    return static_cast<const FunctionProtoType *>(NewT);
+  }
+}; // class RISCVZicfilpFuncSigLabelMangler
+
+} // anonymous namespace
+
 //
 
 /// Mangles the name of the declaration D and emits that name to the given
@@ -7412,6 +7534,17 @@ void ItaniumMangleContextImpl::mangleModuleInitializer(const Module *M,
   }
 }
 
+void ItaniumMangleContextImpl::mangleForRISCVZicfilpFuncSigLabel(
+    const FunctionType &FT, const bool IsCXXInstanceMethod,
+    const bool IsCXXVirtualMethod, raw_ostream &Out) {
+  if (IsCXXInstanceMethod)
+    // member methods uses a dummy class named `v` in place of real classes
+    Out << "M1v";
+
+  RISCVZicfilpFuncSigLabelMangler Mangler(*this, Out, IsCXXVirtualMethod);
+  Mangler.mangleType(QualType(&FT, 0));
+}
+
 ItaniumMangleContext *ItaniumMangleContext::create(ASTContext &Context,
                                                    DiagnosticsEngine &Diags,
                                                    bool IsAux) {
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 5ba098144a74e7..3042bc2c105de4 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -69,6 +69,7 @@
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/ConvertUTF.h"
 #include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/RISCVISAUtils.h"
 #include "llvm/Support/TimeProfiler.h"
 #include "llvm/Support/xxhash.h"
 #include "llvm/TargetParser/RISCVISAInfo.h"
@@ -453,6 +454,18 @@ CodeGenModule::CodeGenModule(ASTContext &C,
   if (Context.getTargetInfo().getTriple().getArch() == llvm::Triple::x86)
     getModule().addModuleFlag(llvm::Module::Error, "NumRegisterParameters",
                               CodeGenOpts.NumRegisterParameters);
+
+  if (CodeGenOpts.CFProtectionBranch &&
+      getTarget().checkCFProtectionBranchSupported(getDiags())) {
+    auto Scheme = CodeGenOpts.getCFBranchLabelScheme();
+    if (getTarget().checkCFBranchLabelSchemeSupported(Scheme, getDiags())) {
+      if (Scheme == CFBranchLabelSchemeKind::Default)
+        Scheme = getTarget().getDefaultCFBranchLabelScheme();
+
+      UseRISCVZicfilpFuncSigCFI =
+          (Scheme == CFBranchLabelSchemeKind::FuncSig && getTriple().isRISCV());
+    }
+  }
 }
 
 CodeGenModule::~CodeGenModule() {}
@@ -912,6 +925,8 @@ void CodeGenModule::Release() {
   }
   if (LangOpts.Sanitize.has(SanitizerKind::KCFI))
     finalizeKCFITypes();
+  if (UseRISCVZicfilpFuncSigCFI)
+    finalizeRISCVZicfilpFuncSigLabels();
   emitAtAvailableLinkGuard();
   if (Context.getTargetInfo().getTriple().isWasm())
     EmitMainVoidAlias();
@@ -2829,6 +2844,39 @@ void CodeGenModule::CreateFunctionTypeMetadataForIcall(const FunctionDecl *FD,
       F->addTypeMetadata(0, llvm::ConstantAsMetadata::get(CrossDsoTypeId));
 }
 
+uint32_t
+CodeGenModule::calcRISCVZicfilpFuncSigLabel(const FunctionType &FT,
+                                            const bool IsCXXInstanceMethod,
+                                            const bool IsCXXVirtualMethod) {
+  std::string OutName;
+  llvm::raw_string_ostream Out(OutName);
+  MangleContext &MC = getCXXABI().getMangleContext();
+  cast<ItaniumMangleContext>(MC).mangleForRISCVZicfilpFuncSigLabel(
+      FT, IsCXXInstanceMethod, IsCXXVirtualMethod, Out);
+  return llvm::RISCVISAUtils::zicfilpFuncSigHash(OutName);
+}
+
+uint32_t CodeGenModule::calcRISCVZicfilpFuncSigLabel(const FunctionDecl &FD) {
+  if (FD.isMain())
+    // All main functions use `int main(int, char**)` for label calculation
+    // according to psABI spec. This value is the pre-calculated label.
+    return 0xd0639;
+
+  if (isa<CXXDestructorDecl>(FD))
+    // All destructors use `void (void*)` for label calculation according to the
+    // psABI spec. This value is the pre-calculated label.
+    return 0x639c2;
+
+  bool IsCXXInstanceMethod = false;
+  bool IsCXXVirtualMethod = false;
+  if (const auto *const MD = dyn_cast<CXXMethodDecl>(&FD)) {
+    IsCXXInstanceMethod = MD->isInstance();
+    IsCXXVirtualMethod = MD->isVirtual();
+  }
+  return calcRISCVZicfilpFuncSigLabel(*FD.getFunctionType(),
+                                      IsCXXInstanceMethod, IsCXXVirtualMethod);
+}
+
 void CodeGenModule::setKCFIType(const FunctionDecl *FD, llvm::Function *F) {
   llvm::LLVMContext &Ctx = F->getContext();
   llvm::MDBuilder MDB(Ctx);
@@ -2837,6 +2885,16 @@ void CodeGenModule::setKCFIType(const FunctionDecl *FD, llvm::Function *F) {
                      Ctx, MDB.createConstant(CreateKCFITypeId(FD->getType()))));
 }
 
+void CodeGenModule::setRISCVZicfilpFuncSigLabel(const FunctionDecl *FD,
+                                                llvm::Function *F) {
+  llvm::LLVMContext &Ctx = F->getContext();
+  llvm::MDBuilder MDB(Ctx);
+  F->setMetadata(
+      "riscv_lpad_label",
+      llvm::MDNode::get(Ctx, MDB.createConstant(llvm::ConstantInt::get(
+                                 Int32Ty, calcRISCVZicfilpFuncSigLabel(*FD)))));
+}
+
 static bool allowKCFIIdentifier(StringRef Name) {
   // KCFI type identifier constants are only necessary for external assembly
   // functions, which means it's safe to skip unusual names. Subset of
@@ -2877,6 +2935,29 @@ void CodeGenModule::finalizeKCFITypes() {
   }
 }
 
+void CodeGenModule::finalizeRISCVZicfilpFuncSigLabels() {
+  llvm::Module &M = getModule();
+  for (llvm::Function &F : M.functions())
+    finalizeRISCVZicfilpFuncSigLabel(F);
+}
+
+void CodeGenModule::finalizeRISCVZicfilpFuncSigLabel(llvm::Function &F) {
+  const unsigned MDKindID = F.getContext().getMDKindID("riscv_lpad_label");
+  if (!F.hasAddressTaken() && F.hasLocalLinkage()) {
+    F.eraseMetadata(MDKindID);
+    return;
+  }
+
+  const llvm::MDNode *MD = F.getMetadata(MDKindID);
+  if (!MD) {
+    GlobalDecl GD;
+    if (!lookupRepresentativeDecl(F.getName(), GD))
+      return;
+
+    setRISCVZicfilpFuncSigLabel(cast<FunctionDecl>(GD.getDecl()), &F);
+  }
+}
+
 void CodeGenModule::SetFunctionAttributes(GlobalDecl GD, llvm::Function *F,
                                           bool IsIncompleteFunction,
                                           bool IsThunk) {
@@ -2962,6 +3043,9 @@ void CodeGenModule::SetFunctionAttributes(GlobalDecl GD, llvm::Function *F,
   if (LangOpts.Sanitize.has(SanitizerKind::KCFI))
     setKCFIType(FD, F);
 
+  if (UseRISCVZicfilpFuncSigCFI)
+    setRISCVZicfilpFuncSigLabel(FD, F);
+
   if (getLangOpts().OpenMP && FD->hasAttr<OMPDeclareSimdDeclAttr>())
     getOpenMPRuntime().emitDeclareSimdFunction(FD, F);
 
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index c58bb88035ca8a..a24c92dd2ba546 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -1556,9 +1556,26 @@ class CodeGenModule : public CodeGenTypeCache {
   /// Set type metadata to the given function.
   void setKCFIType(const FunctionDecl *FD, llvm::Function *F);
 
+  /// Set RISC-V Zicfilp func-sig CFI label in metadata of the given function.
+  void setRISCVZicfilpFuncSigLabel(const FunctionDecl *FD, llvm::Function *F);
+
   /// Emit KCFI type identifier constants and remove unused identifiers.
   void finalizeKCFITypes();
 
+  /// Fixup RISCV Zicfilp func-sig CFI labels
+  void finalizeRISCVZicfilpFuncSigLabels();
+
+  /// Fixup RISCV Zicfilp func-sig CFI label for llvm::Function
+  void finalizeRISCVZicfilpFuncSigLabel(llvm::Function &F);
+
+  /// Calculate RISC-V Zicfilp func-sig scheme CFI label
+  uint32_t calcRISCVZicfilpFuncSigLabel(const FunctionType &FT,
+                                        const bool IsCXXInstanceMethod,
+                                        const bool IsCXXVirtualMethod);
+
+  /// Calculate RISC-V Zicfilp func-sig scheme CFI label
+  uint32_t calcRISCVZicfilpFuncSigLabel(const FunctionDecl &FD);
+
   /// Whether this function's return type has no side effects, and thus may
   /// be trivially discarded if it is unused.
   bool MayDropFunctionReturn(const ASTContext &Context,
@@ -1676,6 +1693,8 @@ class CodeGenModule : public CodeGenTypeCache {
     MustTailCallUndefinedGlobals.insert(Global);
   }
 
+  bool UseRISCVZicfilpFuncSigCFI;
+
 private:
   bool shouldDropDLLAttribute(const Decl *D, const llvm::GlobalValue *GV) const;
 
diff --git a/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-class-covariant-virtual-method.cpp b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-class-covariant-virtual-method.cpp
new file mode 100644
index 00000000000000..f536acbf867b85
--- /dev/null
+++ b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-class-covariant-virtual-method.cpp
@@ -0,0 +1,33 @@
+// REQUIRES: riscv-registered-target
+// RUN: %clang_cc1 -triple riscv64 -target-cpu generic-rv64 -target-feature \
+// RUN:  +experimental-zicfilp -fcf-protection=branch \
+// RUN:  -mcf-branch-label-scheme=func-sig -emit-llvm -o - -x c++ %s \
+// RUN:  | FileCheck %s --check-prefixes=PTR,LREF,RREF
+
+class Class {
+public:
+  // test - virtual methods with return type that can possibly be covariant
+  //        mangle return class as `class v`
+  // PTR-LABEL: define{{.*}} @_ZN5Class35virtualMethodWithCovariantPtrReturnEv
+  // PTR-SAME: ({{.*}}){{.* !riscv_lpad_label}} [[MD_PTR:![0-9]+]] {{.*}}{
+  //
+  virtual Class *virtualMethodWithCovariantPtrReturn() { return this; }
+
+  // LREF-LABEL: define{{.*}} @_ZN5Class36virtualMethodWithCovariantLRefReturnEv
+  // LREF-SAME: ({{.*}}){{.* !riscv_lpad_label}} [[MD_LREF:![0-9]+]] {{.*}}{
+  //
+  virtual Class &virtualMethodWithCovariantLRefReturn() { return *this; }
+
+  // RREF-LABEL: define{{.*}} @_ZN5Class36virtualMethodWithCovariantRRefReturnEv
+  // RREF-SAME: ({{.*}}){{.* !riscv_lpad_label}} [[MD_RREF:![0-9]+]] {{.*}}{
+  //
+  virtual Class &&virtualMethodWithCovariantRRefReturn() {
+    return static_cast<Class&&>(*this);
+  }
+};
+
+// PTR-DAG: [[MD_PTR]] = !{i32 996333}
+// LREF-DAG: [[MD_LREF]] = !{i32 918198}
+// RREF-DAG: [[MD_RREF]] = !{i32 86168}
+
+void use() { Class C; }
diff --git a/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-class-destructor.cpp b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-class-destructor.cpp
new file mode 100644
index 00000000000000..196bb69c2f61ac
--- /dev/null
+++ b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-class-destructor.cpp
@@ -0,0 +1,17 @@
+// REQUIRES: riscv-registered-target
+// RUN: %clang_cc1 -triple riscv64 -target-cpu generic-rv64 -target-feature \
+// RUN:  +experimental-zicfilp -fcf-protection=branch \
+// RUN:  -mcf-branch-label-scheme=func-sig -emit-llvm -o - -x c++ %s \
+// RUN:  | FileCheck %s
+
+class Class {
+public:
+  // test - destructors should use `void (*)(void*)`
+  // CHECK-LABEL: define{{.*}} @_ZN5ClassD1Ev({{.*}})
+  // CHECK-SAME: {{.* !riscv_lpad_label}} [[MD:![0-9]+]] {{.*}}{
+  // CHECK: [[MD]] = !{i32 408002}
+  //
+  ~Class() {}
+};
+
+void use() { Class C; }
diff --git a/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-class-incovariant-virtual-method.cpp b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-class-incovariant-virtual-method.cpp
new file mode 100644
index 00000000000000..9ff4a83046fb72
--- /dev/null
+++ b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-class-incovariant-virtual-method.cpp
@@ -0,0 +1,18 @@
+// REQUIRES: riscv-registered-target
+// RUN: %clang_cc1 -triple riscv64 -target-cpu generic-rv64 -target-feature \
+// RUN:  +experimental-zicfilp -fcf-protection=branch \
+// RUN:  -mcf-branch-label-scheme=func-sig -emit-llvm -o - -x c++ %s \
+// RUN:  | FileCheck %s
+
+class Class {
+public:
+  // test - virtual methods with return type that cannot be covariant mangle
+  //        return type as it is declared
+  // CHECK-LABEL: define{{.*}} @_ZN5Class34virtualMethodWithIncovariantReturnEv
+  // CHECK-SAME: ({{.*}}){{.* !riscv_lpad_label}} [[MD:![0-9]+]] {{.*}}{
+  // CHECK: [[MD]] = !{i32 910118}
+  //
+  virtual int virtualMethodWithIncovariantReturn() { return 0; }
+};
+
+void use() { Class C; }
diff --git a/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-class-method.cpp b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-class-method.cpp
new file mode 100644
index 00000000000000..8e335b838b5df1
--- /dev/null
+++ b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-class-method.cpp
@@ -0,0 +1,18 @@
+// REQUIRES: riscv-registered-target
+// RUN: %clang_cc1 -triple riscv64 -target-cpu generic-rv64 -target-feature \
+// RUN:  +experimental-zicfilp -fcf-protection=branch \
+// RUN:  -mcf-branch-label-scheme=func-sig -emit-llvm -o - -x c++ %s \
+// RUN:  | FileCheck %s
+
+class Class {
+public:
+  // test - C++ member functions should use "pointer-to-member" types, with
+  //        `<class type>` being `1v` (i.e. using a dummy class named `v`)
+  // CHECK-LABEL: define{{.*}} @_ZN5Class14instanceMethodEv({{.*}})
+  // CHECK-SAME: {{.* !riscv_lpad_label}} [[MD:![0-9]+]] {{.*}}{
+  // CHECK: [[MD]] = !{i32 974748}
+  //
+  void instanceMethod() {}
+};
+
+void use(Class &C) { C.instanceMethod(); }
diff --git a/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-class-static-method.cpp b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-class-static-method.cpp
new file mode 100644
index 00000000000000..d1bf9914e84a9b
--- /dev/null
+++ b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-class-static-method.cpp
@@ -0,0 +1,21 @@
+// REQUIRES: riscv-registered-target
+// RUN: %clang_cc1 -triple riscv64 -target-cpu generic-rv64 -target-feature \
+// RUN:  +experimental-zicfilp -fcf-protection=branch \
+// RUN:  -mcf-branch-label-scheme=func-sig -emit-llvm -o - -x c++ %s \
+// RUN:  | FileCheck %s
+
+// CHECK-LABEL: define{{.*}} @_Z13nonMemberFuncv()
+// CHECK-SAME: {{.* !riscv_lpad_label}} [[MD:![0-9]+]] {{.*}}{
+//
+void nonMemberFunc() {}
+
+class Class {
+public:
+  // test - static methods are mangled as non-member function
+  // CHECK-LABEL: define{{.*}} @_ZN5Class12staticMethodEv()
+  // CHECK-SAME: {{.* !riscv_lpad_label}} [[MD]] {{.*}}{
+  //
+  static void staticMethod() {}
+};
+
+void use() { Class::staticMethod(); }
diff --git a/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-func-empty-param-list.c b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-func-empty-param-list.c
new file mode 100644
index 00000000000000..3a122acb600493
--- /dev/null
+++ b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-func-empty-param-list.c
@@ -0,0 +1,15 @@
+// REQUIRES: riscv-registered-target
+// RUN: %clang_cc1 -triple riscv64 -target-cpu generic-rv64 -target-feature \
+// RUN:  +experimental-zicfilp -fcf-protection=branch \
+// RUN:  -mcf-branch-label-scheme=func-sig -emit-llvm -o - -x c %s \
+// RUN:  | FileCheck %s
+
+// test - functions with an empty parameter list are treated as `void (*)(void)`
+// CHECK-LABEL: define{{.*}} @funcWithEmptyParameterList()
+// CHECK-SAME: {{.* !riscv_lpad_label}} [[MD:![0-9]+]] {{.*}}{
+//
+void funcWithEmptyParameterList() {}
+// CHECK-LABEL: define{{.*}} @funcWithVoidParameterList()
+// CHECK-SAME: {{.* !riscv_lpad_label}} [[MD]] {{.*}}{
+//
+void funcWithVoidParameterList(void) {}
diff --git a/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-func-empty-param-list.cpp b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-func-empty-param-list.cpp
new file mode 100644
index 00000000000000..561889057f2d5b
--- /dev/null
+++ b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-func-empty-param-list.cpp
@@ -0,0 +1,15 @@
+// REQUIRES: riscv-registered-target
+// RUN: %clang_cc1 -triple riscv64 -target-cpu generic-rv64 -target-feature \
+// RUN:  +experimental-zicfilp -fcf-protection=branch \
+// RUN:  -mcf-branch-label-scheme=func-sig -emit-llvm -o - -x c++ %s \
+// RUN:  | FileCheck %s
+
+// test - functions with an empty parameter list are treated as `void (*)(void)`
+// CHECK-LABEL: define{{.*}} @_Z26funcWithEmptyParameterListv()
+// CHECK-SAME: {{.* !riscv_lpad_label}} [[MD:![0-9]+]] {{.*}}{
+//
+void funcWithEmptyParameterList() {}
+// CHECK-LABEL: define{{.*}} @_Z25funcWithVoidParameterListv()
+// CHECK-SAME: {{.* !riscv_lpad_label}} [[MD]] {{.*}}{
+//
+void funcWithVoidParameterList(void) {}
diff --git a/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-ignore-exception-spec.cpp b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-ignore-exception-spec.cpp
new file mode 100644
index 00000000000000..921fcfbb9c08de
--- /dev/null
+++ b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-ignore-exception-spec.cpp
@@ -0,0 +1,15 @@
+// REQUIRES: riscv-registered-target
+// RUN: %clang_cc1 -triple riscv64 -target-cpu generic-rv64 -target-feature \
+// RUN:  +experimental-zicfilp -fcf-protection=branch \
+// RUN:  -mcf-branch-label-scheme=func-sig -fcxx-exceptions -fexceptions \
+// RUN:  -emit-llvm -o - -x c++ %s | FileCheck %s
+
+// test - `<exception-spec>` should be ignored
+// CHECK-LABEL: define{{.*}} @_Z9funcThrowv()
+// CHECK-SAME: {{.* !riscv_lpad_label}} [[MD:![0-9]+]] {{.*}}{
+///
+void funcThrow() { throw 0; }
+// CHECK-LABEL: define{{.*}} @_Z12funcNoExceptv()
+// CHECK-SAME: {{.* !riscv_lpad_label}} [[MD]] {{.*}}{
+//
+void funcNoExcept() noexcept {}
diff --git a/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-main.cpp b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-main.cpp
new file mode 100644
index 00000000000000..1a8cc4b5723c67
--- /dev/null
+++ b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-main.cpp
@@ -0,0 +1,39 @@
+// REQUIRES: riscv-registered-target
+// RUN: %clang_cc1 -DNO_PARAM -triple riscv64 -target-cpu generic-rv64 \
+// RUN:  -target-feature +experimental-zicfilp -fcf-protection=branch \
+// RUN:  -mcf-branch-label-scheme=func-sig -emit-llvm -o - -x c++ %s \
+// RUN:  | FileCheck %s
+// RUN: %clang_cc1 -DONE_PARAM -triple riscv64 -target-cpu generic-rv64 \
+// RUN:  -target-feature +experimental-zicfilp -fcf-protection=branch \
+// RUN:  -mcf-branch-label-scheme=func-sig -emit-llvm -o - -x c++ %s \
+// RUN:  | FileCheck %s
+// RUN: %clang_cc1 -DTWO_PARAMS -triple riscv64 -target-cpu generic-rv64 \
+// RUN:  -target-feature +experimental-zicfilp -fcf-protection=branch \
+// RUN:  -mcf-branch-label-scheme=func-sig -emit-llvm -o - -x c++ %s \
+// RUN:  | FileCheck %s
+// RUN: %clang_cc1 -DTWO_PARAMS -triple riscv64 -target-cpu generic-rv64 \
+// RUN:  -target-feature +experimental-zicfilp -fcf-protection=branch \
+// RUN:  -mcf-branch-label-scheme=func-sig -emit-llvm -o - -x c++ %s \
+// RUN:  | FileCheck %s
+
+// test - main functions should use `int (*)(int, char**)`
+// CHECK-LABEL: define{{.*}} @main({{.*}})
+// CHECK-SAME: {{.* !riscv_lpad_label}} [[MD:![0-9]+]] {{.*}}{
+// CHECK: [[MD]] = !{i32 853561}
+//
+
+#ifdef NO_PARAM
+int main() { return 0; }
+#endif
+
+#ifdef ONE_PARAM
+int main(int argc) { return argc; }
+#endif
+
+#ifdef TWO_PARAMS
+int main(int argc, char **argv) { return argc; }
+#endif
+
+#ifdef THREE_PARAMS
+int main(int argc, char **argv, char **envp) { return argc; }
+#endif
diff --git a/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-wchar-t.cpp b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-wchar-t.cpp
new file mode 100644
index 00000000000000..14de1b7bbfdda9
--- /dev/null
+++ b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-wchar-t.cpp
@@ -0,0 +1,12 @@
+// REQUIRES: riscv-registered-target
+// RUN: %clang_cc1 -triple riscv64 -target-cpu generic-rv64 -target-feature \
+// RUN:  +experimental-zicfilp -fcf-protection=branch \
+// RUN:  -mcf-branch-label-scheme=func-sig -emit-llvm -o - -x c++ %s \
+// RUN:  | FileCheck %s
+
+// test - `wchar_t` in C++ should be mangled to `wchar_t` in C
+// CHECK-LABEL: define{{.*}} @_Z14funcWithWCharTw({{.*}})
+// CHECK-SAME: {{.* !riscv_lpad_label}} [[MD:![0-9]+]] {{.*}}{
+// CHECK: [[MD]] = !{i32 374765}
+//
+void funcWithWCharT(wchar_t) {}
diff --git a/llvm/include/llvm/Support/RISCVISAUtils.h b/llvm/include/llvm/Support/RISCVISAUtils.h
index 77f8c3e45f1ab5..b569184fddff0a 100644
--- a/llvm/include/llvm/Support/RISCVISAUtils.h
+++ b/llvm/include/llvm/Support/RISCVISAUtils.h
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// Utilities shared by TableGen and RISCVISAInfo.
+// Utilities shared by TableGen, RISCVISAInfo and other RISC-V specifics.
 //
 //===----------------------------------------------------------------------===//
 
@@ -42,6 +42,10 @@ struct ExtensionComparator {
 typedef std::map<std::string, ExtensionVersion, ExtensionComparator>
     OrderedExtensionMap;
 
+/// Obtain a 20-bit integer from a (function-signature) string using the method
+/// defined in the psABI for Zicfilp func-sig CFI scheme
+uint32_t zicfilpFuncSigHash(const StringRef FuncSig);
+
 } // namespace RISCVISAUtils
 
 } // namespace llvm
diff --git a/llvm/lib/Support/RISCVISAUtils.cpp b/llvm/lib/Support/RISCVISAUtils.cpp
index d6b002e66e7ab2..386fb157ecdee4 100644
--- a/llvm/lib/Support/RISCVISAUtils.cpp
+++ b/llvm/lib/Support/RISCVISAUtils.cpp
@@ -6,12 +6,13 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// Utilities shared by TableGen and RISCVISAInfo.
+// Utilities shared by TableGen, RISCVISAInfo and other RISC-V specifics.
 //
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Support/RISCVISAUtils.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/MD5.h"
 #include <cassert>
 
 using namespace llvm;
@@ -90,3 +91,22 @@ bool llvm::RISCVISAUtils::compareExtension(const std::string &LHS,
   // If the rank is same, it must be sorted by lexicographic order.
   return LHS < RHS;
 }
+
+uint32_t llvm::RISCVISAUtils::zicfilpFuncSigHash(const StringRef FuncSig) {
+  const llvm::MD5::MD5Result MD5Result =
+      llvm::MD5::hash({(const uint8_t *)FuncSig.data(), FuncSig.size()});
+
+  uint64_t MD5High = MD5Result.high();
+  uint64_t MD5Low = MD5Result.low();
+  while (MD5High && MD5Low) {
+    const uint32_t Low20Bits = MD5Low & 0xFFFFFULL;
+    if (Low20Bits)
+      return Low20Bits;
+
+    // Logical right shift MD5 result by 20 bits
+    MD5Low = (MD5High & 0xFFFFF) << 44 | MD5Low >> 20;
+    MD5High >>= 20;
+  }
+
+  return llvm::MD5Hash("RISC-V") & 0xFFFFFULL;
+}



More information about the llvm-commits mailing list