| //===-- CodeGen/CGObjCMacConstantLiteralUtil.h - ----------------*- C++ -*-===// | |
| // | |
| // 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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This should be used for things that effect the ABI of | |
| // Obj-C constant initializer literals (`-fobjc-constant-literals`) to allow | |
| // future changes without breaking the ABI promises. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #ifndef LLVM_CLANG_LIB_CODEGEN_CGOBJCMACCONSTANTLITERALUTIL_H | |
| #define LLVM_CLANG_LIB_CODEGEN_CGOBJCMACCONSTANTLITERALUTIL_H | |
| #include "CGObjCRuntime.h" | |
| #include "clang/AST/ExprObjC.h" | |
| #include "clang/AST/Type.h" | |
| #include "llvm/ADT/APFloat.h" | |
| #include "llvm/ADT/APSInt.h" | |
| #include "llvm/ADT/DenseMapInfo.h" | |
| #include <numeric> | |
| namespace clang { | |
| namespace CodeGen { | |
| namespace CGObjCMacConstantLiteralUtil { | |
| class NSConstantNumberMapInfo { | |
| enum class MapInfoType { | |
| Empty, | |
| Int, | |
| Float, | |
| }; | |
| MapInfoType InfoType; | |
| CanQualType QType; | |
| llvm::APSInt Int; | |
| llvm::APFloat Float; | |
| /// Default constructor that can create an Empty info entry. | |
| explicit NSConstantNumberMapInfo(MapInfoType I = MapInfoType::Empty) | |
| : InfoType(I), QType(), Int(), Float(0.0) {} | |
| bool isEmpty() const { return InfoType == MapInfoType::Empty; } | |
| public: | |
| NSConstantNumberMapInfo(CanQualType QT, const llvm::APSInt &V) | |
| : InfoType(MapInfoType::Int), QType(QT), Int(V), Float(0.0) {} | |
| NSConstantNumberMapInfo(CanQualType QT, const llvm::APFloat &V) | |
| : InfoType(MapInfoType::Float), QType(QT), Int(), Float(V) {} | |
| unsigned getHashValue() const { | |
| assert(!isEmpty() && "Cannot hash empty map info!"); | |
| unsigned QTypeHash = llvm::DenseMapInfo<QualType>::getHashValue(QType); | |
| if (InfoType == MapInfoType::Int) | |
| return llvm::detail::combineHashValue((unsigned)Int.getZExtValue(), | |
| QTypeHash); | |
| assert(InfoType == MapInfoType::Float); | |
| return llvm::detail::combineHashValue( | |
| (unsigned)Float.bitcastToAPInt().getZExtValue(), QTypeHash); | |
| } | |
| bool operator==(const NSConstantNumberMapInfo &RHS) const { | |
| if (InfoType != RHS.InfoType || QType != RHS.QType) | |
| return false; | |
| // Handle the empty equality. | |
| if (isEmpty()) | |
| return true; | |
| if (InfoType == MapInfoType::Int) | |
| return llvm::APSInt::isSameValue(Int, RHS.Int); | |
| assert(InfoType == MapInfoType::Float); | |
| // handle -0, NaN, and infinities correctly | |
| return Float.bitwiseIsEqual(RHS.Float); | |
| } | |
| }; | |
| using std::iota; | |
| class NSDictionaryBuilder { | |
| SmallVector<std::pair<llvm::Constant *, llvm::Constant *>, 16> Elements; | |
| uint64_t Opts; | |
| public: | |
| enum class Options : uint64_t { Sorted = 1 }; | |
| NSDictionaryBuilder( | |
| const ObjCDictionaryLiteral *E, | |
| ArrayRef<std::pair<llvm::Constant *, llvm::Constant *>> KeysAndObjects, | |
| const Options O = Options::Sorted) { | |
| Opts = static_cast<uint64_t>(O); | |
| uint64_t const NumElements = KeysAndObjects.size(); | |
| // Reserve the capacity for the sorted keys & values | |
| Elements.reserve(NumElements); | |
| // Setup the element indicies 0 ..< NumElements | |
| SmallVector<size_t, 16> ElementIndicies(NumElements); | |
| std::iota(ElementIndicies.begin(), ElementIndicies.end(), 0); | |
| // Now perform the sorts and shift the indicies as needed | |
| std::stable_sort( | |
| ElementIndicies.begin(), ElementIndicies.end(), | |
| [E, O](size_t LI, size_t RI) { | |
| Expr *const LK = E->getKeyValueElement(LI).Key->IgnoreImpCasts(); | |
| Expr *const RK = E->getKeyValueElement(RI).Key->IgnoreImpCasts(); | |
| if (!isa<ObjCStringLiteral>(LK) || !isa<ObjCStringLiteral>(RK)) | |
| llvm_unreachable("Non-constant literals should not be sorted to " | |
| "maintain existing behavior"); | |
| // NOTE: Using the `StringLiteral->getString()` since it checks that | |
| // `chars` are 1 byte | |
| StringRef LKS = cast<ObjCStringLiteral>(LK)->getString()->getString(); | |
| StringRef RKS = cast<ObjCStringLiteral>(RK)->getString()->getString(); | |
| // Do an alpha sort to aid in with de-dupe at link time | |
| // `O(log n)` worst case lookup at runtime supported by `Foundation` | |
| if (O == Options::Sorted) | |
| return LKS < RKS; | |
| llvm_unreachable("Unexpected `NSDictionaryBuilder::Options given"); | |
| }); | |
| // Finally use the sorted indicies to insert into `Elements`. | |
| for (auto &Idx : ElementIndicies) { | |
| Elements.push_back(KeysAndObjects[Idx]); | |
| } | |
| } | |
| SmallVectorImpl<std::pair<llvm::Constant *, llvm::Constant *>> & | |
| getElements() { | |
| return Elements; | |
| } | |
| Options getOptions() const { return static_cast<Options>(Opts); } | |
| uint64_t getNumElements() const { return Elements.size(); } | |
| }; | |
| } // namespace CGObjCMacConstantLiteralUtil | |
| } // namespace CodeGen | |
| } // namespace clang | |
| namespace llvm { | |
| using namespace clang::CodeGen::CGObjCMacConstantLiteralUtil; | |
| template <> struct DenseMapInfo<NSConstantNumberMapInfo> { | |
| static unsigned getHashValue(const NSConstantNumberMapInfo &S) { | |
| return S.getHashValue(); | |
| } | |
| static bool isEqual(const NSConstantNumberMapInfo &LHS, | |
| const NSConstantNumberMapInfo &RHS) { | |
| return LHS == RHS; | |
| } | |
| }; | |
| } // namespace llvm | |
| #endif |