From 588590c5137e4751bc6eaff1c4e06e81e849b1fa Mon Sep 17 00:00:00 2001 From: Sirui Mu Date: Fri, 19 Apr 2024 11:50:33 +0800 Subject: [PATCH] [CIR] Add `cir.dyn_cast` operation (#483) 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 . --- .../CIR/Dialect/Builder/CIRBaseBuilder.h | 21 ++++ .../include/clang/CIR/Dialect/IR/CIRAttrs.td | 52 ++++++++ clang/include/clang/CIR/Dialect/IR/CIROps.td | 65 ++++++++++ clang/lib/CIR/CodeGen/CIRGenBuilder.h | 20 +-- clang/lib/CIR/CodeGen/CIRGenCXXABI.h | 12 +- clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp | 60 ++++----- clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 79 ++++-------- clang/lib/CIR/Dialect/IR/CIRAttrs.cpp | 48 +++++++ clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 5 + clang/lib/CIR/Dialect/IR/MissingFeatures.h | 7 ++ .../lib/CIR/Dialect/Transforms/CMakeLists.txt | 1 + .../Dialect/Transforms/LoweringPrepare.cpp | 37 +++++- .../Transforms/LoweringPrepareCXXABI.h | 36 ++++++ .../LoweringPrepareItaniumCXXABI.cpp | 117 ++++++++++++++++++ clang/test/CIR/CodeGen/dynamic-cast.cpp | 112 +++++++++-------- clang/test/CIR/IR/invalid.cir | 44 +++++++ 16 files changed, 553 insertions(+), 163 deletions(-) create mode 100644 clang/lib/CIR/Dialect/Transforms/LoweringPrepareCXXABI.h create mode 100644 clang/lib/CIR/Dialect/Transforms/LoweringPrepareItaniumCXXABI.cpp diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index 5f4bc1874674..659819b17ab2 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -54,6 +54,18 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { getAttr(typ, val)); } + mlir::cir::ConstantOp getConstant(mlir::Location loc, mlir::TypedAttr attr) { + return create(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"); @@ -181,6 +193,11 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { mlir::cir::CastKind::ptr_to_int, src); } + mlir::Value createPtrToBoolCast(mlir::Value v) { + return create(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. @@ -213,6 +230,10 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { src); } + mlir::Value createPtrIsNull(mlir::Value ptr) { + return createNot(createPtrToBoolCast(ptr)); + } + // // Block handling helpers // ---------------------- diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index f5712d9de417..d2b56c3fa6c1 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -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 //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 24bd0a744155..b89ba14f1451 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -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 //===----------------------------------------------------------------------===// @@ -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); }]>]; } diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index 4839a2aaa355..e3d1d825af33 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -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. @@ -588,10 +585,6 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy { return create(loc, ty, getZeroAttr(ty)); } - mlir::cir::ConstantOp getConstant(mlir::Location loc, mlir::TypedAttr attr) { - return create(loc, attr.getType(), attr); - } - // // Operation creation helpers // -------------------------- @@ -655,9 +648,12 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy { lhs, rhs); } - mlir::Value createPtrToBoolCast(mlir::Value v) { - return create(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(loc, destType, castKind, src, info); } cir::Address createBaseClassAddr(mlir::Location loc, cir::Address addr, @@ -890,10 +886,6 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy { return create(loc, resultTy, objectPtr, memberPtr); } - - mlir::Value createPtrIsNull(mlir::Value ptr) { - return createNot(createPtrToBoolCast(ptr)); - } }; } // namespace cir diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h index 3a74daa1225e..0e1ffd53c79d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h @@ -19,6 +19,7 @@ #include "CIRGenFunction.h" #include "CIRGenModule.h" +#include "mlir/IR/Attributes.h" #include "clang/AST/Mangle.h" namespace cir { @@ -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, diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp index 1de67ca5897c..7201cfa4fe8a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/CIR/Dialect/IR/CIRAttrs.h" #include #include #include @@ -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) { @@ -1126,45 +1129,36 @@ mlir::Value CIRGenFunction::buildDynamicCast(Address ThisAddr, destRecordTy = destTy->castAs()->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( + 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( - 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(); + auto castInfo = CGM.getCXXABI().buildDynamicCastInfo(*this, loc, srcRecordTy, + destRecordTy); + return builder.createDynCast(loc, ThisAddr.getPointer(), destCirTy, isRefCast, + castInfo); } diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index 79c59f8787fc..28cbc2a0ab01 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -27,6 +27,7 @@ #include "clang/AST/VTableBuilder.h" #include "clang/Basic/Linkage.h" #include "clang/Basic/TargetInfo.h" +#include "clang/CIR/Dialect/IR/CIRAttrs.h" #include "llvm/Support/ErrorHandling.h" using namespace cir; @@ -283,15 +284,15 @@ class CIRGenItaniumCXXABI : public cir::CIRGenCXXABI { void buildBadCastCall(CIRGenFunction &CGF, mlir::Location loc) override; - bool shouldDynamicCastCallBeNullChecked(bool SrcIsPtr, - QualType SrcRecordTy) override { - return SrcIsPtr; - } + // The traditional clang CodeGen emits calls to `__dynamic_cast` directly into + // LLVM in the `emitDynamicCastCall` function. In CIR, `dynamic_cast` + // expressions are lowered to `cir.dyn_cast` ops instead of calls to runtime + // functions. So during CIRGen we don't need the `emitDynamicCastCall` + // function that clang CodeGen has. - mlir::Value buildDynamicCastCall(CIRGenFunction &CGF, mlir::Location Loc, - Address Value, QualType SrcRecordTy, - QualType DestTy, - QualType DestRecordTy) 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, @@ -2287,55 +2288,29 @@ static mlir::cir::FuncOp getItaniumDynamicCastFn(CIRGenFunction &CGF) { return CGF.CGM.createRuntimeFunction(FTy, "__dynamic_cast"); } -mlir::Value CIRGenItaniumCXXABI::buildDynamicCastCall( - CIRGenFunction &CGF, mlir::Location Loc, Address Value, - QualType SrcRecordTy, QualType DestTy, QualType DestRecordTy) { - mlir::Type ptrdiffTy = CGF.ConvertType(CGF.getContext().getPointerDiffType()); +mlir::cir::DynamicCastInfoAttr CIRGenItaniumCXXABI::buildDynamicCastInfo( + CIRGenFunction &CGF, mlir::Location Loc, QualType SrcRecordTy, + QualType DestRecordTy) { + auto srcRtti = CGF.CGM.getAddrOfRTTIDescriptor(Loc, SrcRecordTy) + .cast(); + auto destRtti = CGF.CGM.getAddrOfRTTIDescriptor(Loc, DestRecordTy) + .cast(); - mlir::Value srcRtti = CGF.getBuilder().getConstant( - Loc, - CGF.CGM.getAddrOfRTTIDescriptor(Loc, SrcRecordTy.getUnqualifiedType()) - .cast()); - mlir::Value destRtti = CGF.getBuilder().getConstant( - Loc, - CGF.CGM.getAddrOfRTTIDescriptor(Loc, DestRecordTy.getUnqualifiedType()) - .cast()); + auto runtimeFuncOp = getItaniumDynamicCastFn(CGF); + auto badCastFuncOp = getBadCastFn(CGF); + auto runtimeFuncRef = mlir::FlatSymbolRefAttr::get(runtimeFuncOp); + auto badCastFuncRef = mlir::FlatSymbolRefAttr::get(badCastFuncOp); - // Compute the offset hint. const CXXRecordDecl *srcDecl = SrcRecordTy->getAsCXXRecordDecl(); const CXXRecordDecl *destDecl = DestRecordTy->getAsCXXRecordDecl(); - mlir::Value offsetHint = CGF.getBuilder().getConstAPInt( - Loc, ptrdiffTy, - llvm::APSInt::get(computeOffsetHint(CGF.getContext(), srcDecl, destDecl) - .getQuantity())); - - // Emit the call to __dynamic_cast. - mlir::Value srcPtr = - CGF.getBuilder().createBitcast(Value.getPointer(), CGF.VoidPtrTy); - mlir::Value args[4] = {srcPtr, srcRtti, destRtti, offsetHint}; - mlir::Value castedPtr = - CGF.buildRuntimeCall(Loc, getItaniumDynamicCastFn(CGF), args); - - assert(castedPtr.getType().isa() && - "the return value of __dynamic_cast should be a ptr"); - - /// C++ [expr.dynamic.cast]p9: - /// A failed cast to reference type throws std::bad_cast - if (DestTy->isReferenceType()) { - // Emit a cir.if that checks the casted value. - mlir::Value castedValueIsNull = CGF.getBuilder().createPtrIsNull(castedPtr); - CGF.getBuilder().create( - Loc, castedValueIsNull, false, [&](mlir::OpBuilder &, mlir::Location) { - buildBadCastCall(CGF, Loc); - // TODO(cir): remove this once buildBadCastCall inserts unreachable - CGF.getBuilder().createYield(Loc); - }); - } + auto offsetHint = computeOffsetHint(CGF.getContext(), srcDecl, destDecl); + + mlir::Type ptrdiffTy = CGF.ConvertType(CGF.getContext().getPointerDiffType()); + auto offsetHintAttr = + mlir::cir::IntAttr::get(ptrdiffTy, offsetHint.getQuantity()); - // Note that castedPtr is a void*. Cast it to a pointer to the destination - // type before return. - mlir::Type destCIRTy = CGF.ConvertType(DestTy); - return CGF.getBuilder().createBitcast(castedPtr, destCIRTy); + return mlir::cir::DynamicCastInfoAttr::get(srcRtti, destRtti, runtimeFuncRef, + badCastFuncRef, offsetHintAttr); } mlir::Value CIRGenItaniumCXXABI::buildDynamicCastToVoid(CIRGenFunction &CGF, diff --git a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp index 6fac55194972..c31726d72365 100644 --- a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp @@ -446,6 +446,54 @@ DataMemberAttr::verify(function_ref emitError, return success(); } +//===----------------------------------------------------------------------===// +// DynamicCastInfoAtttr definitions +//===----------------------------------------------------------------------===// + +std::string DynamicCastInfoAttr::getAlias() const { + // The alias looks like: `dyn_cast_info__` + + std::string alias = "dyn_cast_info_"; + + alias.append(getSrcRtti().getSymbol().getValue()); + alias.push_back('_'); + alias.append(getDestRtti().getSymbol().getValue()); + + return alias; +} + +LogicalResult DynamicCastInfoAttr::verify( + function_ref emitError, + mlir::cir::GlobalViewAttr srcRtti, mlir::cir::GlobalViewAttr destRtti, + mlir::FlatSymbolRefAttr runtimeFunc, mlir::FlatSymbolRefAttr badCastFunc, + mlir::cir::IntAttr offsetHint) { + auto isRttiPtr = [](mlir::Type ty) { + // RTTI pointers are !cir.ptr. + + auto ptrTy = ty.dyn_cast(); + if (!ptrTy) + return false; + + auto pointeeIntTy = ptrTy.getPointee().dyn_cast(); + if (!pointeeIntTy) + return false; + + return pointeeIntTy.isUnsigned() && pointeeIntTy.getWidth() == 8; + }; + + if (!isRttiPtr(srcRtti.getType())) { + emitError() << "srcRtti must be an RTTI pointer"; + return failure(); + } + + if (!isRttiPtr(destRtti.getType())) { + emitError() << "destRtti must be an RTTI pointer"; + return failure(); + } + + return success(); +} + //===----------------------------------------------------------------------===// // CIR Dialect //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 7313288b9211..407cf07cde7f 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -99,6 +99,11 @@ struct CIROpAsmDialectInterface : public OpAsmDialectInterface { os << cmpThreeWayInfoAttr.getAlias(); return AliasResult::FinalAlias; } + if (auto dynCastInfoAttr = + attr.dyn_cast()) { + os << dynCastInfoAttr.getAlias(); + return AliasResult::FinalAlias; + } return AliasResult::NoAlias; } diff --git a/clang/lib/CIR/Dialect/IR/MissingFeatures.h b/clang/lib/CIR/Dialect/IR/MissingFeatures.h index d8271533cd98..e21fc0e0b191 100644 --- a/clang/lib/CIR/Dialect/IR/MissingFeatures.h +++ b/clang/lib/CIR/Dialect/IR/MissingFeatures.h @@ -20,6 +20,13 @@ namespace cir { struct MissingFeatures { // C++ ABI support static bool cxxABI() { return false; } + static bool setCallingConv() { return false; } + + // Address space related + static bool addressSpace() { return false; } + + // Sanitizers + static bool buildTypeCheck() { return false; } }; } // namespace cir diff --git a/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt index 2a8a4dfeae92..8bd6a06b7c4e 100644 --- a/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt +++ b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt @@ -1,6 +1,7 @@ add_clang_library(MLIRCIRTransforms LifetimeCheck.cpp LoweringPrepare.cpp + LoweringPrepareItaniumCXXABI.cpp MergeCleanups.cpp DropAST.cpp IdiomRecognizer.cpp diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp index e59e5eb8a7fe..00ec54867c9f 100644 --- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// +#include "LoweringPrepareCXXABI.h" #include "PassDetail.h" #include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/IR/BuiltinAttributes.h" @@ -25,6 +26,8 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Path.h" +#include + using cir::CIRBaseBuilderTy; using namespace mlir; using namespace mlir::cir; @@ -68,6 +71,7 @@ struct LoweringPreparePass : public LoweringPrepareBase { void runOnOp(Operation *op); void lowerThreeWayCmpOp(CmpThreeWayOp op); void lowerGlobalOp(GlobalOp op); + void lowerDynamicCastOp(DynamicCastOp op); void lowerStdFindOp(StdFindOp op); void lowerIterBeginOp(IterBeginOp op); void lowerIterEndOp(IterEndOp op); @@ -100,7 +104,23 @@ struct LoweringPreparePass : public LoweringPrepareBase { /// ----------- clang::ASTContext *astCtx; - void setASTContext(clang::ASTContext *c) { astCtx = c; } + std::shared_ptr<::cir::LoweringPrepareCXXABI> cxxABI; + + void setASTContext(clang::ASTContext *c) { + astCtx = c; + switch (c->getCXXABIKind()) { + case clang::TargetCXXABI::GenericItanium: + case clang::TargetCXXABI::GenericAArch64: + case clang::TargetCXXABI::AppleARM64: + // TODO: this isn't quite right, clang uses AppleARM64CXXABI which + // inherits from ARMCXXABI. We'll have to follow suit. + cxxABI.reset(::cir::LoweringPrepareCXXABI::createItaniumABI()); + break; + + default: + llvm_unreachable("NYI"); + } + } /// Tracks current module. ModuleOp theModule; @@ -377,6 +397,15 @@ void LoweringPreparePass::buildCXXGlobalInitFunc() { builder.create(f.getLoc()); } +void LoweringPreparePass::lowerDynamicCastOp(DynamicCastOp op) { + CIRBaseBuilderTy builder(getContext()); + builder.setInsertionPointAfter(op); + + auto loweredValue = cxxABI->lowerDynamicCast(builder, op); + op.replaceAllUsesWith(loweredValue); + op.erase(); +} + static void lowerArrayDtorCtorIntoLoop(CIRBaseBuilderTy &builder, mlir::Operation *op, mlir::Type eltTy, mlir::Value arrayAddr, @@ -507,6 +536,8 @@ void LoweringPreparePass::runOnOp(Operation *op) { lowerThreeWayCmpOp(threeWayCmp); } else if (auto getGlobal = dyn_cast(op)) { lowerGlobalOp(getGlobal); + } else if (auto dynamicCast = dyn_cast(op)) { + lowerDynamicCastOp(dynamicCast); } else if (auto stdFind = dyn_cast(op)) { lowerStdFindOp(stdFind); } else if (auto iterBegin = dyn_cast(op)) { @@ -535,8 +566,8 @@ void LoweringPreparePass::runOnOperation() { SmallVector opsToTransform; op->walk([&](Operation *op) { - if (isa(op)) + if (isa(op)) opsToTransform.push_back(op); }); diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepareCXXABI.h b/clang/lib/CIR/Dialect/Transforms/LoweringPrepareCXXABI.h new file mode 100644 index 000000000000..2a094bad8702 --- /dev/null +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepareCXXABI.h @@ -0,0 +1,36 @@ +//====- LoweringPrepareCXXABI.h -------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file provides the LoweringPrepareCXXABI class, which is the base class +// for ABI specific functionalities that are required during LLVM lowering +// prepare. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_CIR_LOWERING_PREPARE_CXX_ABI_H +#define LLVM_CLANG_LIB_CIR_LOWERING_PREPARE_CXX_ABI_H + +#include "mlir/IR/Value.h" +#include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h" +#include "clang/CIR/Dialect/IR/CIRDialect.h" + +namespace cir { + +class LoweringPrepareCXXABI { +public: + static LoweringPrepareCXXABI *createItaniumABI(); + + virtual mlir::Value lowerDynamicCast(CIRBaseBuilderTy &builder, + mlir::cir::DynamicCastOp op) = 0; + + virtual ~LoweringPrepareCXXABI() {} +}; + +} // namespace cir + +#endif // LLVM_CLANG_LIB_CIR_LOWERING_PREPARE_CXX_ABI_H diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepareItaniumCXXABI.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepareItaniumCXXABI.cpp new file mode 100644 index 000000000000..3619648056cc --- /dev/null +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepareItaniumCXXABI.cpp @@ -0,0 +1,117 @@ +//====- LoweringPrepareItaniumCXXABI.h - Itanium ABI specific code --------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file provides Itanium C++ ABI specific code that is used during LLVMIR +// lowering prepare. +// +//===----------------------------------------------------------------------===// + +#include "../IR/MissingFeatures.h" +#include "LoweringPrepareCXXABI.h" +#include "mlir/IR/BuiltinAttributes.h" +#include "mlir/IR/Value.h" +#include "mlir/IR/ValueRange.h" +#include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h" +#include "clang/CIR/Dialect/IR/CIRAttrs.h" +#include "clang/CIR/Dialect/IR/CIRDialect.h" + +using namespace cir; + +namespace { + +class LoweringPrepareItaniumCXXABI : public LoweringPrepareCXXABI { +public: + mlir::Value lowerDynamicCast(CIRBaseBuilderTy &builder, + mlir::cir::DynamicCastOp op) override; +}; + +} // namespace + +LoweringPrepareCXXABI *LoweringPrepareCXXABI::createItaniumABI() { + return new LoweringPrepareItaniumCXXABI(); +} + +static void buildBadCastCall(CIRBaseBuilderTy &builder, mlir::Location loc, + mlir::FlatSymbolRefAttr badCastFuncRef) { + // TODO(cir): set the calling convention to __cxa_bad_cast. + assert(!MissingFeatures::setCallingConv()); + + builder.create(loc, badCastFuncRef, mlir::ValueRange{}); + builder.create(loc); + builder.clearInsertionPoint(); +} + +static mlir::Value buildDynamicCastAfterNullCheck(CIRBaseBuilderTy &builder, + mlir::cir::DynamicCastOp op) { + auto loc = op->getLoc(); + auto srcValue = op.getSrc(); + auto castInfo = op.getInfo().cast(); + + // TODO(cir): consider address space + assert(!MissingFeatures::addressSpace()); + + auto srcPtr = builder.createBitcast(srcValue, builder.getVoidPtrTy()); + auto srcRtti = builder.getConstant(loc, castInfo.getSrcRtti()); + auto destRtti = builder.getConstant(loc, castInfo.getDestRtti()); + auto offsetHint = builder.getConstant(loc, castInfo.getOffsetHint()); + + auto dynCastFuncRef = castInfo.getRuntimeFunc(); + mlir::Value dynCastFuncArgs[4] = {srcPtr, srcRtti, destRtti, offsetHint}; + + // TODO(cir): set the calling convention for __dynamic_cast. + assert(!MissingFeatures::setCallingConv()); + mlir::Value castedPtr = + builder + .create(loc, dynCastFuncRef, + builder.getVoidPtrTy(), dynCastFuncArgs) + .getResult(0); + + assert(castedPtr.getType().isa() && + "the return value of __dynamic_cast should be a ptr"); + + /// C++ [expr.dynamic.cast]p9: + /// A failed cast to reference type throws std::bad_cast + if (op.isRefcast()) { + // Emit a cir.if that checks the casted value. + mlir::Value castedValueIsNull = builder.createPtrIsNull(castedPtr); + builder.create( + loc, castedValueIsNull, false, [&](mlir::OpBuilder &, mlir::Location) { + buildBadCastCall(builder, loc, castInfo.getBadCastFunc()); + }); + } + + // Note that castedPtr is a void*. Cast it to a pointer to the destination + // type before return. + return builder.createBitcast(castedPtr, op.getType()); +} + +mlir::Value +LoweringPrepareItaniumCXXABI::lowerDynamicCast(CIRBaseBuilderTy &builder, + mlir::cir::DynamicCastOp op) { + auto loc = op->getLoc(); + auto srcValue = op.getSrc(); + + assert(!MissingFeatures::buildTypeCheck()); + + if (op.isRefcast()) + return buildDynamicCastAfterNullCheck(builder, op); + + auto srcValueIsNull = builder.createPtrToBoolCast(srcValue); + return builder + .create( + loc, srcValueIsNull, + [&](mlir::OpBuilder &, mlir::Location) { + builder.createYield( + loc, builder.getNullPtr(op.getType(), loc).getResult()); + }, + [&](mlir::OpBuilder &, mlir::Location) { + builder.createYield(loc, + buildDynamicCastAfterNullCheck(builder, op)); + }) + .getResult(); +} diff --git a/clang/test/CIR/CodeGen/dynamic-cast.cpp b/clang/test/CIR/CodeGen/dynamic-cast.cpp index f9648ff72f08..ea31b4460c12 100644 --- a/clang/test/CIR/CodeGen/dynamic-cast.cpp +++ b/clang/test/CIR/CodeGen/dynamic-cast.cpp @@ -1,75 +1,81 @@ -// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fclangir -emit-cir %s -o %t.cir -// RUN: FileCheck --input-file=%t.cir %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2>&1 | FileCheck %s -check-prefix=BEFORE +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir -emit-cir -mmlir --mlir-print-ir-after=cir-lowering-prepare %s -o %t.cir 2>&1 | FileCheck %s -check-prefix=AFTER struct Base { virtual ~Base(); }; -// CHECK: !ty_22Base22 = !cir.struct struct Derived : Base {}; -// CHECK: !ty_22Derived22 = !cir.struct -// CHECK: cir.func private @__dynamic_cast(!cir.ptr, !cir.ptr, !cir.ptr, !s64i) -> !cir.ptr +// BEFORE: #dyn_cast_info__ZTI4Base__ZTI7Derived = #cir.dyn_cast_info<#cir.global_view<@_ZTI4Base> : !cir.ptr, #cir.global_view<@_ZTI7Derived> : !cir.ptr, @__dynamic_cast, @__cxa_bad_cast, #cir.int<0> : !s64i> +// BEFORE: !ty_22Base22 = !cir.struct +// BEFORE: !ty_22Derived22 = !cir.struct Derived *ptr_cast(Base *b) { return dynamic_cast(b); } -// CHECK: cir.func @_Z8ptr_castP4Base -// CHECK: %[[#V1:]] = cir.load %{{.+}} : cir.ptr >, !cir.ptr -// CHECK-NEXT: %[[#V2:]] = cir.cast(ptr_to_bool, %[[#V1]] : !cir.ptr), !cir.bool -// CHECK-NEXT: %[[#V3:]] = cir.unary(not, %[[#V2]]) : !cir.bool, !cir.bool -// CHECK-NEXT: %{{.+}} = cir.ternary(%[[#V3]], true { -// CHECK-NEXT: %[[#V4:]] = cir.const(#cir.ptr : !cir.ptr) : !cir.ptr -// CHECK-NEXT: cir.yield %[[#V4]] : !cir.ptr -// CHECK-NEXT: }, false { -// CHECK-NEXT: %[[#V5:]] = cir.const(#cir.global_view<@_ZTI4Base> : !cir.ptr) : !cir.ptr -// CHECK-NEXT: %[[#V6:]] = cir.const(#cir.global_view<@_ZTI7Derived> : !cir.ptr) : !cir.ptr -// CHECK-NEXT: %[[#V7:]] = cir.const(#cir.int<0> : !s64i) : !s64i -// CHECK-NEXT: %[[#V8:]] = cir.cast(bitcast, %2 : !cir.ptr), !cir.ptr -// CHECK-NEXT: %[[#V9:]] = cir.call @__dynamic_cast(%[[#V8]], %[[#V5]], %[[#V6]], %[[#V7]]) : (!cir.ptr, !cir.ptr, !cir.ptr, !s64i) -> !cir.ptr -// CHECK-NEXT: %[[#V10:]] = cir.cast(bitcast, %[[#V9]] : !cir.ptr), !cir.ptr -// CHECK-NEXT: cir.yield %[[#V10]] : !cir.ptr -// CHECK-NEXT: }) : (!cir.bool) -> !cir.ptr -// CHECK: cir.func private @__cxa_bad_cast() +// BEFORE: cir.func @_Z8ptr_castP4Base +// BEFORE: %{{.+}} = cir.dyn_cast(ptr, %{{.+}} : !cir.ptr, #dyn_cast_info__ZTI4Base__ZTI7Derived) -> !cir.ptr +// BEFORE: } + +// AFTER: cir.func @_Z8ptr_castP4Base +// AFTER: %[[#SRC_IS_NULL:]] = cir.cast(ptr_to_bool, %{{.+}} : !cir.ptr), !cir.bool +// AFTER-NEXT: %{{.+}} = cir.ternary(%[[#SRC_IS_NULL]], true { +// AFTER-NEXT: %[[#NULL:]] = cir.const(#cir.ptr : !cir.ptr) : !cir.ptr +// AFTER-NEXT: cir.yield %[[#NULL]] : !cir.ptr +// AFTER-NEXT: }, false { +// AFTER-NEXT: %[[#SRC_VOID_PTR:]] = cir.cast(bitcast, %2 : !cir.ptr), !cir.ptr +// AFTER-NEXT: %[[#SRC_RTTI:]] = cir.const(#cir.global_view<@_ZTI4Base> : !cir.ptr) : !cir.ptr +// AFTER-NEXT: %[[#DEST_RTTI:]] = cir.const(#cir.global_view<@_ZTI7Derived> : !cir.ptr) : !cir.ptr +// AFTER-NEXT: %[[#OFFSET_HINT:]] = cir.const(#cir.int<0> : !s64i) : !s64i +// AFTER-NEXT: %[[#CASTED_PTR:]] = cir.call @__dynamic_cast(%[[#SRC_VOID_PTR]], %[[#SRC_RTTI]], %[[#DEST_RTTI]], %[[#OFFSET_HINT]]) : (!cir.ptr, !cir.ptr, !cir.ptr, !s64i) -> !cir.ptr +// AFTER-NEXT: %[[#RESULT:]] = cir.cast(bitcast, %[[#CASTED_PTR]] : !cir.ptr), !cir.ptr +// AFTER-NEXT: cir.yield %[[#RESULT]] : !cir.ptr +// AFTER-NEXT: }) : (!cir.bool) -> !cir.ptr +// AFTER: } Derived &ref_cast(Base &b) { return dynamic_cast(b); } -// CHECK: cir.func @_Z8ref_castR4Base -// CHECK: %[[#V11:]] = cir.load %{{.+}} : cir.ptr >, !cir.ptr -// CHECK-NEXT: %[[#V12:]] = cir.const(#cir.global_view<@_ZTI4Base> : !cir.ptr) : !cir.ptr -// CHECK-NEXT: %[[#V13:]] = cir.const(#cir.global_view<@_ZTI7Derived> : !cir.ptr) : !cir.ptr -// CHECK-NEXT: %[[#V14:]] = cir.const(#cir.int<0> : !s64i) : !s64i -// CHECK-NEXT: %[[#V15:]] = cir.cast(bitcast, %[[#V11]] : !cir.ptr), !cir.ptr -// CHECK-NEXT: %[[#V16:]] = cir.call @__dynamic_cast(%[[#V15]], %[[#V12]], %[[#V13]], %[[#V14]]) : (!cir.ptr, !cir.ptr, !cir.ptr, !s64i) -> !cir.ptr -// CHECK-NEXT: %[[#V17:]] = cir.cast(ptr_to_bool, %[[#V16]] : !cir.ptr), !cir.bool -// CHECK-NEXT: %[[#V18:]] = cir.unary(not, %[[#V17]]) : !cir.bool, !cir.bool -// CHECK-NEXT: cir.if %[[#V18]] { -// CHECK-NEXT: cir.call @__cxa_bad_cast() : () -> () -// CHECK-NEXT: cir.unreachable -// CHECK-NEXT: } -// CHECK-NEXT: %{{.+}} = cir.cast(bitcast, %[[#V16]] : !cir.ptr), !cir.ptr +// BEFORE: cir.func @_Z8ref_castR4Base +// BEFORE: %{{.+}} = cir.dyn_cast(ref, %{{.+}} : !cir.ptr, #dyn_cast_info__ZTI4Base__ZTI7Derived) -> !cir.ptr +// BEFORE: } + +// AFTER: cir.func @_Z8ref_castR4Base +// AFTER: %[[#SRC_VOID_PTR:]] = cir.cast(bitcast, %{{.+}} : !cir.ptr), !cir.ptr +// AFTER-NEXT: %[[#SRC_RTTI:]] = cir.const(#cir.global_view<@_ZTI4Base> : !cir.ptr) : !cir.ptr +// AFTER-NEXT: %[[#DEST_RTTI:]] = cir.const(#cir.global_view<@_ZTI7Derived> : !cir.ptr) : !cir.ptr +// AFTER-NEXT: %[[#OFFSET_HINT:]] = cir.const(#cir.int<0> : !s64i) : !s64i +// AFTER-NEXT: %[[#CASTED_PTR:]] = cir.call @__dynamic_cast(%[[#SRC_VOID_PTR]], %[[#SRC_RTTI]], %[[#DEST_RTTI]], %[[#OFFSET_HINT]]) : (!cir.ptr, !cir.ptr, !cir.ptr, !s64i) -> !cir.ptr +// AFTER-NEXT: %[[#CASTED_PTR_IS_NOT_NULL:]] = cir.cast(ptr_to_bool, %[[#CASTED_PTR]] : !cir.ptr), !cir.bool +// AFTER-NEXT: %[[#CASTED_PTR_IS_NULL:]] = cir.unary(not, %[[#CASTED_PTR_IS_NOT_NULL]]) : !cir.bool, !cir.bool +// AFTER-NEXT: cir.if %[[#CASTED_PTR_IS_NULL]] { +// AFTER-NEXT: cir.call @__cxa_bad_cast() : () -> () +// AFTER-NEXT: cir.unreachable +// AFTER-NEXT: } +// AFTER-NEXT: %{{.+}} = cir.cast(bitcast, %[[#CASTED_PTR]] : !cir.ptr), !cir.ptr +// AFTER: } void *ptr_cast_to_complete(Base *ptr) { return dynamic_cast(ptr); } -// CHECK: cir.func @_Z20ptr_cast_to_completeP4Base -// CHECK: %[[#V19:]] = cir.load %{{.+}} : cir.ptr >, !cir.ptr -// CHECK-NEXT: %[[#V20:]] = cir.cast(ptr_to_bool, %[[#V19]] : !cir.ptr), !cir.bool -// CHECK-NEXT: %[[#V21:]] = cir.unary(not, %[[#V20]]) : !cir.bool, !cir.bool -// CHECK-NEXT: %{{.+}} = cir.ternary(%[[#V21]], true { -// CHECK-NEXT: %[[#V22:]] = cir.const(#cir.ptr : !cir.ptr) : !cir.ptr -// CHECK-NEXT: cir.yield %[[#V22]] : !cir.ptr -// CHECK-NEXT: }, false { -// CHECK-NEXT: %[[#V23:]] = cir.cast(bitcast, %[[#V19]] : !cir.ptr), !cir.ptr> -// CHECK-NEXT: %[[#V24:]] = cir.load %[[#V23]] : cir.ptr >, !cir.ptr -// CHECK-NEXT: %[[#V25:]] = cir.vtable.address_point( %[[#V24]] : !cir.ptr, vtable_index = 0, address_point_index = -2) : cir.ptr -// CHECK-NEXT: %[[#V26:]] = cir.load %[[#V25]] : cir.ptr , !s64i -// CHECK-NEXT: %[[#V27:]] = cir.cast(bitcast, %[[#V19]] : !cir.ptr), !cir.ptr -// CHECK-NEXT: %[[#V28:]] = cir.ptr_stride(%[[#V27]] : !cir.ptr, %[[#V26]] : !s64i), !cir.ptr -// CHECK-NEXT: %[[#V29:]] = cir.cast(bitcast, %[[#V28]] : !cir.ptr), !cir.ptr -// CHECK-NEXT: cir.yield %[[#V29]] : !cir.ptr -// CHECK-NEXT: }) : (!cir.bool) -> !cir.ptr +// BEFORE: cir.func @_Z20ptr_cast_to_completeP4Base +// BEFORE: %[[#V19:]] = cir.load %{{.+}} : cir.ptr >, !cir.ptr +// BEFORE-NEXT: %[[#V20:]] = cir.cast(ptr_to_bool, %[[#V19]] : !cir.ptr), !cir.bool +// BEFORE-NEXT: %[[#V21:]] = cir.unary(not, %[[#V20]]) : !cir.bool, !cir.bool +// BEFORE-NEXT: %{{.+}} = cir.ternary(%[[#V21]], true { +// BEFORE-NEXT: %[[#V22:]] = cir.const(#cir.ptr : !cir.ptr) : !cir.ptr +// BEFORE-NEXT: cir.yield %[[#V22]] : !cir.ptr +// BEFORE-NEXT: }, false { +// BEFORE-NEXT: %[[#V23:]] = cir.cast(bitcast, %[[#V19]] : !cir.ptr), !cir.ptr> +// BEFORE-NEXT: %[[#V24:]] = cir.load %[[#V23]] : cir.ptr >, !cir.ptr +// BEFORE-NEXT: %[[#V25:]] = cir.vtable.address_point( %[[#V24]] : !cir.ptr, vtable_index = 0, address_point_index = -2) : cir.ptr +// BEFORE-NEXT: %[[#V26:]] = cir.load %[[#V25]] : cir.ptr , !s64i +// BEFORE-NEXT: %[[#V27:]] = cir.cast(bitcast, %[[#V19]] : !cir.ptr), !cir.ptr +// BEFORE-NEXT: %[[#V28:]] = cir.ptr_stride(%[[#V27]] : !cir.ptr, %[[#V26]] : !s64i), !cir.ptr +// BEFORE-NEXT: %[[#V29:]] = cir.cast(bitcast, %[[#V28]] : !cir.ptr), !cir.ptr +// BEFORE-NEXT: cir.yield %[[#V29]] : !cir.ptr +// BEFORE-NEXT: }) : (!cir.bool) -> !cir.ptr diff --git a/clang/test/CIR/IR/invalid.cir b/clang/test/CIR/IR/invalid.cir index 78956fab3980..abc24a79bc4b 100644 --- a/clang/test/CIR/IR/invalid.cir +++ b/clang/test/CIR/IR/invalid.cir @@ -1062,3 +1062,47 @@ module { cir.return } } + +// ----- + +!s64i = !cir.int +!s8i = !cir.int +!u32i = !cir.int +!u8i = !cir.int +!void = !cir.void + +!Base = !cir.struct ()>>>}> +!Derived = !cir.struct ()>>>}>}> + +module { + cir.global "private" constant external @_ZTI4Base : !cir.ptr + cir.global "private" constant external @_ZTI7Derived : !cir.ptr + cir.func private @__dynamic_cast(!cir.ptr, !cir.ptr, !cir.ptr, !s64i) -> !cir.ptr + cir.func private @__cxa_bad_cast() + cir.func @test(%arg0 : !cir.ptr) { + // expected-error@+1 {{srcRtti must be an RTTI pointer}} + %0 = cir.dyn_cast(ptr, %arg0 : !cir.ptr, #cir.dyn_cast_info<#cir.global_view<@_ZTI4Base> : !cir.ptr, #cir.global_view<@_ZTI7Derived> : !cir.ptr, @__dynamic_cast, @__cxa_bad_cast, #cir.int<0> : !s64i>) -> !cir.ptr + } +} + +// ----- + +!s64i = !cir.int +!s8i = !cir.int +!u32i = !cir.int +!u8i = !cir.int +!void = !cir.void + +!Base = !cir.struct ()>>>}> +!Derived = !cir.struct ()>>>}>}> + +module { + cir.global "private" constant external @_ZTI4Base : !cir.ptr + cir.global "private" constant external @_ZTI7Derived : !cir.ptr + cir.func private @__dynamic_cast(!cir.ptr, !cir.ptr, !cir.ptr, !s64i) -> !cir.ptr + cir.func private @__cxa_bad_cast() + cir.func @test(%arg0 : !cir.ptr) { + // expected-error@+1 {{destRtti must be an RTTI pointer}} + %0 = cir.dyn_cast(ptr, %arg0 : !cir.ptr, #cir.dyn_cast_info<#cir.global_view<@_ZTI4Base> : !cir.ptr, #cir.global_view<@_ZTI7Derived> : !cir.ptr, @__dynamic_cast, @__cxa_bad_cast, #cir.int<0> : !s64i>) -> !cir.ptr + } +}