================ @@ -3140,6 +3140,269 @@ ASTContext::getPointerAuthVTablePointerDiscriminator(const CXXRecordDecl *RD) { return llvm::getPointerAuthStableSipHash(Str); } +/// Encode a function type for use in the discriminator of a function pointer +/// type. We can't use the itanium scheme for this since C has quite permissive +/// rules for type compatibility that we need to be compatible with. +/// +/// Formally, this function associates every function pointer type T with an +/// encoded string E(T). Let the equivalence relation T1 ~ T2 be defined as +/// E(T1) == E(T2). E(T) is part of the ABI of values of type T. C type +/// compatibility requires equivalent treatment under the ABI, so +/// CCompatible(T1, T2) must imply E(T1) == E(T2), that is, CCompatible must be +/// a subset of ~. Crucially, however, it must be a proper subset because +/// CCompatible is not an equivalence relation: for example, int[] is compatible +/// with both int[1] and int[2], but the latter are not compatible with each +/// other. Therefore this encoding function must be careful to only distinguish +/// types if there is no third type with which they are both required to be +/// compatible. +static void encodeTypeForFunctionPointerAuth(ASTContext &Ctx, raw_ostream &OS, + QualType QT) { + // FIXME: Consider address space qualifiers. + const Type *T = QT.getCanonicalType().getTypePtr(); + + // FIXME: Consider using the C++ type mangling when we encounter a construct + // that is incompatible with C. + + switch (T->getTypeClass()) { + case Type::Atomic: + return encodeTypeForFunctionPointerAuth( + Ctx, OS, cast<AtomicType>(T)->getValueType()); + + case Type::LValueReference: + OS << "R"; + encodeTypeForFunctionPointerAuth(Ctx, OS, + cast<ReferenceType>(T)->getPointeeType()); + return; + case Type::RValueReference: + OS << "O"; + encodeTypeForFunctionPointerAuth(Ctx, OS, + cast<ReferenceType>(T)->getPointeeType()); + return; + + case Type::Pointer: + // C11 6.7.6.1p2: + // For two pointer types to be compatible, both shall be identically + // qualified and both shall be pointers to compatible types. + // FIXME: we should also consider pointee types. + OS << "P"; + return; + + case Type::ObjCObjectPointer: + case Type::BlockPointer: + OS << "P"; + return; + + case Type::Complex: + OS << "C"; + return encodeTypeForFunctionPointerAuth( + Ctx, OS, cast<ComplexType>(T)->getElementType()); + + case Type::VariableArray: + case Type::ConstantArray: + case Type::IncompleteArray: + case Type::ArrayParameter: + // C11 6.7.6.2p6: + // For two array types to be compatible, both shall have compatible + // element types, and if both size specifiers are present, and are integer + // constant expressions, then both size specifiers shall have the same + // constant value [...] + // + // So since ElemType[N] has to be compatible ElemType[], we can't encode the + // width of the array. + OS << "A"; + return encodeTypeForFunctionPointerAuth( + Ctx, OS, cast<ArrayType>(T)->getElementType()); + + case Type::ObjCInterface: + case Type::ObjCObject: + OS << "<objc_object>"; + return; + + case Type::Enum: + // C11 6.7.2.2p4: + // Each enumerated type shall be compatible with char, a signed integer + // type, or an unsigned integer type. + // + // So we have to treat enum types as integers. + OS << "i"; + return; + + case Type::FunctionNoProto: + case Type::FunctionProto: { + // C11 6.7.6.3p15: + // For two function types to be compatible, both shall specify compatible + // return types. Moreover, the parameter type lists, if both are present, + // shall agree in the number of parameters and in the use of the ellipsis + // terminator; corresponding parameters shall have compatible types. + // + // That paragraph goes on to describe how unprototyped functions are to be + // handled, which we ignore here. Unprototyped function pointers are hashed + // as though they were prototyped nullary functions since thats probably + // what the user meant. This behavior is non-conforming. + // FIXME: If we add a "custom discriminator" function type attribute we + // should encode functions as their discriminators. + OS << "F"; + auto *FuncType = cast<FunctionType>(T); + encodeTypeForFunctionPointerAuth(Ctx, OS, FuncType->getReturnType()); + if (auto *FPT = dyn_cast<FunctionProtoType>(FuncType)) { + for (QualType Param : FPT->param_types()) { + Param = Ctx.getSignatureParameterType(Param); + encodeTypeForFunctionPointerAuth(Ctx, OS, Param); + } + if (FPT->isVariadic()) + OS << "z"; + } + OS << "E"; + return; + } + + case Type::MemberPointer: { + OS << "M"; + auto *MPT = T->getAs<MemberPointerType>(); + encodeTypeForFunctionPointerAuth(Ctx, OS, QualType(MPT->getClass(), 0)); + encodeTypeForFunctionPointerAuth(Ctx, OS, MPT->getPointeeType()); + return; + } + case Type::ExtVector: + case Type::Vector: + OS << "Dv" << Ctx.getTypeSizeInChars(T).getQuantity(); + break; + + // Don't bother discriminating based on these types. + case Type::Pipe: + case Type::BitInt: + case Type::ConstantMatrix: + OS << "?"; + return; + + case Type::Builtin: { + const BuiltinType *BTy = T->getAs<BuiltinType>(); + switch (BTy->getKind()) { +#define SIGNED_TYPE(Id, SingletonId) \ + case BuiltinType::Id: \ + OS << "i"; \ + return; +#define UNSIGNED_TYPE(Id, SingletonId) \ + case BuiltinType::Id: \ + OS << "i"; \ + return; +#define PLACEHOLDER_TYPE(Id, SingletonId) case BuiltinType::Id: +#define BUILTIN_TYPE(Id, SingletonId) +#include "clang/AST/BuiltinTypes.def" + llvm_unreachable("placeholder types should not appear here."); + + case BuiltinType::Half: + OS << "Dh"; + return; + case BuiltinType::Float: + OS << "f"; + return; + case BuiltinType::Double: + OS << "d"; + return; + case BuiltinType::LongDouble: + OS << "e"; + return; + case BuiltinType::Float16: + OS << "DF16_"; + return; + case BuiltinType::Float128: + OS << "g"; + return; + + case BuiltinType::Void: + OS << "v"; + return; + + case BuiltinType::ObjCId: + case BuiltinType::ObjCClass: + case BuiltinType::ObjCSel: + case BuiltinType::NullPtr: + OS << "P"; + return; + + // Don't bother discriminating based on OpenCL types. + case BuiltinType::OCLSampler: + case BuiltinType::OCLEvent: + case BuiltinType::OCLClkEvent: + case BuiltinType::OCLQueue: + case BuiltinType::OCLReserveID: + case BuiltinType::BFloat16: + case BuiltinType::VectorQuad: + case BuiltinType::VectorPair: + OS << "?"; + return; + + // Don't bother discriminating based on these seldom-used types. + case BuiltinType::Ibm128: + return; +#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ + case BuiltinType::Id: \ + return; +#include "clang/Basic/OpenCLImageTypes.def" +#define EXT_OPAQUE_TYPE(ExtType, Id, Ext) \ + case BuiltinType::Id: \ + return; +#include "clang/Basic/OpenCLExtensionTypes.def" +#define SVE_TYPE(Name, Id, SingletonId) \ + case BuiltinType::Id: \ + return; +#include "clang/Basic/AArch64SVEACLETypes.def" + case BuiltinType::Dependent: + llvm_unreachable("should never get here"); + case BuiltinType::AMDGPUBufferRsrc: + case BuiltinType::WasmExternRef: +#define RVV_TYPE(Name, Id, SingletonId) case BuiltinType::Id: +#include "clang/Basic/RISCVVTypes.def" + llvm_unreachable("not yet implemented"); + } + } + case Type::Record: { + RecordDecl *RD = T->getAs<RecordType>()->getDecl(); + IdentifierInfo *II = RD->getIdentifier(); + if (!II) + if (const TypedefNameDecl *Typedef = RD->getTypedefNameForAnonDecl()) ---------------- ahatanak wrote:
Note that this patch doesn't have the code to resign function pointers, which is needed for type conversions. That will be implemented in subsequent patches. https://github.com/llvm/llvm-project/pull/96992 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits