[clang] [CIR] Add support for accessing members of base classes (PR #143195)
Andy Kaylor via cfe-commits
cfe-commits at lists.llvm.org
Fri Jun 6 16:32:40 PDT 2025
https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/143195
>From 6fb28507d57ce04faa712b630e308ba029230fd5 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Fri, 30 May 2025 16:07:19 -0700
Subject: [PATCH 1/2] [CIR] Add support for accessing members of base classes
This change adds the support for accessing a member of a base class
from a derived class object.
---
.../CIR/Dialect/Builder/CIRBaseBuilder.h | 5 ++
clang/include/clang/CIR/Dialect/IR/CIROps.td | 44 ++++++++++++
clang/include/clang/CIR/MissingFeatures.h | 5 ++
clang/lib/CIR/CodeGen/Address.h | 14 ++++
clang/lib/CIR/CodeGen/CIRGenBuilder.cpp | 12 ++++
clang/lib/CIR/CodeGen/CIRGenBuilder.h | 12 ++++
clang/lib/CIR/CodeGen/CIRGenClass.cpp | 69 +++++++++++++++++++
clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 34 +++++++--
clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 22 ++++++
clang/lib/CIR/CodeGen/CIRGenFunction.h | 7 ++
clang/lib/CIR/CodeGen/CIRGenModule.cpp | 29 ++++++++
clang/lib/CIR/CodeGen/CIRGenModule.h | 5 ++
clang/lib/CIR/CodeGen/CMakeLists.txt | 1 +
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 33 +++++++++
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.h | 10 +++
clang/test/CIR/CodeGen/class.cpp | 49 +++++++++++++
16 files changed, 346 insertions(+), 5 deletions(-)
create mode 100644 clang/lib/CIR/CodeGen/CIRGenClass.cpp
diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index 7a5f192618a5d..a3754f4de66b0 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -278,6 +278,11 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
return createCast(loc, cir::CastKind::bitcast, src, newTy);
}
+ mlir::Value createPtrBitcast(mlir::Value src, mlir::Type newPointeeTy) {
+ assert(mlir::isa<cir::PointerType>(src.getType()) && "expected ptr src");
+ return createBitcast(src, getPointerTo(newPointeeTy));
+ }
+
//===--------------------------------------------------------------------===//
// Binary Operators
//===--------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 00878f7dd8ed7..962b5be051610 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2231,4 +2231,48 @@ def VecTernaryOp : CIR_Op<"vec.ternary",
let hasVerifier = 1;
}
+//===----------------------------------------------------------------------===//
+// BaseClassAddrOp
+//===----------------------------------------------------------------------===//
+
+def BaseClassAddrOp : CIR_Op<"base_class_addr"> {
+ let summary = "Get the base class address for a class/struct";
+ let description = [{
+ The `cir.base_class_addr` operaration gets the address of a particular
+ non-virtual base class given a derived class pointer. The offset in bytes
+ of the base class must be passed in, since it is easier for the front end
+ to calculate that than the MLIR passes. The operation contains a flag for
+ whether or not the operand may be nullptr. That depends on the context and
+ cannot be known by the operation, and that information affects how the
+ operation is lowered.
+
+ Example:
+ ```c++
+ struct Base { };
+ struct Derived : Base { };
+ Derived d;
+ Base& b = d;
+ ```
+ will generate
+ ```mlir
+ %3 = cir.base_class_addr (%1 : !cir.ptr<!rec_Derived> nonnull) [0] -> !cir.ptr<!rec_Base>
+ ```
+ }];
+
+ // The validity of the relationship of derived and base cannot yet be
+ // verified, currently not worth adding a verifier.
+ let arguments = (ins
+ Arg<CIR_PointerType, "derived class pointer", [MemRead]>:$derived_addr,
+ IndexAttr:$offset, UnitAttr:$assume_not_null);
+
+ let results = (outs Res<CIR_PointerType, "">:$base_addr);
+
+ let assemblyFormat = [{
+ `(`
+ $derived_addr `:` qualified(type($derived_addr))
+ (`nonnull` $assume_not_null^)?
+ `)` `[` $offset `]` `->` qualified(type($base_addr)) attr-dict
+ }];
+}
+
#endif // CLANG_CIR_DIALECT_IR_CIROPS_TD
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index f1e0c15d41f64..b4e928f32f130 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -151,6 +151,11 @@ struct MissingFeatures {
static bool cxxabiAppleARM64CXXABI() { return false; }
static bool cxxabiStructorImplicitParam() { return false; }
+ // Address class
+ static bool addressOffset() { return false; }
+ static bool addressIsKnownNonNull() { return false; }
+ static bool addressPointerAuthInfo() { return false; }
+
// Misc
static bool cirgenABIInfo() { return false; }
static bool abiArgInfo() { return false; }
diff --git a/clang/lib/CIR/CodeGen/Address.h b/clang/lib/CIR/CodeGen/Address.h
index 2cc8ada783197..6f76c3ebb1c5e 100644
--- a/clang/lib/CIR/CodeGen/Address.h
+++ b/clang/lib/CIR/CodeGen/Address.h
@@ -21,6 +21,9 @@
namespace clang::CIRGen {
+// Forward declaration to avoid a circular dependency
+class CIRGenBuilderTy;
+
class Address {
// The boolean flag indicates whether the pointer is known to be non-null.
@@ -65,11 +68,22 @@ class Address {
return pointerAndKnownNonNull.getPointer() != nullptr;
}
+ /// Return address with different element type, a bitcast pointer, and
+ /// the same alignment.
+ Address withElementType(CIRGenBuilderTy &builder, mlir::Type ElemTy) const;
+
mlir::Value getPointer() const {
assert(isValid());
return pointerAndKnownNonNull.getPointer();
}
+ mlir::Value getBasePointer() const {
+ // TODO(cir): Remove the version above when we catchup with OG codegen on
+ // ptr auth.
+ assert(isValid() && "pointer isn't valid");
+ return getPointer();
+ }
+
mlir::Type getType() const {
assert(mlir::cast<cir::PointerType>(
pointerAndKnownNonNull.getPointer().getType())
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
index 5620821a5375a..4c8c6ed289c3b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
@@ -38,3 +38,15 @@ mlir::Value CIRGenBuilderTy::getArrayElement(mlir::Location arrayLocBegin,
const mlir::Type flatPtrTy = basePtr.getType();
return create<cir::PtrStrideOp>(arrayLocEnd, flatPtrTy, basePtr, idx);
}
+
+// This can't be defined in Address.h because that file is included by
+// CIRGenBuilder.h
+Address Address::withElementType(CIRGenBuilderTy &builder,
+ mlir::Type elemTy) const {
+ assert(!cir::MissingFeatures::addressOffset());
+ assert(!cir::MissingFeatures::addressIsKnownNonNull());
+ assert(!cir::MissingFeatures::addressPointerAuthInfo());
+
+ return Address(builder.createPtrBitcast(getBasePointer(), elemTy), elemTy,
+ getAlignment());
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index 5f33ae1af35ee..03077ee062a65 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -309,6 +309,18 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
return create<cir::BinOp>(loc, cir::BinOpKind::Div, lhs, rhs);
}
+ Address createBaseClassAddr(mlir::Location loc, Address addr,
+ mlir::Type destType, unsigned offset,
+ bool assumeNotNull) {
+ if (destType == addr.getElementType())
+ return addr;
+
+ auto ptrTy = getPointerTo(destType);
+ auto baseAddr = create<cir::BaseClassAddrOp>(
+ loc, ptrTy, addr.getPointer(), mlir::APInt(64, offset), assumeNotNull);
+ return Address(baseAddr, destType, addr.getAlignment());
+ }
+
cir::LoadOp createLoad(mlir::Location loc, Address addr,
bool isVolatile = false) {
mlir::IntegerAttr align = getAlignmentAttr(addr.getAlignment());
diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
new file mode 100644
index 0000000000000..7fe022abe504d
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 contains code dealing with C++ code generation of classes
+//
+//===----------------------------------------------------------------------===//
+
+#include "CIRGenFunction.h"
+
+#include "clang/AST/RecordLayout.h"
+#include "clang/CIR/MissingFeatures.h"
+
+using namespace clang;
+using namespace clang::CIRGen;
+
+Address
+CIRGenFunction::getAddressOfBaseClass(Address value,
+ const CXXRecordDecl *derived,
+ CastExpr::path_const_iterator pathBegin,
+ CastExpr::path_const_iterator pathEnd,
+ bool nullCheckValue, SourceLocation loc) {
+ assert(pathBegin != pathEnd && "Base path should not be empty!");
+
+ CastExpr::path_const_iterator start = pathBegin;
+
+ if ((*start)->isVirtual()) {
+ // The implementation here is actually complete, but let's flag this
+ // as an error until the rest of the virtual base class support is in place.
+ cgm.errorNYI(loc, "getAddrOfBaseClass: virtual base");
+ return Address::invalid();
+ }
+
+ // Compute the static offset of the ultimate destination within its
+ // allocating subobject (the virtual base, if there is one, or else
+ // the "complete" object that we see).
+ CharUnits nonVirtualOffset =
+ cgm.computeNonVirtualBaseClassOffset(derived, start, pathEnd);
+
+ // Get the base pointer type.
+ mlir::Type baseValueTy = convertType((pathEnd[-1])->getType());
+ assert(!cir::MissingFeatures::addressSpace());
+
+ // The if statement here is redundant now, but it will be needed when we add
+ // support for virtual base classes.
+ // If there is no virtual base, use cir.base_class_addr. It takes care of
+ // the adjustment and the null pointer check.
+ if (nonVirtualOffset.isZero()) {
+ assert(!cir::MissingFeatures::sanitizers());
+ return builder.createBaseClassAddr(getLoc(loc), value, baseValueTy, 0,
+ /*assumeNotNull=*/true);
+ }
+
+ assert(!cir::MissingFeatures::sanitizers());
+
+ // Apply the offset
+ value = builder.createBaseClassAddr(getLoc(loc), value, baseValueTy,
+ nonVirtualOffset.getQuantity(),
+ /*assumeNotNull=*/true);
+
+ // Cast to the destination type.
+ value = value.withElementType(builder, baseValueTy);
+
+ return value;
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 1175fdc0be2cf..8e72d8000428d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -97,9 +97,14 @@ Address CIRGenFunction::emitPointerWithAlignment(const Expr *expr,
case CK_UncheckedDerivedToBase:
case CK_DerivedToBase: {
- cgm.errorNYI(expr->getSourceRange(),
- "emitPointerWithAlignment: derived-to-base cast");
- return Address::invalid();
+ assert(!cir::MissingFeatures::opTBAA());
+ assert(!cir::MissingFeatures::addressIsKnownNonNull());
+ Address addr = emitPointerWithAlignment(ce->getSubExpr(), baseInfo);
+ const CXXRecordDecl *derived =
+ ce->getSubExpr()->getType()->getPointeeCXXRecordDecl();
+ return getAddressOfBaseClass(
+ addr, derived, ce->path_begin(), ce->path_end(),
+ shouldNullCheckClassCastValue(ce), ce->getExprLoc());
}
case CK_AnyPointerToBlockPointerCast:
@@ -823,8 +828,6 @@ LValue CIRGenFunction::emitCastLValue(const CastExpr *e) {
case CK_NonAtomicToAtomic:
case CK_AtomicToNonAtomic:
case CK_Dynamic:
- case CK_UncheckedDerivedToBase:
- case CK_DerivedToBase:
case CK_ToUnion:
case CK_BaseToDerived:
case CK_LValueBitCast:
@@ -863,6 +866,27 @@ LValue CIRGenFunction::emitCastLValue(const CastExpr *e) {
return lv;
}
+ case CK_UncheckedDerivedToBase:
+ case CK_DerivedToBase: {
+ const auto *derivedClassTy =
+ e->getSubExpr()->getType()->castAs<clang::RecordType>();
+ auto *derivedClassDecl = cast<CXXRecordDecl>(derivedClassTy->getDecl());
+
+ LValue lv = emitLValue(e->getSubExpr());
+ Address thisAddr = lv.getAddress();
+
+ // Perform the derived-to-base conversion
+ Address baseAddr = getAddressOfBaseClass(
+ thisAddr, derivedClassDecl, e->path_begin(), e->path_end(),
+ /*NullCheckValue=*/false, e->getExprLoc());
+
+ // TODO: Support accesses to members of base classes in TBAA. For now, we
+ // conservatively pretend that the complete object is of the base class
+ // type.
+ assert(!cir::MissingFeatures::opTBAA());
+ return makeAddrLValue(baseAddr, e->getType(), lv.getBaseInfo());
+ }
+
case CK_ZeroToOCLOpaqueType:
llvm_unreachable("NULL to OpenCL opaque type lvalue cast is not valid");
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index b008ee9b472a1..e32a5c836be02 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -16,6 +16,7 @@
#include "CIRGenCall.h"
#include "CIRGenValue.h"
#include "mlir/IR/Location.h"
+#include "clang/AST/ExprCXX.h"
#include "clang/AST/GlobalDecl.h"
#include "clang/CIR/MissingFeatures.h"
@@ -629,4 +630,25 @@ void CIRGenFunction::emitNullInitialization(mlir::Location loc, Address destPtr,
builder.createStore(loc, zeroValue, destPtr);
}
+// TODO(cir): should be shared with LLVM codegen.
+bool CIRGenFunction::shouldNullCheckClassCastValue(const CastExpr *ce) {
+ const Expr *e = ce->getSubExpr();
+
+ if (ce->getCastKind() == CK_UncheckedDerivedToBase)
+ return false;
+
+ if (isa<CXXThisExpr>(e->IgnoreParens())) {
+ // We always assume that 'this' is never null.
+ return false;
+ }
+
+ if (const ImplicitCastExpr *ice = dyn_cast<ImplicitCastExpr>(ce)) {
+ // And that glvalue casts are never null.
+ if (ice->isGLValue())
+ return false;
+ }
+
+ return true;
+}
+
} // namespace clang::CIRGen
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index ee014adc961be..78732da52d459 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -430,6 +430,8 @@ class CIRGenFunction : public CIRGenTypeCache {
// TODO: Add symbol table support
}
+ bool shouldNullCheckClassCastValue(const CastExpr *ce);
+
LValue makeNaturalAlignPointeeAddrLValue(mlir::Value v, clang::QualType t);
/// Construct an address with the natural alignment of T. If a pointer to T
@@ -445,6 +447,11 @@ class CIRGenFunction : public CIRGenTypeCache {
return Address(ptr, convertTypeForMem(t), alignment);
}
+ Address getAddressOfBaseClass(Address value, const CXXRecordDecl *derived,
+ CastExpr::path_const_iterator pathBegin,
+ CastExpr::path_const_iterator pathEnd,
+ bool nullCheckValue, SourceLocation loc);
+
LValue makeAddrLValue(Address addr, QualType ty,
AlignmentSource source = AlignmentSource::Type) {
return makeAddrLValue(addr, ty, LValueBaseInfo(source));
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index ce3c57e5f20a8..e0f65bba69f08 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -19,6 +19,7 @@
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclOpenACC.h"
#include "clang/AST/GlobalDecl.h"
+#include "clang/AST/RecordLayout.h"
#include "clang/Basic/SourceManager.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "clang/CIR/Interfaces/CIROpInterfaces.h"
@@ -1677,6 +1678,34 @@ bool CIRGenModule::verifyModule() const {
return mlir::verify(theModule).succeeded();
}
+// TODO(cir): this can be shared with LLVM codegen.
+CharUnits CIRGenModule::computeNonVirtualBaseClassOffset(
+ const CXXRecordDecl *derivedClass, CastExpr::path_const_iterator start,
+ CastExpr::path_const_iterator end) {
+ CharUnits offset = CharUnits::Zero();
+
+ const ASTContext &astContext = getASTContext();
+ const CXXRecordDecl *rd = derivedClass;
+
+ for (CastExpr::path_const_iterator i = start; i != end; ++i) {
+ const CXXBaseSpecifier *base = *i;
+ assert(!base->isVirtual() && "Should not see virtual bases here!");
+
+ // Get the layout.
+ const ASTRecordLayout &layout = astContext.getASTRecordLayout(rd);
+
+ const auto *baseDecl = cast<CXXRecordDecl>(
+ base->getType()->castAs<clang::RecordType>()->getDecl());
+
+ // Add the offset.
+ offset += layout.getBaseClassOffset(baseDecl);
+
+ rd = baseDecl;
+ }
+
+ return offset;
+}
+
DiagnosticBuilder CIRGenModule::errorNYI(SourceLocation loc,
llvm::StringRef feature) {
unsigned diagID = diags.getCustomDiagID(
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 7055170ac7ac3..5b112a0685509 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -141,6 +141,11 @@ class CIRGenModule : public CIRGenTypeCache {
getAddrOfGlobalVar(const VarDecl *d, mlir::Type ty = {},
ForDefinition_t isForDefinition = NotForDefinition);
+ CharUnits
+ computeNonVirtualBaseClassOffset(const CXXRecordDecl *derivedClass,
+ CastExpr::path_const_iterator start,
+ CastExpr::path_const_iterator end);
+
/// Return a constant array for the given string.
mlir::Attribute getConstantArrayFromStringLiteral(const StringLiteral *e);
diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt
index 185a0e10547af..8bfcd2773d07a 100644
--- a/clang/lib/CIR/CodeGen/CMakeLists.txt
+++ b/clang/lib/CIR/CodeGen/CMakeLists.txt
@@ -10,6 +10,7 @@ add_clang_library(clangCIR
CIRGenerator.cpp
CIRGenBuilder.cpp
CIRGenCall.cpp
+ CIRGenClass.cpp
CIRGenCXXABI.cpp
CIRGenCXXExpr.cpp
CIRGenDecl.cpp
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 8059836db6b71..210afba86444a 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -673,6 +673,38 @@ mlir::LogicalResult CIRToLLVMPtrStrideOpLowering::matchAndRewrite(
return mlir::success();
}
+mlir::LogicalResult CIRToLLVMBaseClassAddrOpLowering::matchAndRewrite(
+ cir::BaseClassAddrOp baseClassOp, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ const mlir::Type resultType =
+ getTypeConverter()->convertType(baseClassOp.getType());
+ mlir::Value derivedAddr = adaptor.getDerivedAddr();
+ llvm::SmallVector<mlir::LLVM::GEPArg, 1> offset = {
+ adaptor.getOffset().getZExtValue()};
+ mlir::Type byteType = mlir::IntegerType::get(resultType.getContext(), 8,
+ mlir::IntegerType::Signless);
+ if (adaptor.getOffset().getZExtValue() == 0) {
+ rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(
+ baseClassOp, resultType, adaptor.getDerivedAddr());
+ return mlir::success();
+ }
+
+ if (baseClassOp.getAssumeNotNull()) {
+ rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(
+ baseClassOp, resultType, byteType, derivedAddr, offset);
+ } else {
+ auto loc = baseClassOp.getLoc();
+ mlir::Value isNull = rewriter.create<mlir::LLVM::ICmpOp>(
+ loc, mlir::LLVM::ICmpPredicate::eq, derivedAddr,
+ rewriter.create<mlir::LLVM::ZeroOp>(loc, derivedAddr.getType()));
+ mlir::Value adjusted = rewriter.create<mlir::LLVM::GEPOp>(
+ loc, resultType, byteType, derivedAddr, offset);
+ rewriter.replaceOpWithNewOp<mlir::LLVM::SelectOp>(baseClassOp, isNull,
+ derivedAddr, adjusted);
+ }
+ return mlir::success();
+}
+
mlir::LogicalResult CIRToLLVMAllocaOpLowering::matchAndRewrite(
cir::AllocaOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
@@ -1752,6 +1784,7 @@ void ConvertCIRToLLVMPass::runOnOperation() {
dl);
patterns.add<
// clang-format off
+ CIRToLLVMBaseClassAddrOpLowering,
CIRToLLVMBinOpLowering,
CIRToLLVMBrCondOpLowering,
CIRToLLVMBrOpLowering,
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
index d1efa4e181a07..fef7e240940e3 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -297,6 +297,16 @@ class CIRToLLVMPtrStrideOpLowering
mlir::ConversionPatternRewriter &) const override;
};
+class CIRToLLVMBaseClassAddrOpLowering
+ : public mlir::OpConversionPattern<cir::BaseClassAddrOp> {
+public:
+ using mlir::OpConversionPattern<cir::BaseClassAddrOp>::OpConversionPattern;
+
+ mlir::LogicalResult
+ matchAndRewrite(cir::BaseClassAddrOp op, OpAdaptor,
+ mlir::ConversionPatternRewriter &) const override;
+};
+
class CIRToLLVMStackSaveOpLowering
: public mlir::OpConversionPattern<cir::StackSaveOp> {
public:
diff --git a/clang/test/CIR/CodeGen/class.cpp b/clang/test/CIR/CodeGen/class.cpp
index 7c41bdb7112e9..6f5c1b7769c70 100644
--- a/clang/test/CIR/CodeGen/class.cpp
+++ b/clang/test/CIR/CodeGen/class.cpp
@@ -51,3 +51,52 @@ class Derived : public Base {
int use(Derived *d) { return d->b; }
+// CIR: cir.func @_Z3useP7Derived(%[[ARG0:.*]]: !cir.ptr<!rec_Derived>
+// CIR: %[[D_ADDR:.*]] = cir.alloca !cir.ptr<!rec_Derived>, !cir.ptr<!cir.ptr<!rec_Derived>>, ["d", init]
+// CIR: cir.store %[[ARG0]], %[[D_ADDR]]
+// CIR: %[[D_PTR:.*]] = cir.load align(8) %0
+// CIR: %[[D_B_ADDR:.*]] = cir.get_member %[[D_PTR]][1] {name = "b"}
+// CIR: %[[D_B:.*]] = cir.load align(4) %[[D_B_ADDR]]
+
+// LLVM: define{{.*}} i32 @_Z3useP7Derived
+// LLVM: getelementptr %class.Derived, ptr %{{.*}}, i32 0, i32 1
+
+// OGCG: define{{.*}} i32 @_Z3useP7Derived
+// OGCG: getelementptr inbounds nuw %class.Derived, ptr %{{.*}}, i32 0, i32 1
+
+int use_base() {
+ Derived d;
+ return d.a;
+}
+
+// CIR: cir.func @_Z8use_basev
+// CIR: %[[D_ADDR:.*]] = cir.alloca !rec_Derived, !cir.ptr<!rec_Derived>, ["d"]
+// CIR: %[[BASE_ADDR:.*]] cir.base_class_addr(%[[D_ADDR]] : !cir.ptr<!rec_Derived> nonnull) [0] -> !cir.ptr<!rec_Base>
+// CIR: %[[D_A_ADDR:.*]] = cir.get_member %2[0] {name = "a"} : !cir.ptr<!rec_Base> -> !cir.ptr<!s32i>
+// CIR: %[[D_A:.*]] = cir.load align(4) %3 : !cir.ptr<!s32i>, !s32i
+
+// LLVM: define{{.*}} i32 @_Z8use_basev
+// LLVM: %[[D:.*]] = alloca %class.Derived
+// LLVM: %[[D_A_ADDR:.*]] = getelementptr %class.Base, ptr %[[D]], i32 0, i32 0
+
+// OGCG: define{{.*}} i32 @_Z8use_basev
+// OGCG: %[[D:.*]] = alloca %class.Derived
+// OGCG: %[[D_A_ADDR:.*]] = getelementptr inbounds nuw %class.Base, ptr %[[D]], i32 0, i32 0
+
+int use_base_via_pointer(Derived *d) {
+ return d->a;
+}
+
+// CIR: cir.func @_Z20use_base_via_pointerP7Derived(%[[ARG0:.*]]: !cir.ptr<!rec_Derived>
+// CIR: %[[D_ADDR:.*]] = cir.alloca !cir.ptr<!rec_Derived>, !cir.ptr<!cir.ptr<!rec_Derived>>, ["d", init]
+// CIR: cir.store %[[ARG0]], %[[D_ADDR]]
+// CIR: %[[D:.*]] = cir.load align(8) %[[D_ADDR]]
+// CIR: %[[BASE_ADDR:.*]] = cir.base_class_addr(%[[D]] : !cir.ptr<!rec_Derived> nonnull) [0] -> !cir.ptr<!rec_Base>
+// CIR: %[[D_A_ADDR:.*]] = cir.get_member %[[BASE_ADDR]][0] {name = "a"}
+// CIR: %[[D_A:.*]] = cir.load align(4) %[[D_A_ADDR]]
+
+// LLVM: define{{.*}} i32 @_Z20use_base_via_pointerP7Derived
+// LLVM: %[[D_A_ADDR:.*]] = getelementptr %class.Base, ptr %{{.*}}, i32 0, i32 0
+
+// OGCG: define{{.*}} i32 @_Z20use_base_via_pointerP7Derived
+// OGCG: %[[D_A_ADDR:.*]] = getelementptr inbounds nuw %class.Base, ptr %{{.*}}, i32 0, i32 0
>From 3cab953633928fb14cdd3d49e093f088b20e9a6d Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Fri, 6 Jun 2025 16:31:00 -0700
Subject: [PATCH 2/2] Use range for path iterators
---
clang/lib/CIR/CodeGen/CIRGenClass.cpp | 20 ++++++++------------
clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 12 ++++++------
clang/lib/CIR/CodeGen/CIRGenFunction.h | 8 ++++----
clang/lib/CIR/CodeGen/CIRGenModule.cpp | 7 +++----
clang/lib/CIR/CodeGen/CIRGenModule.h | 7 +++----
5 files changed, 24 insertions(+), 30 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index 7fe022abe504d..4cdaa480121dd 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -18,17 +18,13 @@
using namespace clang;
using namespace clang::CIRGen;
-Address
-CIRGenFunction::getAddressOfBaseClass(Address value,
- const CXXRecordDecl *derived,
- CastExpr::path_const_iterator pathBegin,
- CastExpr::path_const_iterator pathEnd,
- bool nullCheckValue, SourceLocation loc) {
- assert(pathBegin != pathEnd && "Base path should not be empty!");
+Address CIRGenFunction::getAddressOfBaseClass(
+ Address value, const CXXRecordDecl *derived,
+ llvm::iterator_range<CastExpr::path_const_iterator> path,
+ bool nullCheckValue, SourceLocation loc) {
+ assert(!path.empty() && "Base path should not be empty!");
- CastExpr::path_const_iterator start = pathBegin;
-
- if ((*start)->isVirtual()) {
+ if ((*path.begin())->isVirtual()) {
// The implementation here is actually complete, but let's flag this
// as an error until the rest of the virtual base class support is in place.
cgm.errorNYI(loc, "getAddrOfBaseClass: virtual base");
@@ -39,10 +35,10 @@ CIRGenFunction::getAddressOfBaseClass(Address value,
// allocating subobject (the virtual base, if there is one, or else
// the "complete" object that we see).
CharUnits nonVirtualOffset =
- cgm.computeNonVirtualBaseClassOffset(derived, start, pathEnd);
+ cgm.computeNonVirtualBaseClassOffset(derived, path);
// Get the base pointer type.
- mlir::Type baseValueTy = convertType((pathEnd[-1])->getType());
+ mlir::Type baseValueTy = convertType((path.end()[-1])->getType());
assert(!cir::MissingFeatures::addressSpace());
// The if statement here is redundant now, but it will be needed when we add
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 8e72d8000428d..cd724dbc1193e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -102,9 +102,9 @@ Address CIRGenFunction::emitPointerWithAlignment(const Expr *expr,
Address addr = emitPointerWithAlignment(ce->getSubExpr(), baseInfo);
const CXXRecordDecl *derived =
ce->getSubExpr()->getType()->getPointeeCXXRecordDecl();
- return getAddressOfBaseClass(
- addr, derived, ce->path_begin(), ce->path_end(),
- shouldNullCheckClassCastValue(ce), ce->getExprLoc());
+ return getAddressOfBaseClass(addr, derived, ce->path(),
+ shouldNullCheckClassCastValue(ce),
+ ce->getExprLoc());
}
case CK_AnyPointerToBlockPointerCast:
@@ -876,9 +876,9 @@ LValue CIRGenFunction::emitCastLValue(const CastExpr *e) {
Address thisAddr = lv.getAddress();
// Perform the derived-to-base conversion
- Address baseAddr = getAddressOfBaseClass(
- thisAddr, derivedClassDecl, e->path_begin(), e->path_end(),
- /*NullCheckValue=*/false, e->getExprLoc());
+ Address baseAddr =
+ getAddressOfBaseClass(thisAddr, derivedClassDecl, e->path(),
+ /*NullCheckValue=*/false, e->getExprLoc());
// TODO: Support accesses to members of base classes in TBAA. For now, we
// conservatively pretend that the complete object is of the base class
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 78732da52d459..018fe4bdac599 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -447,10 +447,10 @@ class CIRGenFunction : public CIRGenTypeCache {
return Address(ptr, convertTypeForMem(t), alignment);
}
- Address getAddressOfBaseClass(Address value, const CXXRecordDecl *derived,
- CastExpr::path_const_iterator pathBegin,
- CastExpr::path_const_iterator pathEnd,
- bool nullCheckValue, SourceLocation loc);
+ Address getAddressOfBaseClass(
+ Address value, const CXXRecordDecl *derived,
+ llvm::iterator_range<CastExpr::path_const_iterator> path,
+ bool nullCheckValue, SourceLocation loc);
LValue makeAddrLValue(Address addr, QualType ty,
AlignmentSource source = AlignmentSource::Type) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index e0f65bba69f08..371bff4d761ef 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -1680,15 +1680,14 @@ bool CIRGenModule::verifyModule() const {
// TODO(cir): this can be shared with LLVM codegen.
CharUnits CIRGenModule::computeNonVirtualBaseClassOffset(
- const CXXRecordDecl *derivedClass, CastExpr::path_const_iterator start,
- CastExpr::path_const_iterator end) {
+ const CXXRecordDecl *derivedClass,
+ llvm::iterator_range<CastExpr::path_const_iterator> path) {
CharUnits offset = CharUnits::Zero();
const ASTContext &astContext = getASTContext();
const CXXRecordDecl *rd = derivedClass;
- for (CastExpr::path_const_iterator i = start; i != end; ++i) {
- const CXXBaseSpecifier *base = *i;
+ for (const CXXBaseSpecifier *base : path) {
assert(!base->isVirtual() && "Should not see virtual bases here!");
// Get the layout.
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 5b112a0685509..4ed779eab244d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -141,10 +141,9 @@ class CIRGenModule : public CIRGenTypeCache {
getAddrOfGlobalVar(const VarDecl *d, mlir::Type ty = {},
ForDefinition_t isForDefinition = NotForDefinition);
- CharUnits
- computeNonVirtualBaseClassOffset(const CXXRecordDecl *derivedClass,
- CastExpr::path_const_iterator start,
- CastExpr::path_const_iterator end);
+ CharUnits computeNonVirtualBaseClassOffset(
+ const CXXRecordDecl *derivedClass,
+ llvm::iterator_range<CastExpr::path_const_iterator> path);
/// Return a constant array for the given string.
mlir::Attribute getConstantArrayFromStringLiteral(const StringLiteral *e);
More information about the cfe-commits
mailing list