llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Andy Kaylor (andykaylor) <details> <summary>Changes</summary> This change upstreams the code to generate CIR for bool casts and reinterpret casts involving member pointer types and the code to lower these casts for the Itanium C++ ABI. --- Full diff: https://github.com/llvm/llvm-project/pull/175996.diff 9 Files Affected: - (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+1) - (modified) clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp (+5) - (modified) clang/lib/CIR/Dialect/IR/CIRDialect.cpp (+9-2) - (modified) clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp (+40-2) - (modified) clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h (+18) - (modified) clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp (+61) - (modified) clang/test/CIR/CodeGen/pointer-to-data-member-cast.cpp (+55) - (added) clang/test/CIR/CodeGen/pointer-to-member-func-cast.cpp (+71) - (added) clang/test/CIR/IR/invalid-cast.cir (+27) ``````````diff diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index ceb9899a00ac4..8352f99380845 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -241,6 +241,7 @@ def CIR_CastOp : CIR_Op<"cast", [ // The input and output types should match the cast kind. let hasVerifier = 1; let hasFolder = 1; + let hasCXXABILowering = 1; let extraLLVMLoweringPatternDecl = [{ mlir::Type convertTy(mlir::Type ty) const; diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 2a4e6d9b89ee4..8a3af1ffcdbdf 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -2243,6 +2243,11 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { return builder.getNullDataMemberPtr(ty, cgf.getLoc(subExpr->getExprLoc())); } + case CK_ReinterpretMemberPointer: { + mlir::Value src = Visit(subExpr); + return builder.createBitcast(cgf.getLoc(subExpr->getExprLoc()), src, + cgf.convertType(destTy)); + } case CK_BaseToDerivedMemberPointer: case CK_DerivedToBaseMemberPointer: { mlir::Value src = Visit(subExpr); diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 6c4607abb40e7..1be7fe06d8be3 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -657,9 +657,16 @@ LogicalResult cir::CastOp::verify() { << "requires floating point !cir.complex type for result"; return success(); } - default: - llvm_unreachable("Unknown CastOp kind?"); + case cir::CastKind::member_ptr_to_bool: { + if (!mlir::isa<cir::DataMemberType, cir::MethodType>(srcType)) + return emitOpError() + << "requires !cir.data_member or !cir.method type for source"; + if (!mlir::isa<cir::BoolType>(resType)) + return emitOpError() << "requires !cir.bool type for result"; + return success(); + } } + llvm_unreachable("Unknown CastOp kind?"); } static bool isIntOrBoolCast(cir::CastOp op) { diff --git a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp index 145f8574893f4..2a928a556b762 100644 --- a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp +++ b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp @@ -57,8 +57,8 @@ class CIRGenericCXXABILoweringPattern : public mlir::ConversionPattern { matchAndRewrite(mlir::Operation *op, llvm::ArrayRef<mlir::Value> operands, mlir::ConversionPatternRewriter &rewriter) const override { // Do not match on operations that have dedicated ABI lowering rewrite rules - if (llvm::isa<cir::AllocaOp, cir::BaseDataMemberOp, cir::ConstantOp, - cir::CmpOp, cir::DerivedDataMemberOp, cir::FuncOp, + if (llvm::isa<cir::AllocaOp, cir::BaseDataMemberOp, cir::CastOp, cir::CmpOp, + cir::ConstantOp, cir::DerivedDataMemberOp, cir::FuncOp, cir::GetRuntimeMemberOp, cir::GlobalOp>(op)) return mlir::failure(); @@ -130,6 +130,44 @@ mlir::LogicalResult CIRAllocaOpABILowering::matchAndRewrite( return mlir::success(); } +mlir::LogicalResult CIRCastOpABILowering::matchAndRewrite( + cir::CastOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + mlir::Type srcTy = op.getSrc().getType(); + assert((mlir::isa<cir::DataMemberType, cir::MethodType>(srcTy)) && + "input to bitcast in ABI lowering must be a data member or method"); + + switch (op.getKind()) { + case cir::CastKind::bitcast: { + mlir::Type destTy = getTypeConverter()->convertType(op.getType()); + mlir::Value loweredResult; + if (mlir::isa<cir::DataMemberType>(srcTy)) + loweredResult = lowerModule->getCXXABI().lowerDataMemberBitcast( + op, destTy, adaptor.getSrc(), rewriter); + else + loweredResult = lowerModule->getCXXABI().lowerMethodBitcast( + op, destTy, adaptor.getSrc(), rewriter); + rewriter.replaceOp(op, loweredResult); + return mlir::success(); + } + case cir::CastKind::member_ptr_to_bool: { + mlir::Value loweredResult; + if (mlir::isa<cir::MethodType>(srcTy)) + loweredResult = lowerModule->getCXXABI().lowerMethodToBoolCast( + op, adaptor.getSrc(), rewriter); + else + loweredResult = lowerModule->getCXXABI().lowerDataMemberToBoolCast( + op, adaptor.getSrc(), rewriter); + rewriter.replaceOp(op, loweredResult); + return mlir::success(); + } + default: + break; + } + + return mlir::failure(); +} + mlir::LogicalResult CIRConstantOpABILowering::matchAndRewrite( cir::ConstantOp op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const { diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h index 0dedfa7221f5f..a7ff9f848fb7a 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRCXXABI.h @@ -81,6 +81,24 @@ class CIRCXXABI { virtual mlir::Value lowerDataMemberCmp(cir::CmpOp op, mlir::Value loweredLhs, mlir::Value loweredRhs, mlir::OpBuilder &builder) const = 0; + + virtual mlir::Value + lowerDataMemberBitcast(cir::CastOp op, mlir::Type loweredDstTy, + mlir::Value loweredSrc, + mlir::OpBuilder &builder) const = 0; + + virtual mlir::Value + lowerDataMemberToBoolCast(cir::CastOp op, mlir::Value loweredSrc, + mlir::OpBuilder &builder) const = 0; + + virtual mlir::Value lowerMethodBitcast(cir::CastOp op, + mlir::Type loweredDstTy, + mlir::Value loweredSrc, + mlir::OpBuilder &builder) const = 0; + + virtual mlir::Value lowerMethodToBoolCast(cir::CastOp op, + mlir::Value loweredSrc, + mlir::OpBuilder &builder) const = 0; }; /// Creates an Itanium-family ABI. diff --git a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp index d944fa3294684..82f17340264c4 100644 --- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp +++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp @@ -67,6 +67,21 @@ class LowerItaniumCXXABI : public CIRCXXABI { mlir::Value lowerDataMemberCmp(cir::CmpOp op, mlir::Value loweredLhs, mlir::Value loweredRhs, mlir::OpBuilder &builder) const override; + + mlir::Value lowerDataMemberBitcast(cir::CastOp op, mlir::Type loweredDstTy, + mlir::Value loweredSrc, + mlir::OpBuilder &builder) const override; + + mlir::Value + lowerDataMemberToBoolCast(cir::CastOp op, mlir::Value loweredSrc, + mlir::OpBuilder &builder) const override; + + mlir::Value lowerMethodBitcast(cir::CastOp op, mlir::Type loweredDstTy, + mlir::Value loweredSrc, + mlir::OpBuilder &builder) const override; + + mlir::Value lowerMethodToBoolCast(cir::CastOp op, mlir::Value loweredSrc, + mlir::OpBuilder &builder) const override; }; } // namespace @@ -249,4 +264,50 @@ LowerItaniumCXXABI::lowerDataMemberCmp(cir::CmpOp op, mlir::Value loweredLhs, loweredRhs); } +mlir::Value LowerItaniumCXXABI::lowerDataMemberBitcast( + cir::CastOp op, mlir::Type loweredDstTy, mlir::Value loweredSrc, + mlir::OpBuilder &builder) const { + if (loweredSrc.getType() == loweredDstTy) + return loweredSrc; + + return cir::CastOp::create(builder, op.getLoc(), loweredDstTy, + cir::CastKind::bitcast, loweredSrc); +} + +mlir::Value LowerItaniumCXXABI::lowerDataMemberToBoolCast( + cir::CastOp op, mlir::Value loweredSrc, mlir::OpBuilder &builder) const { + // Itanium C++ ABI 2.3: + // A NULL pointer is represented as -1. + auto nullAttr = cir::IntAttr::get(getPtrDiffCIRTy(lm), -1); + auto nullValue = cir::ConstantOp::create(builder, op.getLoc(), nullAttr); + return cir::CmpOp::create(builder, op.getLoc(), cir::CmpOpKind::ne, + loweredSrc, nullValue); +} + +mlir::Value +LowerItaniumCXXABI::lowerMethodBitcast(cir::CastOp op, mlir::Type loweredDstTy, + mlir::Value loweredSrc, + mlir::OpBuilder &builder) const { + if (loweredSrc.getType() == loweredDstTy) + return loweredSrc; + + return loweredSrc; +} + +mlir::Value LowerItaniumCXXABI::lowerMethodToBoolCast( + cir::CastOp op, mlir::Value loweredSrc, mlir::OpBuilder &builder) const { + // Itanium C++ ABI 2.3.2: + // + // In the standard representation, a null member function pointer is + // represented with ptr set to a null pointer. The value of adj is + // unspecified for null member function pointers. + cir::IntType ptrdiffCIRTy = getPtrDiffCIRTy(lm); + mlir::Value ptrdiffZero = cir::ConstantOp::create( + builder, op.getLoc(), cir::IntAttr::get(ptrdiffCIRTy, 0)); + mlir::Value ptrField = cir::ExtractMemberOp::create( + builder, op.getLoc(), ptrdiffCIRTy, loweredSrc, 0); + return cir::CmpOp::create(builder, op.getLoc(), cir::CmpOpKind::ne, ptrField, + ptrdiffZero); +} + } // namespace cir diff --git a/clang/test/CIR/CodeGen/pointer-to-data-member-cast.cpp b/clang/test/CIR/CodeGen/pointer-to-data-member-cast.cpp index 91cf4b1ae5386..d3aca69c3914c 100644 --- a/clang/test/CIR/CodeGen/pointer-to-data-member-cast.cpp +++ b/clang/test/CIR/CodeGen/pointer-to-data-member-cast.cpp @@ -133,3 +133,58 @@ auto derived_to_base_zero_offset(int Derived::*ptr) -> int Base1::* { // OGCG-NEXT: store i64 %{{.*}}, ptr %[[PTR_ADDR]] // OGCG-NEXT: %[[RET:.*]] = load i64, ptr %[[PTR_ADDR]] // OGCG-NEXT: ret i64 %[[RET]] + +struct Foo { + int a; +}; + +struct Bar { + int a; +}; + +bool to_bool(int Foo::*x) { + return x; +} + +// CIR-BEFORE: cir.func {{.*}} @_Z7to_boolM3Fooi +// CIR-BEFORE: %[[X:.*]] = cir.load{{.*}} %{{.*}} : !cir.ptr<!cir.data_member<!s32i in !rec_Foo>>, !cir.data_member<!s32i in !rec_Foo> +// CIR-BEFORE: %{{.*}} = cir.cast member_ptr_to_bool %[[X]] : !cir.data_member<!s32i in !rec_Foo> -> !cir.bool + +// CIR-AFTER: cir.func {{.*}} @_Z7to_boolM3Fooi +// CIR-AFTER: %[[NULL_VAL:.*]] = cir.const #cir.int<-1> : !s64i +// CIR-AFTER: %[[BOOL_VAL:.*]] = cir.cmp(ne, %{{.*}}, %[[NULL_VAL]]) : !s64i, !cir.bool + +// LLVM: define {{.*}} i1 @_Z7to_boolM3Fooi +// LLVM: %[[X:.*]] = load i64, ptr %{{.*}} +// LLVM: %[[IS_NULL:.*]] = icmp ne i64 %[[X]], -1 + +// OGCG: define {{.*}} i1 @_Z7to_boolM3Fooi +// OGCG: %[[X:.*]] = load i64, ptr %{{.*}} +// OGCG: %[[IS_NULL:.*]] = icmp ne i64 %[[X]], -1 + +auto bitcast(int Foo::*x) { + return reinterpret_cast<int Bar::*>(x); +} + +// CIR-BEFORE: cir.func {{.*}} @_Z7bitcastM3Fooi +// CIR-BEFORE: %[[X:.*]] = cir.load{{.*}} %{{.*}} : !cir.ptr<!cir.data_member<!s32i in !rec_Foo>>, !cir.data_member<!s32i in !rec_Foo> +// CIR-BEFORE: %{{.*}} = cir.cast bitcast %[[X]] : !cir.data_member<!s32i in !rec_Foo> -> !cir.data_member<!s32i in !rec_Bar> + +// CIR-AFTER: cir.func {{.*}} @_Z7bitcastM3Fooi(%[[ARG0:.*]]: !s64i +// CIR-AFTER: %[[X_ADDR:.*]] = cir.alloca !s64i, !cir.ptr<!s64i>, ["x", init] {alignment = 8 : i64} +// CIR-AFTER: %[[RET_ADDR:.*]] = cir.alloca !s64i, !cir.ptr<!s64i>, ["__retval"] {alignment = 8 : i64} +// CIR-AFTER: cir.store %[[ARG0]], %[[X_ADDR]] +// CIR-AFTER: %[[X:.*]] = cir.load{{.*}} %[[X_ADDR]] +// CIR-AFTER: cir.store %[[X]], %[[RET_ADDR]] +// CIR-AFTER: %[[RET:.*]] = cir.load %[[RET_ADDR]] +// CIR-AFTER: cir.return %[[RET]] : !s64i + +// LLVM: define {{.*}} i64 @_Z7bitcastM3Fooi +// LLVM: %[[X:.*]] = load i64, ptr %{{.*}} +// LLVM: store i64 %[[X]], ptr %[[RET_ADDR:.*]] +// LLVM: %[[RET:.*]] = load i64, ptr %[[RET_ADDR:.*]] +// LLVM: ret i64 %[[RET]] + +// OGCG: define {{.*}} i64 @_Z7bitcastM3Fooi +// OGCG: %[[X:.*]] = load i64, ptr %{{.*}} +// OGCG: ret i64 %[[X]] diff --git a/clang/test/CIR/CodeGen/pointer-to-member-func-cast.cpp b/clang/test/CIR/CodeGen/pointer-to-member-func-cast.cpp new file mode 100644 index 0000000000000..4f18a6a6a9540 --- /dev/null +++ b/clang/test/CIR/CodeGen/pointer-to-member-func-cast.cpp @@ -0,0 +1,71 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fclangir -emit-cir -mmlir -mlir-print-ir-before=cir-cxxabi-lowering %s -o %t.cir 2> %t-before.cir +// RUN: FileCheck --check-prefix=CIR-BEFORE --input-file=%t-before.cir %s +// RUN: FileCheck --check-prefix=CIR-AFTER --input-file=%t.cir %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll --check-prefix=LLVM %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll --check-prefix=OGCG %s + +struct Foo { + void m1(int); + virtual void m2(int); + virtual void m3(int); +}; + +struct Bar { + void m4(); +}; + +bool memfunc_to_bool(void (Foo::*func)(int)) { + return func; +} + +// CIR-BEFORE: cir.func {{.*}} @_Z15memfunc_to_boolM3FooFviE +// CIR-BEFORE: %{{.*}} = cir.cast member_ptr_to_bool %{{.*}} : !cir.method<!cir.func<(!s32i)> in !rec_Foo> -> !cir.bool + +// CIR-AFTER: cir.func {{.*}} @_Z15memfunc_to_boolM3FooFviE +// CIR-AFTER: %[[FUNC:.*]] = cir.load{{.*}} %{{.*}} : !cir.ptr<!rec_anon_struct>, !rec_anon_struct +// CIR-AFTER: %[[NULL_VAL:.*]] = cir.const #cir.int<0> : !s64i +// CIR-AFTER: %[[FUNC_PTR:.*]] = cir.extract_member %[[FUNC]][0] : !rec_anon_struct -> !s64i +// CIR-AFTER: %[[BOOL_VAL:.*]] = cir.cmp(ne, %[[FUNC_PTR]], %[[NULL_VAL]]) : !s64i, !cir.bool + +// LLVM: define {{.*}} i1 @_Z15memfunc_to_boolM3FooFviE +// LLVM: %[[FUNC:.*]] = load { i64, i64 }, ptr %{{.*}} +// LLVM: %[[FUNC_PTR:.*]] = extractvalue { i64, i64 } %[[FUNC]], 0 +// LLVM: %{{.*}} = icmp ne i64 %[[FUNC_PTR]], 0 + +// Note: OGCG uses an extra temporary for the function argument because it +// composes it from coerced arguments. We'll do that in CIR too after +// calling convention lowering is implemented. + +// OGCG: define {{.*}} i1 @_Z15memfunc_to_boolM3FooFviE +// OGCG: %[[FUNC_TMP:.*]] = load { i64, i64 }, ptr %{{.*}} +// OGCG: store { i64, i64 } %[[FUNC_TMP]], ptr %[[FUNC_ADDR:.*]] +// OGCG: %[[FUNC:.*]] = load { i64, i64 }, ptr %[[FUNC_ADDR]] +// OGCG: %[[FUNC_PTR:.*]] = extractvalue { i64, i64 } %[[FUNC]], 0 +// OGCG: %{{.*}} = icmp ne i64 %[[FUNC_PTR]], 0 + +auto memfunc_reinterpret(void (Foo::*func)(int)) -> void (Bar::*)() { + return reinterpret_cast<void (Bar::*)()>(func); +} + +// CIR-BEFORE: cir.func {{.*}} @_Z19memfunc_reinterpretM3FooFviE +// CIR-BEFORE: %{{.*}} = cir.cast bitcast %{{.*}} : !cir.method<!cir.func<(!s32i)> in !rec_Foo> -> !cir.method<!cir.func<()> in !rec_Bar> + +// CIR-AFTER: cir.func {{.*}} @_Z19memfunc_reinterpretM3FooFviE +// CIR-AFTER: %[[FUNC:.*]] = cir.load{{.*}} %{{.*}} : !cir.ptr<!rec_anon_struct>, !rec_anon_struct +// CIR-AFTER: cir.store %[[FUNC]], %[[RET_ADDR:.*]] : !rec_anon_struct, !cir.ptr<!rec_anon_struct> +// CIR-AFTER: %[[RET:.*]] = cir.load{{.*}} %[[RET_ADDR]] : !cir.ptr<!rec_anon_struct>, !rec_anon_struct +// CIR-AFTER: cir.return %[[RET]] : !rec_anon_struct + +// LLVM: define {{.*}} { i64, i64 } @_Z19memfunc_reinterpretM3FooFviE +// LLVM: %[[FUNC:.*]] = load { i64, i64 }, ptr %{{.*}} +// LLVM: store { i64, i64 } %[[FUNC]], ptr %[[RET_ADDR:.*]] +// LLVM: %[[RET:.*]] = load { i64, i64 }, ptr %[[RET_ADDR]] +// LLVM: ret { i64, i64 } %[[RET]] + +// OGCG: define {{.*}} { i64, i64 } @_Z19memfunc_reinterpretM3FooFviE +// OGCG: %[[FUNC:.*]] = load { i64, i64 }, ptr %{{.*}} +// OGCG: store { i64, i64 } %[[FUNC]], ptr %[[RET_ADDR:.*]] +// OGCG: %[[RET:.*]] = load { i64, i64 }, ptr %[[RET_ADDR]] +// OGCG: ret { i64, i64 } %[[RET]] diff --git a/clang/test/CIR/IR/invalid-cast.cir b/clang/test/CIR/IR/invalid-cast.cir new file mode 100644 index 0000000000000..321934215e84d --- /dev/null +++ b/clang/test/CIR/IR/invalid-cast.cir @@ -0,0 +1,27 @@ +// RUN: cir-opt %s -verify-diagnostics -split-input-file + +// ----- + +!s32i = !cir.int<s, 32> +!rec_Foo = !cir.record<struct "Foo" {!s32i}> + +module { + cir.func no_inline dso_local @_Z7to_boolM3Fooi(%arg0: !cir.data_member<!s32i in !rec_Foo>) -> !s32i { + // expected-error@+1 {{requires !cir.bool type for result}} + %0 = cir.cast member_ptr_to_bool %arg0 : !cir.data_member<!s32i in !rec_Foo> -> !s32i + cir.return %0 : !s32i + } +} + +// ----- + +!s32i = !cir.int<s, 32> +!rec_Foo = !cir.record<struct "Foo" {!s32i}> + +module { + cir.func no_inline dso_local @_Z7to_boolM3Fooi(%arg0: !s32i) -> !cir.bool { + // expected-error@+1 {{requires !cir.data_member or !cir.method type for source}} + %0 = cir.cast member_ptr_to_bool %arg0 : !s32i -> !cir.bool + cir.return %0 : !cir.bool + } +} `````````` </details> https://github.com/llvm/llvm-project/pull/175996 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
