blob: f9eb54bec9aafa1cb84e71a592b9503082ede6d2 [file] [edit]
//===-- 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,
Tombstone,
Int,
Float,
};
MapInfoType InfoType;
CanQualType 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(), Int(), Float(0.0) {}
bool isEmptyOrTombstone() const {
return InfoType == MapInfoType::Empty || InfoType == MapInfoType::Tombstone;
}
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(!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<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 NSConstantNumberMapInfo getEmptyKey() {
return NSConstantNumberMapInfo::getEmptyKey();
}
static NSConstantNumberMapInfo getTombstoneKey() {
return NSConstantNumberMapInfo::getTombstoneKey();
}
static unsigned getHashValue(const NSConstantNumberMapInfo &S) {
return S.getHashValue();
}
static bool isEqual(const NSConstantNumberMapInfo &LHS,
const NSConstantNumberMapInfo &RHS) {
return LHS == RHS;
}
};
} // namespace llvm
#endif