https://github.com/kumarak updated 
https://github.com/llvm/llvm-project/pull/204360

>From db53c6495578f44efde3d7cc7b0c4b730f8fc0f0 Mon Sep 17 00:00:00 2001
From: AkshayK <[email protected]>
Date: Tue, 16 Jun 2026 11:14:06 -0400
Subject: [PATCH 1/4] [CIR] Drive pointer and vptr width from a CIR-native
 data-layout entry

PointerType and VPtrType hardcoded size/alignment to 64/8, aborting record
layout on 32-bit-pointer targets such as nvptx/spirv32. Attach a CIR-native
cir.ptr data-layout entry at module setup and read the pointer width from it,
so the CIR type system needs no LLVM-dialect dependency; the entry is stripped
during CIR->LLVM lowering. 64-bit targets are unchanged.
---
 clang/lib/CIR/CodeGen/CIRGenerator.cpp        | 25 ++++++++-
 clang/lib/CIR/Dialect/IR/CIRTypes.cpp         | 55 +++++++++++++++++--
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 15 +++++
 .../test/CIR/CodeGen/pointer-width-32bit.cpp  | 44 +++++++++++++++
 4 files changed, 132 insertions(+), 7 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/pointer-width-32bit.cpp

diff --git a/clang/lib/CIR/CodeGen/CIRGenerator.cpp 
b/clang/lib/CIR/CodeGen/CIRGenerator.cpp
index d4fcbb6e42f3e..61efaebad9b82 100644
--- a/clang/lib/CIR/CodeGen/CIRGenerator.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenerator.cpp
@@ -21,6 +21,7 @@
 #include "clang/AST/DeclGroup.h"
 #include "clang/CIR/CIRGenerator.h"
 #include "clang/CIR/InitAllDialects.h"
+#include "clang/CIR/MissingFeatures.h"
 #include "llvm/IR/DataLayout.h"
 
 using namespace cir;
@@ -42,7 +43,29 @@ static void setMLIRDataLayout(mlir::ModuleOp &mod, const 
llvm::DataLayout &dl) {
   mlir::MLIRContext *mlirContext = mod.getContext();
   mlir::DataLayoutSpecInterface dlSpec =
       mlir::translateDataLayout(dl, mlirContext);
-  mod->setAttr(mlir::DLTIDialect::kDataLayoutAttrName, dlSpec);
+
+  // Add a CIR-native pointer data-layout entry so cir.ptr / cir.vptr size and
+  // alignment are driven by the data layout rather than hardcoded.
+  // The value stores {size-in-bits, abi-align-in-bits} keyed on cir.ptr.
+  //
+  // TODO(cir): Only the default address space is recorded and
+  // address-space-dependent pointer sizes are not modeled yet. Emit
+  // per-address-space entries.
+  assert(!cir::MissingFeatures::dataLayoutPtrHandlingBasedOnLangAS());
+  constexpr unsigned kBitsInByte = 8;
+  unsigned ptrSizeBits = dl.getPointerSizeInBits(/*AS=*/0);
+  unsigned ptrAlignBits =
+      dl.getPointerABIAlignment(/*AS=*/0).value() * kBitsInByte;
+  auto ptrKey = cir::PointerType::get(cir::VoidType::get(mlirContext));
+  auto ptrVal = mlir::DenseI32ArrayAttr::get(
+      mlirContext,
+      {static_cast<int32_t>(ptrSizeBits), static_cast<int32_t>(ptrAlignBits)});
+  llvm::SmallVector<mlir::DataLayoutEntryInterface> entries(
+      dlSpec.getEntries().begin(), dlSpec.getEntries().end());
+  entries.push_back(mlir::DataLayoutEntryAttr::get(ptrKey, ptrVal));
+
+  mod->setAttr(mlir::DLTIDialect::kDataLayoutAttrName,
+               mlir::DataLayoutSpecAttr::get(mlirContext, entries));
 }
 
 void CIRGenerator::Initialize(ASTContext &astContext) {
diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp 
b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
index 9c2a40e3681aa..d357cc623be3d 100644
--- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
@@ -580,20 +580,62 @@ void RecordType::removeABIConversionNamePrefix() {
 // Data Layout information for types
 
//===----------------------------------------------------------------------===//
 
+// A CIR-native pointer data-layout entry stores {size-in-bits,
+// abi-align-in-bits} as a dense i32 array keyed on a cir.ptr type (see
+// setMLIRDataLayout in CIRGenerator).
+namespace {
+constexpr static uint64_t kBitsInByte = 8;
+
+// Defaults used only when the module carries no cir.ptr data-layout entry
+// (e.g. CIR parsed from text without a data layout). These mirror the MLIR 
LLVM
+// dialect's pointer defaults.
+constexpr static uint64_t kDefaultPointerSizeBits = 64;
+constexpr static uint64_t kDefaultPointerAlignment = 8;
+
+enum class CIRPtrDLPos { Size = 0, AbiAlign = 1 };
+
+// Returns the requested field of the cir.ptr data-layout entry.
+std::optional<uint64_t> getPointerSpecValue(mlir::DataLayoutEntryListRef 
params,
+                                            CIRPtrDLPos pos) {
+  for (mlir::DataLayoutEntryInterface entry : params) {
+    if (!entry.isTypeEntry())
+      continue;
+    auto spec = mlir::dyn_cast<mlir::DenseI32ArrayAttr>(entry.getValue());
+    assert(spec && spec.size() == 2 &&
+           "malformed cir.ptr data layout entry: expected a pair of i32 "
+           "{size-in-bits, abi-align-in-bits}");
+    return static_cast<uint64_t>(spec[static_cast<int>(pos)]);
+  }
+  return std::nullopt;
+}
+} // namespace
+
 llvm::TypeSize
 PointerType::getTypeSizeInBits(const ::mlir::DataLayout &dataLayout,
                                ::mlir::DataLayoutEntryListRef params) const {
+  // The pointer width comes from the CIR-native data-layout entry keyed on
+  // cir.ptr, which records the width for the default address space; fall back
+  // to 64 bits if the module carries no such entry.
   // FIXME: improve this in face of address spaces
   assert(!cir::MissingFeatures::dataLayoutPtrHandlingBasedOnLangAS());
-  return llvm::TypeSize::getFixed(64);
+  if (std::optional<uint64_t> size =
+          getPointerSpecValue(params, CIRPtrDLPos::Size))
+    return llvm::TypeSize::getFixed(*size);
+  return llvm::TypeSize::getFixed(kDefaultPointerSizeBits);
 }
 
 uint64_t
 PointerType::getABIAlignment(const ::mlir::DataLayout &dataLayout,
                              ::mlir::DataLayoutEntryListRef params) const {
+  // As with the size, the alignment is taken from the default-address-space
+  // cir.ptr data-layout entry. Address-space-dependent alignments are not yet
+  // modeled.
   // FIXME: improve this in face of address spaces
   assert(!cir::MissingFeatures::dataLayoutPtrHandlingBasedOnLangAS());
-  return 8;
+  if (std::optional<uint64_t> align =
+          getPointerSpecValue(params, CIRPtrDLPos::AbiAlign))
+    return *align / kBitsInByte;
+  return kDefaultPointerAlignment;
 }
 
 llvm::TypeSize
@@ -1112,14 +1154,15 @@ DataMemberType::getABIAlignment(const 
::mlir::DataLayout &dataLayout,
 llvm::TypeSize
 VPtrType::getTypeSizeInBits(const mlir::DataLayout &dataLayout,
                             mlir::DataLayoutEntryListRef params) const {
-  // FIXME: consider size differences under different ABIs
-  return llvm::TypeSize::getFixed(64);
+  // A vtable pointer is an ordinary data pointer; size it as a cir.ptr.
+  return dataLayout.getTypeSizeInBits(
+      cir::PointerType::get(cir::VoidType::get(getContext())));
 }
 
 uint64_t VPtrType::getABIAlignment(const mlir::DataLayout &dataLayout,
                                    mlir::DataLayoutEntryListRef params) const {
-  // FIXME: consider alignment differences under different ABIs
-  return 8;
+  return dataLayout.getTypeABIAlignment(
+      cir::PointerType::get(cir::VoidType::get(getContext())));
 }
 
 
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 0e412090a16da..db018d47e10e3 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -3902,6 +3902,21 @@ void ConvertCIRToLLVMPass::runOnOperation() {
   if (failed(applyPartialConversion(ops, target, std::move(patterns))))
     signalPassFailure();
 
+  // Drop the cir.ptr-keyed data-layout entries: they drove pointer-width
+  // queries up to this point, but the LLVM IR exporter rejects CIR types.
+  if (auto dlSpec = mlir::dyn_cast_or_null<mlir::DataLayoutSpecAttr>(
+          module->getAttr(mlir::DLTIDialect::kDataLayoutAttrName))) {
+    llvm::SmallVector<mlir::DataLayoutEntryInterface> kept;
+    for (mlir::DataLayoutEntryInterface entry : dlSpec.getEntries()) {
+      if (entry.isTypeEntry() &&
+          mlir::isa<cir::PointerType>(mlir::cast<mlir::Type>(entry.getKey())))
+        continue;
+      kept.push_back(entry);
+    }
+    module->setAttr(mlir::DLTIDialect::kDataLayoutAttrName,
+                    mlir::DataLayoutSpecAttr::get(module.getContext(), kept));
+  }
+
   // Emit the llvm.global_ctors array.
   buildCtorDtorList(module, cir::CIRDialect::getGlobalCtorsAttrName(),
                     "llvm.global_ctors", [](mlir::Attribute attr) {
diff --git a/clang/test/CIR/CodeGen/pointer-width-32bit.cpp 
b/clang/test/CIR/CodeGen/pointer-width-32bit.cpp
new file mode 100644
index 0000000000000..15f3ec92d7e16
--- /dev/null
+++ b/clang/test/CIR/CodeGen/pointer-width-32bit.cpp
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 -std=c++20 -triple nvptx-nvidia-cuda -fclangir -emit-cir %s 
-o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -std=c++20 -triple nvptx-nvidia-cuda -fclangir -emit-llvm 
%s -o %t-cir.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -std=c++20 -triple nvptx-nvidia-cuda -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+// On a target with 32-bit pointers (e.g. nvptx) both a data pointer (!cir.ptr)
+// and the vtable pointer (!cir.vptr) are 4 bytes wide. The pointer width is
+// carried by a CIR-native data-layout entry keyed on cir.ptr, so the field
+// following a pointer lands at the AST-mandated offset. Sizing pointers as a
+// hardcoded 64 bits previously tripped the record layout builder 
(insertPadding:
+// assertion `offset >= size`) on every record containing a pointer.
+
+struct S {
+  int *p;
+  int x;
+};
+
+S s;
+
+class A {
+public:
+  virtual void f();
+  int x;
+};
+
+void A::f() {}
+
+// The module carries a CIR-native pointer data-layout entry ({size, abi-align}
+// in bits) that drives both cir.ptr and cir.vptr widths. The 4-byte pointer is
+// immediately followed by 'x' at offset 4 with no padding, and each record is
+// 4-byte aligned.
+// CIR-DAG: !rec_S = !cir.struct<"S" {!cir.ptr<!s32i>, !s32i}>
+// CIR-DAG: !rec_A = !cir.struct<class "A" {!cir.vptr, !s32i}>
+// CIR-DAG: !cir.ptr<!cir.void> = array<i32: 32, 32>
+// CIR: cir.global external @s = #cir.zero : !rec_S {alignment = 4 : i64}
+// CIR: cir.global{{.*}}@_ZTV1A = #cir.vtable<{{.*}}{alignment = 4 : i64}
+
+// LLVM: @s = global %struct.S zeroinitializer, align 4
+// LLVM: @_ZTV1A = global { [3 x ptr] } {{.*}}, align 4
+
+// OGCG: @s = global %struct.S zeroinitializer, align 4
+// OGCG: @_ZTV1A = {{.*}}constant { [3 x ptr] } {{.*}}, align 4

>From c5e162eec6215d36b8c854a37601914293347a19 Mon Sep 17 00:00:00 2001
From: AkshayK <[email protected]>
Date: Mon, 29 Jun 2026 17:35:07 -0400
Subject: [PATCH 2/4] [CIR] Use a CIR-native #cir.ptr_spec for the pointer
 data-layout entry

Use CIR-owned #cir.ptr_spec attribute as the value of the cir.ptr
data-layout entry.

Expose the spec construction as cir::setMLIRDataLayout, shared between
CIRGen and cir-translate: the latter previously derived the data layout
from the target triple without the cir.ptr entry, so pointer-width
queries silently fell back to 64 bits (e.g. cir.ptr_diff on T** divided
by 8 instead of 4 on nvptx).
---
 clang/include/clang/CIR/CIRDataLayoutSpec.h   |  34 ++++++
 .../include/clang/CIR/Dialect/IR/CIRAttrs.td  |  46 +++++++
 .../include/clang/CIR/Dialect/IR/CIRTypes.td  |   4 +-
 clang/lib/CIR/CodeGen/CIRDataLayoutSpec.cpp   |  49 ++++++++
 clang/lib/CIR/CodeGen/CIRGenerator.cpp        |  33 +----
 clang/lib/CIR/CodeGen/CMakeLists.txt          |   1 +
 clang/lib/CIR/Dialect/IR/CIRAttrs.cpp         |  22 ++++
 clang/lib/CIR/Dialect/IR/CIRTypes.cpp         | 113 ++++++++++++------
 .../test/CIR/CodeGen/pointer-width-32bit.cpp  |  14 +--
 clang/test/CIR/IR/pointer-data-layout.cir     |  58 +++++++++
 .../CIR/Tools/pointer-width-from-triple.cir   |  22 ++++
 clang/tools/cir-translate/cir-translate.cpp   |   7 +-
 12 files changed, 324 insertions(+), 79 deletions(-)
 create mode 100644 clang/include/clang/CIR/CIRDataLayoutSpec.h
 create mode 100644 clang/lib/CIR/CodeGen/CIRDataLayoutSpec.cpp
 create mode 100644 clang/test/CIR/IR/pointer-data-layout.cir
 create mode 100644 clang/test/CIR/Tools/pointer-width-from-triple.cir

diff --git a/clang/include/clang/CIR/CIRDataLayoutSpec.h 
b/clang/include/clang/CIR/CIRDataLayoutSpec.h
new file mode 100644
index 0000000000000..c270f6283afe4
--- /dev/null
+++ b/clang/include/clang/CIR/CIRDataLayoutSpec.h
@@ -0,0 +1,34 @@
+//===-- CIRDataLayoutSpec.h - DLTI data layout for CIR modules --*- 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 file declares a helper that attaches the DLTI data-layout spec,
+// including the CIR-native pointer entry, to a CIR module.
+//
+//===----------------------------------------------------------------------===//
+#ifndef CLANG_CIR_CIRDATALAYOUTSPEC_H
+#define CLANG_CIR_CIRDATALAYOUTSPEC_H
+
+namespace llvm {
+class DataLayout;
+} // namespace llvm
+
+namespace mlir {
+class ModuleOp;
+} // namespace mlir
+
+namespace cir {
+
+/// Translate \p dl into a DLTI data-layout spec and attach it to \p mod.
+/// On top of the plain mlir::translateDataLayout entries this adds a
+/// #cir.ptr_spec entry keyed on !cir.ptr, which CIR pointer types read for
+/// their size and alignment; without it pointer widths default to 64 bits.
+void setMLIRDataLayout(mlir::ModuleOp mod, const llvm::DataLayout &dl);
+
+} // namespace cir
+
+#endif // CLANG_CIR_CIRDATALAYOUTSPEC_H
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td 
b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index ec99899355b17..a886a74033d2e 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -1048,6 +1048,52 @@ def CIR_TargetAddressSpaceAttr : CIR_Attr< 
"TargetAddressSpace",
   let canHaveIllegalCXXABIType = 0;
 }
 
+//===----------------------------------------------------------------------===//
+// PtrSpecAttr
+//===----------------------------------------------------------------------===//
+
+def CIR_PtrSpecAttr : CIR_Attr<"PtrSpec", "ptr_spec"> {
+  let summary = "!cir.ptr data layout spec";
+  let description = [{
+    Pointer data layout for `!cir.ptr`: `size`, `abi` and `preferred` are
+    required bitwidths; `index` is the optional bitwidth for index
+    computations, defaulting to `size`. All present values must be divisible
+    by 8, with `preferred` >= `abi`.
+
+    This is the CIR-native analogue of the ptr dialect's `#ptr.spec`; a
+    native attribute keeps CIR's data-layout queries free of any ptr-dialect
+    dependency.
+
+    Used as the value of the `!cir.ptr<!cir.void>`-keyed data-layout entry:
+
+    ```mlir
+    #dlti.dl_spec<!cir.ptr<!cir.void> =
+        #cir.ptr_spec<size = 64, abi = 64, preferred = 64>>
+    ```
+  }];
+  let parameters = (ins
+    "uint32_t":$size,
+    "uint32_t":$abi,
+    "uint32_t":$preferred,
+    DefaultValuedParameter<"uint32_t", "kOptionalSpecValue">:$index
+  );
+  let skipDefaultBuilders = 1;
+  let builders = [
+    AttrBuilder<(ins "uint32_t":$size, "uint32_t":$abi, "uint32_t":$preferred,
+                     CArg<"uint32_t", "kOptionalSpecValue">:$index), [{
+      return $_get($_ctxt, size, abi, preferred, index);
+    }]>
+  ];
+  let assemblyFormat = "`<` struct(params) `>`";
+  let extraClassDeclaration = [{
+    /// Sentinel value marking the optional `index` parameter as unset.
+    static constexpr uint32_t kOptionalSpecValue =
+        std::numeric_limits<uint32_t>::max();
+  }];
+  let genVerifyDecl = 1;
+  let canHaveIllegalCXXABIType = 0;
+}
+
 
//===----------------------------------------------------------------------===//
 // ConstComplexAttr
 
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td 
b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
index 9ed151392dc30..da7ab09b68f59 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
@@ -239,7 +239,9 @@ def CIR_ComplexType : CIR_Type<"Complex", "complex", [
 
//===----------------------------------------------------------------------===//
 
 def CIR_PointerType : CIR_Type<"Pointer", "ptr", [
-    DeclareTypeInterfaceMethods<DataLayoutTypeInterface>,
+    DeclareTypeInterfaceMethods<DataLayoutTypeInterface,
+        ["getPreferredAlignment", "getIndexBitwidth", "areCompatible",
+         "verifyEntries"]>,
     DeclareTypeInterfaceMethods<CIR_SizedTypeInterface>
 ]> {
   let summary = "CIR pointer type";
diff --git a/clang/lib/CIR/CodeGen/CIRDataLayoutSpec.cpp 
b/clang/lib/CIR/CodeGen/CIRDataLayoutSpec.cpp
new file mode 100644
index 0000000000000..a25fb46da5e8e
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/CIRDataLayoutSpec.cpp
@@ -0,0 +1,49 @@
+//===--- CIRDataLayoutSpec.cpp - DLTI data layout for CIR modules 
---------===//
+//
+// 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 file attaches the DLTI data-layout spec, including the CIR-native
+// pointer entry, to a CIR module.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/CIR/CIRDataLayoutSpec.h"
+
+#include "mlir/Dialect/DLTI/DLTI.h"
+#include "mlir/IR/BuiltinOps.h"
+#include "mlir/Target/LLVMIR/Import.h"
+
+#include "clang/CIR/Dialect/IR/CIRAttrs.h"
+#include "clang/CIR/Dialect/IR/CIRTypes.h"
+#include "clang/CIR/MissingFeatures.h"
+#include "llvm/IR/DataLayout.h"
+
+void cir::setMLIRDataLayout(mlir::ModuleOp mod, const llvm::DataLayout &dl) {
+  mlir::MLIRContext *mlirContext = mod.getContext();
+  mlir::DataLayoutSpecInterface dlSpec =
+      mlir::translateDataLayout(dl, mlirContext);
+
+  // Append the !cir.ptr-keyed #cir.ptr_spec entry.
+  // TODO(cir): only the default address space is recorded.
+  assert(!cir::MissingFeatures::dataLayoutPtrHandlingBasedOnLangAS());
+  constexpr unsigned kBitsInByte = 8;
+  unsigned ptrSizeBits = dl.getPointerSizeInBits(/*AS=*/0);
+  unsigned ptrAbiBits =
+      dl.getPointerABIAlignment(/*AS=*/0).value() * kBitsInByte;
+  unsigned ptrPrefBits =
+      dl.getPointerPrefAlignment(/*AS=*/0).value() * kBitsInByte;
+  unsigned ptrIndexBits = dl.getIndexSizeInBits(/*AS=*/0);
+  auto ptrKey = cir::PointerType::get(cir::VoidType::get(mlirContext));
+  auto ptrSpec = cir::PtrSpecAttr::get(mlirContext, ptrSizeBits, ptrAbiBits,
+                                       ptrPrefBits, ptrIndexBits);
+  llvm::SmallVector<mlir::DataLayoutEntryInterface> entries(
+      dlSpec.getEntries().begin(), dlSpec.getEntries().end());
+  entries.push_back(mlir::DataLayoutEntryAttr::get(ptrKey, ptrSpec));
+
+  mod->setAttr(mlir::DLTIDialect::kDataLayoutAttrName,
+               mlir::DataLayoutSpecAttr::get(mlirContext, entries));
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenerator.cpp 
b/clang/lib/CIR/CodeGen/CIRGenerator.cpp
index 61efaebad9b82..941322cfdc2b4 100644
--- a/clang/lib/CIR/CodeGen/CIRGenerator.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenerator.cpp
@@ -16,9 +16,9 @@
 #include "mlir/Dialect/OpenACC/OpenACC.h"
 #include "mlir/Dialect/OpenMP/OpenMPDialect.h"
 #include "mlir/IR/MLIRContext.h"
-#include "mlir/Target/LLVMIR/Import.h"
 
 #include "clang/AST/DeclGroup.h"
+#include "clang/CIR/CIRDataLayoutSpec.h"
 #include "clang/CIR/CIRGenerator.h"
 #include "clang/CIR/InitAllDialects.h"
 #include "clang/CIR/MissingFeatures.h"
@@ -39,35 +39,6 @@ CIRGenerator::~CIRGenerator() {
   assert(deferredInlineMemberFuncDefs.empty() || diags.hasErrorOccurred());
 }
 
-static void setMLIRDataLayout(mlir::ModuleOp &mod, const llvm::DataLayout &dl) 
{
-  mlir::MLIRContext *mlirContext = mod.getContext();
-  mlir::DataLayoutSpecInterface dlSpec =
-      mlir::translateDataLayout(dl, mlirContext);
-
-  // Add a CIR-native pointer data-layout entry so cir.ptr / cir.vptr size and
-  // alignment are driven by the data layout rather than hardcoded.
-  // The value stores {size-in-bits, abi-align-in-bits} keyed on cir.ptr.
-  //
-  // TODO(cir): Only the default address space is recorded and
-  // address-space-dependent pointer sizes are not modeled yet. Emit
-  // per-address-space entries.
-  assert(!cir::MissingFeatures::dataLayoutPtrHandlingBasedOnLangAS());
-  constexpr unsigned kBitsInByte = 8;
-  unsigned ptrSizeBits = dl.getPointerSizeInBits(/*AS=*/0);
-  unsigned ptrAlignBits =
-      dl.getPointerABIAlignment(/*AS=*/0).value() * kBitsInByte;
-  auto ptrKey = cir::PointerType::get(cir::VoidType::get(mlirContext));
-  auto ptrVal = mlir::DenseI32ArrayAttr::get(
-      mlirContext,
-      {static_cast<int32_t>(ptrSizeBits), static_cast<int32_t>(ptrAlignBits)});
-  llvm::SmallVector<mlir::DataLayoutEntryInterface> entries(
-      dlSpec.getEntries().begin(), dlSpec.getEntries().end());
-  entries.push_back(mlir::DataLayoutEntryAttr::get(ptrKey, ptrVal));
-
-  mod->setAttr(mlir::DLTIDialect::kDataLayoutAttrName,
-               mlir::DataLayoutSpecAttr::get(mlirContext, entries));
-}
-
 void CIRGenerator::Initialize(ASTContext &astContext) {
   using namespace llvm;
 
@@ -84,7 +55,7 @@ void CIRGenerator::Initialize(ASTContext &astContext) {
   mlir::ModuleOp mod = cgm->getModule();
   llvm::DataLayout layout =
       llvm::DataLayout(astContext.getTargetInfo().getDataLayoutString());
-  setMLIRDataLayout(mod, layout);
+  cir::setMLIRDataLayout(mod, layout);
 }
 
 bool CIRGenerator::verifyModule() const { return cgm->verifyModule(); }
diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt 
b/clang/lib/CIR/CodeGen/CMakeLists.txt
index 79d0a49570b3e..4c8718e3eceb3 100644
--- a/clang/lib/CIR/CodeGen/CMakeLists.txt
+++ b/clang/lib/CIR/CodeGen/CMakeLists.txt
@@ -7,6 +7,7 @@ set(
 get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
 
 add_clang_library(clangCIR
+  CIRDataLayoutSpec.cpp
   CIRGenerator.cpp
   CIRGenAsm.cpp
   CIRGenAtomic.cpp
diff --git a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp 
b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
index 0678bcd882260..264e836718c81 100644
--- a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
@@ -172,6 +172,28 @@ bool TargetAddressSpaceAttr::isValidPtrIntCast(
   llvm_unreachable("isValidPtrIntCast for TargetAddressSpaceAttr NYI");
 }
 
+//===----------------------------------------------------------------------===//
+// PtrSpecAttr definitions
+//===----------------------------------------------------------------------===//
+
+LogicalResult PtrSpecAttr::verify(function_ref<InFlightDiagnostic()> emitError,
+                                  uint32_t size, uint32_t abi,
+                                  uint32_t preferred, uint32_t index) {
+  constexpr unsigned kBitsInByte = 8;
+  if (size % kBitsInByte != 0)
+    return emitError() << "size entry must be divisible by 8";
+  if (abi % kBitsInByte != 0)
+    return emitError() << "abi entry must be divisible by 8";
+  if (preferred % kBitsInByte != 0)
+    return emitError() << "preferred entry must be divisible by 8";
+  if (index != kOptionalSpecValue && index % kBitsInByte != 0)
+    return emitError() << "index entry must be divisible by 8";
+  if (abi > preferred)
+    return emitError() << "preferred alignment is expected to be at least "
+                          "as large as ABI alignment";
+  return success();
+}
+
 
//===----------------------------------------------------------------------===//
 // General CIR parsing / printing
 
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp 
b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
index d357cc623be3d..e9f52ed5b212d 100644
--- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
@@ -580,62 +580,105 @@ void RecordType::removeABIConversionNamePrefix() {
 // Data Layout information for types
 
//===----------------------------------------------------------------------===//
 
-// A CIR-native pointer data-layout entry stores {size-in-bits,
-// abi-align-in-bits} as a dense i32 array keyed on a cir.ptr type (see
-// setMLIRDataLayout in CIRGenerator).
+// The cir.ptr data-layout entry holds a #cir.ptr_spec attribute (see
+// cir::setMLIRDataLayout).
 namespace {
 constexpr static uint64_t kBitsInByte = 8;
 
-// Defaults used only when the module carries no cir.ptr data-layout entry
-// (e.g. CIR parsed from text without a data layout). These mirror the MLIR 
LLVM
-// dialect's pointer defaults.
+// Defaults used when the module carries no cir.ptr data-layout entry.
 constexpr static uint64_t kDefaultPointerSizeBits = 64;
 constexpr static uint64_t kDefaultPointerAlignment = 8;
 
-enum class CIRPtrDLPos { Size = 0, AbiAlign = 1 };
-
-// Returns the requested field of the cir.ptr data-layout entry.
-std::optional<uint64_t> getPointerSpecValue(mlir::DataLayoutEntryListRef 
params,
-                                            CIRPtrDLPos pos) {
+/// Returns the default-address-space #cir.ptr_spec entry, or a synthesized
+/// 64-bit default when there is none. Per-AS entries are not modeled yet.
+cir::PtrSpecAttr getPointerSpec(mlir::DataLayoutEntryListRef params,
+                                cir::PointerType type) {
+  // FIXME: improve this in face of address spaces
+  assert(!cir::MissingFeatures::dataLayoutPtrHandlingBasedOnLangAS());
   for (mlir::DataLayoutEntryInterface entry : params) {
     if (!entry.isTypeEntry())
       continue;
-    auto spec = mlir::dyn_cast<mlir::DenseI32ArrayAttr>(entry.getValue());
-    assert(spec && spec.size() == 2 &&
-           "malformed cir.ptr data layout entry: expected a pair of i32 "
-           "{size-in-bits, abi-align-in-bits}");
-    return static_cast<uint64_t>(spec[static_cast<int>(pos)]);
+    auto key =
+        mlir::cast<cir::PointerType>(mlir::cast<mlir::Type>(entry.getKey()));
+    if (key.getAddrSpace())
+      continue;
+    if (auto spec = mlir::dyn_cast<cir::PtrSpecAttr>(entry.getValue()))
+      return spec;
   }
-  return std::nullopt;
+  return cir::PtrSpecAttr::get(type.getContext(), kDefaultPointerSizeBits,
+                               kDefaultPointerAlignment * kBitsInByte,
+                               kDefaultPointerAlignment * kBitsInByte,
+                               kDefaultPointerSizeBits);
 }
 } // namespace
 
 llvm::TypeSize
 PointerType::getTypeSizeInBits(const ::mlir::DataLayout &dataLayout,
                                ::mlir::DataLayoutEntryListRef params) const {
-  // The pointer width comes from the CIR-native data-layout entry keyed on
-  // cir.ptr, which records the width for the default address space; fall back
-  // to 64 bits if the module carries no such entry.
-  // FIXME: improve this in face of address spaces
-  assert(!cir::MissingFeatures::dataLayoutPtrHandlingBasedOnLangAS());
-  if (std::optional<uint64_t> size =
-          getPointerSpecValue(params, CIRPtrDLPos::Size))
-    return llvm::TypeSize::getFixed(*size);
-  return llvm::TypeSize::getFixed(kDefaultPointerSizeBits);
+  return llvm::TypeSize::getFixed(getPointerSpec(params, *this).getSize());
 }
 
 uint64_t
 PointerType::getABIAlignment(const ::mlir::DataLayout &dataLayout,
                              ::mlir::DataLayoutEntryListRef params) const {
-  // As with the size, the alignment is taken from the default-address-space
-  // cir.ptr data-layout entry. Address-space-dependent alignments are not yet
-  // modeled.
-  // FIXME: improve this in face of address spaces
-  assert(!cir::MissingFeatures::dataLayoutPtrHandlingBasedOnLangAS());
-  if (std::optional<uint64_t> align =
-          getPointerSpecValue(params, CIRPtrDLPos::AbiAlign))
-    return *align / kBitsInByte;
-  return kDefaultPointerAlignment;
+  return getPointerSpec(params, *this).getAbi() / kBitsInByte;
+}
+
+uint64_t PointerType::getPreferredAlignment(
+    const ::mlir::DataLayout &dataLayout,
+    ::mlir::DataLayoutEntryListRef params) const {
+  return getPointerSpec(params, *this).getPreferred() / kBitsInByte;
+}
+
+std::optional<uint64_t>
+PointerType::getIndexBitwidth(const ::mlir::DataLayout &dataLayout,
+                              ::mlir::DataLayoutEntryListRef params) const {
+  cir::PtrSpecAttr spec = getPointerSpec(params, *this);
+  if (spec.getIndex() == cir::PtrSpecAttr::kOptionalSpecValue)
+    return spec.getSize();
+  return spec.getIndex();
+}
+
+llvm::LogicalResult
+PointerType::verifyEntries(mlir::DataLayoutEntryListRef entries,
+                           mlir::Location loc) const {
+  for (mlir::DataLayoutEntryInterface entry : entries) {
+    if (!entry.isTypeEntry())
+      continue;
+    auto key = mlir::cast<PointerType>(mlir::cast<mlir::Type>(entry.getKey()));
+    if (!mlir::isa<cir::PtrSpecAttr>(entry.getValue()))
+      return mlir::emitError(loc) << "expected layout attribute for " << key
+                                  << " to be a #cir.ptr_spec attribute";
+    if (!mlir::isa<cir::VoidType>(key.getPointee()))
+      return mlir::emitError(loc) << "expected !cir.ptr data layout entry for "
+                                  << key << " to use !cir.void as pointee";
+    // Per-address-space pointer layouts are not supported yet.
+    if (key.getAddrSpace())
+      return mlir::emitError(loc)
+             << "!cir.ptr data layout entries are currently limited to the "
+                "default address space";
+  }
+  return mlir::success();
+}
+
+bool PointerType::areCompatible(
+    mlir::DataLayoutEntryListRef oldLayout,
+    mlir::DataLayoutEntryListRef newLayout, mlir::DataLayoutSpecInterface,
+    const mlir::DataLayoutIdentifiedEntryMap &) const {
+  // A nested spec may only override with the same size and a compatible ABI
+  // alignment. TODO(cir): match by address space once per-AS specs exist.
+  cir::PtrSpecAttr oldSpec = getPointerSpec(oldLayout, *this);
+  uint64_t size = oldSpec.getSize();
+  uint64_t abi = oldSpec.getAbi();
+  for (mlir::DataLayoutEntryInterface newEntry : newLayout) {
+    if (!newEntry.isTypeEntry())
+      continue;
+    auto newSpec = mlir::cast<cir::PtrSpecAttr>(newEntry.getValue());
+    if (size != newSpec.getSize() || abi < newSpec.getAbi() ||
+        abi % newSpec.getAbi() != 0)
+      return false;
+  }
+  return true;
 }
 
 llvm::TypeSize
diff --git a/clang/test/CIR/CodeGen/pointer-width-32bit.cpp 
b/clang/test/CIR/CodeGen/pointer-width-32bit.cpp
index 15f3ec92d7e16..5efe0f9f5e21d 100644
--- a/clang/test/CIR/CodeGen/pointer-width-32bit.cpp
+++ b/clang/test/CIR/CodeGen/pointer-width-32bit.cpp
@@ -5,12 +5,10 @@
 // RUN: %clang_cc1 -std=c++20 -triple nvptx-nvidia-cuda -emit-llvm %s -o %t.ll
 // RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
 
-// On a target with 32-bit pointers (e.g. nvptx) both a data pointer (!cir.ptr)
-// and the vtable pointer (!cir.vptr) are 4 bytes wide. The pointer width is
-// carried by a CIR-native data-layout entry keyed on cir.ptr, so the field
-// following a pointer lands at the AST-mandated offset. Sizing pointers as a
-// hardcoded 64 bits previously tripped the record layout builder 
(insertPadding:
-// assertion `offset >= size`) on every record containing a pointer.
+// On a 32-bit-pointer target such as nvptx, !cir.ptr and !cir.vptr are 4 bytes
+// wide, driven by the #cir.ptr_spec data-layout entry. Hardcoded 64-bit widths
+// used to trip the record layout builder (insertPadding: offset >= size) on
+// every record containing a pointer.
 
 struct S {
   int *p;
@@ -27,13 +25,13 @@ class A {
 
 void A::f() {}
 
-// The module carries a CIR-native pointer data-layout entry ({size, abi-align}
+// The module carries a #cir.ptr_spec pointer data-layout entry 
(size/abi/preferred
 // in bits) that drives both cir.ptr and cir.vptr widths. The 4-byte pointer is
 // immediately followed by 'x' at offset 4 with no padding, and each record is
 // 4-byte aligned.
 // CIR-DAG: !rec_S = !cir.struct<"S" {!cir.ptr<!s32i>, !s32i}>
 // CIR-DAG: !rec_A = !cir.struct<class "A" {!cir.vptr, !s32i}>
-// CIR-DAG: !cir.ptr<!cir.void> = array<i32: 32, 32>
+// CIR-DAG: !cir.ptr<!cir.void> = #cir.ptr_spec<size = 32, abi = 32, preferred 
= 32, index = 32>
 // CIR: cir.global external @s = #cir.zero : !rec_S {alignment = 4 : i64}
 // CIR: cir.global{{.*}}@_ZTV1A = #cir.vtable<{{.*}}{alignment = 4 : i64}
 
diff --git a/clang/test/CIR/IR/pointer-data-layout.cir 
b/clang/test/CIR/IR/pointer-data-layout.cir
new file mode 100644
index 0000000000000..938216cae2be3
--- /dev/null
+++ b/clang/test/CIR/IR/pointer-data-layout.cir
@@ -0,0 +1,58 @@
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+
+// A well-formed #cir.ptr_spec entry round-trips without diagnostics.
+module attributes {dlti.dl_spec = #dlti.dl_spec<
+    !cir.ptr<!cir.void> = #cir.ptr_spec<size = 32, abi = 32, preferred = 32>>} 
{
+}
+
+// -----
+
+// The entry value must be a #cir.ptr_spec attribute.
+// expected-error @below {{expected layout attribute for '!cir.ptr<!cir.void>' 
to be a #cir.ptr_spec attribute}}
+module attributes {dlti.dl_spec = #dlti.dl_spec<
+    !cir.ptr<!cir.void> = dense<32> : vector<2xi64>>} {
+}
+
+// -----
+
+// The entry key must use !cir.void as pointee.
+// expected-error @below {{expected !cir.ptr data layout entry for 
'!cir.ptr<!cir.int<s, 32>>' to use !cir.void as pointee}}
+module attributes {dlti.dl_spec = #dlti.dl_spec<
+    !cir.ptr<!cir.int<s, 32>> = #cir.ptr_spec<size = 32, abi = 32, preferred = 
32>>} {
+}
+
+// -----
+
+// Per-address-space pointer layouts are not supported yet.
+// expected-error @below {{!cir.ptr data layout entries are currently limited 
to the default address space}}
+module attributes {dlti.dl_spec = #dlti.dl_spec<
+    !cir.ptr<!cir.void, target_address_space(5)> = #cir.ptr_spec<size = 32, 
abi = 32, preferred = 32>>} {
+}
+
+// -----
+
+// expected-error @+2 {{size entry must be divisible by 8}}
+module attributes {dlti.dl_spec = #dlti.dl_spec<
+    !cir.ptr<!cir.void> = #cir.ptr_spec<size = 33, abi = 32, preferred = 32>>} 
{
+}
+
+// -----
+
+// expected-error @+2 {{abi entry must be divisible by 8}}
+module attributes {dlti.dl_spec = #dlti.dl_spec<
+    !cir.ptr<!cir.void> = #cir.ptr_spec<size = 32, abi = 33, preferred = 64>>} 
{
+}
+
+// -----
+
+// expected-error @+2 {{index entry must be divisible by 8}}
+module attributes {dlti.dl_spec = #dlti.dl_spec<
+    !cir.ptr<!cir.void> = #cir.ptr_spec<size = 32, abi = 32, preferred = 32, 
index = 33>>} {
+}
+
+// -----
+
+// expected-error @+2 {{preferred alignment is expected to be at least as 
large as ABI alignment}}
+module attributes {dlti.dl_spec = #dlti.dl_spec<
+    !cir.ptr<!cir.void> = #cir.ptr_spec<size = 64, abi = 64, preferred = 32>>} 
{
+}
diff --git a/clang/test/CIR/Tools/pointer-width-from-triple.cir 
b/clang/test/CIR/Tools/pointer-width-from-triple.cir
new file mode 100644
index 0000000000000..f534c641c1746
--- /dev/null
+++ b/clang/test/CIR/Tools/pointer-width-from-triple.cir
@@ -0,0 +1,22 @@
+// RUN: cir-translate --cir-to-llvmir --target nvptx-nvidia-cuda 
--disable-cc-lowering %s -o %t.nvptx.ll
+// RUN: FileCheck %s -input-file %t.nvptx.ll -check-prefix=NVPTX
+// RUN: cir-translate --cir-to-llvmir --target x86_64-unknown-linux-gnu 
--disable-cc-lowering %s -o %t.x86.ll
+// RUN: FileCheck %s -input-file %t.x86.ll -check-prefix=X86
+
+// A data layout derived from the target triple must carry the #cir.ptr_spec
+// entry, just as CIRGen emits it. cir.ptr_diff bakes the pointee size into
+// the emitted division: 4 bytes on nvptx, not the 64-bit fallback of 8.
+
+!s64i = !cir.int<s, 64>
+module {
+  cir.func @f(%arg0: !cir.ptr<!cir.ptr<!s64i>>, %arg1: 
!cir.ptr<!cir.ptr<!s64i>>) -> !s64i {
+    %0 = cir.ptr_diff %arg0, %arg1 : !cir.ptr<!cir.ptr<!s64i>> -> !s64i
+    cir.return %0 : !s64i
+  }
+}
+
+// NVPTX-LABEL: define i64 @f
+// NVPTX: sdiv exact i64 %{{.*}}, 4
+
+// X86-LABEL: define i64 @f
+// X86: sdiv exact i64 %{{.*}}, 8
diff --git a/clang/tools/cir-translate/cir-translate.cpp 
b/clang/tools/cir-translate/cir-translate.cpp
index 26adf2cf47d17..cf456d958b27b 100644
--- a/clang/tools/cir-translate/cir-translate.cpp
+++ b/clang/tools/cir-translate/cir-translate.cpp
@@ -19,10 +19,10 @@
 #include "mlir/InitAllTranslations.h"
 #include "mlir/Support/LogicalResult.h"
 #include "mlir/Target/LLVMIR/Dialect/All.h"
-#include "mlir/Target/LLVMIR/Import.h"
 #include "mlir/Tools/mlir-translate/MlirTranslateMain.h"
 #include "mlir/Tools/mlir-translate/Translation.h"
 
+#include "llvm/IR/DataLayout.h"
 #include "llvm/IR/Module.h"
 #include "llvm/TargetParser/Host.h"
 
@@ -30,6 +30,7 @@
 #include "clang/Basic/DiagnosticIDs.h"
 #include "clang/Basic/DiagnosticOptions.h"
 #include "clang/Basic/TargetInfo.h"
+#include "clang/CIR/CIRDataLayoutSpec.h"
 #include "clang/CIR/Dialect/IR/CIRDialect.h"
 #include "clang/CIR/Dialect/Passes.h"
 #include "clang/CIR/InitAllDialects.h"
@@ -110,9 +111,7 @@ llvm::LogicalResult 
prepareCIRModuleDataLayout(mlir::ModuleOp mod,
   context->loadDialect<mlir::DLTIDialect, mlir::LLVM::LLVMDialect,
                        mlir::omp::OpenMPDialect>();
 
-  mlir::DataLayoutSpecInterface dlSpec =
-      mlir::translateDataLayout(llvm::DataLayout(layoutString), context);
-  mod->setAttr(mlir::DLTIDialect::kDataLayoutAttrName, dlSpec);
+  cir::setMLIRDataLayout(mod, llvm::DataLayout(layoutString));
 
   return llvm::success();
 }

>From f788c86772e3ddd3bb7ab99907d28a7fac394abe Mon Sep 17 00:00:00 2001
From: AkshayK <[email protected]>
Date: Fri, 3 Jul 2026 09:24:02 -0400
Subject: [PATCH 3/4] [CIR] Size data member pointers from the data layout

Derive DataMemberType size/alignment from the pointer index width
(ptrdiff_t under Itanium) instead of hardcoding 64/8, which tripped
the record layout builder on 32-bit targets.
---
 clang/lib/CIR/Dialect/IR/CIRTypes.cpp         | 19 ++++++++++++++----
 .../test/CIR/CodeGen/pointer-width-32bit.cpp  | 20 +++++++++++++++----
 2 files changed, 31 insertions(+), 8 deletions(-)

diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp 
b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
index e9f52ed5b212d..909c7550b1fb3 100644
--- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
@@ -1174,20 +1174,31 @@ BoolType::getABIAlignment(const ::mlir::DataLayout 
&dataLayout,
 //  DataMemberType Definitions
 
//===----------------------------------------------------------------------===//
 
+static mlir::Type getDataMemberLayoutType(const mlir::DataLayout &dataLayout,
+                                          mlir::MLIRContext *ctx) {
+  // Itanium ABI: a data member pointer is a ptrdiff_t, an integer of the
+  // pointer index width.
+  // TODO: consider data member pointer layout in other ABIs
+  auto voidPtrTy = cir::PointerType::get(cir::VoidType::get(ctx));
+  uint64_t width = dataLayout.getTypeIndexBitwidth(voidPtrTy).value_or(
+      dataLayout.getTypeSizeInBits(voidPtrTy).getFixedValue());
+  return cir::IntType::get(ctx, width, /*is_signed=*/true);
+}
+
 llvm::TypeSize
 DataMemberType::getTypeSizeInBits(const ::mlir::DataLayout &dataLayout,
                                   ::mlir::DataLayoutEntryListRef params) const 
{
-  // FIXME: consider size differences under different ABIs
   assert(!MissingFeatures::cxxABI());
-  return llvm::TypeSize::getFixed(64);
+  return dataLayout.getTypeSizeInBits(
+      getDataMemberLayoutType(dataLayout, getContext()));
 }
 
 uint64_t
 DataMemberType::getABIAlignment(const ::mlir::DataLayout &dataLayout,
                                 ::mlir::DataLayoutEntryListRef params) const {
-  // FIXME: consider alignment differences under different ABIs
   assert(!MissingFeatures::cxxABI());
-  return 8;
+  return dataLayout.getTypeABIAlignment(
+      getDataMemberLayoutType(dataLayout, getContext()));
 }
 
 
//===----------------------------------------------------------------------===//
diff --git a/clang/test/CIR/CodeGen/pointer-width-32bit.cpp 
b/clang/test/CIR/CodeGen/pointer-width-32bit.cpp
index 5efe0f9f5e21d..0e2a7a1bc1390 100644
--- a/clang/test/CIR/CodeGen/pointer-width-32bit.cpp
+++ b/clang/test/CIR/CodeGen/pointer-width-32bit.cpp
@@ -25,18 +25,30 @@ class A {
 
 void A::f() {}
 
-// The module carries a #cir.ptr_spec pointer data-layout entry 
(size/abi/preferred
-// in bits) that drives both cir.ptr and cir.vptr widths. The 4-byte pointer is
-// immediately followed by 'x' at offset 4 with no padding, and each record is
-// 4-byte aligned.
+// A data member pointer is ptrdiff_t-sized, so 'x' again lands at offset 4.
+struct M {
+  int S::*pm;
+  int x;
+};
+
+M m;
+
+// Each 4-byte pointer is followed by 'x' at offset 4 with no padding; records
+// are 4-byte aligned.
 // CIR-DAG: !rec_S = !cir.struct<"S" {!cir.ptr<!s32i>, !s32i}>
 // CIR-DAG: !rec_A = !cir.struct<class "A" {!cir.vptr, !s32i}>
+// -emit-cir prints after cir-cxxabi-lowering, so M's member pointer is
+// already a 32-bit integer here.
+// CIR-DAG: !rec_M = !cir.struct<"M" {!s32i, !s32i}>
 // CIR-DAG: !cir.ptr<!cir.void> = #cir.ptr_spec<size = 32, abi = 32, preferred 
= 32, index = 32>
 // CIR: cir.global external @s = #cir.zero : !rec_S {alignment = 4 : i64}
+// CIR: cir.global external @m = #cir.const_record<{#cir.int<-1> : !s32i, 
#cir.int<0> : !s32i}> : !rec_M {alignment = 4 : i64}
 // CIR: cir.global{{.*}}@_ZTV1A = #cir.vtable<{{.*}}{alignment = 4 : i64}
 
 // LLVM: @s = global %struct.S zeroinitializer, align 4
+// LLVM: @m = global %struct.M { i32 -1, i32 0 }, align 4
 // LLVM: @_ZTV1A = global { [3 x ptr] } {{.*}}, align 4
 
 // OGCG: @s = global %struct.S zeroinitializer, align 4
+// OGCG: @m = global %struct.M { i32 -1, i32 0 }, align 4
 // OGCG: @_ZTV1A = {{.*}}constant { [3 x ptr] } {{.*}}, align 4

>From f6a9f01904a0d6428f3791bb47525fcf129ab0a6 Mon Sep 17 00:00:00 2001
From: AkshayK <[email protected]>
Date: Tue, 30 Jun 2026 12:39:21 -0400
Subject: [PATCH 4/4] [CIR][ARM] Add base 32-bit ARM (GenericARM) codegen and
 lowering

Add the GenericARM transform-pass CXXABI dispatch (ARM method-pointer ABI), 
drive the size_t width of exception allocation and cir.copy memcpy from the 
data layout, and implement the NEON vget_lane/vgetq_lane intrinsics in 
CIRGenBuiltinAArch64.cpp.
---
 clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp       |  4 +--
 .../lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp  | 36 +++++++++++++++++++
 clang/lib/CIR/CodeGen/CIRGenFunction.h        |  4 +++
 .../TargetLowering/LowerItaniumCXXABI.cpp     |  8 +++++
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 23 ++++++++----
 .../CodeGen/ARM/arm-aggregate-copy-size.cpp   | 21 +++++++++++
 .../CIR/CodeGen/ARM/arm-record-layout.cpp     | 36 +++++++++++++++++++
 .../CIR/CodeGen/ARM/arm-throw-alloc-size.cpp  | 22 ++++++++++++
 .../CodeGenBuiltins/ARM/arm-neon-vget-lane.c  | 25 +++++++++++++
 9 files changed, 169 insertions(+), 10 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/ARM/arm-aggregate-copy-size.cpp
 create mode 100644 clang/test/CIR/CodeGen/ARM/arm-record-layout.cpp
 create mode 100644 clang/test/CIR/CodeGen/ARM/arm-throw-alloc-size.cpp
 create mode 100644 clang/test/CIR/CodeGenBuiltins/ARM/arm-neon-vget-lane.c

diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 425e07715c9f4..bffede7832ea0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -2790,9 +2790,7 @@ emitTargetArchBuiltinExpr(CIRGenFunction *cgf, unsigned 
builtinID,
   case llvm::Triple::armeb:
   case llvm::Triple::thumb:
   case llvm::Triple::thumbeb:
-    // These are actually NYI, but that will be reported by emitBuiltinExpr.
-    // At this point, we don't even know that the builtin is target-specific.
-    return std::nullopt;
+    return cgf->emitARMBuiltinExpr(builtinID, e, returnValue, arch);
   case llvm::Triple::aarch64:
   case llvm::Triple::aarch64_32:
   case llvm::Triple::aarch64_be:
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp
index e3418d561df34..bee57f364972b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp
@@ -1844,6 +1844,42 @@ static const std::pair<unsigned, unsigned> 
neonEquivalentIntrinsicMap[] = {
      NEON::BI__builtin_neon_vstl1q_lane_s64},
 };
 
+std::optional<mlir::Value>
+CIRGenFunction::emitARMBuiltinExpr(unsigned builtinID, const CallExpr *expr,
+                                   ReturnValueSlot returnValue,
+                                   llvm::Triple::ArchType arch) {
+  // Only the NEON lane-read intrinsics are implemented for 32-bit ARM; they
+  // lower to a vector element extraction. Other ARM builtins report errorNYI.
+  switch (builtinID) {
+  case NEON::BI__builtin_neon_vget_lane_i8:
+  case NEON::BI__builtin_neon_vget_lane_i16:
+  case NEON::BI__builtin_neon_vget_lane_i32:
+  case NEON::BI__builtin_neon_vget_lane_i64:
+  case NEON::BI__builtin_neon_vget_lane_bf16:
+  case NEON::BI__builtin_neon_vget_lane_f32:
+  case NEON::BI__builtin_neon_vgetq_lane_i8:
+  case NEON::BI__builtin_neon_vgetq_lane_i16:
+  case NEON::BI__builtin_neon_vgetq_lane_i32:
+  case NEON::BI__builtin_neon_vgetq_lane_i64:
+  case NEON::BI__builtin_neon_vgetq_lane_bf16:
+  case NEON::BI__builtin_neon_vgetq_lane_f32:
+  case NEON::BI__builtin_neon_vduph_lane_bf16:
+  case NEON::BI__builtin_neon_vduph_laneq_bf16: {
+    mlir::Location loc = getLoc(expr->getExprLoc());
+    mlir::Value vec = emitScalarExpr(expr->getArg(0));
+    mlir::Value index = emitScalarExpr(expr->getArg(1));
+    return cir::VecExtractOp::create(builder, loc, vec, index);
+  }
+  default:
+    break;
+  }
+
+  cgm.errorNYI(expr->getSourceRange(),
+               std::string("unimplemented ARM builtin call: ") +
+                   getContext().BuiltinInfo.getName(builtinID));
+  return mlir::Value{};
+}
+
 std::optional<mlir::Value>
 CIRGenFunction::emitAArch64BuiltinExpr(unsigned builtinID, const CallExpr 
*expr,
                                        ReturnValueSlot returnValue,
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h 
b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 322355fde3957..39455339fba29 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1542,6 +1542,10 @@ class CIRGenFunction : public CIRGenTypeCache {
   emitAArch64BuiltinExpr(unsigned builtinID, const CallExpr *expr,
                          ReturnValueSlot returnValue,
                          llvm::Triple::ArchType arch);
+  std::optional<mlir::Value> emitARMBuiltinExpr(unsigned builtinID,
+                                                const CallExpr *expr,
+                                                ReturnValueSlot returnValue,
+                                                llvm::Triple::ArchType arch);
   std::optional<mlir::Value> emitAArch64SMEBuiltinExpr(unsigned builtinID,
                                                        const CallExpr *expr);
   std::optional<mlir::Value> emitAArch64SVEBuiltinExpr(unsigned builtinID,
diff --git 
a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp 
b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
index 5218d4979f353..f5a0f51f7ab86 100644
--- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
@@ -144,6 +144,14 @@ std::unique_ptr<CIRCXXABI> createItaniumCXXABI(LowerModule 
&lm) {
         /*useARMMethodPtrABI=*/true,
         /*use32BitVTableOffsetABI=*/true);
 
+  case clang::TargetCXXABI::GenericARM:
+    // 32-bit ARM uses the ARM method-pointer encoding but, unlike AppleARM64,
+    // does not use 32-bit vtable offsets.
+    return std::make_unique<LowerItaniumCXXABI>(
+        lm,
+        /*useARMMethodPtrABI=*/true,
+        /*use32BitVTableOffsetABI=*/false);
+
   case clang::TargetCXXABI::GenericItanium:
     return std::make_unique<LowerItaniumCXXABI>(lm);
 
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index db018d47e10e3..f778ef98efbf7 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -212,9 +212,13 @@ mlir::LogicalResult 
CIRToLLVMCopyOpLowering::matchAndRewrite(
     cir::CopyOp op, OpAdaptor adaptor,
     mlir::ConversionPatternRewriter &rewriter) const {
   mlir::DataLayout layout(op->getParentOfType<mlir::ModuleOp>());
+  // The llvm.memcpy length is size_t-wide (target-dependent), so take its
+  // width from the data layout rather than hardcoding i64.
+  auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
+  mlir::Type lenTy =
+      rewriter.getIntegerType(layout.getTypeSizeInBits(llvmPtrTy));
   const mlir::Value length = mlir::LLVM::ConstantOp::create(
-      rewriter, op.getLoc(), rewriter.getI64Type(),
-      op.getCopySizeInBytes(layout));
+      rewriter, op.getLoc(), lenTy, op.getCopySizeInBytes(layout));
   assert(!cir::MissingFeatures::aggValueSlotVolatile());
 
   uint64_t dstTypeAlign = dataLayout.getTypeABIAlignment(convertTypeForMemory(
@@ -4077,15 +4081,20 @@ mlir::LogicalResult 
CIRToLLVMThrowOpLowering::matchAndRewrite(
 mlir::LogicalResult CIRToLLVMAllocExceptionOpLowering::matchAndRewrite(
     cir::AllocExceptionOp op, OpAdaptor adaptor,
     mlir::ConversionPatternRewriter &rewriter) const {
-  // Get or create `declare ptr @__cxa_allocate_exception(i64)`
+  // Get or create `declare ptr @__cxa_allocate_exception(size_t)`. thrown_size
+  // is size_t, so take its width from the data layout rather than hardcoding
+  // i64; otherwise the call mismatches the runtime on 32-bit targets.
   StringRef fnName = "__cxa_allocate_exception";
   auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
-  auto int64Ty = mlir::IntegerType::get(rewriter.getContext(), 64);
-  auto fnTy = mlir::LLVM::LLVMFunctionType::get(llvmPtrTy, {int64Ty});
+  mlir::DataLayout layout(op->getParentOfType<mlir::ModuleOp>());
+  auto sizeTTy = mlir::IntegerType::get(rewriter.getContext(),
+                                        layout.getTypeSizeInBits(llvmPtrTy));
+  auto fnTy = mlir::LLVM::LLVMFunctionType::get(llvmPtrTy, {sizeTTy});
 
   createLLVMFuncOpIfNotExist(rewriter, op, fnName, fnTy);
-  auto exceptionSize = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
-                                                      adaptor.getSizeAttr());
+  auto exceptionSize = mlir::LLVM::ConstantOp::create(
+      rewriter, op.getLoc(), sizeTTy,
+      rewriter.getIntegerAttr(sizeTTy, op.getSize()));
 
   auto allocaExceptionCall = mlir::LLVM::CallOp::create(
       rewriter, op.getLoc(), mlir::TypeRange{llvmPtrTy}, fnName,
diff --git a/clang/test/CIR/CodeGen/ARM/arm-aggregate-copy-size.cpp 
b/clang/test/CIR/CodeGen/ARM/arm-aggregate-copy-size.cpp
new file mode 100644
index 0000000000000..e2f6e38ced7b2
--- /dev/null
+++ b/clang/test/CIR/CodeGen/ARM/arm-aggregate-copy-size.cpp
@@ -0,0 +1,21 @@
+// The llvm.memcpy length for a cir.copy (aggregate pass-by-value) is 
size_t-wide:
+// i32 on 32-bit ARM, not the i64 used by 64-bit targets.
+//
+// RUN: %clang_cc1 -std=c++20 -triple arm-linux-gnueabihf -fclangir -emit-cir 
%s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -std=c++20 -triple arm-linux-gnueabihf -fclangir -emit-llvm 
%s -o %t.ll
+// RUN: FileCheck --check-prefix=ARM --input-file=%t.ll %s
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-llvm %s -o %t-x86.ll
+// RUN: FileCheck --check-prefix=X86 --input-file=%t-x86.ll %s
+
+struct P { int x; int y; };
+int sum(P p);
+int use() { P p; p.x = 1; p.y = 2; return sum(p); }
+
+// The cir.copy memcpy length width is resolved later, during lowering to LLVM.
+// CIR-LABEL: cir.func{{.*}} @_Z3usev()
+// CIR: cir.copy {{.*}} : !cir.ptr<!rec_P>
+
+// ARM: call void @llvm.memcpy.p0.p0.i32(ptr {{.*}}, ptr {{.*}}, i32 8, i1 
false)
+
+// X86: call void @llvm.memcpy.p0.p0.i64(ptr {{.*}}, ptr {{.*}}, i64 8, i1 
false)
diff --git a/clang/test/CIR/CodeGen/ARM/arm-record-layout.cpp 
b/clang/test/CIR/CodeGen/ARM/arm-record-layout.cpp
new file mode 100644
index 0000000000000..82a96911c02e0
--- /dev/null
+++ b/clang/test/CIR/CodeGen/ARM/arm-record-layout.cpp
@@ -0,0 +1,36 @@
+// 32-bit ARM lowers end-to-end through CIR (GenericARM CXXABI, vtables); 
records
+// and vtables are 4-byte aligned with 4-byte pointers.
+//
+// RUN: %clang_cc1 -std=c++20 -triple arm-linux-gnueabihf -fclangir -emit-cir 
%s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -std=c++20 -triple arm-linux-gnueabihf -fclangir -emit-llvm 
%s -o %t.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t.ll %s
+// RUN: %clang_cc1 -std=c++20 -triple arm-linux-gnueabihf -emit-llvm %s -o 
%t-ogcg.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t-ogcg.ll %s
+
+struct S {
+  int *p;
+  int x;
+};
+
+S s;
+
+class A {
+public:
+  virtual void f();
+  int x;
+};
+
+void A::f() {}
+
+// CIR-DAG: !rec_S = !cir.struct<"S" {!cir.ptr<!s32i>, !s32i}>
+// CIR-DAG: !rec_A = !cir.struct<class "A" {!cir.vptr, !s32i}>
+// CIR-DAG: !cir.ptr<!cir.void> = #cir.ptr_spec<size = 32, abi = 32, preferred 
= 32, index = 32>
+// CIR: cir.global external @s = #cir.zero : !rec_S {alignment = 4 : i64}
+// CIR: cir.global {{.*}}@_ZTV1A = #cir.vtable<{{.*}}{alignment = 4 : i64}
+
+// LLVM: @s = global %struct.S zeroinitializer, align 4
+// LLVM: @_ZTV1A = global { [3 x ptr] } {{.*}}, align 4
+
+// OGCG: @s = global %struct.S zeroinitializer, align 4
+// OGCG: @_ZTV1A = {{.*}}constant { [3 x ptr] } {{.*}}, align 4
diff --git a/clang/test/CIR/CodeGen/ARM/arm-throw-alloc-size.cpp 
b/clang/test/CIR/CodeGen/ARM/arm-throw-alloc-size.cpp
new file mode 100644
index 0000000000000..5899a04da4878
--- /dev/null
+++ b/clang/test/CIR/CodeGen/ARM/arm-throw-alloc-size.cpp
@@ -0,0 +1,22 @@
+// __cxa_allocate_exception's thrown_size is size_t: i32 on 32-bit ARM, not the
+// i64 used by 64-bit targets.
+//
+// RUN: %clang_cc1 -std=c++20 -triple arm-linux-gnueabihf -fcxx-exceptions 
-fexceptions -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -std=c++20 -triple arm-linux-gnueabihf -fcxx-exceptions 
-fexceptions -fclangir -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=ARM --input-file=%t.ll %s
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu 
-fcxx-exceptions -fexceptions -fclangir -emit-llvm %s -o %t-x86.ll
+// RUN: FileCheck --check-prefix=X86 --input-file=%t-x86.ll %s
+
+void f() { throw 42; }
+
+// The size_t width for the __cxa_allocate_exception call is resolved later,
+// during lowering to LLVM.
+// CIR-LABEL: cir.func{{.*}} @_Z1fv()
+// CIR: cir.alloc.exception 4
+
+// ARM: declare ptr @__cxa_allocate_exception(i32)
+// ARM: call ptr @__cxa_allocate_exception(i32 4)
+
+// X86: declare ptr @__cxa_allocate_exception(i64)
+// X86: call ptr @__cxa_allocate_exception(i64 4)
diff --git a/clang/test/CIR/CodeGenBuiltins/ARM/arm-neon-vget-lane.c 
b/clang/test/CIR/CodeGenBuiltins/ARM/arm-neon-vget-lane.c
new file mode 100644
index 0000000000000..bc7b76c9dcc8a
--- /dev/null
+++ b/clang/test/CIR/CodeGenBuiltins/ARM/arm-neon-vget-lane.c
@@ -0,0 +1,25 @@
+// On 32-bit ARM the NEON vget_lane/vgetq_lane intrinsics lower to
+// __builtin_neon_* (unlike AArch64); check CIR lowers them to a vector 
extract.
+
+// RUN: %clang_cc1 -triple armv7-unknown-linux-gnueabihf -target-feature +neon 
-ffreestanding -fclangir -emit-cir %s -o - | FileCheck %s --check-prefix=CIR
+// RUN: %clang_cc1 -triple armv7-unknown-linux-gnueabihf -target-feature +neon 
-ffreestanding -fclangir -emit-llvm %s -o - | FileCheck %s --check-prefix=LLVM
+
+#include <arm_neon.h>
+
+// CIR-LABEL: cir.func{{.*}} @get_s32(
+// CIR: cir.vec.extract {{.*}} : !cir.vector<4 x !s32i>
+// LLVM-LABEL: define dso_local i32 @get_s32(
+// LLVM: extractelement <4 x i32> %{{.*}}, i32 2
+int get_s32(int32x4_t v) { return vgetq_lane_s32(v, 2); }
+
+// CIR-LABEL: cir.func{{.*}} @get_f32(
+// CIR: cir.vec.extract {{.*}} : !cir.vector<4 x !cir.float>
+// LLVM-LABEL: define dso_local float @get_f32(
+// LLVM: extractelement <4 x float> %{{.*}}, i32 1
+float get_f32(float32x4_t v) { return vgetq_lane_f32(v, 1); }
+
+// CIR-LABEL: cir.func{{.*}} @get_s16(
+// CIR: cir.vec.extract {{.*}} : !cir.vector<4 x !s16i>
+// LLVM-LABEL: define dso_local i16 @get_s16(
+// LLVM: extractelement <4 x i16> %{{.*}}, i32 3
+short get_s16(int16x4_t v) { return vget_lane_s16(v, 3); }

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to