[clang] [ObjC] Emit number, array, and dictionary literals as constants (PR #185130)
Akira Hatanaka via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 16 18:55:47 PDT 2026
================
@@ -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);
----------------
ahatanak wrote:
Indices are still needed here because we use the keys of `ObjCDictionaryLiteral` to sort the elements.
We could refactor to avoid indices, but the tradeoff isn't obvious to me.
https://github.com/llvm/llvm-project/pull/185130
More information about the cfe-commits
mailing list