//===-- 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 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::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); } static inline NSConstantNumberMapInfo getEmptyKey() { return NSConstantNumberMapInfo(); } 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, 16> Elements; uint64_t Opts; public: enum class Options : uint64_t { Sorted = 1 }; NSDictionaryBuilder( const ObjCDictionaryLiteral *E, ArrayRef> KeysAndObjects, const Options O = Options::Sorted) { Opts = static_cast(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 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(LK) || !isa(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(LK)->getString()->getString(); StringRef RKS = cast(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> & getElements() { return Elements; } Options getOptions() const { return static_cast(Opts); } uint64_t getNumElements() const { return Elements.size(); } }; } // namespace CGObjCMacConstantLiteralUtil } // namespace CodeGen } // namespace clang namespace llvm { using namespace clang::CodeGen::CGObjCMacConstantLiteralUtil; template <> struct DenseMapInfo { static NSConstantNumberMapInfo getEmptyKey() { return NSConstantNumberMapInfo::getEmptyKey(); } static unsigned getHashValue(const NSConstantNumberMapInfo &S) { return S.getHashValue(); } static bool isEqual(const NSConstantNumberMapInfo &LHS, const NSConstantNumberMapInfo &RHS) { return LHS == RHS; } }; } // namespace llvm #endif