Author: Andy Kaylor
Date: 2025-08-14T15:14:12-07:00
New Revision: a1529cd85a9f99a263de72e16ed2f7567c519456

URL: 
https://github.com/llvm/llvm-project/commit/a1529cd85a9f99a263de72e16ed2f7567c519456
DIFF: 
https://github.com/llvm/llvm-project/commit/a1529cd85a9f99a263de72e16ed2f7567c519456.diff

LOG: [CIR] Add index support for global_view (#153254)

The #cir.global_view attribute was initially added without support for
the optional index list. This change adds index list support. This is
used when the address of an array or structure member is used as an
initializer.

This patch does not include support for taking the address of a
structure or class member. That will be added later.

Added: 
    

Modified: 
    clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
    clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
    clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h
    clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
    clang/lib/CIR/CodeGen/CIRGenBuilder.h
    clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
    clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp
    clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
    clang/test/CIR/CodeGen/globals.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h 
b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index 986c8c3d133ac..0bf3cb26be850 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -214,9 +214,10 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
 
   /// Get constant address of a global variable as an MLIR attribute.
   cir::GlobalViewAttr getGlobalViewAttr(cir::PointerType type,
-                                        cir::GlobalOp globalOp) {
+                                        cir::GlobalOp globalOp,
+                                        mlir::ArrayAttr indices = {}) {
     auto symbol = mlir::FlatSymbolRefAttr::get(globalOp.getSymNameAttr());
-    return cir::GlobalViewAttr::get(type, symbol);
+    return cir::GlobalViewAttr::get(type, symbol, indices);
   }
 
   mlir::Value createGetGlobal(mlir::Location loc, cir::GlobalOp global) {

diff  --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td 
b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index 9db236eb5580e..e899db84bb615 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -379,13 +379,20 @@ def CIR_GlobalViewAttr : CIR_Attr<"GlobalView", 
"global_view", [
 ]> {
   let summary = "Provides constant access to a global address";
   let description = [{
-    Get constant address of global `symbol`. It provides a way to access 
globals
-    from other global and always produces a pointer.
+    Get constant address of global `symbol` and optionally apply offsets to
+    access existing subelements. It provides a way to access globals from other
+    global and always produces a pointer.
 
     The type of the input symbol can be 
diff erent from `#cir.global_view`
     output type, since a given view of the global might require a static
     cast for initializing other globals.
 
+    A list of indices can be optionally passed and each element subsequently
+    indexes underlying types. For `symbol` types like `!cir.array`
+    and `!cir.record`, it leads to the constant address of sub-elements, while
+    for `!cir.ptr`, an offset is applied. The first index is relative to the
+    original symbol type, not the produced one.
+
     The result type of this attribute may be an integer type. In such a case,
     the pointer to the referenced global is casted to an integer and this
     attribute represents the casted result.
@@ -396,16 +403,49 @@ def CIR_GlobalViewAttr : CIR_Attr<"GlobalView", 
"global_view", [
       cir.global external @s = @".str2": !cir.ptr<i8>
       cir.global external @x = #cir.global_view<@s> : !cir.ptr<i8>
       cir.global external @s_addr = #cir.global_view<@s> : !s64i
+
+      cir.global external @rgb = #cir.const_array<[0 : i8, -23 : i8, 33 : i8]
+                                                   : !cir.array<i8 x 3>>
+      cir.global external @elt_ptr = #cir.global_view<@rgb, [1]> : !cir.ptr<i8>
+    ```
+
+    Note, that unlike LLVM IR's gep instruction, CIR doesn't add the leading
+    zero index when it's known to be constant zero, e.g. for pointers, i.e. we
+    use indexes exactly to access sub elements or for the offset. The leading
+    zero index is added later in the lowering.
+
+    Example:
+    ```
+    struct A {
+      int a;
+    };
+
+    struct B:  virtual A {
+      int b;
+    };
+    ```
+    VTT for B in CIR:
+    ```
+    cir.global linkonce_odr @_ZTT1B = #cir.const_array<[
+              #cir.global_view<@_ZTV1B, [0 : i32, 3 : i32]> : !cir.ptr<!u8i>]>
+                   : !cir.array<!cir.ptr<!u8i> x 1>
+    ```
+    VTT for B in LLVM IR:
+    ```
+    @_ZTT1B = linkonce_odr global [1 x ptr] [ptr getelementptr inbounds
+              ({ [3 x ptr] }, ptr @_ZTV1B, i32 0, i32 0, i32 3)], align 8
     ```
   }];
 
   let parameters = (ins AttributeSelfTypeParameter<"">:$type,
-                        "mlir::FlatSymbolRefAttr":$symbol);
+                        "mlir::FlatSymbolRefAttr":$symbol,
+                        OptionalParameter<"mlir::ArrayAttr">:$indices);
 
   let builders = [
     AttrBuilderWithInferredContext<(ins "mlir::Type":$type,
-                                        "mlir::FlatSymbolRefAttr":$symbol), [{
-      return $_get(type.getContext(), type, symbol);
+                                        "mlir::FlatSymbolRefAttr":$symbol,
+                                        CArg<"mlir::ArrayAttr", 
"{}">:$indices), [{
+      return $_get(type.getContext(), type, symbol, indices);
     }]>
   ];
 
@@ -413,6 +453,7 @@ def CIR_GlobalViewAttr : CIR_Attr<"GlobalView", 
"global_view", [
   let assemblyFormat = [{
     `<`
       $symbol
+      (`,` $indices^)?
     `>`
   }];
 }

diff  --git a/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h 
b/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h
index a46c2679d0481..ecc681ee310e3 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h
+++ b/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h
@@ -35,6 +35,13 @@ class CIRDataLayout {
 
   bool isBigEndian() const { return bigEndian; }
 
+  /// Internal helper method that returns requested alignment for type.
+  llvm::Align getAlignment(mlir::Type ty, bool abiOrPref) const;
+
+  llvm::Align getABITypeAlign(mlir::Type ty) const {
+    return getAlignment(ty, true);
+  }
+
   /// Returns the maximum number of bytes that may be overwritten by
   /// storing the specified type.
   ///
@@ -48,6 +55,19 @@ class CIRDataLayout {
             baseSize.isScalable()};
   }
 
+  /// Returns the offset in bytes between successive objects of the
+  /// specified type, including alignment padding.
+  ///
+  /// If Ty is a scalable vector type, the scalable property will be set and
+  /// the runtime size will be a positive integer multiple of the base size.
+  ///
+  /// This is the amount that alloca reserves for this type. For example,
+  /// returns 12 or 16 for x86_fp80, depending on alignment.
+  llvm::TypeSize getTypeAllocSize(mlir::Type ty) const {
+    // Round up to the next alignment boundary.
+    return llvm::alignTo(getTypeStoreSize(ty), getABITypeAlign(ty).value());
+  }
+
   llvm::TypeSize getTypeSizeInBits(mlir::Type ty) const;
 };
 

diff  --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
index 4a5a1dd53a05a..755c76c89a645 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
@@ -7,6 +7,7 @@
 
//===----------------------------------------------------------------------===//
 
 #include "CIRGenBuilder.h"
+#include "llvm/ADT/TypeSwitch.h"
 
 using namespace clang::CIRGen;
 
@@ -66,6 +67,69 @@ clang::CIRGen::CIRGenBuilderTy::getConstFP(mlir::Location 
loc, mlir::Type t,
   return create<cir::ConstantOp>(loc, cir::FPAttr::get(t, fpVal));
 }
 
+void CIRGenBuilderTy::computeGlobalViewIndicesFromFlatOffset(
+    int64_t offset, mlir::Type ty, cir::CIRDataLayout layout,
+    llvm::SmallVectorImpl<int64_t> &indices) {
+  if (!offset)
+    return;
+
+  auto getIndexAndNewOffset =
+      [](int64_t offset, int64_t eltSize) -> std::pair<int64_t, int64_t> {
+    int64_t divRet = offset / eltSize;
+    if (divRet < 0)
+      divRet -= 1; // make sure offset is positive
+    int64_t modRet = offset - (divRet * eltSize);
+    return {divRet, modRet};
+  };
+
+  mlir::Type subType =
+      llvm::TypeSwitch<mlir::Type, mlir::Type>(ty)
+          .Case<cir::ArrayType>([&](auto arrayTy) {
+            int64_t eltSize = 
layout.getTypeAllocSize(arrayTy.getElementType());
+            const auto [index, newOffset] =
+                getIndexAndNewOffset(offset, eltSize);
+            indices.push_back(index);
+            offset = newOffset;
+            return arrayTy.getElementType();
+          })
+          .Case<cir::RecordType>([&](auto recordTy) {
+            ArrayRef<mlir::Type> elts = recordTy.getMembers();
+            int64_t pos = 0;
+            for (size_t i = 0; i < elts.size(); ++i) {
+              int64_t eltSize =
+                  (int64_t)layout.getTypeAllocSize(elts[i]).getFixedValue();
+              unsigned alignMask = layout.getABITypeAlign(elts[i]).value() - 1;
+              if (recordTy.getPacked())
+                alignMask = 0;
+              // Union's fields have the same offset, so no need to change pos
+              // here, we just need to find eltSize that is greater then the
+              // required offset. The same is true for the similar union type
+              // check below
+              if (!recordTy.isUnion())
+                pos = (pos + alignMask) & ~alignMask;
+              assert(offset >= 0);
+              if (offset < pos + eltSize) {
+                indices.push_back(i);
+                offset -= pos;
+                return elts[i];
+              }
+              // No need to update pos here, see the comment above.
+              if (!recordTy.isUnion())
+                pos += eltSize;
+            }
+            llvm_unreachable("offset was not found within the record");
+          })
+          .Default([](mlir::Type otherTy) {
+            llvm_unreachable("unexpected type");
+            return otherTy; // Even though this is unreachable, we need to
+                            // return a type to satisfy the return type of the
+                            // lambda.
+          });
+
+  assert(subType);
+  computeGlobalViewIndicesFromFlatOffset(offset, subType, layout, indices);
+}
+
 // This can't be defined in Address.h because that file is included by
 // CIRGenBuilder.h
 Address Address::withElementType(CIRGenBuilderTy &builder,

diff  --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h 
b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index 8b2538c941f47..59d2adc15a01a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -12,6 +12,7 @@
 #include "Address.h"
 #include "CIRGenRecordLayout.h"
 #include "CIRGenTypeCache.h"
+#include "clang/CIR/Dialect/IR/CIRDataLayout.h"
 #include "clang/CIR/Interfaces/CIRTypeInterfaces.h"
 #include "clang/CIR/MissingFeatures.h"
 
@@ -401,6 +402,14 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
   mlir::Value maybeBuildArrayDecay(mlir::Location loc, mlir::Value arrayPtr,
                                    mlir::Type eltTy);
 
+  // Convert byte offset to sequence of high-level indices suitable for
+  // GlobalViewAttr. Ideally we shouldn't deal with low-level offsets at all
+  // but currently some parts of Clang AST, which we don't want to touch just
+  // yet, return them.
+  void computeGlobalViewIndicesFromFlatOffset(
+      int64_t offset, mlir::Type ty, cir::CIRDataLayout layout,
+      llvm::SmallVectorImpl<int64_t> &indices);
+
   /// Creates a versioned global variable. If the symbol is already taken, an 
ID
   /// will be appended to the symbol. The returned global must always be 
queried
   /// for its name so it can be referenced correctly.

diff  --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
index 87ea34df6be59..c2b3734173923 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
@@ -388,11 +388,20 @@ class ConstantLValueEmitter
   /// Return GEP-like value offset
   mlir::ArrayAttr getOffset(mlir::Type ty) {
     int64_t offset = value.getLValueOffset().getQuantity();
-    if (offset == 0)
-      return {};
+    cir::CIRDataLayout layout(cgm.getModule());
+    SmallVector<int64_t, 3> idxVec;
+    cgm.getBuilder().computeGlobalViewIndicesFromFlatOffset(offset, ty, layout,
+                                                            idxVec);
+
+    llvm::SmallVector<mlir::Attribute, 3> indices;
+    for (int64_t i : idxVec) {
+      mlir::IntegerAttr intAttr = cgm.getBuilder().getI32IntegerAttr(i);
+      indices.push_back(intAttr);
+    }
 
-    cgm.errorNYI("ConstantLValueEmitter: global view with offset");
-    return {};
+    if (indices.empty())
+      return {};
+    return cgm.getBuilder().getArrayAttr(indices);
   }
 
   /// Apply the value offset to the given constant.
@@ -400,10 +409,11 @@ class ConstantLValueEmitter
     // Handle attribute constant LValues.
     if (auto attr = mlir::dyn_cast<mlir::Attribute>(c.value)) {
       if (auto gv = mlir::dyn_cast<cir::GlobalViewAttr>(attr)) {
-        if (value.getLValueOffset().getQuantity() == 0)
-          return gv;
-        cgm.errorNYI("ConstantLValue: global view with offset");
-        return {};
+        auto baseTy = mlir::cast<cir::PointerType>(gv.getType()).getPointee();
+        mlir::Type destTy = cgm.getTypes().convertTypeForMem(destType);
+        assert(!gv.getIndices() && "Global view is already indexed");
+        return cir::GlobalViewAttr::get(destTy, gv.getSymbol(),
+                                        getOffset(baseTy));
       }
       llvm_unreachable("Unsupported attribute type to offset");
     }

diff  --git a/clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp 
b/clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp
index 8b806b406a536..42d45819de0f3 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp
@@ -23,10 +23,30 @@ void CIRDataLayout::reset(mlir::DataLayoutSpecInterface 
spec) {
   }
 }
 
+llvm::Align CIRDataLayout::getAlignment(mlir::Type ty, bool useABIAlign) const 
{
+  if (auto recTy = llvm::dyn_cast<cir::RecordType>(ty)) {
+    // Packed record types always have an ABI alignment of one.
+    if (recTy && recTy.getPacked() && useABIAlign)
+      return llvm::Align(1);
+
+    // Get the layout annotation... which is lazily created on demand.
+    llvm_unreachable("getAlignment()) for record type is not implemented");
+  }
+
+  // FIXME(cir): This does not account for 
diff ernt address spaces, and relies
+  // on CIR's data layout to give the proper alignment.
+  assert(!cir::MissingFeatures::addressSpace());
+
+  // Fetch type alignment from MLIR's data layout.
+  unsigned align = useABIAlign ? layout.getTypeABIAlignment(ty)
+                               : layout.getTypePreferredAlignment(ty);
+  return llvm::Align(align);
+}
+
 // The implementation of this method is provided inline as it is particularly
 // well suited to constant folding when called on a specific Type subclass.
 llvm::TypeSize CIRDataLayout::getTypeSizeInBits(mlir::Type ty) const {
-  assert(!cir::MissingFeatures::dataLayoutTypeIsSized());
+  assert(cir::isSized(ty) && "Cannot getTypeInfo() on a type that is 
unsized!");
 
   if (auto recordTy = llvm::dyn_cast<cir::RecordType>(ty)) {
     // FIXME(cir): CIR record's data layout implementation doesn't do a good 
job
@@ -38,5 +58,6 @@ llvm::TypeSize CIRDataLayout::getTypeSizeInBits(mlir::Type 
ty) const {
   // on CIR's data layout to give the proper ABI-specific type width.
   assert(!cir::MissingFeatures::addressSpace());
 
+  // This is calling mlir::DataLayout::getTypeSizeInBits().
   return llvm::TypeSize::getFixed(layout.getTypeSizeInBits(ty));
 }

diff  --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 20b8787d4f55f..1ea296a6887ef 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -426,7 +426,22 @@ mlir::Value 
CIRAttrToValue::visitCirAttr(cir::GlobalViewAttr globalAttr) {
   mlir::Value addrOp = rewriter.create<mlir::LLVM::AddressOfOp>(
       loc, mlir::LLVM::LLVMPointerType::get(rewriter.getContext()), symName);
 
-  assert(!cir::MissingFeatures::globalViewIndices());
+  if (globalAttr.getIndices()) {
+    llvm::SmallVector<mlir::LLVM::GEPArg> indices;
+
+    if (mlir::isa<mlir::LLVM::LLVMArrayType, mlir::LLVM::LLVMStructType>(
+            sourceType))
+      indices.push_back(0);
+
+    for (mlir::Attribute idx : globalAttr.getIndices()) {
+      auto intAttr = mlir::cast<mlir::IntegerAttr>(idx);
+      indices.push_back(intAttr.getValue().getSExtValue());
+    }
+    mlir::Type resTy = addrOp.getType();
+    mlir::Type eltTy = converter->convertType(sourceType);
+    addrOp = rewriter.create<mlir::LLVM::GEPOp>(
+        loc, resTy, eltTy, addrOp, indices, mlir::LLVM::GEPNoWrapFlags::none);
+  }
 
   // The incubator has handling here for the attribute having integer type, but
   // the only test case I could find that reaches it is a direct CIR-to-LLVM IR

diff  --git a/clang/test/CIR/CodeGen/globals.cpp 
b/clang/test/CIR/CodeGen/globals.cpp
index 7a08a4824276e..a3e16139a41a9 100644
--- a/clang/test/CIR/CodeGen/globals.cpp
+++ b/clang/test/CIR/CodeGen/globals.cpp
@@ -2,6 +2,8 @@
 // RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
 // RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-llvm %s -o %t-cir.ll
 // RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -emit-llvm %s 
-o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
 
 // Should constant initialize global with constant address.
 int var = 1;
@@ -11,6 +13,8 @@ int *constAddr = &var;
 
 // LLVM: @constAddr = global ptr @var, align 8
 
+// OGCG: @constAddr = global ptr @var, align 8
+
 // Should constant initialize global with constant address.
 int f();
 int (*constFnAddr)() = f;
@@ -18,3 +22,16 @@ int (*constFnAddr)() = f;
 // CIR: cir.global external @constFnAddr = #cir.global_view<@_Z1fv> : 
!cir.ptr<!cir.func<() -> !s32i>>
 
 // LLVM: @constFnAddr = global ptr @_Z1fv, align 8
+
+// OGCG: @constFnAddr = global ptr @_Z1fv, align 8
+
+int arr[4][16];
+int *constArrAddr = &arr[2][1];
+
+// CIR: cir.global external @constArrAddr = #cir.global_view<@arr, [2 : i32, 1 
: i32]> : !cir.ptr<!s32i>
+
+// The 'inbounds' and 'nuw' flags are inferred by LLVM's constant folder. The
+// same flags show up at -O1 in OGCG.
+// LLVM: @constArrAddr = global ptr getelementptr inbounds nuw (i8, ptr @arr, 
i64 132), align 8
+
+// OGCG: @constArrAddr = global ptr getelementptr (i8, ptr @arr, i64 132), 
align 8


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to