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 + } +}