Skip to content

Commit 6fb2850

Browse files
committed
[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.
1 parent 16b0d2f commit 6fb2850

File tree

16 files changed

+346
-5
lines changed

16 files changed

+346
-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: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2231,4 +2231,48 @@ def VecTernaryOp : CIR_Op<"vec.ternary",
22312231
let hasVerifier = 1;
22322232
}
22332233

2234+
//===----------------------------------------------------------------------===//
2235+
// BaseClassAddrOp
2236+
//===----------------------------------------------------------------------===//
2237+
2238+
def BaseClassAddrOp : CIR_Op<"base_class_addr"> {
2239+
let summary = "Get the base class address for a class/struct";
2240+
let description = [{
2241+
The `cir.base_class_addr` operaration gets the address of a particular
2242+
non-virtual base class given a derived class pointer. The offset in bytes
2243+
of the base class must be passed in, since it is easier for the front end
2244+
to calculate that than the MLIR passes. The operation contains a flag for
2245+
whether or not the operand may be nullptr. That depends on the context and
2246+
cannot be known by the operation, and that information affects how the
2247+
operation is lowered.
2248+
2249+
Example:
2250+
```c++
2251+
struct Base { };
2252+
struct Derived : Base { };
2253+
Derived d;
2254+
Base& b = d;
2255+
```
2256+
will generate
2257+
```mlir
2258+
%3 = cir.base_class_addr (%1 : !cir.ptr<!rec_Derived> nonnull) [0] -> !cir.ptr<!rec_Base>
2259+
```
2260+
}];
2261+
2262+
// The validity of the relationship of derived and base cannot yet be
2263+
// verified, currently not worth adding a verifier.
2264+
let arguments = (ins
2265+
Arg<CIR_PointerType, "derived class pointer", [MemRead]>:$derived_addr,
2266+
IndexAttr:$offset, UnitAttr:$assume_not_null);
2267+
2268+
let results = (outs Res<CIR_PointerType, "">:$base_addr);
2269+
2270+
let assemblyFormat = [{
2271+
`(`
2272+
$derived_addr `:` qualified(type($derived_addr))
2273+
(`nonnull` $assume_not_null^)?
2274+
`)` `[` $offset `]` `->` qualified(type($base_addr)) attr-dict
2275+
}];
2276+
}
2277+
22342278
#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());
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
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
22+
CIRGenFunction::getAddressOfBaseClass(Address value,
23+
const CXXRecordDecl *derived,
24+
CastExpr::path_const_iterator pathBegin,
25+
CastExpr::path_const_iterator pathEnd,
26+
bool nullCheckValue, SourceLocation loc) {
27+
assert(pathBegin != pathEnd && "Base path should not be empty!");
28+
29+
CastExpr::path_const_iterator start = pathBegin;
30+
31+
if ((*start)->isVirtual()) {
32+
// The implementation here is actually complete, but let's flag this
33+
// as an error until the rest of the virtual base class support is in place.
34+
cgm.errorNYI(loc, "getAddrOfBaseClass: virtual base");
35+
return Address::invalid();
36+
}
37+
38+
// Compute the static offset of the ultimate destination within its
39+
// allocating subobject (the virtual base, if there is one, or else
40+
// the "complete" object that we see).
41+
CharUnits nonVirtualOffset =
42+
cgm.computeNonVirtualBaseClassOffset(derived, start, pathEnd);
43+
44+
// Get the base pointer type.
45+
mlir::Type baseValueTy = convertType((pathEnd[-1])->getType());
46+
assert(!cir::MissingFeatures::addressSpace());
47+
48+
// The if statement here is redundant now, but it will be needed when we add
49+
// support for virtual base classes.
50+
// If there is no virtual base, use cir.base_class_addr. It takes care of
51+
// the adjustment and the null pointer check.
52+
if (nonVirtualOffset.isZero()) {
53+
assert(!cir::MissingFeatures::sanitizers());
54+
return builder.createBaseClassAddr(getLoc(loc), value, baseValueTy, 0,
55+
/*assumeNotNull=*/true);
56+
}
57+
58+
assert(!cir::MissingFeatures::sanitizers());
59+
60+
// Apply the offset
61+
value = builder.createBaseClassAddr(getLoc(loc), value, baseValueTy,
62+
nonVirtualOffset.getQuantity(),
63+
/*assumeNotNull=*/true);
64+
65+
// Cast to the destination type.
66+
value = value.withElementType(builder, baseValueTy);
67+
68+
return value;
69+
}

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

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

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

105110
case CK_AnyPointerToBlockPointerCast:
@@ -823,8 +828,6 @@ LValue CIRGenFunction::emitCastLValue(const CastExpr *e) {
823828
case CK_NonAtomicToAtomic:
824829
case CK_AtomicToNonAtomic:
825830
case CK_Dynamic:
826-
case CK_UncheckedDerivedToBase:
827-
case CK_DerivedToBase:
828831
case CK_ToUnion:
829832
case CK_BaseToDerived:
830833
case CK_LValueBitCast:
@@ -863,6 +866,27 @@ LValue CIRGenFunction::emitCastLValue(const CastExpr *e) {
863866
return lv;
864867
}
865868

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

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
@@ -430,6 +430,8 @@ class CIRGenFunction : public CIRGenTypeCache {
430430
// TODO: Add symbol table support
431431
}
432432

433+
bool shouldNullCheckClassCastValue(const CastExpr *ce);
434+
433435
LValue makeNaturalAlignPointeeAddrLValue(mlir::Value v, clang::QualType t);
434436

435437
/// Construct an address with the natural alignment of T. If a pointer to T
@@ -445,6 +447,11 @@ class CIRGenFunction : public CIRGenTypeCache {
445447
return Address(ptr, convertTypeForMem(t), alignment);
446448
}
447449

450+
Address getAddressOfBaseClass(Address value, const CXXRecordDecl *derived,
451+
CastExpr::path_const_iterator pathBegin,
452+
CastExpr::path_const_iterator pathEnd,
453+
bool nullCheckValue, SourceLocation loc);
454+
448455
LValue makeAddrLValue(Address addr, QualType ty,
449456
AlignmentSource source = AlignmentSource::Type) {
450457
return makeAddrLValue(addr, ty, LValueBaseInfo(source));

0 commit comments

Comments
 (0)