https://github.com/mylai-mtk updated https://github.com/llvm/llvm-project/pull/111661
>From b3a7b9f4bd6d65f837b3cc9e4a0e0bd6fdede6ab Mon Sep 17 00:00:00 2001 From: Ming-Yi Lai <ming-yi....@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 --- 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 | 138 +++++++++++++++++- clang/lib/CodeGen/CodeGenModule.cpp | 124 ++++++++++++++++ clang/lib/CodeGen/CodeGenModule.h | 17 +++ .../mangle-class-covariant-virtual-method.cpp | 39 +++++ .../mangle-class-destructor.cpp | 19 +++ ...angle-class-incovariant-virtual-method.cpp | 20 +++ .../zicfilp-func-sig/mangle-class-method.cpp | 21 +++ .../mangle-class-static-method.cpp | 23 +++ .../mangle-func-empty-param-list.c | 17 +++ .../mangle-func-empty-param-list.cpp | 17 +++ .../mangle-ignore-exception-spec.cpp | 17 +++ .../RISCV/zicfilp-func-sig/mangle-main.cpp | 41 ++++++ .../RISCV/zicfilp-func-sig/mangle-wchar-t.cpp | 14 ++ llvm/include/llvm/Support/RISCVISAUtils.h | 6 +- llvm/lib/Support/RISCVISAUtils.cpp | 22 ++- llvm/unittests/Support/CMakeLists.txt | 1 + llvm/unittests/Support/RISCVISAUtils.cpp | 23 +++ 20 files changed, 573 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 create mode 100644 llvm/unittests/Support/RISCVISAUtils.cpp diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 1fdc488a76507..2349f3eab3950 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1952,6 +1952,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>. /// @@ -1979,6 +1982,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 ca72dcfd4483d..579df6d6bfbb7 100644 --- a/clang/include/clang/AST/Mangle.h +++ b/clang/include/clang/AST/Mangle.h @@ -215,6 +215,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 b5417fcf20ddd..a3e1a09157265 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -6712,6 +6712,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 33a8728728574..b22bc5e50ee9b 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -41,6 +41,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); } @@ -134,6 +142,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)) @@ -386,8 +399,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; } @@ -434,6 +449,8 @@ class CXXNameMangler { NullOut = true; } + virtual ~CXXNameMangler() = default; + struct WithTemplateDepthOffset { unsigned Offset; }; CXXNameMangler(ItaniumMangleContextImpl &C, raw_ostream &Out, WithTemplateDepthOffset Offset) @@ -552,9 +569,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. @@ -565,12 +585,25 @@ 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); void mangleSMEAttrs(unsigned SMEAttrs); + +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); @@ -3111,7 +3144,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 @@ -3694,10 +3729,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) { unsigned SMEAttrs = T->getAArch64SMEAttributes(); if (SMEAttrs) @@ -3742,6 +3781,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). @@ -7233,6 +7276,86 @@ 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 { + const bool WasTopLevelAndCXXVirtualMethod = IsTopLevelAndCXXVirtualMethod; + IsTopLevelAndCXXVirtualMethod = false; // Not top-level anymore + + // 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(); + WasTopLevelAndCXXVirtualMethod && 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"; + } + 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 @@ -7571,6 +7694,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 16e010adbeb5f..cbe69fd91eee4 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -66,6 +66,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() {} @@ -914,6 +927,8 @@ void CodeGenModule::Release() { } if (LangOpts.Sanitize.has(SanitizerKind::KCFI)) finalizeKCFITypes(); + if (UseRISCVZicfilpFuncSigCFI) + finalizeRISCVZicfilpFuncSigLabels(); emitAtAvailableLinkGuard(); if (Context.getTargetInfo().getTriple().isWasm()) EmitMainVoidAlias(); @@ -2894,6 +2909,27 @@ void CodeGenModule::createFunctionTypeMetadataForIcall(const FunctionDecl *FD, F->addTypeMetadata(0, llvm::ConstantAsMetadata::get(CrossDsoTypeId)); } +std::string CodeGenModule::calcRISCVZicfilpFuncSig( + const FunctionType &FT, const bool IsMain, const bool IsCXXInstanceMethod, + const bool IsCXXVirtualMethod, const bool IsCXXDestructor) { + if (IsMain) + // All main functions use `int main(int, char**)` for label calculation + // according to psABI spec + return "FiiPPcE"; + + if (IsCXXDestructor) + // All destructors use `void (void*)` for label calculation according to the + // psABI spec + return "FvPvE"; + + std::string OutName; + llvm::raw_string_ostream Out(OutName); + MangleContext &MC = getCXXABI().getMangleContext(); + cast<ItaniumMangleContext>(MC).mangleForRISCVZicfilpFuncSigLabel( + FT, IsCXXInstanceMethod, IsCXXVirtualMethod, Out); + return OutName; +} + void CodeGenModule::setKCFIType(const FunctionDecl *FD, llvm::Function *F) { llvm::LLVMContext &Ctx = F->getContext(); llvm::MDBuilder MDB(Ctx); @@ -2902,6 +2938,35 @@ 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); + + bool IsCXXInstanceMethod = false; + bool IsCXXVirtualMethod = false; + if (const auto *const MD = dyn_cast<CXXMethodDecl>(&FD)) { + IsCXXInstanceMethod = MD->isInstance(); + IsCXXVirtualMethod = MD->isVirtual(); + } + bool IsMain; + if (const IdentifierInfo *const ID = FD.getIdentifier()) + IsMain = ID->isStr("main"); + else + IsMain = false; + const std::string FuncSig = calcRISCVZicfilpFuncSig( + *FD.getFunctionType(), IsMain, IsCXXInstanceMethod, IsCXXVirtualMethod, + isa<CXXDestructorDecl>(FD)); + F->setMetadata("riscv_lpad_func_sig", + llvm::MDNode::get(Ctx, MDB.createString(FuncSig))); + + const uint32_t Label = llvm::RISCVISAUtils::zicfilpFuncSigHash(FuncSig); + F->setMetadata( + "riscv_lpad_label", + llvm::MDNode::get( + Ctx, MDB.createConstant(llvm::ConstantInt::get(Int32Ty, Label)))); +} + 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 @@ -2942,6 +3007,62 @@ 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 FuncSigMDKindID = + F.getContext().getMDKindID("riscv_lpad_func_sig"); + const unsigned LabelMDKindID = F.getContext().getMDKindID("riscv_lpad_label"); + if (!F.hasAddressTaken() && F.hasLocalLinkage()) { + F.eraseMetadata(FuncSigMDKindID); + F.eraseMetadata(LabelMDKindID); + return; + } + + if (F.getMetadata(FuncSigMDKindID)) { + assert(F.getMetadata(LabelMDKindID) && + "riscv_lpad_label should had been set when setting " + "riscv_lpad_func_sig"); + return; + } + + if (GlobalDecl GD; lookupRepresentativeDecl(F.getName(), GD)) { + setRISCVZicfilpFuncSigLabel(*cast<FunctionDecl>(GD.getDecl()), &F); + return; + } + + // If F is a declaration, and we cannot find a non-zero lpad value for it + // (since the C/C++ declaration cannot be found), we should refrain from + // adding an explicit 0-valued riscv_lpad_label to avoid emitting a 0-valued + // referencing lpad value in the .riscv.lpadinfo section, since it's illegal + // to have a 0-valued referencing lpad value as they are assumed to be + // generated from C/C++ references with known signature (and thus a non-zero + // lpad value can be obtained) + if (F.isDeclaration()) + return; + + // If function has a body and requires an lpad insn, but does not specify the + // label in riscv_lpad_label metadata and we cannot generate it from C/C++ + // declaration (since the C/C++ declaration cannot be found), we use a + // permissive 0 as the label. This results in a 0-valued definitive lpad label + // in the .riscv.lpadinfo section, which can be overridden by other non-zero + // lpad labels if there's one at the static link stage. If there's no non-zero + // lpad label at the static link stage, the resulting executable would have to + // use this permissive 0 as the final lpad label, which is not ideal but is + // the best we can provide. + assert(!F.getMetadata(LabelMDKindID)); + llvm::LLVMContext &Ctx = F.getContext(); + llvm::MDBuilder MDB(Ctx); + F.setMetadata( + LabelMDKindID, + llvm::MDNode::get( + Ctx, MDB.createConstant(llvm::ConstantInt::get(Int32Ty, 0)))); +} + void CodeGenModule::SetFunctionAttributes(GlobalDecl GD, llvm::Function *F, bool IsIncompleteFunction, bool IsThunk) { @@ -3026,6 +3147,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 1db5c3bc4e4ef..1d0567d45b689 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1639,9 +1639,24 @@ 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 function signature based on RISC-V Zicfilp func-sig scheme + std::string calcRISCVZicfilpFuncSig(const FunctionType &FT, const bool IsMain, + const bool IsCXXInstanceMethod, + const bool IsCXXVirtualMethod, + const bool IsCXXDestructor); + /// 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, @@ -1810,6 +1825,8 @@ class CodeGenModule : public CodeGenTypeCache { return !getLangOpts().CPlusPlus; } + 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 0000000000000..f7260ac131b62 --- /dev/null +++ b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-class-covariant-virtual-method.cpp @@ -0,0 +1,39 @@ +// 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_func_sig}} [[SIG_MD_PTR:![0-9]+]] + // PTR-SAME: {{.* !riscv_lpad_label}} [[LB_MD_PTR:![0-9]+]] {{.*}}{ + // + virtual Class *virtualMethodWithCovariantPtrReturn() { return this; } + + // LREF-LABEL: define{{.*}} @_ZN5Class36virtualMethodWithCovariantLRefReturnEv + // LREF-SAME: ({{.*}}){{.* !riscv_lpad_func_sig}} [[SIG_MD_LREF:![0-9]+]] + // LREF-SAME: {{.* !riscv_lpad_label}} [[LB_MD_LREF:![0-9]+]] {{.*}}{ + // + virtual Class &virtualMethodWithCovariantLRefReturn() { return *this; } + + // RREF-LABEL: define{{.*}} @_ZN5Class36virtualMethodWithCovariantRRefReturnEv + // RREF-SAME: ({{.*}}){{.* !riscv_lpad_func_sig}} [[SIG_MD_RREF:![0-9]+]] + // RREF-SAME: {{.* !riscv_lpad_label}} [[LB_MD_RREF:![0-9]+]] {{.*}}{ + // + virtual Class &&virtualMethodWithCovariantRRefReturn() { + return static_cast<Class&&>(*this); + } +}; + +// PTR-DAG: [[SIG_MD_PTR]] = !{!"M1vFP1vvE"} +// PTR-DAG: [[LB_MD_PTR]] = !{i32 996333} +// LREF-DAG: [[SIG_MD_LREF]] = !{!"M1vFR1vvE"} +// LREF-DAG: [[LB_MD_LREF]] = !{i32 918198} +// RREF-DAG: [[SIG_MD_RREF]] = !{!"M1vFO1vvE"} +// RREF-DAG: [[LB_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 0000000000000..92d390506c509 --- /dev/null +++ b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-class-destructor.cpp @@ -0,0 +1,19 @@ +// 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_func_sig}} [[SIG_MD:![0-9]+]] + // CHECK-SAME: {{.* !riscv_lpad_label}} [[LB_MD:![0-9]+]] {{.*}}{ + // CHECK-DAG: [[SIG_MD]] = !{!"FvPvE"} + // CHECK-DAG: [[LB_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 0000000000000..33b9175208073 --- /dev/null +++ b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-class-incovariant-virtual-method.cpp @@ -0,0 +1,20 @@ +// 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_func_sig}} [[SIG_MD:![0-9]+]] + // CHECK-SAME: {{.* !riscv_lpad_label}} [[LB_MD:![0-9]+]] {{.*}}{ + // CHECK-DAG: [[SIG_MD]] = !{!"M1vFivE"} + // CHECK-DAG: [[LB_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 0000000000000..62e18ccba236d --- /dev/null +++ b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-class-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 + +class Class { +public: + // test - C++ member functions should use "pointer-to-member-type" mangling + // rule, with `<class type>` being `1v` instead of the real class + // (i.e. use a dummy class named `v`) + // CHECK-LABEL: define{{.*}} @_ZN5Class14instanceMethodEv({{.*}}) + // CHECK-SAME: {{.* !riscv_lpad_func_sig}} [[SIG_MD:![0-9]+]] + // CHECK-SAME: {{.* !riscv_lpad_label}} [[LB_MD:![0-9]+]] {{.*}}{ + // CHECK-DAG: [[SIG_MD]] = !{!"M1vFvvE"} + // CHECK-DAG: [[LB_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 0000000000000..c6cc44a460868 --- /dev/null +++ b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-class-static-method.cpp @@ -0,0 +1,23 @@ +// 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_func_sig}} [[SIG_MD:![0-9]+]] +// CHECK-SAME: {{.* !riscv_lpad_label}} [[LB_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_func_sig}} [[SIG_MD]] + // CHECK-SAME: {{.* !riscv_lpad_label}} [[LB_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 0000000000000..c2ece35e86b2e --- /dev/null +++ b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-func-empty-param-list.c @@ -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 + +// test - functions with an empty parameter list are treated as `void (*)(void)` +// CHECK-LABEL: define{{.*}} @funcWithEmptyParameterList() +// CHECK-SAME: {{.* !riscv_lpad_func_sig}} [[SIG_MD:![0-9]+]] +// CHECK-SAME: {{.* !riscv_lpad_label}} [[LB_MD:![0-9]+]] {{.*}}{ +// +void funcWithEmptyParameterList() {} +// CHECK-LABEL: define{{.*}} @funcWithVoidParameterList() +// CHECK-SAME: {{.* !riscv_lpad_func_sig}} [[SIG_MD]] +// CHECK-SAME: {{.* !riscv_lpad_label}} [[LB_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 0000000000000..80a2222549cce --- /dev/null +++ b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-func-empty-param-list.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 + +// test - functions with an empty parameter list are treated as `void (*)(void)` +// CHECK-LABEL: define{{.*}} @_Z26funcWithEmptyParameterListv() +// CHECK-SAME: {{.* !riscv_lpad_func_sig}} [[SIG_MD:![0-9]+]] +// CHECK-SAME: {{.* !riscv_lpad_label}} [[LB_MD:![0-9]+]] {{.*}}{ +// +void funcWithEmptyParameterList() {} +// CHECK-LABEL: define{{.*}} @_Z25funcWithVoidParameterListv() +// CHECK-SAME: {{.* !riscv_lpad_func_sig}} [[SIG_MD]] +// CHECK-SAME: {{.* !riscv_lpad_label}} [[LB_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 0000000000000..4de989eb31757 --- /dev/null +++ b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-ignore-exception-spec.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 -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_func_sig}} [[SIG_MD:![0-9]+]] +// CHECK-SAME: {{.* !riscv_lpad_label}} [[LB_MD:![0-9]+]] {{.*}}{ +/// +void funcThrow() { throw 0; } +// CHECK-LABEL: define{{.*}} @_Z12funcNoExceptv() +// CHECK-SAME: {{.* !riscv_lpad_func_sig}} [[SIG_MD]] +// CHECK-SAME: {{.* !riscv_lpad_label}} [[LB_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 0000000000000..724312807b2b5 --- /dev/null +++ b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-main.cpp @@ -0,0 +1,41 @@ +// 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_func_sig}} [[SIG_MD:![0-9]+]] +// CHECK-SAME: {{.* !riscv_lpad_label}} [[LB_MD:![0-9]+]] {{.*}}{ +// CHECK-DAG: [[SIG_MD]] = !{!"FiiPPcE"} +// CHECK-DAG: [[LB_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 0000000000000..4d19d482d6b5d --- /dev/null +++ b/clang/test/CodeGen/RISCV/zicfilp-func-sig/mangle-wchar-t.cpp @@ -0,0 +1,14 @@ +// 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_func_sig}} [[SIG_MD:![0-9]+]] +// CHECK-SAME: {{.* !riscv_lpad_label}} [[LB_MD:![0-9]+]] {{.*}}{ +// CHECK-DAG: [[SIG_MD]] = !{!"FviE"} +// CHECK-DAG: [[LB_MD]] = !{i32 374765} +// +void funcWithWCharT(wchar_t) {} diff --git a/llvm/include/llvm/Support/RISCVISAUtils.h b/llvm/include/llvm/Support/RISCVISAUtils.h index 165bb08d66431..7f0d5b8aac70a 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. // //===----------------------------------------------------------------------===// @@ -43,6 +43,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 d6b002e66e7ab..ff8bd101505eb 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; +} diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt index d048e871fd0fb..48a5fe424ddeb 100644 --- a/llvm/unittests/Support/CMakeLists.txt +++ b/llvm/unittests/Support/CMakeLists.txt @@ -78,6 +78,7 @@ add_llvm_unittest(SupportTests ReverseIterationTest.cpp ReplaceFileTest.cpp RISCVAttributeParserTest.cpp + RISCVISAUtils.cpp ScaledNumberTest.cpp ScopedPrinterTest.cpp SHA256.cpp diff --git a/llvm/unittests/Support/RISCVISAUtils.cpp b/llvm/unittests/Support/RISCVISAUtils.cpp new file mode 100644 index 0000000000000..6f5619ac67731 --- /dev/null +++ b/llvm/unittests/Support/RISCVISAUtils.cpp @@ -0,0 +1,23 @@ +//===- LLvm/unittest/Support/RISCVISAUtils.cpp - RISCVISAUtils tests ------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/RISCVISAUtils.h" +#include "gtest/gtest.h" + +using namespace llvm::RISCVISAUtils; + +TEST(ZicfilpFuncSigHash, testCommonCase) { + // A common representitive case is the signature of the main function + EXPECT_EQ(zicfilpFuncSigHash("FiiPPcE"), 853561U); +} + +// The lowest 20 bits of a MD5 hash should be discarded if they're all zeros +TEST(ZicfilpFuncSigHash, testDiscardAllZeroLabels) { + // as_number(md5('20412333')) = 0x7a13472ff22eb53e31f6a76027000000 + EXPECT_EQ(zicfilpFuncSigHash("20412333"), 0x60270U); +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits