Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CIR] Add cir.dyn_cast operation #483

Merged
merged 2 commits into from
Apr 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading