[clang] [CIR] Emit inbounds/inrange for vtable address points in initializers (PR #227520)
Bruno Cardoso Lopes via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 29 16:58:54 PDT 2026
https://github.com/bcardosolopes created https://github.com/llvm/llvm-project/pull/227520
c68a4ca2a920 (https://github.com/llvm/llvm-project/pull/226904) lowers a GEP in a global initializer directly to a constant expression. The IRBuilder path it replaced went through TargetFolder, which infers inbounds (and then nuw) for in-range offsets from a global. CIR never emitted any flags for #cir.global_view, so it had only been getting them from that inference, and 7 CIR tests started failing.
Classic codegen emits no flags on a plain address constant, but it does emit `inbounds inrange(...)` for vtable address points (VTT entries and the vptr of a constant-initialized object). Emit the same in CIR:
- Add an `address_point` flag to #cir.global_view, spelled `#cir.global_view<@vtable, [vtable index, slot], address_point>`.
- Set it where CIRGen builds VTT entries and constant vptrs, and keep it when CIRGen and CXXABILowering rebuild the attribute.
- When lowering to LLVM, make an address point `inbounds`, with an `inrange` covering the one vtable in the group that it points into. The bounds come from the vtable global's type, so they also work when the vtable is only declared.
VTT and vptr initializers now match classic codegen. Tests that check plain address constants now expect no flags, which is also what classic codegen emits.
>From 495c22269a69f5103324d24d38dd052588819173 Mon Sep 17 00:00:00 2001
From: Bruno Cardoso Lopes <bruno.cardoso at gmail.com>
Date: Tue, 29 Sep 2026 16:45:07 -0700
Subject: [PATCH] [CIR] Emit inbounds and inrange for vtable address points in
initializers
c68a4ca2a920 (https://github.com/llvm/llvm-project/pull/226904) lowers a
GEP in a global initializer directly to a constant expression. The
IRBuilder path it replaced went through TargetFolder, which infers inbounds
(and then nuw) for in-range offsets from a global. CIR never emitted any
flags for #cir.global_view, so it had only been getting them from that
inference, and 7 CIR tests started failing.
Classic codegen emits no flags on a plain address constant, but it does emit
`inbounds inrange(...)` for vtable address points (VTT entries and the vptr
of a constant-initialized object). Emit the same in CIR:
- Add an `address_point` flag to #cir.global_view, spelled
`#cir.global_view<@vtable, [vtable index, slot], address_point>`.
- Set it where CIRGen builds VTT entries and constant vptrs, and keep it
when CIRGen and CXXABILowering rebuild the attribute.
- When lowering to LLVM, make an address point `inbounds`, with an
`inrange` covering the one vtable in the group that it points into. The
bounds come from the vtable global's type, so they also work when the
vtable is only declared.
VTT and vptr initializers now match classic codegen. Tests that check plain
address constants now expect no flags, which is also what classic codegen
emits.
---
.../include/clang/CIR/Dialect/IR/CIRAttrs.td | 20 +++++++----
clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp | 6 ++--
clang/lib/CIR/CodeGen/CIRGenModule.cpp | 8 +++--
clang/lib/CIR/CodeGen/CIRGenVTables.cpp | 6 ++--
clang/lib/CIR/Dialect/IR/CIRAttrs.cpp | 18 ++++++++++
.../CIR/Dialect/Transforms/CXXABILowering.cpp | 5 +--
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 27 ++++++++++++--
.../test/CIR/CodeGen/constexpr-ptr-offset.cpp | 2 +-
clang/test/CIR/CodeGen/global-ptr-init.cpp | 18 +++++++---
clang/test/CIR/CodeGen/globals.cpp | 2 +-
.../test/CIR/CodeGen/self-ref-temporaries.cpp | 4 +--
clang/test/CIR/CodeGen/vtt.cpp | 28 +++++++--------
clang/test/CIR/CodeGenCXX/base-layout.cpp | 4 +--
clang/test/CIR/CodeGenCXX/vtable-linkage.cpp | 4 +--
.../test/CIR/IR/global-view-address-point.cir | 35 +++++++++++++++++++
15 files changed, 143 insertions(+), 44 deletions(-)
create mode 100644 clang/test/CIR/IR/global-view-address-point.cir
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index 358412b75fc05..64b8016971c76 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -1033,28 +1033,35 @@ def CIR_GlobalViewAttr : CIR_ValueLikeAttr<"GlobalView", "global_view"> {
int b;
};
```
+ `address_point` marks a vtable address point: `symbol` is a vtable and
+ `indices` are the vtable within the group and the address point within
+ it. As in classic codegen, it lowers to an `inbounds` GEP whose `inrange`
+ spans that one vtable.
+
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>
+ #cir.global_view<@_ZTV1B, [0 : i32, 3 : i32], address_point>
+ : !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
+ inrange(-24, 0) (i8, ptr @_ZTV1B, i64 24)], align 8
```
}];
let parameters = (ins AttributeSelfTypeParameter<"">:$type,
"mlir::FlatSymbolRefAttr":$symbol,
- OptionalParameter<"mlir::ArrayAttr">:$indices);
+ OptionalParameter<"mlir::ArrayAttr">:$indices,
+ DefaultValuedParameter<"bool", "false">:$address_point);
let builders = [
AttrBuilderWithInferredContext<(ins "mlir::Type":$type,
"mlir::FlatSymbolRefAttr":$symbol,
- CArg<"mlir::ArrayAttr", "{}">:$indices), [{
- return $_get(type.getContext(), type, symbol, indices);
+ CArg<"mlir::ArrayAttr", "{}">:$indices,
+ CArg<"bool", "false">:$addressPoint), [{
+ return $_get(type.getContext(), type, symbol, indices, addressPoint);
}]>
];
@@ -1063,6 +1070,7 @@ def CIR_GlobalViewAttr : CIR_ValueLikeAttr<"GlobalView", "global_view"> {
`<`
$symbol
(`,` $indices^)?
+ `` custom<GlobalViewAddressPoint>($address_point)
`>`
}];
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
index 46aabba5b567a..ba2feb8bc6ff2 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
@@ -333,9 +333,9 @@ mlir::Attribute buildRecordHelper(ConstantEmitter &emitter,
mlir::ArrayAttr indices = builder.getArrayAttr(
{builder.getI32IntegerAttr(apOp.getAddressPoint().getIndex()),
builder.getI32IntegerAttr(apOp.getAddressPoint().getOffset())});
- elements[0] =
- cir::GlobalViewAttr::get(cir::VPtrType::get(builder.getContext()),
- apOp.getNameAttr(), indices);
+ elements[0] = cir::GlobalViewAttr::get(
+ cir::VPtrType::get(builder.getContext()), apOp.getNameAttr(), indices,
+ /*addressPoint=*/true);
}
for (auto [idx, base] : llvm::enumerate(cxxrd->bases())) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 007788d7e27c7..0019a8f0aa940 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -1120,8 +1120,12 @@ static mlir::Attribute createNewGlobalView(CIRGenModule &cgm,
else if (isa<cir::ArrayType>(oldTy))
newPtrTy = cast<cir::PointerType>(attr.getType());
- if (newPtrTy)
- return bld.getGlobalViewAttr(newPtrTy, newGlob, newInds);
+ if (newPtrTy) {
+ cir::GlobalViewAttr view =
+ bld.getGlobalViewAttr(newPtrTy, newGlob, newInds);
+ return cir::GlobalViewAttr::get(view.getType(), view.getSymbol(),
+ view.getIndices(), attr.getAddressPoint());
+ }
// This may be unreachable in practice, but keep it as errorNYI while CIR
// is still under development.
diff --git a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
index 123246f3b3d3f..4c9ee965bebec 100644
--- a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
@@ -499,8 +499,10 @@ void CIRGenVTables::emitVTTDefinition(cir::GlobalOp vttOp,
};
auto indicesAttr = mlir::ArrayAttr::get(mlirContext, indices);
- cir::GlobalViewAttr init = cgm.getBuilder().getGlobalViewAttr(
- cgm.getBuilder().getUInt8PtrTy(), vtable, indicesAttr);
+ auto init = cir::GlobalViewAttr::get(
+ cgm.getBuilder().getUInt8PtrTy(),
+ mlir::FlatSymbolRefAttr::get(vtable.getSymNameAttr()), indicesAttr,
+ /*addressPoint=*/true);
vttComponents.push_back(init);
}
diff --git a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
index 2649ed1449739..388de73108bee 100644
--- a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
@@ -57,6 +57,11 @@ parseDataMemberPath(mlir::AsmParser &parser,
static void printDataMemberPath(mlir::AsmPrinter &p,
mlir::DenseI32ArrayAttr memberPath);
+static mlir::ParseResult parseGlobalViewAddressPoint(mlir::AsmParser &parser,
+ bool &addressPoint);
+
+static void printGlobalViewAddressPoint(mlir::AsmPrinter &p, bool addressPoint);
+
#define GET_ATTRDEF_CLASSES
#include "clang/CIR/Dialect/IR/CIROpsAttributes.cpp.inc"
@@ -353,6 +358,19 @@ static void printDataMemberPath(AsmPrinter &p,
p.printStrippedAttrOrType(memberPath);
}
+static ParseResult parseGlobalViewAddressPoint(AsmParser &parser,
+ bool &addressPoint) {
+ addressPoint = parser.parseOptionalComma().succeeded();
+ if (addressPoint)
+ return parser.parseKeyword("address_point");
+ return success();
+}
+
+static void printGlobalViewAddressPoint(AsmPrinter &p, bool addressPoint) {
+ if (addressPoint)
+ p << ", address_point";
+}
+
//===----------------------------------------------------------------------===//
// IntAttr definitions
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
index 9a0e40bd4f506..750366bfa78c2 100644
--- a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
@@ -210,7 +210,8 @@ mlir::Attribute rewriteAttribute(const mlir::TypeConverter &tc,
return cir::GlobalViewAttr::get(
tc.convertType(gva.getType()), gva.getSymbol(),
mlir::cast<mlir::ArrayAttr>(
- rewriteAttribute(tc, ctx, gva.getIndices())));
+ rewriteAttribute(tc, ctx, gva.getIndices())),
+ gva.getAddressPoint());
})
.Case<cir::GlobalOffsetAttr>([&tc](cir::GlobalOffsetAttr goa) {
return cir::GlobalOffsetAttr::get(tc.convertType(goa.getType()),
@@ -504,7 +505,7 @@ static mlir::TypedAttr lowerInitialValue(const LowerModule *lowerModule,
if (auto gva = mlir::dyn_cast_if_present<cir::GlobalViewAttr>(initVal))
return cir::GlobalViewAttr::get(convertedTy, gva.getSymbol(),
- gva.getIndices());
+ gva.getIndices(), gva.getAddressPoint());
if (auto goa = mlir::dyn_cast_if_present<cir::GlobalOffsetAttr>(initVal))
return cir::GlobalOffsetAttr::get(convertedTy, goa.getSymbol(),
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 4d2a99d5015fe..9d89b3d5d979e 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -1018,9 +1018,30 @@ mlir::Value CIRAttrToValue::visitCirAttr(cir::GlobalViewAttr globalAttr) {
}
mlir::Type resTy = addrOp.getType();
mlir::Type eltTy = converter->convertType(sourceType);
- addrOp = mlir::LLVM::GEPOp::create(rewriter, parentOp->getLoc(), resTy,
- eltTy, addrOp, indices,
- mlir::LLVM::GEPNoWrapFlags::none);
+ auto gep = mlir::LLVM::GEPOp::create(rewriter, parentOp->getLoc(), resTy,
+ eltTy, addrOp, indices,
+ mlir::LLVM::GEPNoWrapFlags::none);
+ if (globalAttr.getAddressPoint()) {
+ // Like classic codegen, a vtable address point is inbounds, and only the
+ // one vtable of the group it points into can be accessed through it.
+ auto indices =
+ globalAttr.getIndices().getAsValueRange<mlir::IntegerAttr>();
+ assert(llvm::range_size(indices) == 2 &&
+ "address point takes a vtable index and a slot index");
+ auto vtableTy = mlir::cast<mlir::LLVM::LLVMArrayType>(
+ mlir::cast<mlir::LLVM::LLVMStructType>(eltTy)
+ .getBody()[(*indices.begin()).getZExtValue()]);
+ mlir::DataLayout layout(parentOp->getParentOfType<mlir::ModuleOp>());
+ int64_t slotSize = layout.getTypeSize(vtableTy.getElementType());
+ int64_t offset = (*std::next(indices.begin())).getSExtValue() * slotSize;
+ int64_t size = vtableTy.getNumElements() * slotSize;
+ unsigned indexBits = *layout.getTypeIndexBitwidth(
+ mlir::cast<mlir::LLVM::LLVMPointerType>(addrOp.getType()));
+ gep.setNoWrapFlags(mlir::LLVM::GEPNoWrapFlags::inbounds);
+ gep.setInrangeAttr(mlir::LLVM::ConstantRangeAttr::get(
+ rewriter.getContext(), indexBits, -offset, size - offset));
+ }
+ addrOp = gep;
}
return castGlobalAddrToType(addrOp, globalAttr.getType(), sourceType,
diff --git a/clang/test/CIR/CodeGen/constexpr-ptr-offset.cpp b/clang/test/CIR/CodeGen/constexpr-ptr-offset.cpp
index 5c4f0f13ef758..87e7ca291a045 100644
--- a/clang/test/CIR/CodeGen/constexpr-ptr-offset.cpp
+++ b/clang/test/CIR/CodeGen/constexpr-ptr-offset.cpp
@@ -20,7 +20,7 @@ void test() {
// CIR: cir.global "private" constant cir_private @__const._Z4testv.v = #cir.const_record<{#cir.global_view<@"[[STR_NAME:.*]]", [2 : i32]> : !cir.ptr<!s8i>, #cir.int<3> : !s32i, #cir.zero : !cir.array<!u8i x 4>}> : !rec_View
// CIR: cir.global "private" constant cir_private dso_local @"[[STR_NAME]]" = #cir.const_array<"hello" : !cir.array<!s8i x 5>, trailing_zeros> : !cir.array<!s8i x 6>
-// LLVMCIR: @__const._Z4testv.v = private constant %struct.View <{ ptr getelementptr inbounds nuw (i8, ptr @[[STR_NAME:.*]], i64 2), i32 3, [4 x i8] zeroinitializer }>
+// LLVMCIR: @__const._Z4testv.v = private constant %struct.View <{ ptr getelementptr (i8, ptr @[[STR_NAME:.*]], i64 2), i32 3, [4 x i8] zeroinitializer }>
// LLVMCIR: @[[STR_NAME]] = private {{.*}}constant [6 x i8] c"hello\00"
// OGCG: @[[STR_NAME:.*]] = private {{.*}}constant [6 x i8] c"hello\00"
diff --git a/clang/test/CIR/CodeGen/global-ptr-init.cpp b/clang/test/CIR/CodeGen/global-ptr-init.cpp
index 9a9824881a98c..bd914891b7c81 100644
--- a/clang/test/CIR/CodeGen/global-ptr-init.cpp
+++ b/clang/test/CIR/CodeGen/global-ptr-init.cpp
@@ -3,7 +3,7 @@
// RUN: %clang_cc1 -triple=x86_64-linux-gnu -std=gnu++14 -fclangir -emit-llvm -o %t-cir.ll %s
// RUN: FileCheck -check-prefix=LLVM --input-file=%t-cir.ll %s
// RUN: %clang_cc1 -triple=x86_64-linux-gnu -std=gnu++14 -emit-llvm -o %t.ll %s
-// RUN: FileCheck -check-prefix=OGCG --input-file=%t.ll %s
+// RUN: FileCheck -check-prefix=LLVM --input-file=%t.ll %s
struct B {
virtual void f() {}
@@ -11,6 +11,16 @@ struct B {
};
B x;
-// CIR: cir.global external @x = #cir.const_record<{#cir.global_view<@_ZTV1B, [0 : i32, 2 : i32]> : !cir.vptr}> : !rec_B
-// LLVM: @x = global %struct.B { ptr getelementptr inbounds nuw (i8, ptr @_ZTV1B, i64 16) }, align 8
-// OGCG: @x = global %struct.B { ptr getelementptr inbounds inrange(-16, 24) (i8, ptr @_ZTV1B, i64 16) }, align 8
+// CIR: cir.global external @x = #cir.const_record<{#cir.global_view<@_ZTV1B, [0 : i32, 2 : i32], address_point> : !cir.vptr}> : !rec_B
+// LLVM: @x = global %struct.B { ptr getelementptr inbounds inrange(-16, 24) (i8, ptr @_ZTV1B, i64 16) }, align 8
+
+// The vtable is only declared here: the inrange bound still comes from its type.
+struct K {
+ virtual void f();
+};
+K k;
+
+// CIR: cir.global "private" constant external @_ZTV1K : !rec_anon_struct
+// CIR: cir.global external @k = #cir.const_record<{#cir.global_view<@_ZTV1K, [0 : i32, 2 : i32], address_point> : !cir.vptr}> : !rec_K
+// LLVM: @_ZTV1K = external constant { [3 x ptr] }, align 8
+// LLVM: @k = global %struct.K { ptr getelementptr inbounds inrange(-16, 8) (i8, ptr @_ZTV1K, i64 16) }, align 8
diff --git a/clang/test/CIR/CodeGen/globals.cpp b/clang/test/CIR/CodeGen/globals.cpp
index 44e80fa7d2a6d..22cb448ef4422 100644
--- a/clang/test/CIR/CodeGen/globals.cpp
+++ b/clang/test/CIR/CodeGen/globals.cpp
@@ -32,7 +32,7 @@ int *constArrAddr = &arr[2][1];
// 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
+// LLVM: @constArrAddr = global ptr getelementptr (i8, ptr @arr, i64 132), align 8
// OGCG: @constArrAddr = global ptr getelementptr (i8, ptr @arr, i64 132), align 8
diff --git a/clang/test/CIR/CodeGen/self-ref-temporaries.cpp b/clang/test/CIR/CodeGen/self-ref-temporaries.cpp
index f31bbc03c2e75..4755b9046f820 100644
--- a/clang/test/CIR/CodeGen/self-ref-temporaries.cpp
+++ b/clang/test/CIR/CodeGen/self-ref-temporaries.cpp
@@ -15,7 +15,7 @@ struct SelfRef {
constexpr const SelfRef &sr = SelfRef();
// CIR: cir.global "private" constant internal @_ZGR2sr_ = #cir.const_record<{#cir.global_view<@_ZGR2sr_, [1 : i32]> : !cir.ptr<!s32i>, #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : !s32i, #cir.int<3> : !s32i]> : !cir.array<!s32i x 3>, #cir.zero : !cir.array<!u8i x 4>}> : !rec_SelfRef
// CIR: cir.global constant external @sr = #cir.global_view<@_ZGR2sr_> : !cir.ptr<!rec_SelfRef>
-// LLVMCIR: @_ZGR2sr_ = {{.*}}constant %struct.SelfRef <{ ptr getelementptr inbounds nuw (i8, ptr @_ZGR2sr_, i64 8), [3 x i32] [i32 1, i32 2, i32 3], [4 x i8] zeroinitializer }>, align 8
+// LLVMCIR: @_ZGR2sr_ = {{.*}}constant %struct.SelfRef <{ ptr getelementptr (i8, ptr @_ZGR2sr_, i64 8), [3 x i32] [i32 1, i32 2, i32 3], [4 x i8] zeroinitializer }>, align 8
// OGCG: @_ZGR2sr_ = {{.*}}constant { ptr, [3 x i32] } { ptr getelementptr {{.*}}(i8, ptr @_ZGR2sr_, i64 8), [3 x i32] [i32 1, i32 2, i32 3] }, align 8
// LLVM: @sr = constant ptr @_ZGR2sr_, align 8
@@ -29,7 +29,7 @@ struct MultiSelfRef {
constexpr const MultiSelfRef &msr = MultiSelfRef();
// CIR: cir.global "private" constant internal @_ZGR3msr_ = #cir.const_record<{#cir.global_view<@_ZGR3msr_, [2 : i32]> : !cir.ptr<!s32i>, #cir.global_view<@_ZGR3msr_, [2 : i32]> : !cir.ptr<!s32i>, #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : !s32i, #cir.int<3> : !s32i]> : !cir.array<!s32i x 3>, #cir.zero : !cir.array<!u8i x 4>}> : !rec_MultiSelfRef
// CIR: cir.global constant external @msr = #cir.global_view<@_ZGR3msr_> : !cir.ptr<!rec_MultiSelfRef>
-// LLVMCIR: @_ZGR3msr_ = internal constant %struct.MultiSelfRef <{ ptr getelementptr inbounds nuw (i8, ptr @_ZGR3msr_, i64 16), ptr getelementptr inbounds nuw (i8, ptr @_ZGR3msr_, i64 16), [3 x i32] [i32 1, i32 2, i32 3], [4 x i8] zeroinitializer }>, align 8
+// LLVMCIR: @_ZGR3msr_ = internal constant %struct.MultiSelfRef <{ ptr getelementptr (i8, ptr @_ZGR3msr_, i64 16), ptr getelementptr (i8, ptr @_ZGR3msr_, i64 16), [3 x i32] [i32 1, i32 2, i32 3], [4 x i8] zeroinitializer }>, align 8
// OGCG: @_ZGR3msr_ = {{.*}}constant { ptr, ptr, [3 x i32] } { ptr getelementptr {{.*}}(i8, ptr @_ZGR3msr_, i64 16), ptr getelementptr {{.*}}(i8, ptr @_ZGR3msr_, i64 16), [3 x i32] [i32 1, i32 2, i32 3] }, align 8
// LLVM: @msr = constant ptr @_ZGR3msr_, align 8
diff --git a/clang/test/CIR/CodeGen/vtt.cpp b/clang/test/CIR/CodeGen/vtt.cpp
index 4fa1a5b1f4c27..58d58636e5ab1 100644
--- a/clang/test/CIR/CodeGen/vtt.cpp
+++ b/clang/test/CIR/CodeGen/vtt.cpp
@@ -107,23 +107,23 @@ void D::y() {}
// VTT for D
// CIR-COMMON: cir.global{{.*}} @_ZTT1D = #cir.const_array<[
-// CIR-COMMON-SAME: #cir.global_view<@_ZTV1D, [0 : i32, 3 : i32]> : !cir.ptr<!u8i>,
-// CIR-COMMON-SAME: #cir.global_view<@_ZTC1D0_1B, [0 : i32, 3 : i32]> : !cir.ptr<!u8i>,
-// CIR-COMMON-SAME: #cir.global_view<@_ZTC1D0_1B, [1 : i32, 3 : i32]> : !cir.ptr<!u8i>,
-// CIR-COMMON-SAME: #cir.global_view<@_ZTC1D16_1C, [0 : i32, 3 : i32]> : !cir.ptr<!u8i>,
-// CIR-COMMON-SAME: #cir.global_view<@_ZTC1D16_1C, [1 : i32, 3 : i32]> : !cir.ptr<!u8i>,
-// CIR-COMMON-SAME: #cir.global_view<@_ZTV1D, [2 : i32, 3 : i32]> : !cir.ptr<!u8i>,
-// CIR-COMMON-SAME: #cir.global_view<@_ZTV1D, [1 : i32, 3 : i32]> : !cir.ptr<!u8i>
+// CIR-COMMON-SAME: #cir.global_view<@_ZTV1D, [0 : i32, 3 : i32], address_point> : !cir.ptr<!u8i>,
+// CIR-COMMON-SAME: #cir.global_view<@_ZTC1D0_1B, [0 : i32, 3 : i32], address_point> : !cir.ptr<!u8i>,
+// CIR-COMMON-SAME: #cir.global_view<@_ZTC1D0_1B, [1 : i32, 3 : i32], address_point> : !cir.ptr<!u8i>,
+// CIR-COMMON-SAME: #cir.global_view<@_ZTC1D16_1C, [0 : i32, 3 : i32], address_point> : !cir.ptr<!u8i>,
+// CIR-COMMON-SAME: #cir.global_view<@_ZTC1D16_1C, [1 : i32, 3 : i32], address_point> : !cir.ptr<!u8i>,
+// CIR-COMMON-SAME: #cir.global_view<@_ZTV1D, [2 : i32, 3 : i32], address_point> : !cir.ptr<!u8i>,
+// CIR-COMMON-SAME: #cir.global_view<@_ZTV1D, [1 : i32, 3 : i32], address_point> : !cir.ptr<!u8i>
// CIR-COMMON-SAME: ]> : !cir.array<!cir.ptr<!u8i> x 7> align(8)
// LLVM-COMMON: @_ZTT1D = constant [7 x ptr] [
-// LLVM-COMMON-SAME: ptr getelementptr inbounds nuw (i8, ptr @_ZTV1D, i64 24),
-// LLVM-COMMON-SAME: ptr getelementptr inbounds nuw (i8, ptr @_ZTC1D0_1B, i64 24),
-// LLVM-COMMON-SAME: ptr getelementptr inbounds nuw (i8, ptr @_ZTC1D0_1B, i64 56),
-// LLVM-COMMON-SAME: ptr getelementptr inbounds nuw (i8, ptr @_ZTC1D16_1C, i64 24),
-// LLVM-COMMON-SAME: ptr getelementptr inbounds nuw (i8, ptr @_ZTC1D16_1C, i64 56),
-// LLVM-COMMON-SAME: ptr getelementptr inbounds nuw (i8, ptr @_ZTV1D, i64 96),
-// LLVM-COMMON-SAME: ptr getelementptr inbounds nuw (i8, ptr @_ZTV1D, i64 64)
+// LLVM-COMMON-SAME: ptr getelementptr inbounds inrange(-24, 16) (i8, ptr @_ZTV1D, i64 24),
+// LLVM-COMMON-SAME: ptr getelementptr inbounds inrange(-24, 8) (i8, ptr @_ZTC1D0_1B, i64 24),
+// LLVM-COMMON-SAME: ptr getelementptr inbounds inrange(-24, 8) (i8, ptr @_ZTC1D0_1B, i64 56),
+// LLVM-COMMON-SAME: ptr getelementptr inbounds inrange(-24, 8) (i8, ptr @_ZTC1D16_1C, i64 24),
+// LLVM-COMMON-SAME: ptr getelementptr inbounds inrange(-24, 8) (i8, ptr @_ZTC1D16_1C, i64 56),
+// LLVM-COMMON-SAME: ptr getelementptr inbounds inrange(-24, 8) (i8, ptr @_ZTV1D, i64 96),
+// LLVM-COMMON-SAME: ptr getelementptr inbounds inrange(-24, 8) (i8, ptr @_ZTV1D, i64 64)
// LLVM-COMMON-SAME: ], align 8
// OGCG-COMMON: @_ZTT1D = unnamed_addr constant [7 x ptr] [
diff --git a/clang/test/CIR/CodeGenCXX/base-layout.cpp b/clang/test/CIR/CodeGenCXX/base-layout.cpp
index dc80ef1dbb8dd..7ce0a686faf03 100644
--- a/clang/test/CIR/CodeGenCXX/base-layout.cpp
+++ b/clang/test/CIR/CodeGenCXX/base-layout.cpp
@@ -14,8 +14,8 @@ struct B : V1, V2 {};
struct D : V3, B {};
constinit D d{};
-// CIR: cir.global external @d = #cir.const_record<{#cir.const_record<{#cir.global_view<@_ZTV1D, [0 : i32, 2 : i32]> : !cir.vptr}> : !rec_V3, #cir.const_record<{#cir.const_record<{#cir.global_view<@_ZTV1D, [1 : i32, 2 : i32]> : !cir.vptr}> : !rec_V1, #cir.const_record<{#cir.global_view<@_ZTV1D, [2 : i32, 2 : i32]> : !cir.vptr}> : !rec_V2}> : !rec_B}> : !rec_D
-// LLVM: @d = global %struct.D { %struct.V3 { ptr getelementptr inbounds nuw (i8, ptr @_ZTV1D, i64 16) }, %struct.B { %struct.V1 { ptr getelementptr inbounds nuw (i8, ptr @_ZTV1D, i64 40) }, %struct.V2 { ptr getelementptr inbounds nuw (i8, ptr @_ZTV1D, i64 64) } } }
+// CIR: cir.global external @d = #cir.const_record<{#cir.const_record<{#cir.global_view<@_ZTV1D, [0 : i32, 2 : i32], address_point> : !cir.vptr}> : !rec_V3, #cir.const_record<{#cir.const_record<{#cir.global_view<@_ZTV1D, [1 : i32, 2 : i32], address_point> : !cir.vptr}> : !rec_V1, #cir.const_record<{#cir.global_view<@_ZTV1D, [2 : i32, 2 : i32], address_point> : !cir.vptr}> : !rec_V2}> : !rec_B}> : !rec_D
+// LLVM: @d = global %struct.D { %struct.V3 { ptr getelementptr inbounds inrange(-16, 8) (i8, ptr @_ZTV1D, i64 16) }, %struct.B { %struct.V1 { ptr getelementptr inbounds inrange(-16, 8) (i8, ptr @_ZTV1D, i64 40) }, %struct.V2 { ptr getelementptr inbounds inrange(-16, 8) (i8, ptr @_ZTV1D, i64 64) } } }
// OGCG: @d = global { ptr, ptr, ptr } { ptr getelementptr inbounds inrange(-16, 8) (i8, ptr @_ZTV1D, i64 16), ptr getelementptr inbounds inrange(-16, 8) (i8, ptr @_ZTV1D, i64 40), ptr getelementptr inbounds inrange(-16, 8) (i8, ptr @_ZTV1D, i64 64) }
diff --git a/clang/test/CIR/CodeGenCXX/vtable-linkage.cpp b/clang/test/CIR/CodeGenCXX/vtable-linkage.cpp
index cdef1dbeb086a..4e5440d637db1 100644
--- a/clang/test/CIR/CodeGenCXX/vtable-linkage.cpp
+++ b/clang/test/CIR/CodeGenCXX/vtable-linkage.cpp
@@ -117,11 +117,11 @@ void use_F() {
// CIR-DAG: cir.global "private" constant linkonce_odr comdat @_ZTV1C = #cir.vtable<{#cir.const_array<[#cir.ptr<null> : !cir.ptr<!u8i>, #cir.ptr<null> : !cir.ptr<!u8i>, #cir.ptr<null> : !cir.ptr<!u8i>, #cir.global_view<@_ZTI1C> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1C1fEv> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 5>}> : !{{.*}}align(8)
// CIR-DAG: cir.global constant linkonce_odr comdat @_ZTS1C = #cir.const_array<"1C" : !cir.array<!s8i x 2>, trailing_zeros> : !cir.array<!s8i x 3> align(1)
// CIR-DAG: cir.global constant linkonce_odr comdat @_ZTI1C = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv121__vmi_class_type_infoE, [2 : i32]> : !cir.ptr<!u8i>, #cir.global_view<@_ZTS1C> : !cir.ptr<!u8i>, #cir.int<0> : !u32i, #cir.int<1> : !u32i, #cir.global_view<@_ZTI1B> : !cir.ptr<!u8i>, #cir.int<-8189> : !s64i}> : !{{.*}}align(8)
-// CIR-DAG: cir.global constant linkonce_odr comdat @_ZTT1C = #cir.const_array<[#cir.global_view<@_ZTV1C, [0 : i32, 4 : i32]> : !cir.ptr<!u8i>, #cir.global_view<@_ZTV1C, [0 : i32, 4 : i32]> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 2> align(8)
+// CIR-DAG: cir.global constant linkonce_odr comdat @_ZTT1C = #cir.const_array<[#cir.global_view<@_ZTV1C, [0 : i32, 4 : i32], address_point> : !cir.ptr<!u8i>, #cir.global_view<@_ZTV1C, [0 : i32, 4 : i32], address_point> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 2> align(8)
// LLVM-DAG: @_ZTV1C = linkonce_odr constant { [5 x ptr] } { [5 x ptr] [ptr null, ptr null, ptr null, ptr @_ZTI1C, ptr @_ZN1C1fEv] }, comdat, align 8
// LLVM-DAG: @_ZTS1C = linkonce_odr constant [{{[0-9]}} x i8] c"1C\00", comdat, align 1
// LLVM-DAG: @_ZTI1C = linkonce_odr constant { ptr, ptr, i32, i32, ptr, i64 } { ptr getelementptr{{.*}}({{.*}}, ptr @_ZTVN10__cxxabiv121__vmi_class_type_infoE, i64 {{.*}}), ptr @_ZTS1C, i32 0, i32 1, ptr @_ZTI1B, i64 -8189 }, comdat
-// LLVM-DAG: @_ZTT1C = linkonce_odr{{.*}} constant [2 x ptr] [ptr getelementptr inbounds{{.*}}({{.*}}, ptr @_ZTV1C, {{.*}}, ptr getelementptr inbounds {{.*}}({{.*}}, ptr @_ZTV1C{{.*}})]
+// LLVM-DAG: @_ZTT1C = linkonce_odr{{.*}} constant [2 x ptr] [ptr getelementptr inbounds inrange(-32, 8) (i8, ptr @_ZTV1C, i64 32), ptr getelementptr inbounds inrange(-32, 8) (i8, ptr @_ZTV1C, i64 32)]
// D has a key function that is defined in this translation unit so its vtable is
// defined in the translation unit.
diff --git a/clang/test/CIR/IR/global-view-address-point.cir b/clang/test/CIR/IR/global-view-address-point.cir
new file mode 100644
index 0000000000000..e947e1fd8ad91
--- /dev/null
+++ b/clang/test/CIR/IR/global-view-address-point.cir
@@ -0,0 +1,35 @@
+// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
+// RUN: cir-opt %s -cir-to-llvm | FileCheck %s -check-prefix=MLIR
+// RUN: cir-translate %s -cir-to-llvmir --target x86_64-unknown-linux-gnu --disable-cc-lowering | FileCheck %s -check-prefix=LLVM
+
+!u8i = !cir.int<u, 8>
+!rec_anon_struct = !cir.struct<{data !cir.array<!cir.ptr<!u8i> x 5>, data !cir.array<!cir.ptr<!u8i> x 4>}>
+!rec_B = !cir.struct<"B" {data !cir.vptr}>
+module {
+ cir.global "private" constant external @_ZTV1D : !rec_anon_struct
+
+ // CHECK: #cir.global_view<@_ZTV1D, [0 : i32, 3 : i32], address_point> : !cir.ptr<!u8i>
+ // CHECK: #cir.global_view<@_ZTV1D, [1 : i32, 2 : i32], address_point> : !cir.ptr<!u8i>
+ // CHECK: #cir.global_view<@_ZTV1D, [1 : i32, 2 : i32]> : !cir.ptr<!u8i>
+ cir.global external @vtt = #cir.const_array<[
+ #cir.global_view<@_ZTV1D, [0 : i32, 3 : i32], address_point> : !cir.ptr<!u8i>,
+ #cir.global_view<@_ZTV1D, [1 : i32, 2 : i32], address_point> : !cir.ptr<!u8i>,
+ #cir.global_view<@_ZTV1D, [1 : i32, 2 : i32]> : !cir.ptr<!u8i>
+ ]> : !cir.array<!cir.ptr<!u8i> x 3>
+
+ // CHECK: #cir.global_view<@_ZTV1D, [0 : i32, 3 : i32], address_point> : !cir.vptr
+ cir.global external @b = #cir.const_record<{#cir.global_view<@_ZTV1D, [0 : i32, 3 : i32], address_point> : !cir.vptr}> : !rec_B
+}
+
+// An address point is inbounds and can only reach the vtable it points into.
+
+// MLIR: llvm.getelementptr inbounds inrange <i64, -24, 16> %{{.*}}[0, 0, 3]
+// MLIR: llvm.getelementptr inbounds inrange <i64, -16, 16> %{{.*}}[0, 1, 2]
+// MLIR: llvm.getelementptr %{{.*}}[0, 1, 2]
+// MLIR: llvm.getelementptr inbounds inrange <i64, -24, 16> %{{.*}}[0, 0, 3]
+
+// LLVM: @vtt = global [3 x ptr] [
+// LLVM-SAME: ptr getelementptr inbounds inrange(-24, 16) (i8, ptr @_ZTV1D, i64 24),
+// LLVM-SAME: ptr getelementptr inbounds inrange(-16, 16) (i8, ptr @_ZTV1D, i64 56),
+// LLVM-SAME: ptr getelementptr (i8, ptr @_ZTV1D, i64 56)]
+// LLVM: @b = global %struct.B { ptr getelementptr inbounds inrange(-24, 16) (i8, ptr @_ZTV1D, i64 24) }
More information about the cfe-commits
mailing list