Skip to content

Commit aab15ca

Browse files
andykaylortomtor
authored andcommitted
[CIR] Add support for accessing members of base classes (llvm#143195)
This change adds the support for accessing a member of a base class from a derived class object.
1 parent 64a7774 commit aab15ca

File tree

16 files changed

+339
-5
lines changed

16 files changed

+339
-5
lines changed

clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,11 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
278278
return createCast(loc, cir::CastKind::bitcast, src, newTy);
279279
}
280280

281+
mlir::Value createPtrBitcast(mlir::Value src, mlir::Type newPointeeTy) {
282+
assert(mlir::isa<cir::PointerType>(src.getType()) && "expected ptr src");
283+
return createBitcast(src, getPointerTo(newPointeeTy));
284+
}
285+
281286
//===--------------------------------------------------------------------===//
282287
// Binary Operators
283288
//===--------------------------------------------------------------------===//

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2275,4 +2275,47 @@ def VecTernaryOp : CIR_Op<"vec.ternary",
22752275
let hasFolder = 1;
22762276
}
22772277

2278+
//===----------------------------------------------------------------------===//
2279+
// BaseClassAddrOp
2280+
//===----------------------------------------------------------------------===//
2281+
2282+
def BaseClassAddrOp : CIR_Op<"base_class_addr"> {
2283+
let summary = "Get the base class address for a class/struct";
2284+
let description = [{
2285+
The `cir.base_class_addr` operaration gets the address of a particular
2286+
non-virtual base class given a derived class pointer. The offset in bytes
2287+
of the base class must be passed in, since it is easier for the front end
2288+
to calculate that than the MLIR passes. The operation contains a flag for
2289+
whether or not the operand may be nullptr. That depends on the context and
2290+
cannot be known by the operation, and that information affects how the
2291+
operation is lowered.
2292+
2293+
Example:
2294+
```c++
2295+
struct Base { };
2296+
struct Derived : Base { };
2297+
Derived d;
2298+
Base& b = d;
2299+
```
2300+
will generate
2301+
```mlir
2302+
%3 = cir.base_class_addr %1 : !cir.ptr<!rec_Derived> nonnull [0] -> !cir.ptr<!rec_Base>
2303+
```
2304+
}];
2305+
2306+
// The validity of the relationship of derived and base cannot yet be
2307+
// verified, currently not worth adding a verifier.
2308+
let arguments = (ins
2309+
Arg<CIR_PointerType, "derived class pointer", [MemRead]>:$derived_addr,
2310+
IndexAttr:$offset, UnitAttr:$assume_not_null);
2311+
2312+
let results = (outs Res<CIR_PointerType, "">:$base_addr);
2313+
2314+
let assemblyFormat = [{
2315+
$derived_addr `:` qualified(type($derived_addr))
2316+
(`nonnull` $assume_not_null^)?
2317+
` ` `[` $offset `]` `->` qualified(type($base_addr)) attr-dict
2318+
}];
2319+
}
2320+
22782321
#endif // CLANG_CIR_DIALECT_IR_CIROPS_TD

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,11 @@ struct MissingFeatures {
151151
static bool cxxabiAppleARM64CXXABI() { return false; }
152152
static bool cxxabiStructorImplicitParam() { return false; }
153153

154+
// Address class
155+
static bool addressOffset() { return false; }
156+
static bool addressIsKnownNonNull() { return false; }
157+
static bool addressPointerAuthInfo() { return false; }
158+
154159
// Misc
155160
static bool cirgenABIInfo() { return false; }
156161
static bool abiArgInfo() { return false; }

clang/lib/CIR/CodeGen/Address.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121

2222
namespace clang::CIRGen {
2323

24+
// Forward declaration to avoid a circular dependency
25+
class CIRGenBuilderTy;
26+
2427
class Address {
2528

2629
// The boolean flag indicates whether the pointer is known to be non-null.
@@ -65,11 +68,22 @@ class Address {
6568
return pointerAndKnownNonNull.getPointer() != nullptr;
6669
}
6770

71+
/// Return address with different element type, a bitcast pointer, and
72+
/// the same alignment.
73+
Address withElementType(CIRGenBuilderTy &builder, mlir::Type ElemTy) const;
74+
6875
mlir::Value getPointer() const {
6976
assert(isValid());
7077
return pointerAndKnownNonNull.getPointer();
7178
}
7279

80+
mlir::Value getBasePointer() const {
81+
// TODO(cir): Remove the version above when we catchup with OG codegen on
82+
// ptr auth.
83+
assert(isValid() && "pointer isn't valid");
84+
return getPointer();
85+
}
86+
7387
mlir::Type getType() const {
7488
assert(mlir::cast<cir::PointerType>(
7589
pointerAndKnownNonNull.getPointer().getType())

clang/lib/CIR/CodeGen/CIRGenBuilder.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,15 @@ mlir::Value CIRGenBuilderTy::getArrayElement(mlir::Location arrayLocBegin,
3838
const mlir::Type flatPtrTy = basePtr.getType();
3939
return create<cir::PtrStrideOp>(arrayLocEnd, flatPtrTy, basePtr, idx);
4040
}
41+
42+
// This can't be defined in Address.h because that file is included by
43+
// CIRGenBuilder.h
44+
Address Address::withElementType(CIRGenBuilderTy &builder,
45+
mlir::Type elemTy) const {
46+
assert(!cir::MissingFeatures::addressOffset());
47+
assert(!cir::MissingFeatures::addressIsKnownNonNull());
48+
assert(!cir::MissingFeatures::addressPointerAuthInfo());
49+
50+
return Address(builder.createPtrBitcast(getBasePointer(), elemTy), elemTy,
51+
getAlignment());
52+
}

clang/lib/CIR/CodeGen/CIRGenBuilder.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,18 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
309309
return create<cir::BinOp>(loc, cir::BinOpKind::Div, lhs, rhs);
310310
}
311311

312+
Address createBaseClassAddr(mlir::Location loc, Address addr,
313+
mlir::Type destType, unsigned offset,
314+
bool assumeNotNull) {
315+
if (destType == addr.getElementType())
316+
return addr;
317+
318+
auto ptrTy = getPointerTo(destType);
319+
auto baseAddr = create<cir::BaseClassAddrOp>(
320+
loc, ptrTy, addr.getPointer(), mlir::APInt(64, offset), assumeNotNull);
321+
return Address(baseAddr, destType, addr.getAlignment());
322+
}
323+
312324
cir::LoadOp createLoad(mlir::Location loc, Address addr,
313325
bool isVolatile = false) {
314326
mlir::IntegerAttr align = getAlignmentAttr(addr.getAlignment());

clang/lib/CIR/CodeGen/CIRGenClass.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This contains code dealing with C++ code generation of classes
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "CIRGenFunction.h"
14+
15+
#include "clang/AST/RecordLayout.h"
16+
#include "clang/CIR/MissingFeatures.h"
17+
18+
using namespace clang;
19+
using namespace clang::CIRGen;
20+
21+
Address CIRGenFunction::getAddressOfBaseClass(
22+
Address value, const CXXRecordDecl *derived,
23+
llvm::iterator_range<CastExpr::path_const_iterator> path,
24+
bool nullCheckValue, SourceLocation loc) {
25+
assert(!path.empty() && "Base path should not be empty!");
26+
27+
if ((*path.begin())->isVirtual()) {
28+
// The implementation here is actually complete, but let's flag this
29+
// as an error until the rest of the virtual base class support is in place.
30+
cgm.errorNYI(loc, "getAddrOfBaseClass: virtual base");
31+
return Address::invalid();
32+
}
33+
34+
// Compute the static offset of the ultimate destination within its
35+
// allocating subobject (the virtual base, if there is one, or else
36+
// the "complete" object that we see).
37+
CharUnits nonVirtualOffset =
38+
cgm.computeNonVirtualBaseClassOffset(derived, path);
39+
40+
// Get the base pointer type.
41+
mlir::Type baseValueTy = convertType((path.end()[-1])->getType());
42+
assert(!cir::MissingFeatures::addressSpace());
43+
44+
// The if statement here is redundant now, but it will be needed when we add
45+
// support for virtual base classes.
46+
// If there is no virtual base, use cir.base_class_addr. It takes care of
47+
// the adjustment and the null pointer check.
48+
if (nonVirtualOffset.isZero()) {
49+
assert(!cir::MissingFeatures::sanitizers());
50+
return builder.createBaseClassAddr(getLoc(loc), value, baseValueTy, 0,
51+
/*assumeNotNull=*/true);
52+
}
53+
54+
assert(!cir::MissingFeatures::sanitizers());
55+
56+
// Apply the offset
57+
value = builder.createBaseClassAddr(getLoc(loc), value, baseValueTy,
58+
nonVirtualOffset.getQuantity(),
59+
/*assumeNotNull=*/true);
60+
61+
// Cast to the destination type.
62+
value = value.withElementType(builder, baseValueTy);
63+
64+
return value;
65+
}

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,14 @@ Address CIRGenFunction::emitPointerWithAlignment(const Expr *expr,
9898

9999
case CK_UncheckedDerivedToBase:
100100
case CK_DerivedToBase: {
101-
cgm.errorNYI(expr->getSourceRange(),
102-
"emitPointerWithAlignment: derived-to-base cast");
103-
return Address::invalid();
101+
assert(!cir::MissingFeatures::opTBAA());
102+
assert(!cir::MissingFeatures::addressIsKnownNonNull());
103+
Address addr = emitPointerWithAlignment(ce->getSubExpr(), baseInfo);
104+
const CXXRecordDecl *derived =
105+
ce->getSubExpr()->getType()->getPointeeCXXRecordDecl();
106+
return getAddressOfBaseClass(addr, derived, ce->path(),
107+
shouldNullCheckClassCastValue(ce),
108+
ce->getExprLoc());
104109
}
105110

106111
case CK_AnyPointerToBlockPointerCast:
@@ -824,8 +829,6 @@ LValue CIRGenFunction::emitCastLValue(const CastExpr *e) {
824829
case CK_NonAtomicToAtomic:
825830
case CK_AtomicToNonAtomic:
826831
case CK_Dynamic:
827-
case CK_UncheckedDerivedToBase:
828-
case CK_DerivedToBase:
829832
case CK_ToUnion:
830833
case CK_BaseToDerived:
831834
case CK_LValueBitCast:
@@ -864,6 +867,27 @@ LValue CIRGenFunction::emitCastLValue(const CastExpr *e) {
864867
return lv;
865868
}
866869

870+
case CK_UncheckedDerivedToBase:
871+
case CK_DerivedToBase: {
872+
const auto *derivedClassTy =
873+
e->getSubExpr()->getType()->castAs<clang::RecordType>();
874+
auto *derivedClassDecl = cast<CXXRecordDecl>(derivedClassTy->getDecl());
875+
876+
LValue lv = emitLValue(e->getSubExpr());
877+
Address thisAddr = lv.getAddress();
878+
879+
// Perform the derived-to-base conversion
880+
Address baseAddr =
881+
getAddressOfBaseClass(thisAddr, derivedClassDecl, e->path(),
882+
/*NullCheckValue=*/false, e->getExprLoc());
883+
884+
// TODO: Support accesses to members of base classes in TBAA. For now, we
885+
// conservatively pretend that the complete object is of the base class
886+
// type.
887+
assert(!cir::MissingFeatures::opTBAA());
888+
return makeAddrLValue(baseAddr, e->getType(), lv.getBaseInfo());
889+
}
890+
867891
case CK_ZeroToOCLOpaqueType:
868892
llvm_unreachable("NULL to OpenCL opaque type lvalue cast is not valid");
869893
}

clang/lib/CIR/CodeGen/CIRGenFunction.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "CIRGenCall.h"
1717
#include "CIRGenValue.h"
1818
#include "mlir/IR/Location.h"
19+
#include "clang/AST/ExprCXX.h"
1920
#include "clang/AST/GlobalDecl.h"
2021
#include "clang/CIR/MissingFeatures.h"
2122

@@ -629,4 +630,25 @@ void CIRGenFunction::emitNullInitialization(mlir::Location loc, Address destPtr,
629630
builder.createStore(loc, zeroValue, destPtr);
630631
}
631632

633+
// TODO(cir): should be shared with LLVM codegen.
634+
bool CIRGenFunction::shouldNullCheckClassCastValue(const CastExpr *ce) {
635+
const Expr *e = ce->getSubExpr();
636+
637+
if (ce->getCastKind() == CK_UncheckedDerivedToBase)
638+
return false;
639+
640+
if (isa<CXXThisExpr>(e->IgnoreParens())) {
641+
// We always assume that 'this' is never null.
642+
return false;
643+
}
644+
645+
if (const ImplicitCastExpr *ice = dyn_cast<ImplicitCastExpr>(ce)) {
646+
// And that glvalue casts are never null.
647+
if (ice->isGLValue())
648+
return false;
649+
}
650+
651+
return true;
652+
}
653+
632654
} // namespace clang::CIRGen

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,8 @@ class CIRGenFunction : public CIRGenTypeCache {
465465
// TODO: Add symbol table support
466466
}
467467

468+
bool shouldNullCheckClassCastValue(const CastExpr *ce);
469+
468470
LValue makeNaturalAlignPointeeAddrLValue(mlir::Value v, clang::QualType t);
469471

470472
/// Construct an address with the natural alignment of T. If a pointer to T
@@ -480,6 +482,11 @@ class CIRGenFunction : public CIRGenTypeCache {
480482
return Address(ptr, convertTypeForMem(t), alignment);
481483
}
482484

485+
Address getAddressOfBaseClass(
486+
Address value, const CXXRecordDecl *derived,
487+
llvm::iterator_range<CastExpr::path_const_iterator> path,
488+
bool nullCheckValue, SourceLocation loc);
489+
483490
LValue makeAddrLValue(Address addr, QualType ty,
484491
AlignmentSource source = AlignmentSource::Type) {
485492
return makeAddrLValue(addr, ty, LValueBaseInfo(source));

clang/lib/CIR/CodeGen/CIRGenModule.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "clang/AST/DeclBase.h"
2020
#include "clang/AST/DeclOpenACC.h"
2121
#include "clang/AST/GlobalDecl.h"
22+
#include "clang/AST/RecordLayout.h"
2223
#include "clang/Basic/SourceManager.h"
2324
#include "clang/CIR/Dialect/IR/CIRDialect.h"
2425
#include "clang/CIR/Interfaces/CIROpInterfaces.h"
@@ -1683,6 +1684,33 @@ bool CIRGenModule::verifyModule() const {
16831684
return mlir::verify(theModule).succeeded();
16841685
}
16851686

1687+
// TODO(cir): this can be shared with LLVM codegen.
1688+
CharUnits CIRGenModule::computeNonVirtualBaseClassOffset(
1689+
const CXXRecordDecl *derivedClass,
1690+
llvm::iterator_range<CastExpr::path_const_iterator> path) {
1691+
CharUnits offset = CharUnits::Zero();
1692+
1693+
const ASTContext &astContext = getASTContext();
1694+
const CXXRecordDecl *rd = derivedClass;
1695+
1696+
for (const CXXBaseSpecifier *base : path) {
1697+
assert(!base->isVirtual() && "Should not see virtual bases here!");
1698+
1699+
// Get the layout.
1700+
const ASTRecordLayout &layout = astContext.getASTRecordLayout(rd);
1701+
1702+
const auto *baseDecl = cast<CXXRecordDecl>(
1703+
base->getType()->castAs<clang::RecordType>()->getDecl());
1704+
1705+
// Add the offset.
1706+
offset += layout.getBaseClassOffset(baseDecl);
1707+
1708+
rd = baseDecl;
1709+
}
1710+
1711+
return offset;
1712+
}
1713+
16861714
DiagnosticBuilder CIRGenModule::errorNYI(SourceLocation loc,
16871715
llvm::StringRef feature) {
16881716
unsigned diagID = diags.getCustomDiagID(

clang/lib/CIR/CodeGen/CIRGenModule.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ class CIRGenModule : public CIRGenTypeCache {
141141
getAddrOfGlobalVar(const VarDecl *d, mlir::Type ty = {},
142142
ForDefinition_t isForDefinition = NotForDefinition);
143143

144+
CharUnits computeNonVirtualBaseClassOffset(
145+
const CXXRecordDecl *derivedClass,
146+
llvm::iterator_range<CastExpr::path_const_iterator> path);
147+
144148
/// Return a constant array for the given string.
145149
mlir::Attribute getConstantArrayFromStringLiteral(const StringLiteral *e);
146150

clang/lib/CIR/CodeGen/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ add_clang_library(clangCIR
1010
CIRGenerator.cpp
1111
CIRGenBuilder.cpp
1212
CIRGenCall.cpp
13+
CIRGenClass.cpp
1314
CIRGenCXXABI.cpp
1415
CIRGenCXXExpr.cpp
1516
CIRGenDecl.cpp

0 commit comments

Comments
 (0)