Skip to content

Commit d31cd39

Browse files
andykaylorLukacma
authored andcommitted
[CIR] Handle dynamic cast to null (llvm#164732)
This adds support for handling dynamic casts that are known at compile time to always result in a null pointer. For pointer casts, this emits a null pointer value. For reference casts, it calls the __bad_cast function.
1 parent efda61b commit d31cd39

File tree

4 files changed

+65
-4
lines changed

4 files changed

+65
-4
lines changed

clang/lib/CIR/CodeGen/CIRGenCXXABI.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ class CIRGenCXXABI {
124124
virtual void emitRethrow(CIRGenFunction &cgf, bool isNoReturn) = 0;
125125
virtual void emitThrow(CIRGenFunction &cgf, const CXXThrowExpr *e) = 0;
126126

127+
virtual void emitBadCastCall(CIRGenFunction &cgf, mlir::Location loc) = 0;
128+
127129
virtual mlir::Attribute getAddrOfRTTIDescriptor(mlir::Location loc,
128130
QualType ty) = 0;
129131

clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,26 @@ void CIRGenFunction::emitDeleteCall(const FunctionDecl *deleteFD,
801801
emitNewDeleteCall(*this, deleteFD, deleteFTy, deleteArgs);
802802
}
803803

804+
static mlir::Value emitDynamicCastToNull(CIRGenFunction &cgf,
805+
mlir::Location loc, QualType destTy) {
806+
mlir::Type destCIRTy = cgf.convertType(destTy);
807+
assert(mlir::isa<cir::PointerType>(destCIRTy) &&
808+
"result of dynamic_cast should be a ptr");
809+
810+
if (!destTy->isPointerType()) {
811+
mlir::Region *currentRegion = cgf.getBuilder().getBlock()->getParent();
812+
/// C++ [expr.dynamic.cast]p9:
813+
/// A failed cast to reference type throws std::bad_cast
814+
cgf.cgm.getCXXABI().emitBadCastCall(cgf, loc);
815+
816+
// The call to bad_cast will terminate the current block. Create a new block
817+
// to hold any follow up code.
818+
cgf.getBuilder().createBlock(currentRegion, currentRegion->end());
819+
}
820+
821+
return cgf.getBuilder().getNullPtr(destCIRTy, loc);
822+
}
823+
804824
mlir::Value CIRGenFunction::emitDynamicCast(Address thisAddr,
805825
const CXXDynamicCastExpr *dce) {
806826
mlir::Location loc = getLoc(dce->getSourceRange());
@@ -831,10 +851,8 @@ mlir::Value CIRGenFunction::emitDynamicCast(Address thisAddr,
831851
assert(srcRecordTy->isRecordType() && "source type must be a record type!");
832852
assert(!cir::MissingFeatures::emitTypeCheck());
833853

834-
if (dce->isAlwaysNull()) {
835-
cgm.errorNYI(dce->getSourceRange(), "emitDynamicCastToNull");
836-
return {};
837-
}
854+
if (dce->isAlwaysNull())
855+
return emitDynamicCastToNull(*this, loc, destTy);
838856

839857
auto destCirTy = mlir::cast<cir::PointerType>(convertType(destTy));
840858
return cgm.getCXXABI().emitDynamicCast(*this, loc, srcRecordTy, destRecordTy,

clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
120120
return true;
121121
}
122122

123+
void emitBadCastCall(CIRGenFunction &cgf, mlir::Location loc) override;
124+
123125
mlir::Value
124126
getVirtualBaseClassOffset(mlir::Location loc, CIRGenFunction &cgf,
125127
Address thisAddr, const CXXRecordDecl *classDecl,
@@ -1883,6 +1885,11 @@ static void emitCallToBadCast(CIRGenFunction &cgf, mlir::Location loc) {
18831885
cgf.getBuilder().clearInsertionPoint();
18841886
}
18851887

1888+
void CIRGenItaniumCXXABI::emitBadCastCall(CIRGenFunction &cgf,
1889+
mlir::Location loc) {
1890+
emitCallToBadCast(cgf, loc);
1891+
}
1892+
18861893
// TODO(cir): This could be shared with classic codegen.
18871894
static CharUnits computeOffsetHint(ASTContext &astContext,
18881895
const CXXRecordDecl *src,

clang/test/CIR/CodeGen/dynamic-cast-exact.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,37 @@ B *offset_cast(A *a) {
172172
// OGCG-NEXT: br label %[[LABEL_END]]
173173
// OGCG: [[LABEL_END]]:
174174
// OGCG-NEXT: phi ptr [ %[[RESULT]], %[[LABEL_NOTNULL]] ], [ null, %[[LABEL_NULL]] ]
175+
176+
Derived *ptr_cast_always_fail(Base2 *ptr) {
177+
return dynamic_cast<Derived *>(ptr);
178+
}
179+
180+
// CIR: cir.func {{.*}} @_Z20ptr_cast_always_failP5Base2
181+
// CIR: %{{.+}} = cir.load{{.*}} %{{.+}} : !cir.ptr<!cir.ptr<!rec_Base2>>, !cir.ptr<!rec_Base2>
182+
// CIR-NEXT: %[[RESULT:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!rec_Derived>
183+
// CIR-NEXT: cir.store %[[RESULT]], %{{.*}} : !cir.ptr<!rec_Derived>, !cir.ptr<!cir.ptr<!rec_Derived>>
184+
185+
// LLVM: define {{.*}} ptr @_Z20ptr_cast_always_failP5Base2
186+
// LLVM-NEXT: ret ptr null
187+
188+
// OGCG: define {{.*}} ptr @_Z20ptr_cast_always_failP5Base2
189+
// OGCG-NEXT: entry:
190+
// OGCG-NEXT: ret ptr null
191+
192+
Derived &ref_cast_always_fail(Base2 &ref) {
193+
return dynamic_cast<Derived &>(ref);
194+
}
195+
196+
// CIR: cir.func {{.*}} @_Z20ref_cast_always_failR5Base2
197+
// CIR: %{{.+}} = cir.load{{.*}} %{{.+}} : !cir.ptr<!cir.ptr<!rec_Base2>>, !cir.ptr<!rec_Base2>
198+
// CIR-NEXT: cir.call @__cxa_bad_cast() : () -> ()
199+
// CIR-NEXT: cir.unreachable
200+
201+
// LLVM: define {{.*}} ptr @_Z20ref_cast_always_failR5Base2
202+
// LLVM-NEXT: tail call void @__cxa_bad_cast()
203+
// LLVM-NEXT: unreachable
204+
205+
// OGCG: define {{.*}} ptr @_Z20ref_cast_always_failR5Base2
206+
// OGCG-NEXT: entry:
207+
// OGCG-NEXT: tail call void @__cxa_bad_cast()
208+
// OGCG-NEXT: unreachable

0 commit comments

Comments
 (0)