Skip to content

Commit

Permalink
[CIR] Generate cir.dyn_cast for dynamic casts to void ptr
Browse files Browse the repository at this point in the history
This patch update the CIRGen of `dynamic_cast` expressions and make it start to
generate `cir.dyn_cast` operations for `dynamic_cast` expressions that cast to
a void pointer. This patch also updates the lowering prepare pass so that it
lowers such `cir.dyn_cast` operations to the code emitted before this patch.

This patch adds a new attribute `#cir.dyn_cast_to_void` that carries ABI-
specific information about a dynamic-cast-to-void-pointer operation. For
downcasts and sidecasts, the `cir.dyn_cast` operation continues to hold a
`#cir.dyn_cast_info` attribute. For dynamic-cast-to-void-pointer, the
`cir.dyn_cast` operation holds a `#cir.dyn_cast_to_void` attribute instead.
  • Loading branch information
Lancern committed Apr 21, 2024
1 parent ae3e0ad commit 4305b43
Show file tree
Hide file tree
Showing 12 changed files with 170 additions and 100 deletions.
20 changes: 20 additions & 0 deletions clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,33 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
return ::mlir::cir::VoidType::get(getContext());
}

mlir::cir::IntType getUIntNTy(int N) {
return mlir::cir::IntType::get(getContext(), N, false);
}

mlir::cir::IntType getSIntNTy(int N) {
return mlir::cir::IntType::get(getContext(), N, true);
}

mlir::cir::PointerType getVoidPtrTy(unsigned AddrSpace = 0) {
if (AddrSpace)
llvm_unreachable("address space is NYI");
return ::mlir::cir::PointerType::get(
getContext(), ::mlir::cir::VoidType::get(getContext()));
}

mlir::Value createLoad(mlir::Location loc, mlir::Value ptr) {
return create<mlir::cir::LoadOp>(loc, ptr, /*isDeref=*/false,
/*is_volatile=*/false,
/*mem_order=*/mlir::cir::MemOrderAttr{});
}

mlir::Value createAlignedLoad(mlir::Location loc, mlir::Value ptr,
uint64_t alignment) {
// TODO(cir): implement aligned load in CIRBaseBuilder.
return createLoad(loc, ptr);
}

mlir::Value createNot(mlir::Value value) {
return create<mlir::cir::UnaryOp>(value.getLoc(), value.getType(),
mlir::cir::UnaryOpKind::Not, value);
Expand Down
30 changes: 30 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,36 @@ def StructLayoutAttr : CIR_Attr<"StructLayout", "struct_layout"> {
// DynamicCastInfoAttr
//===----------------------------------------------------------------------===//

def DynamicCastToVoidInfoAttr
: CIR_Attr<"DynamicCastToVoidInfo", "dyn_cast_to_void_info"> {
let summary = "ABI specific information about a dynamic cast to void";
let description = [{
Provide ABI specific information about a dynamic cast operation that casts
a struct pointer to a void pointer.

The `vtableElemTy` gives the type of elements in the vtable of the input
object. The `vtableElemAlign` gives the alignment of elements in the vtable
of the input object.
}];

let parameters = (ins "mlir::Type":$vtableElemTy,
"uint64_t":$vtableElemAlign);

let builders = [
AttrBuilderWithInferredContext<(ins "mlir::Type":$vtableElemTy,
"uint64_t":$vtableElemAlign), [{
return $_get(vtableElemTy.getContext(), vtableElemTy, vtableElemAlign);
}]>,
];

let assemblyFormat = [{
`<`
`vtableElemTy` `=` qualified($vtableElemTy) `,`
`vtableElemAlign` `=` $vtableElemAlign
`>`
}];
}

def DynamicCastInfoAttr
: CIR_Attr<"DynamicCastInfo", "dyn_cast_info"> {
let summary = "ABI specific information about a dynamic cast";
Expand Down
18 changes: 11 additions & 7 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -140,14 +140,15 @@ def DynamicCastOp : CIR_Op<"dyn_cast"> {
let summary = "Perform dynamic cast on struct pointers";
let description = [{
The `cir.dyn_cast` operation models part of the semantics of the
`dynamic_cast` operator in C++. It can be used to perform 2 kinds of casts
`dynamic_cast` operator in C++. It can be used to perform 3 kinds of casts
on struct pointers:

- Down-cast, which casts a base class pointer to a derived class pointer;
- Side-cast, which casts a class pointer to a sibling class pointer.
- Side-cast, which casts a class pointer to a sibling class pointer;
- Cast-to-complete, which casts a class pointer to a void pointer.

The input of the operation must be a struct pointer. The result of the
operation is also a struct pointer.
operation is either a struct pointer or a void pointer.

The parameter `kind` specifies the semantics of this operation. If its value
is `ptr`, then the operation models dynamic casts on pointers. Otherwise, if
Expand All @@ -163,17 +164,18 @@ def DynamicCastOp : CIR_Op<"dyn_cast"> {
- Otherwise, the operation will return a null pointer value as its result.

The `info` argument gives detailed information about the requested dynamic
cast operation.
cast operation. It must be either `#cir.dyn_cast_info` or
`#cir.dyn_cast_to_void_info`.
}];

let arguments = (ins DynamicCastKind:$kind,
StructPtr:$src,
DynamicCastInfoAttr:$info);
let results = (outs StructPtr:$result);
AnyAttr:$info);
let results = (outs CIR_PointerType:$result);

let assemblyFormat = [{
`(` $kind `,` $src `:` type($src) `,` qualified($info) `)`
`->` type($result) attr-dict
`->` qualified(type($result)) attr-dict
}];

let extraClassDeclaration = [{
Expand All @@ -182,6 +184,8 @@ def DynamicCastOp : CIR_Op<"dyn_cast"> {
return getKind() == ::mlir::cir::DynamicCastKind::ref;
}
}];

let hasVerifier = 1;
}

//===----------------------------------------------------------------------===//
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CIR/CodeGen/CIRGenBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,7 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy {

mlir::Value createDynCast(mlir::Location loc, mlir::Value src,
mlir::cir::PointerType destType, bool isRefCast,
mlir::cir::DynamicCastInfoAttr info) {
mlir::Attribute info) {
auto castKind = isRefCast ? mlir::cir::DynamicCastKind::ref
: mlir::cir::DynamicCastKind::ptr;
return create<mlir::cir::DynamicCastOp>(loc, destType, castKind, src, info);
Expand Down
7 changes: 3 additions & 4 deletions clang/lib/CIR/CodeGen/CIRGenCXXABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -305,13 +305,12 @@ class CIRGenCXXABI {

virtual void buildBadCastCall(CIRGenFunction &CGF, mlir::Location loc) = 0;

virtual mlir::cir::DynamicCastToVoidInfoAttr
buildDynamicCastToVoidInfo(CIRGenFunction &CGF, QualType SrcRecordTy) = 0;

virtual mlir::cir::DynamicCastInfoAttr
buildDynamicCastInfo(CIRGenFunction &CGF, mlir::Location Loc,
QualType SrcRecordTy, QualType DestRecordTy) = 0;

virtual mlir::Value buildDynamicCastToVoid(CIRGenFunction &CGF,
mlir::Location Loc, Address Value,
QualType SrcRecordTy) = 0;
};

/// Creates and Itanium-family ABI
Expand Down
22 changes: 6 additions & 16 deletions clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1136,27 +1136,17 @@ mlir::Value CIRGenFunction::buildDynamicCast(Address ThisAddr,
if (DCE->isAlwaysNull())
return buildDynamicCastToNull(*this, loc, destTy);

auto destCirTy = ConvertType(destTy).cast<mlir::cir::PointerType>();

if (isDynCastToVoid) {
auto srcIsNull = builder.createPtrIsNull(ThisAddr.getPointer());
return builder
.create<mlir::cir::TernaryOp>(
loc, srcIsNull,
[&](mlir::OpBuilder &, mlir::Location) {
auto nullPtr =
builder.getNullPtr(builder.getVoidPtrTy(), loc).getResult();
builder.createYield(loc, nullPtr);
},
[&](mlir::OpBuilder &, mlir::Location) {
auto castedPtr = CGM.getCXXABI().buildDynamicCastToVoid(
*this, loc, ThisAddr, srcRecordTy);
builder.createYield(loc, castedPtr);
})
.getResult();
auto castInfo =
CGM.getCXXABI().buildDynamicCastToVoidInfo(*this, srcRecordTy);
return builder.createDynCast(loc, ThisAddr.getPointer(), destCirTy, false,
castInfo);
}

assert(destRecordTy->isRecordType() && "dest type must be a record type!");

auto destCirTy = ConvertType(destTy).cast<mlir::cir::PointerType>();
auto castInfo = CGM.getCXXABI().buildDynamicCastInfo(*this, loc, srcRecordTy,
destRecordTy);
return builder.createDynCast(loc, ThisAddr.getPointer(), destCirTy, isRefCast,
Expand Down
74 changes: 21 additions & 53 deletions clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -290,14 +290,14 @@ class CIRGenItaniumCXXABI : public cir::CIRGenCXXABI {
// functions. So during CIRGen we don't need the `emitDynamicCastCall`
// function that clang CodeGen has.

mlir::cir::DynamicCastToVoidInfoAttr
buildDynamicCastToVoidInfo(CIRGenFunction &CGF,
QualType SrcRecordTy) override;

mlir::cir::DynamicCastInfoAttr
buildDynamicCastInfo(CIRGenFunction &CGF, mlir::Location Loc,
QualType SrcRecordTy, QualType DestRecordTy) override;

mlir::Value buildDynamicCastToVoid(CIRGenFunction &CGF, mlir::Location Loc,
Address Value,
QualType SrcRecordTy) override;

/**************************** RTTI Uniqueness ******************************/
protected:
/// Returns true if the ABI requires RTTI type_info objects to be unique
Expand Down Expand Up @@ -2288,6 +2288,23 @@ static mlir::cir::FuncOp getItaniumDynamicCastFn(CIRGenFunction &CGF) {
return CGF.CGM.createRuntimeFunction(FTy, "__dynamic_cast");
}

mlir::cir::DynamicCastToVoidInfoAttr
CIRGenItaniumCXXABI::buildDynamicCastToVoidInfo(CIRGenFunction &CGF,
QualType SrcRecordTy) {
mlir::Type vtableElemTy;
uint64_t vtableElemAlign;
if (CGM.getItaniumVTableContext().isRelativeLayout()) {
vtableElemTy = CGF.getBuilder().getSInt32Ty();
vtableElemAlign = 4;
} else {
vtableElemTy = CGF.ConvertType(CGF.getContext().getPointerDiffType());
vtableElemAlign = CGF.getPointerAlign().getQuantity();
}

return mlir::cir::DynamicCastToVoidInfoAttr::get(vtableElemTy,
vtableElemAlign);
}

mlir::cir::DynamicCastInfoAttr CIRGenItaniumCXXABI::buildDynamicCastInfo(
CIRGenFunction &CGF, mlir::Location Loc, QualType SrcRecordTy,
QualType DestRecordTy) {
Expand All @@ -2312,52 +2329,3 @@ mlir::cir::DynamicCastInfoAttr CIRGenItaniumCXXABI::buildDynamicCastInfo(
return mlir::cir::DynamicCastInfoAttr::get(srcRtti, destRtti, runtimeFuncRef,
badCastFuncRef, offsetHintAttr);
}

mlir::Value CIRGenItaniumCXXABI::buildDynamicCastToVoid(CIRGenFunction &CGF,
mlir::Location Loc,
Address Value,
QualType SrcRecordTy) {
auto *clsDecl =
cast<CXXRecordDecl>(SrcRecordTy->castAs<RecordType>()->getDecl());

// TODO(cir): consider address space in this function.
assert(!UnimplementedFeature::addressSpace());

auto loadOffsetToTopFromVTable =
[&](mlir::Type vtableElemTy, CharUnits vtableElemAlign) -> mlir::Value {
mlir::Type vtablePtrTy = CGF.getBuilder().getPointerTo(vtableElemTy);
mlir::Value vtablePtr = CGF.getVTablePtr(Loc, Value, vtablePtrTy, clsDecl);

// Get the address point in the vtable that contains offset-to-top.
mlir::Value offsetToTopSlotPtr =
CGF.getBuilder().create<mlir::cir::VTableAddrPointOp>(
Loc, vtablePtrTy, mlir::FlatSymbolRefAttr{}, vtablePtr,
/*vtable_index=*/0, -2ULL);
return CGF.getBuilder().createAlignedLoad(
Loc, vtableElemTy, offsetToTopSlotPtr, vtableElemAlign);
};

// Calculate the offset from the given object to its containing complete
// object.
mlir::Value offsetToTop;
if (CGM.getItaniumVTableContext().isRelativeLayout()) {
offsetToTop = loadOffsetToTopFromVTable(CGF.getBuilder().getSInt32Ty(),
CharUnits::fromQuantity(4));
} else {
offsetToTop = loadOffsetToTopFromVTable(
CGF.convertType(CGF.getContext().getPointerDiffType()),
CGF.getPointerAlign());
}

// Finally, add the offset to the given pointer.
// Cast the input pointer to a uint8_t* to allow pointer arithmetic.
auto u8PtrTy = CGF.getBuilder().getUInt8PtrTy();
mlir::Value srcBytePtr =
CGF.getBuilder().createBitcast(Value.getPointer(), u8PtrTy);
// Do the pointer arithmetic.
mlir::Value dstBytePtr = CGF.getBuilder().create<mlir::cir::PtrStrideOp>(
Loc, u8PtrTy, srcBytePtr, offsetToTop);
// Cast the result to a void*.
return CGF.getBuilder().createBitcast(dstBytePtr,
CGF.getBuilder().getVoidPtrTy());
}
20 changes: 20 additions & 0 deletions clang/lib/CIR/Dialect/IR/CIRDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,26 @@ OpFoldResult CastOp::fold(FoldAdaptor adaptor) {
return {};
}

//===----------------------------------------------------------------------===//
// DynamicCastOp
//===----------------------------------------------------------------------===//

LogicalResult DynamicCastOp::verify() {
if (!getInfo()
.isa<mlir::cir::DynamicCastInfoAttr,
mlir::cir::DynamicCastToVoidInfoAttr>()) {
return emitOpError() << "requires #cir.dyn_cast_info or "
"#cir.dyn_cast_to_void_info for info";
}

auto resultPointeeTy = getType().cast<mlir::cir::PointerType>().getPointee();
if (!resultPointeeTy.isa<mlir::cir::VoidType, mlir::cir::StructType>())
return emitOpError()
<< "cir.dyn_cast must produce a void ptr or struct ptr";

return mlir::success();
}

//===----------------------------------------------------------------------===//
// VecCreateOp
//===----------------------------------------------------------------------===//
Expand Down
9 changes: 8 additions & 1 deletion clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,14 @@ void LoweringPreparePass::lowerDynamicCastOp(DynamicCastOp op) {
CIRBaseBuilderTy builder(getContext());
builder.setInsertionPointAfter(op);

auto loweredValue = cxxABI->lowerDynamicCast(builder, op);
mlir::Value loweredValue;
if (op.getInfo().isa<mlir::cir::DynamicCastToVoidInfoAttr>())
loweredValue = cxxABI->lowerDynamicCastToVoid(builder, op);
else if (op.getInfo().isa<mlir::cir::DynamicCastInfoAttr>())
loweredValue = cxxABI->lowerDynamicCast(builder, op);
else
llvm_unreachable("invalid dynamic cast info");

op.replaceAllUsesWith(loweredValue);
op.erase();
}
Expand Down
5 changes: 4 additions & 1 deletion clang/lib/CIR/Dialect/Transforms/LoweringPrepareCXXABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@ class LoweringPrepareCXXABI {
public:
static LoweringPrepareCXXABI *createItaniumABI();

virtual ~LoweringPrepareCXXABI() {}

virtual mlir::Value lowerDynamicCastToVoid(CIRBaseBuilderTy &builder,
mlir::cir::DynamicCastOp op) = 0;
virtual mlir::Value lowerDynamicCast(CIRBaseBuilderTy &builder,
mlir::cir::DynamicCastOp op) = 0;
virtual ~LoweringPrepareCXXABI() {}
};

} // namespace cir
Expand Down
32 changes: 32 additions & 0 deletions clang/lib/CIR/Dialect/Transforms/LoweringPrepareItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ namespace {

class LoweringPrepareItaniumCXXABI : public LoweringPrepareCXXABI {
public:
mlir::Value lowerDynamicCastToVoid(CIRBaseBuilderTy &builder,
mlir::cir::DynamicCastOp op) override;
mlir::Value lowerDynamicCast(CIRBaseBuilderTy &builder,
mlir::cir::DynamicCastOp op) override;
};
Expand Down Expand Up @@ -90,6 +92,36 @@ static mlir::Value buildDynamicCastAfterNullCheck(CIRBaseBuilderTy &builder,
return builder.createBitcast(castedPtr, op.getType());
}

mlir::Value LoweringPrepareItaniumCXXABI::lowerDynamicCastToVoid(
CIRBaseBuilderTy &builder, mlir::cir::DynamicCastOp op) {
auto loc = op.getLoc();
auto info = op.getInfo().cast<mlir::cir::DynamicCastToVoidInfoAttr>();

// TODO(cir): consider address space in this function.
assert(!MissingFeatures::addressSpace());

// Access vtable to get the offset from the given object to its containing
// complete object.
auto vtablePtrTy = builder.getPointerTo(info.getVtableElemTy());
auto vtablePtrPtr =
builder.createBitcast(op.getSrc(), builder.getPointerTo(vtablePtrTy));
auto vtablePtr = builder.createLoad(loc, vtablePtrPtr);
auto offsetToTopSlotPtr = builder.create<mlir::cir::VTableAddrPointOp>(
loc, vtablePtrTy, mlir::FlatSymbolRefAttr{}, vtablePtr,
/*vtable_index=*/0, -2ULL);
auto offsetToTop = builder.createAlignedLoad(loc, offsetToTopSlotPtr,
info.getVtableElemAlign());

// Add the offset to the given pointer to get the cast result.
// Cast the input pointer to a uint8_t* to allow pointer arithmetic.
auto u8PtrTy = builder.getPointerTo(builder.getUIntNTy(8));
auto srcBytePtr = builder.createBitcast(op.getSrc(), u8PtrTy);
auto dstBytePtr = builder.create<mlir::cir::PtrStrideOp>(
loc, u8PtrTy, srcBytePtr, offsetToTop);
// Cast the result to a void*.
return builder.createBitcast(dstBytePtr, builder.getVoidPtrTy());
}

mlir::Value
LoweringPrepareItaniumCXXABI::lowerDynamicCast(CIRBaseBuilderTy &builder,
mlir::cir::DynamicCastOp op) {
Expand Down
Loading

0 comments on commit 4305b43

Please sign in to comment.