Skip to content

Commit 4d4088e

Browse files
authored
[CIR] Add support for dynamic cast to void (#162905)
This adds the support for dynamic cast to void in the Itanium ABI.
1 parent 2914642 commit 4d4088e

File tree

5 files changed

+132
-5
lines changed

5 files changed

+132
-5
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,14 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
127127
cir::BoolType getBoolTy() { return cir::BoolType::get(getContext()); }
128128
cir::VoidType getVoidTy() { return cir::VoidType::get(getContext()); }
129129

130+
cir::IntType getUIntNTy(int n) {
131+
return cir::IntType::get(getContext(), n, false);
132+
}
133+
134+
cir::IntType getSIntNTy(int n) {
135+
return cir::IntType::get(getContext(), n, true);
136+
}
137+
130138
cir::PointerType getPointerTo(mlir::Type ty) {
131139
return cir::PointerType::get(ty);
132140
}

clang/lib/CIR/CodeGen/CIRGenBuilder.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,16 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
380380
/*relative_layout=*/false);
381381
}
382382

383+
mlir::Value createDynCastToVoid(mlir::Location loc, mlir::Value src,
384+
bool vtableUseRelativeLayout) {
385+
// TODO(cir): consider address space here.
386+
assert(!cir::MissingFeatures::addressSpace());
387+
cir::PointerType destTy = getVoidPtrTy();
388+
return cir::DynamicCastOp::create(
389+
*this, loc, destTy, cir::DynamicCastKind::Ptr, src,
390+
cir::DynamicCastInfoAttr{}, vtableUseRelativeLayout);
391+
}
392+
383393
Address createBaseClassAddr(mlir::Location loc, Address addr,
384394
mlir::Type destType, unsigned offset,
385395
bool assumeNotNull) {

clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1945,6 +1945,15 @@ static cir::FuncOp getItaniumDynamicCastFn(CIRGenFunction &cgf) {
19451945
return cgf.cgm.createRuntimeFunction(FTy, "__dynamic_cast");
19461946
}
19471947

1948+
static Address emitDynamicCastToVoid(CIRGenFunction &cgf, mlir::Location loc,
1949+
QualType srcRecordTy, Address src) {
1950+
bool vtableUsesRelativeLayout =
1951+
cgf.cgm.getItaniumVTableContext().isRelativeLayout();
1952+
mlir::Value ptr = cgf.getBuilder().createDynCastToVoid(
1953+
loc, src.getPointer(), vtableUsesRelativeLayout);
1954+
return Address{ptr, src.getAlignment()};
1955+
}
1956+
19481957
static cir::DynamicCastInfoAttr emitDynamicCastInfo(CIRGenFunction &cgf,
19491958
mlir::Location loc,
19501959
QualType srcRecordTy,
@@ -1979,10 +1988,8 @@ mlir::Value CIRGenItaniumCXXABI::emitDynamicCast(CIRGenFunction &cgf,
19791988
bool isCastToVoid = destRecordTy.isNull();
19801989
assert((!isCastToVoid || !isRefCast) && "cannot cast to void reference");
19811990

1982-
if (isCastToVoid) {
1983-
cgm.errorNYI(loc, "emitDynamicCastToVoid");
1984-
return {};
1985-
}
1991+
if (isCastToVoid)
1992+
return emitDynamicCastToVoid(cgf, loc, srcRecordTy, src).getPointer();
19861993

19871994
// If the destination is effectively final, the cast succeeds if and only
19881995
// if the dynamic type of the pointer is exactly the destination type.

clang/lib/CIR/Dialect/Transforms/LoweringPrepareItaniumCXXABI.cpp

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,53 @@ static mlir::Value
9292
buildDynamicCastToVoidAfterNullCheck(cir::CIRBaseBuilderTy &builder,
9393
clang::ASTContext &astCtx,
9494
cir::DynamicCastOp op) {
95-
llvm_unreachable("dynamic cast to void is NYI");
95+
mlir::Location loc = op.getLoc();
96+
bool vtableUsesRelativeLayout = op.getRelativeLayout();
97+
98+
// TODO(cir): consider address space in this function.
99+
assert(!cir::MissingFeatures::addressSpace());
100+
101+
mlir::Type vtableElemTy;
102+
uint64_t vtableElemAlign;
103+
if (vtableUsesRelativeLayout) {
104+
vtableElemTy = builder.getSIntNTy(32);
105+
vtableElemAlign = 4;
106+
} else {
107+
const auto &targetInfo = astCtx.getTargetInfo();
108+
auto ptrdiffTy = targetInfo.getPtrDiffType(clang::LangAS::Default);
109+
bool ptrdiffTyIsSigned = clang::TargetInfo::isTypeSigned(ptrdiffTy);
110+
uint64_t ptrdiffTyWidth = targetInfo.getTypeWidth(ptrdiffTy);
111+
112+
vtableElemTy = cir::IntType::get(builder.getContext(), ptrdiffTyWidth,
113+
ptrdiffTyIsSigned);
114+
vtableElemAlign =
115+
llvm::divideCeil(targetInfo.getPointerAlign(clang::LangAS::Default), 8);
116+
}
117+
118+
// Access vtable to get the offset from the given object to its containing
119+
// complete object.
120+
// TODO: Add a specialized operation to get the object offset?
121+
auto vptrTy = cir::VPtrType::get(builder.getContext());
122+
cir::PointerType vptrPtrTy = builder.getPointerTo(vptrTy);
123+
auto vptrPtr =
124+
cir::VTableGetVPtrOp::create(builder, loc, vptrPtrTy, op.getSrc());
125+
mlir::Value vptr = builder.createLoad(loc, vptrPtr);
126+
mlir::Value elementPtr =
127+
builder.createBitcast(vptr, builder.getPointerTo(vtableElemTy));
128+
mlir::Value minusTwo = builder.getSignedInt(loc, -2, 64);
129+
auto offsetToTopSlotPtr = cir::PtrStrideOp::create(
130+
builder, loc, builder.getPointerTo(vtableElemTy), elementPtr, minusTwo);
131+
mlir::Value offsetToTop =
132+
builder.createAlignedLoad(loc, offsetToTopSlotPtr, vtableElemAlign);
133+
134+
// Add the offset to the given pointer to get the cast result.
135+
// Cast the input pointer to a uint8_t* to allow pointer arithmetic.
136+
cir::PointerType u8PtrTy = builder.getPointerTo(builder.getUIntNTy(8));
137+
mlir::Value srcBytePtr = builder.createBitcast(op.getSrc(), u8PtrTy);
138+
auto dstBytePtr =
139+
cir::PtrStrideOp::create(builder, loc, u8PtrTy, srcBytePtr, offsetToTop);
140+
// Cast the result to a void*.
141+
return builder.createBitcast(dstBytePtr, builder.getVoidPtrTy());
96142
}
97143

98144
mlir::Value

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

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,59 @@ Derived &ref_cast(Base &b) {
101101
// OGCG: br i1 %[[IS_NULL]], label %[[BAD_CAST:.*]], label %[[DONE:.*]]
102102
// OGCG: [[BAD_CAST]]:
103103
// OGCG: call void @__cxa_bad_cast()
104+
105+
void *ptr_cast_to_complete(Base *ptr) {
106+
return dynamic_cast<void *>(ptr);
107+
}
108+
109+
// CIR-BEFORE: cir.func dso_local @_Z20ptr_cast_to_completeP4Base
110+
// CIR-BEFORE: %{{.+}} = cir.dyn_cast ptr %{{.+}} : !cir.ptr<!rec_Base> -> !cir.ptr<!void>
111+
// CIR-BEFORE: }
112+
113+
// CIR-AFTER: cir.func dso_local @_Z20ptr_cast_to_completeP4Base
114+
// CIR-AFTER: %[[SRC:.*]] = cir.load{{.*}} %{{.+}} : !cir.ptr<!cir.ptr<!rec_Base>>, !cir.ptr<!rec_Base>
115+
// CIR-AFTER-NEXT: %[[SRC_IS_NOT_NULL:.*]] = cir.cast ptr_to_bool %[[SRC]] : !cir.ptr<!rec_Base> -> !cir.bool
116+
// CIR-AFTER-NEXT: %{{.+}} = cir.ternary(%[[SRC_IS_NOT_NULL]], true {
117+
// CIR-AFTER-NEXT: %[[VPTR_PTR:.*]] = cir.vtable.get_vptr %[[SRC]] : !cir.ptr<!rec_Base> -> !cir.ptr<!cir.vptr>
118+
// CIR-AFTER-NEXT: %[[VPTR:.*]] = cir.load %[[VPTR_PTR]] : !cir.ptr<!cir.vptr>, !cir.vptr
119+
// CIR-AFTER-NEXT: %[[ELEM_PTR:.*]] = cir.cast bitcast %[[VPTR]] : !cir.vptr -> !cir.ptr<!s64i>
120+
// CIR-AFTER-NEXT: %[[MINUS_TWO:.*]] = cir.const #cir.int<-2> : !s64i
121+
// CIR-AFTER-NEXT: %[[BASE_OFFSET_PTR:.*]] = cir.ptr_stride %[[ELEM_PTR]], %[[MINUS_TWO]] : (!cir.ptr<!s64i>, !s64i) -> !cir.ptr<!s64i>
122+
// CIR-AFTER-NEXT: %[[BASE_OFFSET:.*]] = cir.load{{.*}} %[[BASE_OFFSET_PTR]] : !cir.ptr<!s64i>, !s64i
123+
// CIR-AFTER-NEXT: %[[SRC_BYTES_PTR:.*]] = cir.cast bitcast %[[SRC]] : !cir.ptr<!rec_Base> -> !cir.ptr<!u8i>
124+
// CIR-AFTER-NEXT: %[[DST_BYTES_PTR:.*]] = cir.ptr_stride %[[SRC_BYTES_PTR]], %[[BASE_OFFSET]] : (!cir.ptr<!u8i>, !s64i) -> !cir.ptr<!u8i>
125+
// CIR-AFTER-NEXT: %[[CASTED_PTR:.*]] = cir.cast bitcast %[[DST_BYTES_PTR]] : !cir.ptr<!u8i> -> !cir.ptr<!void>
126+
// CIR-AFTER-NEXT: cir.yield %[[CASTED_PTR]] : !cir.ptr<!void>
127+
// CIR-AFTER-NEXT: }, false {
128+
// CIR-AFTER-NEXT: %[[NULL_PTR:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!void>
129+
// CIR-AFTER-NEXT: cir.yield %[[NULL_PTR]] : !cir.ptr<!void>
130+
// CIR-AFTER-NEXT: }) : (!cir.bool) -> !cir.ptr<!void>
131+
// CIR-AFTER: }
132+
133+
// LLVM: define {{.*}} @_Z20ptr_cast_to_completeP4Base
134+
// LLVM: %[[IS_NOT_NULL:.*]] = icmp ne ptr %[[PTR:.*]], null
135+
// LLVM: br i1 %[[IS_NOT_NULL]], label %[[NOT_NULL:.*]], label %[[NULL:.*]]
136+
// LLVM: [[NOT_NULL]]:
137+
// LLVM: %[[VPTR:.*]] = load ptr, ptr %[[PTR]]
138+
// LLVM: %[[BASE_OFFSET_PTR:.*]] = getelementptr i64, ptr %7, i64 -2
139+
// LLVM: %[[BASE_OFFSET:.*]] = load i64, ptr %[[BASE_OFFSET_PTR]]
140+
// LLVM: %[[RESULT:.*]] = getelementptr i8, ptr %[[PTR]], i64 %[[BASE_OFFSET]]
141+
// LLVM: br label %[[DONE:.*]]
142+
// LLVM: [[NULL]]:
143+
// LLVM: br label %[[DONE]]
144+
// LLVM: [[DONE]]:
145+
// LLVM: %[[RET:.*]] = phi ptr [ null, %[[NULL]] ], [ %[[RESULT]], %[[NOT_NULL]] ]
146+
147+
// OGCG: define {{.*}} @_Z20ptr_cast_to_completeP4Base
148+
// OGCG: %[[IS_NULL:.*]] = icmp eq ptr %[[PTR:.*]], null
149+
// OGCG: br i1 %[[IS_NULL]], label %[[NULL:.*]], label %[[NOT_NULL:.*]]
150+
// OGCG: [[NOT_NULL]]:
151+
// OGCG: %[[VPTR:.*]] = load ptr, ptr %[[PTR]]
152+
// OGCG: %[[BASE_OFFSET_PTR:.*]] = getelementptr inbounds i64, ptr %[[VPTR]], i64 -2
153+
// OGCG: %[[BASE_OFFSET:.*]] = load i64, ptr %[[BASE_OFFSET_PTR]]
154+
// OGCG: %[[RESULT:.*]] = getelementptr inbounds i8, ptr %[[PTR]], i64 %[[BASE_OFFSET]]
155+
// OGCG: br label %[[DONE:.*]]
156+
// OGCG: [[NULL]]:
157+
// OGCG: br label %[[DONE]]
158+
// OGCG: [[DONE]]:
159+
// OGCG: %[[RET:.*]] = phi ptr [ %[[RESULT]], %[[NOT_NULL]] ], [ null, %[[NULL]] ]

0 commit comments

Comments
 (0)