From a30b77665f1515db1d469e2cdab1e0253c305c58 Mon Sep 17 00:00:00 2001 From: Sirui Mu Date: Sun, 7 Apr 2024 22:12:26 +0800 Subject: [PATCH] [CIR] Add cir.dyn_cast operation This patch adds the `cir.dyn_cast` operation to model the C++ `dynamic_cast` operator. CIRGen code for the new operation is also included. During LLVMIR lowering prepare, the operation will be replaced by more basic CIR operations. The new operation is attached an attribute named `#cir.dyn_cast_info` that contains ABI information about the cast. During CIRGen, `CIRGenCXXABI` will generate and populate the attribute. During LLVMIR lowering prepare, the attribute will be used to generate code for the operation. Since the code generated by the new operation is also highly ABI-specific, this patch adds a new class `LoweringPrepareCXXABI` that provides ABI-specific behaviors for lowering prepare. Similar to `CIRGenCXXABI`, the class is an abstract base class that concrete ABIs can inherit and implement. This patch includes a `LoweringPrepareItaniumCXXABI` class that provides Itanium C++ ABI behaviors for lowering prepare. --- .../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 | 75 ++++-------- clang/lib/CIR/Dialect/IR/CIRAttrs.cpp | 48 ++++++++ clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 5 + clang/lib/CIR/Dialect/IR/MissingFeatures.h | 3 + .../lib/CIR/Dialect/Transforms/CMakeLists.txt | 1 + .../Dialect/Transforms/LoweringPrepare.cpp | 37 +++++- .../Transforms/LoweringPrepareCXXABI.h | 34 ++++++ .../LoweringPrepareItaniumCXXABI.cpp | 115 ++++++++++++++++++ clang/test/CIR/CodeGen/dynamic-cast.cpp | 112 +++++++++-------- clang/test/CIR/IR/invalid.cir | 44 +++++++ 16 files changed, 540 insertions(+), 164 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 b111913a126e..a4266f84c72e 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"); @@ -166,6 +178,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. @@ -198,6 +215,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 fa02176b0a5d..6bbfcbb18835 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -123,6 +123,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 //===----------------------------------------------------------------------===// @@ -2641,6 +2701,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 b53c30ce37a4..a2f7ba56f521 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, @@ -888,10 +884,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 a14cbb03d98b..94c41aab2280 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,9 @@ class CIRGenItaniumCXXABI : public cir::CIRGenCXXABI { void buildBadCastCall(CIRGenFunction &CGF, mlir::Location loc) override; - bool shouldDynamicCastCallBeNullChecked(bool SrcIsPtr, - QualType SrcRecordTy) override { - return SrcIsPtr; - } - - 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 +2282,29 @@ static mlir::cir::FuncOp getItaniumDynamicCastFn(CIRGenFunction &CGF) { return CGF.CGM.getOrCreateRuntimeFunction(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 f21d5e4e4b7b..9e4e4ea8f424 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -98,6 +98,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..b7ae4873ee1a 100644 --- a/clang/lib/CIR/Dialect/IR/MissingFeatures.h +++ b/clang/lib/CIR/Dialect/IR/MissingFeatures.h @@ -20,6 +20,9 @@ namespace cir { struct MissingFeatures { // C++ ABI support static bool cxxABI() { return false; } + + // Sanitizers + static bool sanitizers() { 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..bd07d56bbffc --- /dev/null +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepareCXXABI.h @@ -0,0 +1,34 @@ +//====- 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; +}; + +} // 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..1c9b1f1b5f8f --- /dev/null +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepareItaniumCXXABI.cpp @@ -0,0 +1,115 @@ +//====- 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. + 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 + 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. + 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(); + + // TODO: emit a sanitizer check. + // buildTypeCheck( + // TCK_DynamicOperation, DCE->getExprLoc(), + // ThisAddr.getPointer(), srcRecordTy) + assert(!MissingFeatures::sanitizers()); + + 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 fe1ce5baace2..c6fe3d05d0ce 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-enable -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-enable -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-enable -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 ba7758b2ca56..8e19ab9f5c99 100644 --- a/clang/test/CIR/IR/invalid.cir +++ b/clang/test/CIR/IR/invalid.cir @@ -1033,3 +1033,47 @@ cir.func @bad_fetch(%x: !cir.ptr, %y: !cir.float) -> () { %12 = cir.atomic.fetch(xor, %x : !cir.ptr, %y : !cir.float, seq_cst) : !cir.float 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 + } +}