Skip to content

Commit

Permalink
[CIR] Add cir.dyn_cast operation (#483)
Browse files Browse the repository at this point in the history
This PR adds the `cir.dyn_cast` operation for representing
`dynamic_cast` in C++. It contains the following contents:

- [x] A new `cir.dyn_cast` operation.
- [x] ~Two new attributes that will be attached to `cir.dyn_cast`
operations:~
- [x] ~`#cir.dyn_cast_info` attributes, which gives general information
about a dynamic cast (e.g. the source RTTI pointer, the dest RTTI
pointer, etc.)~
- [x] ~`#cir.downcast_info` attribute, which gives even more detailed
information about a dynamic cast that is a down-cast. These information
will be used when rewriting the `cir.dyn_cast` operation with more
fundamental CIR operations.~
- [x] CIRGen support for the new operation and attributes.
- [x] Rewrite the new operation with more fundamental CIR operations in
LoweringPrepare.

~This is a draft PR. Now I only added the new operation / attributes,
and updated the CIRGen part. The LoweringPrepare for the new operation
is not implemented. Hopefully the draft can get some initial feedbacks
from the community and make sure it is on the right direction so we
don't waste time on wrong things.~

Related issue: #470 .
  • Loading branch information
Lancern authored and lanza committed Apr 19, 2024
1 parent c0061fc commit b5429f1
Show file tree
Hide file tree
Showing 16 changed files with 553 additions and 163 deletions.
21 changes: 21 additions & 0 deletions clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,18 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
getAttr<mlir::cir::IntAttr>(typ, val));
}

mlir::cir::ConstantOp getConstant(mlir::Location loc, mlir::TypedAttr attr) {
return create<mlir::cir::ConstantOp>(loc, attr.getType(), attr);
}

mlir::cir::BoolType getBoolTy() {
return ::mlir::cir::BoolType::get(getContext());
}

mlir::cir::VoidType getVoidTy() {
return ::mlir::cir::VoidType::get(getContext());
}

mlir::cir::PointerType getVoidPtrTy(unsigned AddrSpace = 0) {
if (AddrSpace)
llvm_unreachable("address space is NYI");
Expand Down Expand Up @@ -181,6 +193,11 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
mlir::cir::CastKind::ptr_to_int, src);
}

mlir::Value createPtrToBoolCast(mlir::Value v) {
return create<mlir::cir::CastOp>(v.getLoc(), getBoolTy(),
mlir::cir::CastKind::ptr_to_bool, v);
}

// TODO(cir): the following function was introduced to keep in sync with LLVM
// codegen. CIR does not have "zext" operations. It should eventually be
// renamed or removed. For now, we just add whatever cast is required here.
Expand Down Expand Up @@ -213,6 +230,10 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
src);
}

mlir::Value createPtrIsNull(mlir::Value ptr) {
return createNot(createPtrToBoolCast(ptr));
}

//
// Block handling helpers
// ----------------------
Expand Down
52 changes: 52 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,58 @@ def VTableAttr : CIR_Attr<"VTable", "vtable", [TypedAttrInterface]> {
}];
}

//===----------------------------------------------------------------------===//
// DynamicCastInfoAttr
//===----------------------------------------------------------------------===//

def DynamicCastInfoAttr
: CIR_Attr<"DynamicCastInfo", "dyn_cast_info"> {
let summary = "ABI specific information about a dynamic cast";
let description = [{
Provide ABI specific information about a dynamic cast operation.

The `srcRtti` and the `destRtti` parameters give the RTTI of the source
struct type and the destination struct type, respectively.

The `runtimeFunc` parameter gives the `__dynamic_cast` function which is
provided by the runtime. The `badCastFunc` parameter gives the
`__cxa_bad_cast` function which is also provided by the runtime.

The `offsetHint` parameter gives the hint value that should be passed to the
`__dynamic_cast` runtime function.
}];

let parameters = (ins GlobalViewAttr:$srcRtti,
GlobalViewAttr:$destRtti,
"FlatSymbolRefAttr":$runtimeFunc,
"FlatSymbolRefAttr":$badCastFunc,
IntAttr:$offsetHint);

let builders = [
AttrBuilderWithInferredContext<(ins "GlobalViewAttr":$srcRtti,
"GlobalViewAttr":$destRtti,
"FlatSymbolRefAttr":$runtimeFunc,
"FlatSymbolRefAttr":$badCastFunc,
"IntAttr":$offsetHint), [{
return $_get(srcRtti.getContext(), srcRtti, destRtti, runtimeFunc,
badCastFunc, offsetHint);
}]>,
];

let genVerifyDecl = 1;
let assemblyFormat = [{
`<`
qualified($srcRtti) `,` qualified($destRtti) `,`
$runtimeFunc `,` $badCastFunc `,` qualified($offsetHint)
`>`
}];

let extraClassDeclaration = [{
/// Get attribute alias name for this attribute.
std::string getAlias() const;
}];
}

//===----------------------------------------------------------------------===//
// AST Wrappers
//===----------------------------------------------------------------------===//
Expand Down
65 changes: 65 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,66 @@ def CastOp : CIR_Op<"cast", [Pure]> {
let hasFolder = 1;
}

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

def DCK_PtrCast : I32EnumAttrCase<"ptr", 1>;
def DCK_RefCast : I32EnumAttrCase<"ref", 2>;

def DynamicCastKind : I32EnumAttr<
"DynamicCastKind", "dynamic cast kind", [DCK_PtrCast, DCK_RefCast]> {
let cppNamespace = "::mlir::cir";
}

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
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.

The input of the operation must be a struct pointer. The result of the
operation is also a struct 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
its value is `ref`, the operation models dynamic casts on references.
Specifically:

- When the input pointer is a null pointer value:
- If `kind` is `ref`, the operation will invoke undefined behavior. A
sanitizer check will be emitted if sanitizer is on.
- Otherwise, the operation will return a null pointer value as its result.
- When the runtime type check fails:
- If `kind` is `ref`, the operation will throw a `bad_cast` exception.
- Otherwise, the operation will return a null pointer value as its result.

The `info` argument gives detailed information about the requested dynamic
cast operation.
}];

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

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

let extraClassDeclaration = [{
/// Determine whether this operation models reference casting in C++.
bool isRefcast() {
return getKind() == ::mlir::cir::DynamicCastKind::ref;
}
}];
}

//===----------------------------------------------------------------------===//
// ObjSizeOp
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -2653,6 +2713,11 @@ def CallOp : CIR_CallOp<"call"> {
$_state.addOperands(operands);
$_state.addAttribute("callee", callee);
$_state.addTypes(resType);
}]>,
OpBuilder<(ins "SymbolRefAttr":$callee,
CArg<"ValueRange", "{}">:$operands), [{
$_state.addOperands(operands);
$_state.addAttribute("callee", callee);
}]>];
}

Expand Down
20 changes: 6 additions & 14 deletions clang/lib/CIR/CodeGen/CIRGenBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -398,9 +398,6 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy {
llvm_unreachable("Unknown float format!");
}

mlir::cir::BoolType getBoolTy() {
return ::mlir::cir::BoolType::get(getContext());
}
mlir::Type getVirtualFnPtrType(bool isVarArg = false) {
// FIXME: replay LLVM codegen for now, perhaps add a vtable ptr special
// type so it's a bit more clear and C++ idiomatic.
Expand Down Expand Up @@ -588,10 +585,6 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy {
return create<mlir::cir::ConstantOp>(loc, ty, getZeroAttr(ty));
}

mlir::cir::ConstantOp getConstant(mlir::Location loc, mlir::TypedAttr attr) {
return create<mlir::cir::ConstantOp>(loc, attr.getType(), attr);
}

//
// Operation creation helpers
// --------------------------
Expand Down Expand Up @@ -655,9 +648,12 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy {
lhs, rhs);
}

mlir::Value createPtrToBoolCast(mlir::Value v) {
return create<mlir::cir::CastOp>(v.getLoc(), getBoolTy(),
mlir::cir::CastKind::ptr_to_bool, v);
mlir::Value createDynCast(mlir::Location loc, mlir::Value src,
mlir::cir::PointerType destType, bool isRefCast,
mlir::cir::DynamicCastInfoAttr info) {
auto castKind = isRefCast ? mlir::cir::DynamicCastKind::ref
: mlir::cir::DynamicCastKind::ptr;
return create<mlir::cir::DynamicCastOp>(loc, destType, castKind, src, info);
}

cir::Address createBaseClassAddr(mlir::Location loc, cir::Address addr,
Expand Down Expand Up @@ -890,10 +886,6 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy {
return create<mlir::cir::GetRuntimeMemberOp>(loc, resultTy, objectPtr,
memberPtr);
}

mlir::Value createPtrIsNull(mlir::Value ptr) {
return createNot(createPtrToBoolCast(ptr));
}
};

} // namespace cir
Expand Down
12 changes: 4 additions & 8 deletions clang/lib/CIR/CodeGen/CIRGenCXXABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "CIRGenFunction.h"
#include "CIRGenModule.h"

#include "mlir/IR/Attributes.h"
#include "clang/AST/Mangle.h"

namespace cir {
Expand Down Expand Up @@ -304,14 +305,9 @@ class CIRGenCXXABI {

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

virtual bool shouldDynamicCastCallBeNullChecked(bool SrcIsPtr,
QualType SrcRecordTy) = 0;

virtual mlir::Value buildDynamicCastCall(CIRGenFunction &CGF,
mlir::Location Loc, Address Value,
QualType SrcRecordTy,
QualType DestTy,
QualType DestRecordTy) = 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,
Expand Down
60 changes: 27 additions & 33 deletions clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//

#include "clang/CIR/Dialect/IR/CIRAttrs.h"
#include <CIRGenCXXABI.h>
#include <CIRGenFunction.h>
#include <CIRGenModule.h>
Expand Down Expand Up @@ -1113,6 +1114,8 @@ mlir::Value CIRGenFunction::buildDynamicCast(Address ThisAddr,
// If T is "pointer to cv void," then the result is a pointer to the most
// derived object pointed to by v.
bool isDynCastToVoid = destTy->isVoidPointerType();
bool isRefCast = destTy->isReferenceType();

QualType srcRecordTy;
QualType destRecordTy;
if (isDynCastToVoid) {
Expand All @@ -1126,45 +1129,36 @@ mlir::Value CIRGenFunction::buildDynamicCast(Address ThisAddr,
destRecordTy = destTy->castAs<ReferenceType>()->getPointeeType();
}

assert(srcRecordTy->isRecordType() && "source type must be a record type!");
buildTypeCheck(TCK_DynamicOperation, DCE->getExprLoc(), ThisAddr.getPointer(),
srcRecordTy);

if (DCE->isAlwaysNull())
return buildDynamicCastToNull(*this, loc, destTy);

assert(srcRecordTy->isRecordType() && "source type must be a record type!");
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();
}

// C++ [expr.dynamic.cast]p4:
// If the value of v is a null pointer value in the pointer case, the result
// is the null pointer value of type T.
bool shouldNullCheckSrcValue =
CGM.getCXXABI().shouldDynamicCastCallBeNullChecked(srcTy->isPointerType(),
srcRecordTy);

auto buildDynamicCastAfterNullCheck = [&]() -> mlir::Value {
if (isDynCastToVoid)
return CGM.getCXXABI().buildDynamicCastToVoid(*this, loc, ThisAddr,
srcRecordTy);

assert(destRecordTy->isRecordType() &&
"destination type must be a record type!");
return CGM.getCXXABI().buildDynamicCastCall(
*this, loc, ThisAddr, srcRecordTy, destTy, destRecordTy);
};
assert(destRecordTy->isRecordType() && "dest type must be a record type!");

if (!shouldNullCheckSrcValue)
return buildDynamicCastAfterNullCheck();

mlir::Value srcValueIsNull = builder.createPtrIsNull(ThisAddr.getPointer());
return builder
.create<mlir::cir::TernaryOp>(
loc, srcValueIsNull,
[&](mlir::OpBuilder &, mlir::Location) {
builder.createYield(loc,
buildDynamicCastToNull(*this, loc, destTy));
},
[&](mlir::OpBuilder &, mlir::Location) {
builder.createYield(loc, buildDynamicCastAfterNullCheck());
})
.getResult();
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,
castInfo);
}
Loading

0 comments on commit b5429f1

Please sign in to comment.