================ @@ -0,0 +1,201 @@ +//===-- 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/DenseMap.h" +#include "llvm/ADT/DenseMapInfo.h" +#include <numeric> + +namespace clang { +namespace CodeGen { +namespace CGObjCMacConstantLiteralUtil { + +class NSConstantNumberMapInfo { + + enum class MapInfoType { + Empty, + Tombstone, + Int, + Float, + }; + + MapInfoType InfoType; + QualType QType; + llvm::APSInt Int; + llvm::APFloat Float; + + /// Default constructor that can create Empty or Tombstone info entries + explicit NSConstantNumberMapInfo(MapInfoType I = MapInfoType::Empty) + : InfoType(I), QType(QualType()), Int(), Float(0.0) {} + + bool isEmptyOrTombstone() const { + return InfoType == MapInfoType::Empty || InfoType == MapInfoType::Tombstone; + } + +public: + NSConstantNumberMapInfo(const QualType &QT, const llvm::APSInt &V) + : InfoType(MapInfoType::Int), QType(QT), Int(V), Float(0.0) {} + NSConstantNumberMapInfo(const QualType &QT, const llvm::APFloat &V) + : InfoType(MapInfoType::Float), QType(QT), Int(), Float(V) {} + + unsigned getHashValue() const { + assert(!isEmptyOrTombstone() && "Cannot hash empty or tombstone map info!"); + + unsigned QTypeHash = llvm::DenseMapInfo<QualType>::getHashValue( + llvm::DenseMapInfo<QualType>::getTombstoneKey()); + + 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(); + } + + static inline NSConstantNumberMapInfo getTombstoneKey() { + return NSConstantNumberMapInfo(MapInfoType::Tombstone); + } + + bool operator==(const NSConstantNumberMapInfo &RHS) const { + if (InfoType != RHS.InfoType || QType != RHS.QType) + return false; + + // Handle the empty and tombstone equality + if (isEmptyOrTombstone()) + 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<llvm::Constant *, 16> Keys; + SmallVector<llvm::Constant *, 16> Objects; + uint64_t Opts; + +public: + enum class Options : uint64_t { Sorted = 1 }; + + NSDictionaryBuilder(const ObjCDictionaryLiteral *E, + const ArrayRef<llvm::Constant *> &KYS, + const ArrayRef<llvm::Constant *> &OBS, + const Options O = Options::Sorted) { + assert((KYS.size() == OBS.size()) && + "NSConstantDictionary requires key / value pairs!" + "keys and objects not of equal size!"); + + Opts = static_cast<uint64_t>(O); + uint64_t const NumElements = KYS.size(); + + // Reserve the capacity for the sorted keys & values + Keys.reserve(NumElements); + Objects.reserve(NumElements); + + // Setup the element indicies 0 ..< NumElements + SmallVector<size_t, 16> ElementIndicies; + ElementIndicies.reserve(NumElements); + for (size_t i = 0; i < NumElements; i++) { + ElementIndicies.push_back(i); + } + 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 `Keys` and `Objects` + for (auto &Idx : ElementIndicies) { + Keys.push_back(KYS[Idx]); + Objects.push_back(OBS[Idx]); + } + } + + SmallVector<llvm::Constant *, 16> &getKeys() { return Keys; } + + SmallVector<llvm::Constant *, 16> &getObjects() { return Objects; } ---------------- ojhunt wrote:
While you do lose the separate key/value option, the actual usage is still fairly ergonomic as you can just do ```cpp for (auto [Key, Value] : getElements()) ``` Or similar. I also think these should return an ArrayRef just to prevent the perennial accidental copy behavior of refs https://github.com/llvm/llvm-project/pull/185130 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
