[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -78,6 +78,111 @@ class LLVMLoweringInfo { class CIR_Op traits = []> : Op, LLVMLoweringInfo; +//===--===// +// CastOp +//===--===// + +// The enumaration value isn't in sync with clang. mmha wrote: I tried to get the enums in sync. Good news is I found a couple of unused casting kinds wrt. complex numbers which I removed. Bad news is I found one casting kind (bool to float) that's specific to CIR: llvm/clangir#290. I moved it to the bottom on the list so the enum values of classic CG and CIR match up. https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -121,29 +364,173 @@ mlir::Value CIRGenFunction::emitScalarExpr(const Expr *e) { return ScalarExprEmitter(*this, builder).Visit(const_cast(e)); } +[[maybe_unused]] static bool MustVisitNullValue(const Expr *e) { + // If a null pointer expression's type is the C++0x nullptr_t, then + // it's not necessarily a simple constant and it must be evaluated + // for its potential side effects. + return e->getType()->isNullPtrType(); +} + // Emit code for an explicit or implicit cast. Implicit // casts have to handle a more broad range of conversions than explicit // casts, as they handle things like function to ptr-to-function decay // etc. mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { - Expr *e = ce->getSubExpr(); + Expr *subExpr = ce->getSubExpr(); QualType destTy = ce->getType(); CastKind kind = ce->getCastKind(); + // These cases are generally not written to ignore the result of evaluating + // their sub-expressions, so we clear this now. + ignoreResultAssign = false; andykaylor wrote: Yes, I agree that keeping the store is necessary. My point was that even in the incubator this value is being used. https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -121,29 +375,174 @@ mlir::Value CIRGenFunction::emitScalarExpr(const Expr *e) { return ScalarExprEmitter(*this, builder).Visit(const_cast(e)); } +[[maybe_unused]] static bool MustVisitNullValue(const Expr *e) { + // If a null pointer expression's type is the C++0x nullptr_t, then + // it's not necessarily a simple constant and it must be evaluated + // for its potential side effects. + return e->getType()->isNullPtrType(); +} + // Emit code for an explicit or implicit cast. Implicit // casts have to handle a more broad range of conversions than explicit // casts, as they handle things like function to ptr-to-function decay // etc. mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { - Expr *e = ce->getSubExpr(); + Expr *subExpr = ce->getSubExpr(); QualType destTy = ce->getType(); CastKind kind = ce->getCastKind(); + // These cases are generally not written to ignore the result of evaluating + // their sub-expressions, so we clear this now. + ignoreResultAssign = false; erichkeane wrote: Is this variable used? https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -0,0 +1,58 @@ +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir andykaylor wrote: It looks like floating casts are still missing. Can you add float-to-double and double-to-float test? https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
https://github.com/andykaylor approved this pull request. LGTM with one very minor nit https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -0,0 +1,100 @@ +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -DCIR_ONLY %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM + +unsigned char cxxstaticcast_0(unsigned int x) { + return static_cast(x); +} + +// CIR: cir.func @cxxstaticcast_0 +// CIR:%0 = cir.alloca !cir.int, !cir.ptr>, ["x", init] {alignment = 4 : i64} andykaylor wrote: You should use pattern matching for %0 and similar values. https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
https://github.com/andykaylor edited https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -121,29 +364,173 @@ mlir::Value CIRGenFunction::emitScalarExpr(const Expr *e) { return ScalarExprEmitter(*this, builder).Visit(const_cast(e)); } +[[maybe_unused]] static bool MustVisitNullValue(const Expr *e) { + // If a null pointer expression's type is the C++0x nullptr_t, then + // it's not necessarily a simple constant and it must be evaluated + // for its potential side effects. + return e->getType()->isNullPtrType(); +} + // Emit code for an explicit or implicit cast. Implicit // casts have to handle a more broad range of conversions than explicit // casts, as they handle things like function to ptr-to-function decay // etc. mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { - Expr *e = ce->getSubExpr(); + Expr *subExpr = ce->getSubExpr(); QualType destTy = ce->getType(); CastKind kind = ce->getCastKind(); + // These cases are generally not written to ignore the result of evaluating + // their sub-expressions, so we clear this now. + ignoreResultAssign = false; mmha wrote: Note that `ignoreResultAssign` is a member variable and retains its value while we visit the expression. We might get called recursively so we should keep this store. https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -0,0 +1,79 @@ +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s + +unsigned char cxxstaticcast_0(unsigned int x) { + return static_cast(x); +} + +// CHECK: cir.func @cxxstaticcast_0 +// CHECK:%0 = cir.alloca !cir.int, !cir.ptr>, ["x", init] {alignment = 4 : i64} +// CHECK:cir.store %arg0, %0 : !cir.int, !cir.ptr> +// CHECK:%1 = cir.load %0 : !cir.ptr>, !cir.int +// CHECK:%2 = cir.cast(integral, %1 : !cir.int), !cir.int +// CHECK:cir.return %2 : !cir.int +// CHECK: } + + +int cStyleCasts_0(unsigned x1, int x2, float x3, short x4, double x5) { +// CHECK: cir.func @cStyleCasts_0 + + char a = (char)x1; // truncate + // CHECK: %{{[0-9]+}} = cir.cast(integral, %{{[0-9]+}} : !cir.int), !cir.int mmha wrote: I expanded the tests to include LLVM lowering where feasible. `int_to_bool` is implemented in CIR but not in the lowering (as that requires comparisons) so I `ifdef`ed those out for the LLVM checks. https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -121,29 +364,173 @@ mlir::Value CIRGenFunction::emitScalarExpr(const Expr *e) { return ScalarExprEmitter(*this, builder).Visit(const_cast(e)); } +[[maybe_unused]] static bool MustVisitNullValue(const Expr *e) { + // If a null pointer expression's type is the C++0x nullptr_t, then + // it's not necessarily a simple constant and it must be evaluated + // for its potential side effects. + return e->getType()->isNullPtrType(); +} + // Emit code for an explicit or implicit cast. Implicit // casts have to handle a more broad range of conversions than explicit // casts, as they handle things like function to ptr-to-function decay // etc. mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { - Expr *e = ce->getSubExpr(); + Expr *subExpr = ce->getSubExpr(); QualType destTy = ce->getType(); CastKind kind = ce->getCastKind(); + // These cases are generally not written to ignore the result of evaluating + // their sub-expressions, so we clear this now. + ignoreResultAssign = false; andykaylor wrote: The incubator does use the value returned by `TestAndClearIgnoreResultAssign()` in `VisitBinAssign()` and `emitCompoundAssign()` so we'll probably want to keep the call in those places, but I don't see any value in calling the function when we aren't using the value returned. https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -0,0 +1,79 @@ +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s + +unsigned char cxxstaticcast_0(unsigned int x) { + return static_cast(x); +} + +// CHECK: cir.func @cxxstaticcast_0 +// CHECK:%0 = cir.alloca !cir.int, !cir.ptr>, ["x", init] {alignment = 4 : i64} +// CHECK:cir.store %arg0, %0 : !cir.int, !cir.ptr> +// CHECK:%1 = cir.load %0 : !cir.ptr>, !cir.int +// CHECK:%2 = cir.cast(integral, %1 : !cir.int), !cir.int +// CHECK:cir.return %2 : !cir.int +// CHECK: } + + +int cStyleCasts_0(unsigned x1, int x2, float x3, short x4, double x5) { +// CHECK: cir.func @cStyleCasts_0 + + char a = (char)x1; // truncate + // CHECK: %{{[0-9]+}} = cir.cast(integral, %{{[0-9]+}} : !cir.int), !cir.int andykaylor wrote: Maybe add checks for lowering (to LLVM IR through clangir and using classic codegen) here also? https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -0,0 +1,58 @@ +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir bcardosolopes wrote: Has this been addressed? https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
https://github.com/bcardosolopes approved this pull request. LGTM pending on question left in one comment https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
https://github.com/bcardosolopes edited https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -84,26 +96,266 @@ class ScalarExprEmitter : public StmtVisitor { } mlir::Value VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *e) { -mlir::Type type = cgf.convertType(e->getType()); +mlir::Type type = convertType(e->getType()); return builder.create( cgf.getLoc(e->getExprLoc()), type, builder.getCIRBoolAttr(e->getValue())); } - mlir::Value VisitCastExpr(CastExpr *E); + mlir::Value VisitCastExpr(CastExpr *e); + + mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { +return VisitCastExpr(e); + } + + /// Perform a pointer to boolean conversion. + mlir::Value emitPointerToBoolConversion(mlir::Value v, QualType qt) { +// TODO(cir): comparing the ptr to null is done when lowering CIR to LLVM. +// We might want to have a separate pass for these types of conversions. +return cgf.getBuilder().createPtrToBoolCast(v); + } + + mlir::Value emitFloatToBoolConversion(mlir::Value src, mlir::Location loc) { +auto boolTy = builder.getBoolTy(); +return builder.create(loc, boolTy, + cir::CastKind::float_to_bool, src); + } + + mlir::Value emitIntToBoolConversion(mlir::Value srcVal, mlir::Location loc) { +// Because of the type rules of C, we often end up computing a +// logical value, then zero extending it to int, then wanting it +// as a logical value again. +// TODO: optimize this common case here or leave it for later +// CIR passes? +mlir::Type boolTy = convertType(cgf.getContext().BoolTy); +return builder.create(loc, boolTy, cir::CastKind::int_to_bool, + srcVal); + } + + /// Convert the specified expression value to a boolean (!cir.bool) truth + /// value. This is equivalent to "Val != 0". + mlir::Value emitConversionToBool(mlir::Value src, QualType srcType, + mlir::Location loc) { +assert(srcType.isCanonical() && "EmitScalarConversion strips typedefs"); + +if (srcType->isRealFloatingType()) + return emitFloatToBoolConversion(src, loc); + +if ([[maybe_unused]] auto *mpt = llvm::dyn_cast(srcType)) + cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion"); + +if (srcType->isIntegerType()) + return emitIntToBoolConversion(src, loc); + +assert(::mlir::isa(src.getType())); +return emitPointerToBoolConversion(src, srcType); + } + + // Emit a conversion from the specified type to the specified destination + // type, both of which are CIR scalar types. + struct ScalarConversionOpts { +bool treatBooleanAsSigned; +bool emitImplicitIntegerTruncationChecks; +bool emitImplicitIntegerSignChangeChecks; + +ScalarConversionOpts() +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks(false), + emitImplicitIntegerSignChangeChecks(false) {} + +ScalarConversionOpts(clang::SanitizerSet sanOpts) +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks( + sanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation)), + emitImplicitIntegerSignChangeChecks( + sanOpts.has(SanitizerKind::ImplicitIntegerSignChange)) {} + }; + + // Conversion from bool, integral, or floating-point to integral or + // floating-point. Conversions involving other types are handled elsewhere. + // Conversion to bool is handled elsewhere because that's a comparison against + // zero, not a simple cast. This handles both individual scalars and vectors. + mlir::Value emitScalarCast(mlir::Value src, QualType srcType, + QualType dstType, mlir::Type srcTy, + mlir::Type dstTy, ScalarConversionOpts opts) { +assert(!srcType->isMatrixType() && !dstType->isMatrixType() && + "Internal error: matrix types not handled by this function."); +if (mlir::isa(srcTy) || +mlir::isa(dstTy)) + llvm_unreachable("Obsolete code. Don't use mlir::IntegerType with CIR."); + +mlir::Type fullDstTy = dstTy; +assert(!cir::MissingFeatures::vectorType()); + +std::optional castKind; + +if (mlir::isa(srcTy)) { + if (opts.treatBooleanAsSigned) +cgf.getCIRGenModule().errorNYI("signed bool"); + if (cgf.getBuilder().isInt(dstTy)) { +castKind = cir::CastKind::bool_to_int; + } else if (mlir::isa(dstTy)) { +castKind = cir::CastKind::bool_to_float; + } else { +llvm_unreachable("Internal error: Cast to unexpected type"); + } +} else if (cgf.getBuilder().isInt(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { +castKind = cir::CastKind::integral; + } else if (mlir::isa(dstTy)) { +castKind = cir::CastKind::int_to_float; + } else { +llvm_unreachable("Internal error: Cast to unexpected type"); + } +} else if (mlir::isa(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { +/
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -90,24 +89,279 @@ class ScalarExprEmitter : public StmtVisitor { builder.getCIRBoolAttr(e->getValue())); } - mlir::Value VisitCastExpr(CastExpr *E); + mlir::Value VisitCastExpr(CastExpr *e); + + mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { +return VisitCastExpr(e); + } + + /// Perform a pointer to boolean conversion. + mlir::Value emitPointerToBoolConversion(mlir::Value v, QualType qt) { +// TODO(cir): comparing the ptr to null is done when lowering CIR to LLVM. +// We might want to have a separate pass for these types of conversions. +return cgf.getBuilder().createPtrToBoolCast(v); + } + + mlir::Value emitFloatToBoolConversion(mlir::Value src, mlir::Location loc) { +auto boolTy = builder.getBoolTy(); +return builder.create(loc, boolTy, + cir::CastKind::float_to_bool, src); + } + + mlir::Value emitIntToBoolConversion(mlir::Value srcVal, mlir::Location loc) { +// Because of the type rules of C, we often end up computing a +// logical value, then zero extending it to int, then wanting it +// as a logical value again. +// TODO: optimize this common case here or leave it for later +// CIR passes? +mlir::Type boolTy = cgf.convertType(cgf.getContext().BoolTy); +return builder.create(loc, boolTy, cir::CastKind::int_to_bool, + srcVal); + } + + /// Convert the specified expression value to a boolean (!cir.bool) truth + /// value. This is equivalent to "Val != 0". + mlir::Value emitConversionToBool(mlir::Value src, QualType srcType, + mlir::Location loc) { +assert(srcType.isCanonical() && "EmitScalarConversion strips typedefs"); + +if (srcType->isRealFloatingType()) + return emitFloatToBoolConversion(src, loc); + +if (llvm::isa(srcType)) { + cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion"); + auto boolType = cgf.getContext().getBOOLType(); + auto cirBoolType = cgf.convertType(boolType); + CharUnits alignment = cgf.getContext().getTypeAlignInChars(boolType); + auto addr = + builder.createAlloca(loc, builder.getPointerTo(cirBoolType), + cirBoolType, {}, cgf.cgm.getSize(alignment)); + return builder.createLoad(loc, addr); +} + +if (srcType->isIntegerType()) + return emitIntToBoolConversion(src, loc); + +assert(::mlir::isa(src.getType())); +return emitPointerToBoolConversion(src, srcType); + } + + // Emit a conversion from the specified type to the specified destination + // type, both of which are CIR scalar types. + struct ScalarConversionOpts { +bool treatBooleanAsSigned; +bool emitImplicitIntegerTruncationChecks; +bool emitImplicitIntegerSignChangeChecks; + +ScalarConversionOpts() +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks(false), + emitImplicitIntegerSignChangeChecks(false) {} + +ScalarConversionOpts(clang::SanitizerSet sanOpts) +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks( + sanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation)), + emitImplicitIntegerSignChangeChecks( + sanOpts.has(SanitizerKind::ImplicitIntegerSignChange)) {} + }; + + // Conversion from bool, integral, or floating-point to integral or + // floating-point. Conversions involving other types are handled elsewhere. + // Conversion to bool is handled elsewhere because that's a comparison against + // zero, not a simple cast. This handles both individual scalars and vectors. + mlir::Value emitScalarCast(mlir::Value src, QualType srcType, + QualType dstType, mlir::Type srcTy, + mlir::Type dstTy, ScalarConversionOpts opts) { +assert(!srcType->isMatrixType() && !dstType->isMatrixType() && + "Internal error: matrix types not handled by this function."); +if (mlir::isa(srcTy) || +mlir::isa(dstTy)) + llvm_unreachable("Obsolete code. Don't use mlir::IntegerType with CIR."); + +mlir::Type fullDstTy = dstTy; +assert(!cir::MissingFeatures::vectorType()); + +std::optional castKind; + +if (mlir::isa(srcTy)) { + if (opts.treatBooleanAsSigned) +cgf.getCIRGenModule().errorNYI("signed bool"); + if (cgf.getBuilder().isInt(dstTy)) +castKind = cir::CastKind::bool_to_int; + else if (mlir::isa(dstTy)) +castKind = cir::CastKind::bool_to_float; + else +llvm_unreachable("Internal error: Cast to unexpected type"); +} else if (cgf.getBuilder().isInt(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) +castKind = cir::CastKind::integral; + else if (mlir::isa(dstTy)) +castKind = cir::CastKind::int_to_float; + else +llvm_unreachable("Internal error: Cast to unexpected type"); +
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -121,29 +375,174 @@ mlir::Value CIRGenFunction::emitScalarExpr(const Expr *e) { return ScalarExprEmitter(*this, builder).Visit(const_cast(e)); } +[[maybe_unused]] static bool MustVisitNullValue(const Expr *e) { + // If a null pointer expression's type is the C++0x nullptr_t, then + // it's not necessarily a simple constant and it must be evaluated + // for its potential side effects. + return e->getType()->isNullPtrType(); +} + // Emit code for an explicit or implicit cast. Implicit // casts have to handle a more broad range of conversions than explicit // casts, as they handle things like function to ptr-to-function decay // etc. mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { - Expr *e = ce->getSubExpr(); + Expr *subExpr = ce->getSubExpr(); QualType destTy = ce->getType(); CastKind kind = ce->getCastKind(); + // These cases are generally not written to ignore the result of evaluating + // their sub-expressions, so we clear this now. + ignoreResultAssign = false; erichkeane wrote: I spent a while trying to repro the warning I could swear I've seen before, but I've not been able to get this to happen. So disregard. https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
https://github.com/erichkeane approved this pull request. 1 nit, else LGTM. https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -247,6 +280,179 @@ struct ConvertCIRToLLVMPass StringRef getArgument() const override { return "cir-flat-to-llvm"; } }; +mlir::Type CIRToLLVMCastOpLowering::convertTy(mlir::Type ty) const { + return getTypeConverter()->convertType(ty); +} + +mlir::LogicalResult CIRToLLVMCastOpLowering::matchAndRewrite( +cir::CastOp castOp, OpAdaptor adaptor, +mlir::ConversionPatternRewriter &rewriter) const { + // For arithmetic conversions, LLVM IR uses the same instruction to convert + // both individual scalars and entire vectors. This lowering pass handles + // both situations. + + switch (castOp.getKind()) { + case cir::CastKind::array_to_ptrdecay: { +const auto ptrTy = mlir::cast(castOp.getType()); +mlir::Value sourceValue = adaptor.getOperands().front(); +mlir::Type targetType = convertTy(ptrTy); +mlir::Type elementTy = convertTypeForMemory(*getTypeConverter(), dataLayout, +ptrTy.getPointee()); +auto offset = llvm::SmallVector{0}; erichkeane wrote: ```suggestion llvm::SmallVector offset{0}; ``` https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
https://github.com/erichkeane edited https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -121,29 +375,174 @@ mlir::Value CIRGenFunction::emitScalarExpr(const Expr *e) { return ScalarExprEmitter(*this, builder).Visit(const_cast(e)); } +[[maybe_unused]] static bool MustVisitNullValue(const Expr *e) { + // If a null pointer expression's type is the C++0x nullptr_t, then + // it's not necessarily a simple constant and it must be evaluated + // for its potential side effects. + return e->getType()->isNullPtrType(); +} + // Emit code for an explicit or implicit cast. Implicit // casts have to handle a more broad range of conversions than explicit // casts, as they handle things like function to ptr-to-function decay // etc. mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { - Expr *e = ce->getSubExpr(); + Expr *subExpr = ce->getSubExpr(); QualType destTy = ce->getType(); CastKind kind = ce->getCastKind(); + // These cases are generally not written to ignore the result of evaluating + // their sub-expressions, so we clear this now. + ignoreResultAssign = false; erichkeane wrote: You'll have to do a void cast on it then, else this will cause a warning. https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
https://github.com/mmha updated https://github.com/llvm/llvm-project/pull/130690 >From a945e21869e5276c66ac979acd893d9bd9afe2cc Mon Sep 17 00:00:00 2001 From: Morris Hafner Date: Mon, 10 Mar 2025 16:18:34 -0700 Subject: [PATCH 1/4] [CIR] Upstream CastOp and scalar conversions This patch upstreams ClangIR's CastOp with the following exceptions: - No Fixed/FP conversions - No casts between value categories - No complex casts - No array_to_ptrdecay - No address_space - No casts involving record types (member pointers, base/derived casts) - No casts specific to ObjC or OpenCL --- .../CIR/Dialect/Builder/CIRBaseBuilder.h | 62 +++ clang/include/clang/CIR/Dialect/IR/CIROps.td | 105 + clang/include/clang/CIR/MissingFeatures.h | 14 +- clang/lib/CIR/CodeGen/CIRGenBuilder.h | 9 + clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp| 400 +- clang/lib/CIR/CodeGen/CIRGenFunction.h| 3 + clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 170 clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp| 27 ++ .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 207 + .../CIR/Lowering/DirectToLLVM/LowerToLLVM.h | 16 + clang/test/CIR/CodeGen/cast.cpp | 58 +++ clang/test/CIR/IR/cast.cir| 23 + clang/test/CIR/Lowering/cast.cir | 92 13 files changed, 1174 insertions(+), 12 deletions(-) create mode 100644 clang/test/CIR/CodeGen/cast.cpp create mode 100644 clang/test/CIR/IR/cast.cir create mode 100644 clang/test/CIR/Lowering/cast.cir diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index 017ae0c53a984..e5e8132e9f527 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -13,6 +13,7 @@ #include "clang/CIR/Dialect/IR/CIRAttrs.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/Dialect/IR/CIRTypes.h" +#include "llvm/Support/ErrorHandling.h" #include "mlir/IR/Builders.h" #include "mlir/IR/BuiltinTypes.h" @@ -78,6 +79,67 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return create(loc, val, dst); } + //======// + // Cast/Conversion Operators + //======// + + mlir::Value createCast(mlir::Location loc, cir::CastKind kind, + mlir::Value src, mlir::Type newTy) { +if (newTy == src.getType()) + return src; +return create(loc, newTy, kind, src); + } + + mlir::Value createCast(cir::CastKind kind, mlir::Value src, + mlir::Type newTy) { +if (newTy == src.getType()) + return src; +return createCast(src.getLoc(), kind, src, newTy); + } + + mlir::Value createIntCast(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::integral, src, newTy); + } + + mlir::Value createIntToPtr(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::int_to_ptr, src, newTy); + } + + mlir::Value createPtrToInt(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::ptr_to_int, src, newTy); + } + + mlir::Value createPtrToBoolCast(mlir::Value v) { +return createCast(cir::CastKind::ptr_to_bool, v, getBoolTy()); + } + + mlir::Value createBoolToInt(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::bool_to_int, src, newTy); + } + + mlir::Value createBitcast(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::bitcast, src, newTy); + } + + mlir::Value createBitcast(mlir::Location loc, mlir::Value src, +mlir::Type newTy) { +return createCast(loc, cir::CastKind::bitcast, src, newTy); + } + + mlir::Value createPtrBitcast(mlir::Value src, mlir::Type newPointeeTy) { +assert(mlir::isa(src.getType()) && "expected ptr src"); +return createBitcast(src, getPointerTo(newPointeeTy)); + } + + mlir::Value createAddrSpaceCast(mlir::Location loc, mlir::Value src, + mlir::Type newTy) { +return createCast(loc, cir::CastKind::address_space, src, newTy); + } + + mlir::Value createAddrSpaceCast(mlir::Value src, mlir::Type newTy) { +return createAddrSpaceCast(src.getLoc(), src, newTy); + } + // // Block handling helpers // -- diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 77c43e5ace64a..9797960e00867 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -78,6 +78,111 @@ class LLVMLoweringInfo { class CIR_Op traits = []> : Op, LLVMLoweringInfo; +//===--===// +// CastOp +//===--
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -90,20 +89,259 @@ class ScalarExprEmitter : public StmtVisitor { builder.getCIRBoolAttr(e->getValue())); } - mlir::Value VisitCastExpr(CastExpr *E); + mlir::Value VisitCastExpr(CastExpr *e); + + mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { +return VisitCastExpr(e); + } + + /// Perform a pointer to boolean conversion. + mlir::Value emitPointerToBoolConversion(mlir::Value v, QualType qt) { +// TODO(cir): comparing the ptr to null is done when lowering CIR to LLVM. +// We might want to have a separate pass for these types of conversions. +return cgf.getBuilder().createPtrToBoolCast(v); + } + + mlir::Value emitFloatToBoolConversion(mlir::Value src, mlir::Location loc) { +auto boolTy = builder.getBoolTy(); +return builder.create(loc, boolTy, + cir::CastKind::float_to_bool, src); + } + + mlir::Value emitIntToBoolConversion(mlir::Value srcVal, mlir::Location loc) { +// Because of the type rules of C, we often end up computing a +// logical value, then zero extending it to int, then wanting it +// as a logical value again. +// TODO: optimize this common case here or leave it for later +// CIR passes? +mlir::Type boolTy = cgf.convertType(cgf.getContext().BoolTy); +return builder.create(loc, boolTy, cir::CastKind::int_to_bool, + srcVal); + } + + /// Convert the specified expression value to a boolean (!cir.bool) truth + /// value. This is equivalent to "Val != 0". + mlir::Value emitConversionToBool(mlir::Value src, QualType srcType, + mlir::Location loc) { +assert(srcType.isCanonical() && "EmitScalarConversion strips typedefs"); + +if (srcType->isRealFloatingType()) + return emitFloatToBoolConversion(src, loc); + +if (llvm::isa(srcType)) + cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion"); + +if (srcType->isIntegerType()) + return emitIntToBoolConversion(src, loc); + +assert(::mlir::isa(src.getType())); +return emitPointerToBoolConversion(src, srcType); + } + + // Emit a conversion from the specified type to the specified destination + // type, both of which are CIR scalar types. + struct ScalarConversionOpts { +bool treatBooleanAsSigned; +bool emitImplicitIntegerTruncationChecks; +bool emitImplicitIntegerSignChangeChecks; + +ScalarConversionOpts() +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks(false), + emitImplicitIntegerSignChangeChecks(false) {} + +ScalarConversionOpts(clang::SanitizerSet sanOpts) +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks( + sanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation)), + emitImplicitIntegerSignChangeChecks( + sanOpts.has(SanitizerKind::ImplicitIntegerSignChange)) {} + }; + + // Conversion from bool, integral, or floating-point to integral or + // floating-point. Conversions involving other types are handled elsewhere. + // Conversion to bool is handled elsewhere because that's a comparison against + // zero, not a simple cast. This handles both individual scalars and vectors. + mlir::Value emitScalarCast(mlir::Value src, QualType srcType, + QualType dstType, mlir::Type srcTy, + mlir::Type dstTy, ScalarConversionOpts opts) { +assert(!srcType->isMatrixType() && !dstType->isMatrixType() && + "Internal error: matrix types not handled by this function."); +if (mlir::isa(srcTy) || erichkeane wrote: I realize this made sense in the case of 'switching from this function to another' in the incubator, but I think this should probably be an assert instead. https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -90,24 +89,279 @@ class ScalarExprEmitter : public StmtVisitor { builder.getCIRBoolAttr(e->getValue())); } - mlir::Value VisitCastExpr(CastExpr *E); + mlir::Value VisitCastExpr(CastExpr *e); + + mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { +return VisitCastExpr(e); + } + + /// Perform a pointer to boolean conversion. + mlir::Value emitPointerToBoolConversion(mlir::Value v, QualType qt) { +// TODO(cir): comparing the ptr to null is done when lowering CIR to LLVM. +// We might want to have a separate pass for these types of conversions. +return cgf.getBuilder().createPtrToBoolCast(v); + } + + mlir::Value emitFloatToBoolConversion(mlir::Value src, mlir::Location loc) { +auto boolTy = builder.getBoolTy(); erichkeane wrote: probably can't do `auto` here either. https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -90,20 +89,259 @@ class ScalarExprEmitter : public StmtVisitor { builder.getCIRBoolAttr(e->getValue())); } - mlir::Value VisitCastExpr(CastExpr *E); + mlir::Value VisitCastExpr(CastExpr *e); + + mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { +return VisitCastExpr(e); + } + + /// Perform a pointer to boolean conversion. + mlir::Value emitPointerToBoolConversion(mlir::Value v, QualType qt) { +// TODO(cir): comparing the ptr to null is done when lowering CIR to LLVM. +// We might want to have a separate pass for these types of conversions. +return cgf.getBuilder().createPtrToBoolCast(v); + } + + mlir::Value emitFloatToBoolConversion(mlir::Value src, mlir::Location loc) { +auto boolTy = builder.getBoolTy(); +return builder.create(loc, boolTy, + cir::CastKind::float_to_bool, src); + } + + mlir::Value emitIntToBoolConversion(mlir::Value srcVal, mlir::Location loc) { +// Because of the type rules of C, we often end up computing a +// logical value, then zero extending it to int, then wanting it +// as a logical value again. +// TODO: optimize this common case here or leave it for later +// CIR passes? +mlir::Type boolTy = cgf.convertType(cgf.getContext().BoolTy); +return builder.create(loc, boolTy, cir::CastKind::int_to_bool, + srcVal); + } + + /// Convert the specified expression value to a boolean (!cir.bool) truth + /// value. This is equivalent to "Val != 0". + mlir::Value emitConversionToBool(mlir::Value src, QualType srcType, + mlir::Location loc) { +assert(srcType.isCanonical() && "EmitScalarConversion strips typedefs"); + +if (srcType->isRealFloatingType()) + return emitFloatToBoolConversion(src, loc); + +if (llvm::isa(srcType)) + cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion"); + +if (srcType->isIntegerType()) + return emitIntToBoolConversion(src, loc); + +assert(::mlir::isa(src.getType())); +return emitPointerToBoolConversion(src, srcType); + } + + // Emit a conversion from the specified type to the specified destination + // type, both of which are CIR scalar types. + struct ScalarConversionOpts { +bool treatBooleanAsSigned; +bool emitImplicitIntegerTruncationChecks; +bool emitImplicitIntegerSignChangeChecks; + +ScalarConversionOpts() +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks(false), + emitImplicitIntegerSignChangeChecks(false) {} + +ScalarConversionOpts(clang::SanitizerSet sanOpts) +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks( + sanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation)), + emitImplicitIntegerSignChangeChecks( + sanOpts.has(SanitizerKind::ImplicitIntegerSignChange)) {} + }; + + // Conversion from bool, integral, or floating-point to integral or + // floating-point. Conversions involving other types are handled elsewhere. + // Conversion to bool is handled elsewhere because that's a comparison against + // zero, not a simple cast. This handles both individual scalars and vectors. + mlir::Value emitScalarCast(mlir::Value src, QualType srcType, + QualType dstType, mlir::Type srcTy, + mlir::Type dstTy, ScalarConversionOpts opts) { +assert(!srcType->isMatrixType() && !dstType->isMatrixType() && + "Internal error: matrix types not handled by this function."); +if (mlir::isa(srcTy) || erichkeane wrote: Same comment again. https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -121,29 +375,174 @@ mlir::Value CIRGenFunction::emitScalarExpr(const Expr *e) { return ScalarExprEmitter(*this, builder).Visit(const_cast(e)); } +[[maybe_unused]] static bool MustVisitNullValue(const Expr *e) { + // If a null pointer expression's type is the C++0x nullptr_t, then + // it's not necessarily a simple constant and it must be evaluated + // for its potential side effects. + return e->getType()->isNullPtrType(); +} + // Emit code for an explicit or implicit cast. Implicit // casts have to handle a more broad range of conversions than explicit // casts, as they handle things like function to ptr-to-function decay // etc. mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { - Expr *e = ce->getSubExpr(); + Expr *subExpr = ce->getSubExpr(); QualType destTy = ce->getType(); CastKind kind = ce->getCastKind(); + // These cases are generally not written to ignore the result of evaluating + // their sub-expressions, so we clear this now. + ignoreResultAssign = false; + switch (kind) { + case clang::CK_Dependent: +llvm_unreachable("dependent cast kind in CIR gen!"); + case clang::CK_BuiltinFnToFnPtr: +llvm_unreachable("builtin functions are handled elsewhere"); + + case CK_CPointerToObjCPointerCast: + case CK_BlockPointerToObjCPointerCast: + case CK_AnyPointerToBlockPointerCast: + case CK_BitCast: { +mlir::Value src = Visit(const_cast(subExpr)); +mlir::Type dstTy = cgf.convertType(destTy); + +assert(!cir::MissingFeatures::addressSpace()); + +if (cgf.sanOpts.has(SanitizerKind::CFIUnrelatedCast)) + cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(), + "sanitizer support"); + +if (cgf.cgm.getCodeGenOpts().StrictVTablePointers) + cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(), + "strict vtable pointers"); + +// Update heapallocsite metadata when there is an explicit pointer cast. +assert(!cir::MissingFeatures::addHeapAllocSiteMetadata()); + +// If Src is a fixed vector and Dst is a scalable vector, and both have the +// same element type, use the llvm.vector.insert intrinsic to perform the +// bitcast. +assert(!cir::MissingFeatures::scalableVectors()); + +// If Src is a scalable vector and Dst is a fixed vector, and both have the +// same element type, use the llvm.vector.extract intrinsic to perform the +// bitcast. +assert(!cir::MissingFeatures::scalableVectors()); + +// Perform VLAT <-> VLST bitcast through memory. +// TODO: since the llvm.experimental.vector.{insert,extract} intrinsics +// require the element types of the vectors to be the same, we +// need to keep this around for bitcasts between VLAT <-> VLST where +// the element types of the vectors are not the same, until we figure +// out a better way of doing these casts. +assert(!cir::MissingFeatures::scalableVectors()); + +return cgf.getBuilder().createBitcast(cgf.getLoc(subExpr->getSourceRange()), + src, dstTy); + } + + case CK_AtomicToNonAtomic: +cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(), + "CastExpr: ", ce->getCastKindName()); +break; + case CK_NonAtomicToAtomic: + case CK_UserDefinedConversion: +return Visit(const_cast(subExpr)); + case CK_NoOp: { +auto v = Visit(const_cast(subExpr)); +if (v) { + // CK_NoOp can model a pointer qualification conversion, which can remove + // an array bound and change the IR type. + // FIXME: Once pointee types are removed from IR, remove this. + mlir::Type t = cgf.convertType(destTy); + if (t != v.getType()) +cgf.getCIRGenModule().errorNYI("pointer qualification conversion"); +} +return v; + } + + case CK_NullToPointer: { +if (MustVisitNullValue(subExpr)) + cgf.getCIRGenModule().errorNYI( + subExpr->getSourceRange(), + "ignored expression on null to pointer cast"); + +// Note that DestTy is used as the MLIR type instead of a custom +// nullptr type. +mlir::Type ty = cgf.convertType(destTy); +return builder.getNullPtr(ty, cgf.getLoc(subExpr->getExprLoc())); + } + case CK_LValueToRValue: -assert(cgf.getContext().hasSameUnqualifiedType(e->getType(), destTy)); -assert(e->isGLValue() && "lvalue-to-rvalue applied to r-value!"); -return Visit(const_cast(e)); +assert(cgf.getContext().hasSameUnqualifiedType(subExpr->getType(), destTy)); +assert(subExpr->isGLValue() && "lvalue-to-rvalue applied to r-value!"); +return Visit(const_cast(subExpr)); case CK_IntegralCast: { -assert(!cir::MissingFeatures::scalarConversionOpts()); -return emitScalarConversion(Visit(e), e->getType(), destTy, +ScalarConversionOpts opts; +if (auto *ice = dyn_cast(ce)) { + if (!ice->isPartOfExplicitCast()) +
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -90,24 +89,279 @@ class ScalarExprEmitter : public StmtVisitor { builder.getCIRBoolAttr(e->getValue())); } - mlir::Value VisitCastExpr(CastExpr *E); + mlir::Value VisitCastExpr(CastExpr *e); + + mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { +return VisitCastExpr(e); + } + + /// Perform a pointer to boolean conversion. + mlir::Value emitPointerToBoolConversion(mlir::Value v, QualType qt) { +// TODO(cir): comparing the ptr to null is done when lowering CIR to LLVM. +// We might want to have a separate pass for these types of conversions. +return cgf.getBuilder().createPtrToBoolCast(v); + } + + mlir::Value emitFloatToBoolConversion(mlir::Value src, mlir::Location loc) { +auto boolTy = builder.getBoolTy(); +return builder.create(loc, boolTy, + cir::CastKind::float_to_bool, src); + } + + mlir::Value emitIntToBoolConversion(mlir::Value srcVal, mlir::Location loc) { +// Because of the type rules of C, we often end up computing a +// logical value, then zero extending it to int, then wanting it +// as a logical value again. +// TODO: optimize this common case here or leave it for later +// CIR passes? +mlir::Type boolTy = cgf.convertType(cgf.getContext().BoolTy); +return builder.create(loc, boolTy, cir::CastKind::int_to_bool, + srcVal); + } + + /// Convert the specified expression value to a boolean (!cir.bool) truth + /// value. This is equivalent to "Val != 0". + mlir::Value emitConversionToBool(mlir::Value src, QualType srcType, + mlir::Location loc) { +assert(srcType.isCanonical() && "EmitScalarConversion strips typedefs"); + +if (srcType->isRealFloatingType()) + return emitFloatToBoolConversion(src, loc); + +if (llvm::isa(srcType)) { + cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion"); + auto boolType = cgf.getContext().getBOOLType(); + auto cirBoolType = cgf.convertType(boolType); + CharUnits alignment = cgf.getContext().getTypeAlignInChars(boolType); + auto addr = + builder.createAlloca(loc, builder.getPointerTo(cirBoolType), + cirBoolType, {}, cgf.cgm.getSize(alignment)); + return builder.createLoad(loc, addr); erichkeane wrote: I think you mentioned we dont have constants implemented? Else just a bool constant would be sufficient I would expect. https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -84,26 +96,266 @@ class ScalarExprEmitter : public StmtVisitor { } mlir::Value VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *e) { -mlir::Type type = cgf.convertType(e->getType()); +mlir::Type type = convertType(e->getType()); return builder.create( cgf.getLoc(e->getExprLoc()), type, builder.getCIRBoolAttr(e->getValue())); } - mlir::Value VisitCastExpr(CastExpr *E); + mlir::Value VisitCastExpr(CastExpr *e); + + mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { +return VisitCastExpr(e); + } + + /// Perform a pointer to boolean conversion. + mlir::Value emitPointerToBoolConversion(mlir::Value v, QualType qt) { +// TODO(cir): comparing the ptr to null is done when lowering CIR to LLVM. +// We might want to have a separate pass for these types of conversions. +return cgf.getBuilder().createPtrToBoolCast(v); + } + + mlir::Value emitFloatToBoolConversion(mlir::Value src, mlir::Location loc) { +auto boolTy = builder.getBoolTy(); +return builder.create(loc, boolTy, + cir::CastKind::float_to_bool, src); + } + + mlir::Value emitIntToBoolConversion(mlir::Value srcVal, mlir::Location loc) { +// Because of the type rules of C, we often end up computing a +// logical value, then zero extending it to int, then wanting it +// as a logical value again. +// TODO: optimize this common case here or leave it for later +// CIR passes? +mlir::Type boolTy = convertType(cgf.getContext().BoolTy); +return builder.create(loc, boolTy, cir::CastKind::int_to_bool, + srcVal); + } + + /// Convert the specified expression value to a boolean (!cir.bool) truth + /// value. This is equivalent to "Val != 0". + mlir::Value emitConversionToBool(mlir::Value src, QualType srcType, + mlir::Location loc) { +assert(srcType.isCanonical() && "EmitScalarConversion strips typedefs"); + +if (srcType->isRealFloatingType()) + return emitFloatToBoolConversion(src, loc); + +if ([[maybe_unused]] auto *mpt = llvm::dyn_cast(srcType)) + cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion"); + +if (srcType->isIntegerType()) + return emitIntToBoolConversion(src, loc); + +assert(::mlir::isa(src.getType())); +return emitPointerToBoolConversion(src, srcType); + } + + // Emit a conversion from the specified type to the specified destination + // type, both of which are CIR scalar types. + struct ScalarConversionOpts { +bool treatBooleanAsSigned; +bool emitImplicitIntegerTruncationChecks; +bool emitImplicitIntegerSignChangeChecks; + +ScalarConversionOpts() +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks(false), + emitImplicitIntegerSignChangeChecks(false) {} + +ScalarConversionOpts(clang::SanitizerSet sanOpts) +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks( + sanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation)), + emitImplicitIntegerSignChangeChecks( + sanOpts.has(SanitizerKind::ImplicitIntegerSignChange)) {} + }; + + // Conversion from bool, integral, or floating-point to integral or + // floating-point. Conversions involving other types are handled elsewhere. + // Conversion to bool is handled elsewhere because that's a comparison against + // zero, not a simple cast. This handles both individual scalars and vectors. + mlir::Value emitScalarCast(mlir::Value src, QualType srcType, + QualType dstType, mlir::Type srcTy, + mlir::Type dstTy, ScalarConversionOpts opts) { +assert(!srcType->isMatrixType() && !dstType->isMatrixType() && + "Internal error: matrix types not handled by this function."); +if (mlir::isa(srcTy) || +mlir::isa(dstTy)) + llvm_unreachable("Obsolete code. Don't use mlir::IntegerType with CIR."); + +mlir::Type fullDstTy = dstTy; +assert(!cir::MissingFeatures::vectorType()); + +std::optional castKind; + +if (mlir::isa(srcTy)) { + if (opts.treatBooleanAsSigned) +cgf.getCIRGenModule().errorNYI("signed bool"); + if (cgf.getBuilder().isInt(dstTy)) { +castKind = cir::CastKind::bool_to_int; + } else if (mlir::isa(dstTy)) { +castKind = cir::CastKind::bool_to_float; + } else { +llvm_unreachable("Internal error: Cast to unexpected type"); + } +} else if (cgf.getBuilder().isInt(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { +castKind = cir::CastKind::integral; + } else if (mlir::isa(dstTy)) { +castKind = cir::CastKind::int_to_float; + } else { +llvm_unreachable("Internal error: Cast to unexpected type"); + } +} else if (mlir::isa(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { +/
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -90,24 +89,279 @@ class ScalarExprEmitter : public StmtVisitor { builder.getCIRBoolAttr(e->getValue())); } - mlir::Value VisitCastExpr(CastExpr *E); + mlir::Value VisitCastExpr(CastExpr *e); + + mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { +return VisitCastExpr(e); + } + + /// Perform a pointer to boolean conversion. + mlir::Value emitPointerToBoolConversion(mlir::Value v, QualType qt) { +// TODO(cir): comparing the ptr to null is done when lowering CIR to LLVM. +// We might want to have a separate pass for these types of conversions. +return cgf.getBuilder().createPtrToBoolCast(v); + } + + mlir::Value emitFloatToBoolConversion(mlir::Value src, mlir::Location loc) { +auto boolTy = builder.getBoolTy(); +return builder.create(loc, boolTy, + cir::CastKind::float_to_bool, src); + } + + mlir::Value emitIntToBoolConversion(mlir::Value srcVal, mlir::Location loc) { +// Because of the type rules of C, we often end up computing a +// logical value, then zero extending it to int, then wanting it +// as a logical value again. +// TODO: optimize this common case here or leave it for later +// CIR passes? +mlir::Type boolTy = cgf.convertType(cgf.getContext().BoolTy); +return builder.create(loc, boolTy, cir::CastKind::int_to_bool, + srcVal); + } + + /// Convert the specified expression value to a boolean (!cir.bool) truth + /// value. This is equivalent to "Val != 0". + mlir::Value emitConversionToBool(mlir::Value src, QualType srcType, + mlir::Location loc) { +assert(srcType.isCanonical() && "EmitScalarConversion strips typedefs"); + +if (srcType->isRealFloatingType()) + return emitFloatToBoolConversion(src, loc); + +if (llvm::isa(srcType)) { + cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion"); + auto boolType = cgf.getContext().getBOOLType(); + auto cirBoolType = cgf.convertType(boolType); + CharUnits alignment = cgf.getContext().getTypeAlignInChars(boolType); + auto addr = + builder.createAlloca(loc, builder.getPointerTo(cirBoolType), + cirBoolType, {}, cgf.cgm.getSize(alignment)); + return builder.createLoad(loc, addr); +} + +if (srcType->isIntegerType()) + return emitIntToBoolConversion(src, loc); + +assert(::mlir::isa(src.getType())); +return emitPointerToBoolConversion(src, srcType); + } + + // Emit a conversion from the specified type to the specified destination + // type, both of which are CIR scalar types. + struct ScalarConversionOpts { +bool treatBooleanAsSigned; +bool emitImplicitIntegerTruncationChecks; +bool emitImplicitIntegerSignChangeChecks; + +ScalarConversionOpts() +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks(false), + emitImplicitIntegerSignChangeChecks(false) {} + +ScalarConversionOpts(clang::SanitizerSet sanOpts) +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks( + sanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation)), + emitImplicitIntegerSignChangeChecks( + sanOpts.has(SanitizerKind::ImplicitIntegerSignChange)) {} + }; + + // Conversion from bool, integral, or floating-point to integral or + // floating-point. Conversions involving other types are handled elsewhere. + // Conversion to bool is handled elsewhere because that's a comparison against + // zero, not a simple cast. This handles both individual scalars and vectors. + mlir::Value emitScalarCast(mlir::Value src, QualType srcType, + QualType dstType, mlir::Type srcTy, + mlir::Type dstTy, ScalarConversionOpts opts) { +assert(!srcType->isMatrixType() && !dstType->isMatrixType() && + "Internal error: matrix types not handled by this function."); +if (mlir::isa(srcTy) || +mlir::isa(dstTy)) + llvm_unreachable("Obsolete code. Don't use mlir::IntegerType with CIR."); + +mlir::Type fullDstTy = dstTy; +assert(!cir::MissingFeatures::vectorType()); + +std::optional castKind; + +if (mlir::isa(srcTy)) { + if (opts.treatBooleanAsSigned) +cgf.getCIRGenModule().errorNYI("signed bool"); + if (cgf.getBuilder().isInt(dstTy)) +castKind = cir::CastKind::bool_to_int; + else if (mlir::isa(dstTy)) +castKind = cir::CastKind::bool_to_float; + else +llvm_unreachable("Internal error: Cast to unexpected type"); +} else if (cgf.getBuilder().isInt(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) +castKind = cir::CastKind::integral; + else if (mlir::isa(dstTy)) +castKind = cir::CastKind::int_to_float; + else +llvm_unreachable("Internal error: Cast to unexpected type"); +
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -90,24 +89,279 @@ class ScalarExprEmitter : public StmtVisitor { builder.getCIRBoolAttr(e->getValue())); } - mlir::Value VisitCastExpr(CastExpr *E); + mlir::Value VisitCastExpr(CastExpr *e); + + mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { +return VisitCastExpr(e); + } + + /// Perform a pointer to boolean conversion. + mlir::Value emitPointerToBoolConversion(mlir::Value v, QualType qt) { +// TODO(cir): comparing the ptr to null is done when lowering CIR to LLVM. +// We might want to have a separate pass for these types of conversions. +return cgf.getBuilder().createPtrToBoolCast(v); + } + + mlir::Value emitFloatToBoolConversion(mlir::Value src, mlir::Location loc) { +auto boolTy = builder.getBoolTy(); +return builder.create(loc, boolTy, + cir::CastKind::float_to_bool, src); + } + + mlir::Value emitIntToBoolConversion(mlir::Value srcVal, mlir::Location loc) { +// Because of the type rules of C, we often end up computing a +// logical value, then zero extending it to int, then wanting it +// as a logical value again. +// TODO: optimize this common case here or leave it for later +// CIR passes? +mlir::Type boolTy = cgf.convertType(cgf.getContext().BoolTy); +return builder.create(loc, boolTy, cir::CastKind::int_to_bool, + srcVal); + } + + /// Convert the specified expression value to a boolean (!cir.bool) truth + /// value. This is equivalent to "Val != 0". + mlir::Value emitConversionToBool(mlir::Value src, QualType srcType, + mlir::Location loc) { +assert(srcType.isCanonical() && "EmitScalarConversion strips typedefs"); + +if (srcType->isRealFloatingType()) + return emitFloatToBoolConversion(src, loc); + +if (llvm::isa(srcType)) { + cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion"); + auto boolType = cgf.getContext().getBOOLType(); erichkeane wrote: Another case we can't use autos. Also a 3rd way to get a `bool` type? https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -78,6 +78,156 @@ class LLVMLoweringInfo { class CIR_Op traits = []> : Op, LLVMLoweringInfo; +//===--===// +// CastOp +//===--===// + +// CK_Dependent erichkeane wrote: Neat! Thank you! https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -90,24 +89,279 @@ class ScalarExprEmitter : public StmtVisitor { builder.getCIRBoolAttr(e->getValue())); } - mlir::Value VisitCastExpr(CastExpr *E); + mlir::Value VisitCastExpr(CastExpr *e); + + mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { +return VisitCastExpr(e); + } + + /// Perform a pointer to boolean conversion. + mlir::Value emitPointerToBoolConversion(mlir::Value v, QualType qt) { +// TODO(cir): comparing the ptr to null is done when lowering CIR to LLVM. +// We might want to have a separate pass for these types of conversions. +return cgf.getBuilder().createPtrToBoolCast(v); + } + + mlir::Value emitFloatToBoolConversion(mlir::Value src, mlir::Location loc) { +auto boolTy = builder.getBoolTy(); +return builder.create(loc, boolTy, + cir::CastKind::float_to_bool, src); + } + + mlir::Value emitIntToBoolConversion(mlir::Value srcVal, mlir::Location loc) { +// Because of the type rules of C, we often end up computing a +// logical value, then zero extending it to int, then wanting it +// as a logical value again. +// TODO: optimize this common case here or leave it for later +// CIR passes? +mlir::Type boolTy = cgf.convertType(cgf.getContext().BoolTy); erichkeane wrote: why convertType here but `getBoolTy` on 106? https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -121,29 +375,174 @@ mlir::Value CIRGenFunction::emitScalarExpr(const Expr *e) { return ScalarExprEmitter(*this, builder).Visit(const_cast(e)); } +[[maybe_unused]] static bool MustVisitNullValue(const Expr *e) { + // If a null pointer expression's type is the C++0x nullptr_t, then + // it's not necessarily a simple constant and it must be evaluated + // for its potential side effects. + return e->getType()->isNullPtrType(); +} + // Emit code for an explicit or implicit cast. Implicit // casts have to handle a more broad range of conversions than explicit // casts, as they handle things like function to ptr-to-function decay // etc. mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { - Expr *e = ce->getSubExpr(); + Expr *subExpr = ce->getSubExpr(); QualType destTy = ce->getType(); CastKind kind = ce->getCastKind(); + // These cases are generally not written to ignore the result of evaluating + // their sub-expressions, so we clear this now. + ignoreResultAssign = false; + switch (kind) { + case clang::CK_Dependent: +llvm_unreachable("dependent cast kind in CIR gen!"); + case clang::CK_BuiltinFnToFnPtr: +llvm_unreachable("builtin functions are handled elsewhere"); + + case CK_CPointerToObjCPointerCast: + case CK_BlockPointerToObjCPointerCast: + case CK_AnyPointerToBlockPointerCast: + case CK_BitCast: { +mlir::Value src = Visit(const_cast(subExpr)); +mlir::Type dstTy = cgf.convertType(destTy); + +assert(!cir::MissingFeatures::addressSpace()); + +if (cgf.sanOpts.has(SanitizerKind::CFIUnrelatedCast)) + cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(), + "sanitizer support"); + +if (cgf.cgm.getCodeGenOpts().StrictVTablePointers) + cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(), + "strict vtable pointers"); + +// Update heapallocsite metadata when there is an explicit pointer cast. +assert(!cir::MissingFeatures::addHeapAllocSiteMetadata()); + +// If Src is a fixed vector and Dst is a scalable vector, and both have the +// same element type, use the llvm.vector.insert intrinsic to perform the +// bitcast. +assert(!cir::MissingFeatures::scalableVectors()); + +// If Src is a scalable vector and Dst is a fixed vector, and both have the +// same element type, use the llvm.vector.extract intrinsic to perform the +// bitcast. +assert(!cir::MissingFeatures::scalableVectors()); + +// Perform VLAT <-> VLST bitcast through memory. +// TODO: since the llvm.experimental.vector.{insert,extract} intrinsics +// require the element types of the vectors to be the same, we +// need to keep this around for bitcasts between VLAT <-> VLST where +// the element types of the vectors are not the same, until we figure +// out a better way of doing these casts. +assert(!cir::MissingFeatures::scalableVectors()); + +return cgf.getBuilder().createBitcast(cgf.getLoc(subExpr->getSourceRange()), + src, dstTy); + } + + case CK_AtomicToNonAtomic: +cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(), + "CastExpr: ", ce->getCastKindName()); +break; + case CK_NonAtomicToAtomic: + case CK_UserDefinedConversion: +return Visit(const_cast(subExpr)); + case CK_NoOp: { +auto v = Visit(const_cast(subExpr)); +if (v) { + // CK_NoOp can model a pointer qualification conversion, which can remove + // an array bound and change the IR type. + // FIXME: Once pointee types are removed from IR, remove this. + mlir::Type t = cgf.convertType(destTy); + if (t != v.getType()) +cgf.getCIRGenModule().errorNYI("pointer qualification conversion"); +} +return v; + } + + case CK_NullToPointer: { +if (MustVisitNullValue(subExpr)) + cgf.getCIRGenModule().errorNYI( + subExpr->getSourceRange(), + "ignored expression on null to pointer cast"); + +// Note that DestTy is used as the MLIR type instead of a custom +// nullptr type. +mlir::Type ty = cgf.convertType(destTy); +return builder.getNullPtr(ty, cgf.getLoc(subExpr->getExprLoc())); + } + case CK_LValueToRValue: -assert(cgf.getContext().hasSameUnqualifiedType(e->getType(), destTy)); -assert(e->isGLValue() && "lvalue-to-rvalue applied to r-value!"); -return Visit(const_cast(e)); +assert(cgf.getContext().hasSameUnqualifiedType(subExpr->getType(), destTy)); +assert(subExpr->isGLValue() && "lvalue-to-rvalue applied to r-value!"); +return Visit(const_cast(subExpr)); case CK_IntegralCast: { -assert(!cir::MissingFeatures::scalarConversionOpts()); -return emitScalarConversion(Visit(e), e->getType(), destTy, +ScalarConversionOpts opts; +if (auto *ice = dyn_cast(ce)) { + if (!ice->isPartOfExplicitCast()) +
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -121,29 +359,159 @@ mlir::Value CIRGenFunction::emitScalarExpr(const Expr *e) { return ScalarExprEmitter(*this, builder).Visit(const_cast(e)); } +[[maybe_unused]] static bool MustVisitNullValue(const Expr *e) { + // If a null pointer expression's type is the C++0x nullptr_t, then + // it's not necessarily a simple constant and it must be evaluated + // for its potential side effects. + return e->getType()->isNullPtrType(); +} + // Emit code for an explicit or implicit cast. Implicit // casts have to handle a more broad range of conversions than explicit // casts, as they handle things like function to ptr-to-function decay // etc. mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { - Expr *e = ce->getSubExpr(); + Expr *subExpr = ce->getSubExpr(); QualType destTy = ce->getType(); CastKind kind = ce->getCastKind(); + // These cases are generally not written to ignore the result of evaluating + // their sub-expressions, so we clear this now. + ignoreResultAssign = false; + switch (kind) { + case clang::CK_Dependent: +llvm_unreachable("dependent cast kind in CIR gen!"); + case clang::CK_BuiltinFnToFnPtr: +llvm_unreachable("builtin functions are handled elsewhere"); + + case CK_CPointerToObjCPointerCast: + case CK_BlockPointerToObjCPointerCast: + case CK_AnyPointerToBlockPointerCast: + case CK_BitCast: { +mlir::Value src = Visit(const_cast(subExpr)); +mlir::Type dstTy = cgf.convertType(destTy); + +assert(!cir::MissingFeatures::addressSpace()); + +if (cgf.sanOpts.has(SanitizerKind::CFIUnrelatedCast)) + cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(), + "sanitizer support"); + +if (cgf.cgm.getCodeGenOpts().StrictVTablePointers) + cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(), + "strict vtable pointers"); + +// Update heapallocsite metadata when there is an explicit pointer cast. +assert(!cir::MissingFeatures::addHeapAllocSiteMetadata()); + +// If Src is a fixed vector and Dst is a scalable vector, and both have the +// same element type, use the llvm.vector.insert intrinsic to perform the +// bitcast. +assert(!cir::MissingFeatures::scalableVectors()); + +// If Src is a scalable vector and Dst is a fixed vector, and both have the +// same element type, use the llvm.vector.extract intrinsic to perform the +// bitcast. +assert(!cir::MissingFeatures::scalableVectors()); + +// Perform VLAT <-> VLST bitcast through memory. +// TODO: since the llvm.experimental.vector.{insert,extract} intrinsics +// require the element types of the vectors to be the same, we +// need to keep this around for bitcasts between VLAT <-> VLST where +// the element types of the vectors are not the same, until we figure +// out a better way of doing these casts. +assert(!cir::MissingFeatures::scalableVectors()); + +return cgf.getBuilder().createBitcast(cgf.getLoc(subExpr->getSourceRange()), + src, dstTy); + } + + case CK_AtomicToNonAtomic: +cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(), + "CastExpr: ", ce->getCastKindName()); +break; erichkeane wrote: still, atomic-to-non-atomic cast can change types, so probably should return something. https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -247,6 +280,179 @@ struct ConvertCIRToLLVMPass StringRef getArgument() const override { return "cir-flat-to-llvm"; } }; +mlir::Type CIRToLLVMCastOpLowering::convertTy(mlir::Type ty) const { + return getTypeConverter()->convertType(ty); +} + +mlir::LogicalResult CIRToLLVMCastOpLowering::matchAndRewrite( +cir::CastOp castOp, OpAdaptor adaptor, +mlir::ConversionPatternRewriter &rewriter) const { + // For arithmetic conversions, LLVM IR uses the same instruction to convert + // both individual scalars and entire vectors. This lowering pass handles + // both situations. + + switch (castOp.getKind()) { + case cir::CastKind::array_to_ptrdecay: { +const auto ptrTy = mlir::cast(castOp.getType()); +auto sourceValue = adaptor.getOperands().front(); erichkeane wrote: A LOT of uses of 'auto' in this function that don't match our coding standard. https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
https://github.com/mmha updated https://github.com/llvm/llvm-project/pull/130690 >From a945e21869e5276c66ac979acd893d9bd9afe2cc Mon Sep 17 00:00:00 2001 From: Morris Hafner Date: Mon, 10 Mar 2025 16:18:34 -0700 Subject: [PATCH 1/5] [CIR] Upstream CastOp and scalar conversions This patch upstreams ClangIR's CastOp with the following exceptions: - No Fixed/FP conversions - No casts between value categories - No complex casts - No array_to_ptrdecay - No address_space - No casts involving record types (member pointers, base/derived casts) - No casts specific to ObjC or OpenCL --- .../CIR/Dialect/Builder/CIRBaseBuilder.h | 62 +++ clang/include/clang/CIR/Dialect/IR/CIROps.td | 105 + clang/include/clang/CIR/MissingFeatures.h | 14 +- clang/lib/CIR/CodeGen/CIRGenBuilder.h | 9 + clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp| 400 +- clang/lib/CIR/CodeGen/CIRGenFunction.h| 3 + clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 170 clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp| 27 ++ .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 207 + .../CIR/Lowering/DirectToLLVM/LowerToLLVM.h | 16 + clang/test/CIR/CodeGen/cast.cpp | 58 +++ clang/test/CIR/IR/cast.cir| 23 + clang/test/CIR/Lowering/cast.cir | 92 13 files changed, 1174 insertions(+), 12 deletions(-) create mode 100644 clang/test/CIR/CodeGen/cast.cpp create mode 100644 clang/test/CIR/IR/cast.cir create mode 100644 clang/test/CIR/Lowering/cast.cir diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index 017ae0c53a984..e5e8132e9f527 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -13,6 +13,7 @@ #include "clang/CIR/Dialect/IR/CIRAttrs.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/Dialect/IR/CIRTypes.h" +#include "llvm/Support/ErrorHandling.h" #include "mlir/IR/Builders.h" #include "mlir/IR/BuiltinTypes.h" @@ -78,6 +79,67 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return create(loc, val, dst); } + //======// + // Cast/Conversion Operators + //======// + + mlir::Value createCast(mlir::Location loc, cir::CastKind kind, + mlir::Value src, mlir::Type newTy) { +if (newTy == src.getType()) + return src; +return create(loc, newTy, kind, src); + } + + mlir::Value createCast(cir::CastKind kind, mlir::Value src, + mlir::Type newTy) { +if (newTy == src.getType()) + return src; +return createCast(src.getLoc(), kind, src, newTy); + } + + mlir::Value createIntCast(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::integral, src, newTy); + } + + mlir::Value createIntToPtr(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::int_to_ptr, src, newTy); + } + + mlir::Value createPtrToInt(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::ptr_to_int, src, newTy); + } + + mlir::Value createPtrToBoolCast(mlir::Value v) { +return createCast(cir::CastKind::ptr_to_bool, v, getBoolTy()); + } + + mlir::Value createBoolToInt(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::bool_to_int, src, newTy); + } + + mlir::Value createBitcast(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::bitcast, src, newTy); + } + + mlir::Value createBitcast(mlir::Location loc, mlir::Value src, +mlir::Type newTy) { +return createCast(loc, cir::CastKind::bitcast, src, newTy); + } + + mlir::Value createPtrBitcast(mlir::Value src, mlir::Type newPointeeTy) { +assert(mlir::isa(src.getType()) && "expected ptr src"); +return createBitcast(src, getPointerTo(newPointeeTy)); + } + + mlir::Value createAddrSpaceCast(mlir::Location loc, mlir::Value src, + mlir::Type newTy) { +return createCast(loc, cir::CastKind::address_space, src, newTy); + } + + mlir::Value createAddrSpaceCast(mlir::Value src, mlir::Type newTy) { +return createAddrSpaceCast(src.getLoc(), src, newTy); + } + // // Block handling helpers // -- diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 77c43e5ace64a..9797960e00867 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -78,6 +78,111 @@ class LLVMLoweringInfo { class CIR_Op traits = []> : Op, LLVMLoweringInfo; +//===--===// +// CastOp +//===--
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -84,26 +96,266 @@ class ScalarExprEmitter : public StmtVisitor { } mlir::Value VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *e) { -mlir::Type type = cgf.convertType(e->getType()); +mlir::Type type = convertType(e->getType()); return builder.create( cgf.getLoc(e->getExprLoc()), type, builder.getCIRBoolAttr(e->getValue())); } - mlir::Value VisitCastExpr(CastExpr *E); + mlir::Value VisitCastExpr(CastExpr *e); + + mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { +return VisitCastExpr(e); + } + + /// Perform a pointer to boolean conversion. + mlir::Value emitPointerToBoolConversion(mlir::Value v, QualType qt) { +// TODO(cir): comparing the ptr to null is done when lowering CIR to LLVM. +// We might want to have a separate pass for these types of conversions. +return cgf.getBuilder().createPtrToBoolCast(v); + } + + mlir::Value emitFloatToBoolConversion(mlir::Value src, mlir::Location loc) { +auto boolTy = builder.getBoolTy(); +return builder.create(loc, boolTy, + cir::CastKind::float_to_bool, src); + } + + mlir::Value emitIntToBoolConversion(mlir::Value srcVal, mlir::Location loc) { +// Because of the type rules of C, we often end up computing a +// logical value, then zero extending it to int, then wanting it +// as a logical value again. +// TODO: optimize this common case here or leave it for later +// CIR passes? +mlir::Type boolTy = convertType(cgf.getContext().BoolTy); +return builder.create(loc, boolTy, cir::CastKind::int_to_bool, + srcVal); + } + + /// Convert the specified expression value to a boolean (!cir.bool) truth + /// value. This is equivalent to "Val != 0". + mlir::Value emitConversionToBool(mlir::Value src, QualType srcType, + mlir::Location loc) { +assert(srcType.isCanonical() && "EmitScalarConversion strips typedefs"); + +if (srcType->isRealFloatingType()) + return emitFloatToBoolConversion(src, loc); + +if ([[maybe_unused]] auto *mpt = llvm::dyn_cast(srcType)) + cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion"); + +if (srcType->isIntegerType()) + return emitIntToBoolConversion(src, loc); + +assert(::mlir::isa(src.getType())); +return emitPointerToBoolConversion(src, srcType); + } + + // Emit a conversion from the specified type to the specified destination + // type, both of which are CIR scalar types. + struct ScalarConversionOpts { +bool treatBooleanAsSigned; +bool emitImplicitIntegerTruncationChecks; +bool emitImplicitIntegerSignChangeChecks; + +ScalarConversionOpts() +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks(false), + emitImplicitIntegerSignChangeChecks(false) {} + +ScalarConversionOpts(clang::SanitizerSet sanOpts) +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks( + sanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation)), + emitImplicitIntegerSignChangeChecks( + sanOpts.has(SanitizerKind::ImplicitIntegerSignChange)) {} + }; + + // Conversion from bool, integral, or floating-point to integral or + // floating-point. Conversions involving other types are handled elsewhere. + // Conversion to bool is handled elsewhere because that's a comparison against + // zero, not a simple cast. This handles both individual scalars and vectors. + mlir::Value emitScalarCast(mlir::Value src, QualType srcType, + QualType dstType, mlir::Type srcTy, + mlir::Type dstTy, ScalarConversionOpts opts) { +assert(!srcType->isMatrixType() && !dstType->isMatrixType() && + "Internal error: matrix types not handled by this function."); +if (mlir::isa(srcTy) || +mlir::isa(dstTy)) + llvm_unreachable("Obsolete code. Don't use mlir::IntegerType with CIR."); + +mlir::Type fullDstTy = dstTy; +assert(!cir::MissingFeatures::vectorType()); + +std::optional castKind; + +if (mlir::isa(srcTy)) { + if (opts.treatBooleanAsSigned) +cgf.getCIRGenModule().errorNYI("signed bool"); + if (cgf.getBuilder().isInt(dstTy)) { +castKind = cir::CastKind::bool_to_int; + } else if (mlir::isa(dstTy)) { +castKind = cir::CastKind::bool_to_float; + } else { +llvm_unreachable("Internal error: Cast to unexpected type"); + } +} else if (cgf.getBuilder().isInt(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { +castKind = cir::CastKind::integral; + } else if (mlir::isa(dstTy)) { +castKind = cir::CastKind::int_to_float; + } else { +llvm_unreachable("Internal error: Cast to unexpected type"); + } +} else if (mlir::isa(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { +/
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -90,20 +89,259 @@ class ScalarExprEmitter : public StmtVisitor { builder.getCIRBoolAttr(e->getValue())); } - mlir::Value VisitCastExpr(CastExpr *E); + mlir::Value VisitCastExpr(CastExpr *e); + + mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { +return VisitCastExpr(e); + } + + /// Perform a pointer to boolean conversion. + mlir::Value emitPointerToBoolConversion(mlir::Value v, QualType qt) { +// TODO(cir): comparing the ptr to null is done when lowering CIR to LLVM. +// We might want to have a separate pass for these types of conversions. +return cgf.getBuilder().createPtrToBoolCast(v); + } + + mlir::Value emitFloatToBoolConversion(mlir::Value src, mlir::Location loc) { +auto boolTy = builder.getBoolTy(); +return builder.create(loc, boolTy, + cir::CastKind::float_to_bool, src); + } + + mlir::Value emitIntToBoolConversion(mlir::Value srcVal, mlir::Location loc) { +// Because of the type rules of C, we often end up computing a +// logical value, then zero extending it to int, then wanting it +// as a logical value again. +// TODO: optimize this common case here or leave it for later +// CIR passes? +mlir::Type boolTy = cgf.convertType(cgf.getContext().BoolTy); +return builder.create(loc, boolTy, cir::CastKind::int_to_bool, + srcVal); + } + + /// Convert the specified expression value to a boolean (!cir.bool) truth + /// value. This is equivalent to "Val != 0". + mlir::Value emitConversionToBool(mlir::Value src, QualType srcType, + mlir::Location loc) { +assert(srcType.isCanonical() && "EmitScalarConversion strips typedefs"); + +if (srcType->isRealFloatingType()) + return emitFloatToBoolConversion(src, loc); + +if (llvm::isa(srcType)) + cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion"); + +if (srcType->isIntegerType()) + return emitIntToBoolConversion(src, loc); + +assert(::mlir::isa(src.getType())); +return emitPointerToBoolConversion(src, srcType); + } + + // Emit a conversion from the specified type to the specified destination + // type, both of which are CIR scalar types. + struct ScalarConversionOpts { +bool treatBooleanAsSigned; +bool emitImplicitIntegerTruncationChecks; +bool emitImplicitIntegerSignChangeChecks; + +ScalarConversionOpts() +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks(false), + emitImplicitIntegerSignChangeChecks(false) {} + +ScalarConversionOpts(clang::SanitizerSet sanOpts) +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks( + sanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation)), + emitImplicitIntegerSignChangeChecks( + sanOpts.has(SanitizerKind::ImplicitIntegerSignChange)) {} + }; + + // Conversion from bool, integral, or floating-point to integral or + // floating-point. Conversions involving other types are handled elsewhere. + // Conversion to bool is handled elsewhere because that's a comparison against + // zero, not a simple cast. This handles both individual scalars and vectors. + mlir::Value emitScalarCast(mlir::Value src, QualType srcType, + QualType dstType, mlir::Type srcTy, + mlir::Type dstTy, ScalarConversionOpts opts) { +assert(!srcType->isMatrixType() && !dstType->isMatrixType() && + "Internal error: matrix types not handled by this function."); +if (mlir::isa(srcTy) || +mlir::isa(dstTy)) + llvm_unreachable("Obsolete code. Don't use mlir::IntegerType with CIR."); + +mlir::Type fullDstTy = dstTy; +assert(!cir::MissingFeatures::vectorType()); + +std::optional castKind; + +if (mlir::isa(srcTy)) { + if (opts.treatBooleanAsSigned) +cgf.getCIRGenModule().errorNYI("signed bool"); + if (cgf.getBuilder().isInt(dstTy)) +castKind = cir::CastKind::bool_to_int; + else if (mlir::isa(dstTy)) +castKind = cir::CastKind::bool_to_float; + else +llvm_unreachable("Internal error: Cast to unexpected type"); +} else if (cgf.getBuilder().isInt(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) +castKind = cir::CastKind::integral; + else if (mlir::isa(dstTy)) +castKind = cir::CastKind::int_to_float; + else +llvm_unreachable("Internal error: Cast to unexpected type"); +} else if (mlir::isa(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { +// If we can't recognize overflow as undefined behavior, assume that +// overflow saturates. This protects against normal optimizations if we +// are compiling with non-standard FP semantics. +if (!cgf.cgm.getCodeGenOpts().StrictFloatCastOverflow) + cgf.getCIRGenModule().errorNYI("strict f
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -90,20 +89,259 @@ class ScalarExprEmitter : public StmtVisitor { builder.getCIRBoolAttr(e->getValue())); } - mlir::Value VisitCastExpr(CastExpr *E); + mlir::Value VisitCastExpr(CastExpr *e); + + mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { +return VisitCastExpr(e); + } + + /// Perform a pointer to boolean conversion. + mlir::Value emitPointerToBoolConversion(mlir::Value v, QualType qt) { +// TODO(cir): comparing the ptr to null is done when lowering CIR to LLVM. +// We might want to have a separate pass for these types of conversions. +return cgf.getBuilder().createPtrToBoolCast(v); + } + + mlir::Value emitFloatToBoolConversion(mlir::Value src, mlir::Location loc) { +auto boolTy = builder.getBoolTy(); +return builder.create(loc, boolTy, + cir::CastKind::float_to_bool, src); + } + + mlir::Value emitIntToBoolConversion(mlir::Value srcVal, mlir::Location loc) { +// Because of the type rules of C, we often end up computing a +// logical value, then zero extending it to int, then wanting it +// as a logical value again. +// TODO: optimize this common case here or leave it for later +// CIR passes? +mlir::Type boolTy = cgf.convertType(cgf.getContext().BoolTy); +return builder.create(loc, boolTy, cir::CastKind::int_to_bool, + srcVal); + } + + /// Convert the specified expression value to a boolean (!cir.bool) truth + /// value. This is equivalent to "Val != 0". + mlir::Value emitConversionToBool(mlir::Value src, QualType srcType, + mlir::Location loc) { +assert(srcType.isCanonical() && "EmitScalarConversion strips typedefs"); + +if (srcType->isRealFloatingType()) + return emitFloatToBoolConversion(src, loc); + +if (llvm::isa(srcType)) + cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion"); + +if (srcType->isIntegerType()) + return emitIntToBoolConversion(src, loc); + +assert(::mlir::isa(src.getType())); +return emitPointerToBoolConversion(src, srcType); + } + + // Emit a conversion from the specified type to the specified destination + // type, both of which are CIR scalar types. + struct ScalarConversionOpts { +bool treatBooleanAsSigned; +bool emitImplicitIntegerTruncationChecks; +bool emitImplicitIntegerSignChangeChecks; + +ScalarConversionOpts() +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks(false), + emitImplicitIntegerSignChangeChecks(false) {} + +ScalarConversionOpts(clang::SanitizerSet sanOpts) +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks( + sanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation)), + emitImplicitIntegerSignChangeChecks( + sanOpts.has(SanitizerKind::ImplicitIntegerSignChange)) {} + }; + + // Conversion from bool, integral, or floating-point to integral or + // floating-point. Conversions involving other types are handled elsewhere. + // Conversion to bool is handled elsewhere because that's a comparison against + // zero, not a simple cast. This handles both individual scalars and vectors. + mlir::Value emitScalarCast(mlir::Value src, QualType srcType, + QualType dstType, mlir::Type srcTy, + mlir::Type dstTy, ScalarConversionOpts opts) { +assert(!srcType->isMatrixType() && !dstType->isMatrixType() && + "Internal error: matrix types not handled by this function."); +if (mlir::isa(srcTy) || +mlir::isa(dstTy)) + llvm_unreachable("Obsolete code. Don't use mlir::IntegerType with CIR."); + +mlir::Type fullDstTy = dstTy; +assert(!cir::MissingFeatures::vectorType()); + +std::optional castKind; + +if (mlir::isa(srcTy)) { + if (opts.treatBooleanAsSigned) +cgf.getCIRGenModule().errorNYI("signed bool"); + if (cgf.getBuilder().isInt(dstTy)) +castKind = cir::CastKind::bool_to_int; + else if (mlir::isa(dstTy)) +castKind = cir::CastKind::bool_to_float; + else +llvm_unreachable("Internal error: Cast to unexpected type"); +} else if (cgf.getBuilder().isInt(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) +castKind = cir::CastKind::integral; + else if (mlir::isa(dstTy)) +castKind = cir::CastKind::int_to_float; + else +llvm_unreachable("Internal error: Cast to unexpected type"); +} else if (mlir::isa(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { +// If we can't recognize overflow as undefined behavior, assume that +// overflow saturates. This protects against normal optimizations if we +// are compiling with non-standard FP semantics. +if (!cgf.cgm.getCodeGenOpts().StrictFloatCastOverflow) + cgf.getCIRGenModule().errorNYI("strict f
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -121,29 +359,159 @@ mlir::Value CIRGenFunction::emitScalarExpr(const Expr *e) { return ScalarExprEmitter(*this, builder).Visit(const_cast(e)); } +[[maybe_unused]] static bool MustVisitNullValue(const Expr *e) { + // If a null pointer expression's type is the C++0x nullptr_t, then + // it's not necessarily a simple constant and it must be evaluated + // for its potential side effects. + return e->getType()->isNullPtrType(); +} + // Emit code for an explicit or implicit cast. Implicit // casts have to handle a more broad range of conversions than explicit // casts, as they handle things like function to ptr-to-function decay // etc. mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { - Expr *e = ce->getSubExpr(); + Expr *subExpr = ce->getSubExpr(); QualType destTy = ce->getType(); CastKind kind = ce->getCastKind(); + // These cases are generally not written to ignore the result of evaluating + // their sub-expressions, so we clear this now. + ignoreResultAssign = false; + switch (kind) { + case clang::CK_Dependent: +llvm_unreachable("dependent cast kind in CIR gen!"); + case clang::CK_BuiltinFnToFnPtr: +llvm_unreachable("builtin functions are handled elsewhere"); + + case CK_CPointerToObjCPointerCast: + case CK_BlockPointerToObjCPointerCast: + case CK_AnyPointerToBlockPointerCast: + case CK_BitCast: { +mlir::Value src = Visit(const_cast(subExpr)); +mlir::Type dstTy = cgf.convertType(destTy); + +assert(!cir::MissingFeatures::addressSpace()); + +if (cgf.sanOpts.has(SanitizerKind::CFIUnrelatedCast)) + cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(), + "sanitizer support"); + +if (cgf.cgm.getCodeGenOpts().StrictVTablePointers) + cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(), + "strict vtable pointers"); + +// Update heapallocsite metadata when there is an explicit pointer cast. +assert(!cir::MissingFeatures::addHeapAllocSiteMetadata()); + +// If Src is a fixed vector and Dst is a scalable vector, and both have the +// same element type, use the llvm.vector.insert intrinsic to perform the +// bitcast. +assert(!cir::MissingFeatures::scalableVectors()); + +// If Src is a scalable vector and Dst is a fixed vector, and both have the +// same element type, use the llvm.vector.extract intrinsic to perform the +// bitcast. +assert(!cir::MissingFeatures::scalableVectors()); + +// Perform VLAT <-> VLST bitcast through memory. +// TODO: since the llvm.experimental.vector.{insert,extract} intrinsics +// require the element types of the vectors to be the same, we +// need to keep this around for bitcasts between VLAT <-> VLST where +// the element types of the vectors are not the same, until we figure +// out a better way of doing these casts. +assert(!cir::MissingFeatures::scalableVectors()); + +return cgf.getBuilder().createBitcast(cgf.getLoc(subExpr->getSourceRange()), + src, dstTy); + } + + case CK_AtomicToNonAtomic: +cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(), + "CastExpr: ", ce->getCastKindName()); +break; + case CK_NonAtomicToAtomic: + case CK_UserDefinedConversion: +return Visit(const_cast(subExpr)); + case CK_NoOp: { +auto v = Visit(const_cast(subExpr)); +if (v) { + // CK_NoOp can model a pointer qualification conversion, which can remove + // an array bound and change the IR type. + // FIXME: Once pointee types are removed from IR, remove this. + mlir::Type t = cgf.convertType(destTy); + if (t != v.getType()) +cgf.getCIRGenModule().errorNYI("pointer qualification conversion"); +} +return v; + } + + case CK_NullToPointer: { +if (MustVisitNullValue(subExpr)) + cgf.getCIRGenModule().errorNYI( + subExpr->getSourceRange(), + "ignored expression on null to pointer cast"); + +// Note that DestTy is used as the MLIR type instead of a custom +// nullptr type. +mlir::Type ty = cgf.convertType(destTy); +return builder.getNullPtr(ty, cgf.getLoc(subExpr->getExprLoc())); + } + case CK_LValueToRValue: -assert(cgf.getContext().hasSameUnqualifiedType(e->getType(), destTy)); -assert(e->isGLValue() && "lvalue-to-rvalue applied to r-value!"); -return Visit(const_cast(e)); +assert(cgf.getContext().hasSameUnqualifiedType(subExpr->getType(), destTy)); +assert(subExpr->isGLValue() && "lvalue-to-rvalue applied to r-value!"); +return Visit(const_cast(subExpr)); case CK_IntegralCast: { -assert(!cir::MissingFeatures::scalarConversionOpts()); -return emitScalarConversion(Visit(e), e->getType(), destTy, -ce->getExprLoc()); +ScalarConversionOpts opts; +if (auto *ice = dyn_cast(c
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -84,26 +96,266 @@ class ScalarExprEmitter : public StmtVisitor { } mlir::Value VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *e) { -mlir::Type type = cgf.convertType(e->getType()); +mlir::Type type = convertType(e->getType()); return builder.create( cgf.getLoc(e->getExprLoc()), type, builder.getCIRBoolAttr(e->getValue())); } - mlir::Value VisitCastExpr(CastExpr *E); + mlir::Value VisitCastExpr(CastExpr *e); + + mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { +return VisitCastExpr(e); + } + + /// Perform a pointer to boolean conversion. + mlir::Value emitPointerToBoolConversion(mlir::Value v, QualType qt) { +// TODO(cir): comparing the ptr to null is done when lowering CIR to LLVM. +// We might want to have a separate pass for these types of conversions. +return cgf.getBuilder().createPtrToBoolCast(v); + } + + mlir::Value emitFloatToBoolConversion(mlir::Value src, mlir::Location loc) { +auto boolTy = builder.getBoolTy(); +return builder.create(loc, boolTy, + cir::CastKind::float_to_bool, src); + } + + mlir::Value emitIntToBoolConversion(mlir::Value srcVal, mlir::Location loc) { +// Because of the type rules of C, we often end up computing a +// logical value, then zero extending it to int, then wanting it +// as a logical value again. +// TODO: optimize this common case here or leave it for later +// CIR passes? +mlir::Type boolTy = convertType(cgf.getContext().BoolTy); +return builder.create(loc, boolTy, cir::CastKind::int_to_bool, + srcVal); + } + + /// Convert the specified expression value to a boolean (!cir.bool) truth + /// value. This is equivalent to "Val != 0". + mlir::Value emitConversionToBool(mlir::Value src, QualType srcType, + mlir::Location loc) { +assert(srcType.isCanonical() && "EmitScalarConversion strips typedefs"); + +if (srcType->isRealFloatingType()) + return emitFloatToBoolConversion(src, loc); + +if ([[maybe_unused]] auto *mpt = llvm::dyn_cast(srcType)) + cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion"); + +if (srcType->isIntegerType()) + return emitIntToBoolConversion(src, loc); + +assert(::mlir::isa(src.getType())); +return emitPointerToBoolConversion(src, srcType); + } + + // Emit a conversion from the specified type to the specified destination + // type, both of which are CIR scalar types. + struct ScalarConversionOpts { +bool treatBooleanAsSigned; +bool emitImplicitIntegerTruncationChecks; +bool emitImplicitIntegerSignChangeChecks; + +ScalarConversionOpts() +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks(false), + emitImplicitIntegerSignChangeChecks(false) {} + +ScalarConversionOpts(clang::SanitizerSet sanOpts) +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks( + sanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation)), + emitImplicitIntegerSignChangeChecks( + sanOpts.has(SanitizerKind::ImplicitIntegerSignChange)) {} + }; + + // Conversion from bool, integral, or floating-point to integral or + // floating-point. Conversions involving other types are handled elsewhere. + // Conversion to bool is handled elsewhere because that's a comparison against + // zero, not a simple cast. This handles both individual scalars and vectors. + mlir::Value emitScalarCast(mlir::Value src, QualType srcType, + QualType dstType, mlir::Type srcTy, + mlir::Type dstTy, ScalarConversionOpts opts) { +assert(!srcType->isMatrixType() && !dstType->isMatrixType() && + "Internal error: matrix types not handled by this function."); +if (mlir::isa(srcTy) || +mlir::isa(dstTy)) + llvm_unreachable("Obsolete code. Don't use mlir::IntegerType with CIR."); + +mlir::Type fullDstTy = dstTy; +assert(!cir::MissingFeatures::vectorType()); + +std::optional castKind; + +if (mlir::isa(srcTy)) { + if (opts.treatBooleanAsSigned) +cgf.getCIRGenModule().errorNYI("signed bool"); + if (cgf.getBuilder().isInt(dstTy)) { +castKind = cir::CastKind::bool_to_int; + } else if (mlir::isa(dstTy)) { +castKind = cir::CastKind::bool_to_float; + } else { +llvm_unreachable("Internal error: Cast to unexpected type"); + } +} else if (cgf.getBuilder().isInt(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { +castKind = cir::CastKind::integral; + } else if (mlir::isa(dstTy)) { +castKind = cir::CastKind::int_to_float; + } else { +llvm_unreachable("Internal error: Cast to unexpected type"); + } +} else if (mlir::isa(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { +/
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -121,29 +359,159 @@ mlir::Value CIRGenFunction::emitScalarExpr(const Expr *e) { return ScalarExprEmitter(*this, builder).Visit(const_cast(e)); } +[[maybe_unused]] static bool MustVisitNullValue(const Expr *e) { + // If a null pointer expression's type is the C++0x nullptr_t, then + // it's not necessarily a simple constant and it must be evaluated + // for its potential side effects. + return e->getType()->isNullPtrType(); +} + // Emit code for an explicit or implicit cast. Implicit // casts have to handle a more broad range of conversions than explicit // casts, as they handle things like function to ptr-to-function decay // etc. mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { - Expr *e = ce->getSubExpr(); + Expr *subExpr = ce->getSubExpr(); QualType destTy = ce->getType(); CastKind kind = ce->getCastKind(); + // These cases are generally not written to ignore the result of evaluating + // their sub-expressions, so we clear this now. + ignoreResultAssign = false; + switch (kind) { + case clang::CK_Dependent: +llvm_unreachable("dependent cast kind in CIR gen!"); + case clang::CK_BuiltinFnToFnPtr: +llvm_unreachable("builtin functions are handled elsewhere"); + + case CK_CPointerToObjCPointerCast: + case CK_BlockPointerToObjCPointerCast: + case CK_AnyPointerToBlockPointerCast: + case CK_BitCast: { +mlir::Value src = Visit(const_cast(subExpr)); +mlir::Type dstTy = cgf.convertType(destTy); + +assert(!cir::MissingFeatures::addressSpace()); + +if (cgf.sanOpts.has(SanitizerKind::CFIUnrelatedCast)) + cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(), + "sanitizer support"); + +if (cgf.cgm.getCodeGenOpts().StrictVTablePointers) + cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(), + "strict vtable pointers"); + +// Update heapallocsite metadata when there is an explicit pointer cast. +assert(!cir::MissingFeatures::addHeapAllocSiteMetadata()); + +// If Src is a fixed vector and Dst is a scalable vector, and both have the +// same element type, use the llvm.vector.insert intrinsic to perform the +// bitcast. +assert(!cir::MissingFeatures::scalableVectors()); + +// If Src is a scalable vector and Dst is a fixed vector, and both have the +// same element type, use the llvm.vector.extract intrinsic to perform the +// bitcast. +assert(!cir::MissingFeatures::scalableVectors()); + +// Perform VLAT <-> VLST bitcast through memory. +// TODO: since the llvm.experimental.vector.{insert,extract} intrinsics +// require the element types of the vectors to be the same, we +// need to keep this around for bitcasts between VLAT <-> VLST where +// the element types of the vectors are not the same, until we figure +// out a better way of doing these casts. +assert(!cir::MissingFeatures::scalableVectors()); + +return cgf.getBuilder().createBitcast(cgf.getLoc(subExpr->getSourceRange()), + src, dstTy); + } + + case CK_AtomicToNonAtomic: +cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(), + "CastExpr: ", ce->getCastKindName()); +break; erichkeane wrote: what does this fall through to? Does it result in a sensible value? https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -78,6 +78,111 @@ class LLVMLoweringInfo { class CIR_Op traits = []> : Op, LLVMLoweringInfo; +//===--===// +// CastOp +//===--===// + +// The enumaration value isn't in sync with clang. erichkeane wrote: We'd discussed offline about trying to sync the two enums in some way, right? Was that not something viable? https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -90,20 +89,259 @@ class ScalarExprEmitter : public StmtVisitor { builder.getCIRBoolAttr(e->getValue())); } - mlir::Value VisitCastExpr(CastExpr *E); + mlir::Value VisitCastExpr(CastExpr *e); + + mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { +return VisitCastExpr(e); + } + + /// Perform a pointer to boolean conversion. + mlir::Value emitPointerToBoolConversion(mlir::Value v, QualType qt) { +// TODO(cir): comparing the ptr to null is done when lowering CIR to LLVM. +// We might want to have a separate pass for these types of conversions. +return cgf.getBuilder().createPtrToBoolCast(v); + } + + mlir::Value emitFloatToBoolConversion(mlir::Value src, mlir::Location loc) { +auto boolTy = builder.getBoolTy(); +return builder.create(loc, boolTy, + cir::CastKind::float_to_bool, src); + } + + mlir::Value emitIntToBoolConversion(mlir::Value srcVal, mlir::Location loc) { +// Because of the type rules of C, we often end up computing a +// logical value, then zero extending it to int, then wanting it +// as a logical value again. +// TODO: optimize this common case here or leave it for later +// CIR passes? +mlir::Type boolTy = cgf.convertType(cgf.getContext().BoolTy); +return builder.create(loc, boolTy, cir::CastKind::int_to_bool, + srcVal); + } + + /// Convert the specified expression value to a boolean (!cir.bool) truth + /// value. This is equivalent to "Val != 0". + mlir::Value emitConversionToBool(mlir::Value src, QualType srcType, + mlir::Location loc) { +assert(srcType.isCanonical() && "EmitScalarConversion strips typedefs"); + +if (srcType->isRealFloatingType()) + return emitFloatToBoolConversion(src, loc); + +if (llvm::isa(srcType)) + cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion"); + +if (srcType->isIntegerType()) + return emitIntToBoolConversion(src, loc); + +assert(::mlir::isa(src.getType())); +return emitPointerToBoolConversion(src, srcType); + } + + // Emit a conversion from the specified type to the specified destination + // type, both of which are CIR scalar types. + struct ScalarConversionOpts { +bool treatBooleanAsSigned; +bool emitImplicitIntegerTruncationChecks; +bool emitImplicitIntegerSignChangeChecks; + +ScalarConversionOpts() +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks(false), + emitImplicitIntegerSignChangeChecks(false) {} + +ScalarConversionOpts(clang::SanitizerSet sanOpts) +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks( + sanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation)), + emitImplicitIntegerSignChangeChecks( + sanOpts.has(SanitizerKind::ImplicitIntegerSignChange)) {} + }; + + // Conversion from bool, integral, or floating-point to integral or + // floating-point. Conversions involving other types are handled elsewhere. + // Conversion to bool is handled elsewhere because that's a comparison against + // zero, not a simple cast. This handles both individual scalars and vectors. + mlir::Value emitScalarCast(mlir::Value src, QualType srcType, + QualType dstType, mlir::Type srcTy, + mlir::Type dstTy, ScalarConversionOpts opts) { +assert(!srcType->isMatrixType() && !dstType->isMatrixType() && + "Internal error: matrix types not handled by this function."); +if (mlir::isa(srcTy) || +mlir::isa(dstTy)) + llvm_unreachable("Obsolete code. Don't use mlir::IntegerType with CIR."); + +mlir::Type fullDstTy = dstTy; +assert(!cir::MissingFeatures::vectorType()); + +std::optional castKind; + +if (mlir::isa(srcTy)) { + if (opts.treatBooleanAsSigned) +cgf.getCIRGenModule().errorNYI("signed bool"); + if (cgf.getBuilder().isInt(dstTy)) +castKind = cir::CastKind::bool_to_int; + else if (mlir::isa(dstTy)) +castKind = cir::CastKind::bool_to_float; + else +llvm_unreachable("Internal error: Cast to unexpected type"); +} else if (cgf.getBuilder().isInt(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) +castKind = cir::CastKind::integral; + else if (mlir::isa(dstTy)) +castKind = cir::CastKind::int_to_float; + else +llvm_unreachable("Internal error: Cast to unexpected type"); +} else if (mlir::isa(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { +// If we can't recognize overflow as undefined behavior, assume that +// overflow saturates. This protects against normal optimizations if we +// are compiling with non-standard FP semantics. +if (!cgf.cgm.getCodeGenOpts().StrictFloatCastOverflow) + cgf.getCIRGenModule().errorNYI("strict f
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -90,20 +89,259 @@ class ScalarExprEmitter : public StmtVisitor { builder.getCIRBoolAttr(e->getValue())); } - mlir::Value VisitCastExpr(CastExpr *E); + mlir::Value VisitCastExpr(CastExpr *e); + + mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { +return VisitCastExpr(e); + } + + /// Perform a pointer to boolean conversion. + mlir::Value emitPointerToBoolConversion(mlir::Value v, QualType qt) { +// TODO(cir): comparing the ptr to null is done when lowering CIR to LLVM. +// We might want to have a separate pass for these types of conversions. +return cgf.getBuilder().createPtrToBoolCast(v); + } + + mlir::Value emitFloatToBoolConversion(mlir::Value src, mlir::Location loc) { +auto boolTy = builder.getBoolTy(); +return builder.create(loc, boolTy, + cir::CastKind::float_to_bool, src); + } + + mlir::Value emitIntToBoolConversion(mlir::Value srcVal, mlir::Location loc) { +// Because of the type rules of C, we often end up computing a +// logical value, then zero extending it to int, then wanting it +// as a logical value again. +// TODO: optimize this common case here or leave it for later +// CIR passes? +mlir::Type boolTy = cgf.convertType(cgf.getContext().BoolTy); +return builder.create(loc, boolTy, cir::CastKind::int_to_bool, + srcVal); + } + + /// Convert the specified expression value to a boolean (!cir.bool) truth + /// value. This is equivalent to "Val != 0". + mlir::Value emitConversionToBool(mlir::Value src, QualType srcType, + mlir::Location loc) { +assert(srcType.isCanonical() && "EmitScalarConversion strips typedefs"); + +if (srcType->isRealFloatingType()) + return emitFloatToBoolConversion(src, loc); + +if (llvm::isa(srcType)) + cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion"); erichkeane wrote: This branch needs to return something. https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -78,6 +79,67 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return create(loc, val, dst); } + //======// + // Cast/Conversion Operators + //======// + + mlir::Value createCast(mlir::Location loc, cir::CastKind kind, + mlir::Value src, mlir::Type newTy) { +if (newTy == src.getType()) + return src; +return create(loc, newTy, kind, src); + } + + mlir::Value createCast(cir::CastKind kind, mlir::Value src, + mlir::Type newTy) { +if (newTy == src.getType()) + return src; +return createCast(src.getLoc(), kind, src, newTy); + } + + mlir::Value createIntCast(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::integral, src, newTy); + } + + mlir::Value createIntToPtr(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::int_to_ptr, src, newTy); + } + + mlir::Value createPtrToInt(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::ptr_to_int, src, newTy); + } + + mlir::Value createPtrToBoolCast(mlir::Value v) { +return createCast(cir::CastKind::ptr_to_bool, v, getBoolTy()); + } + + mlir::Value createBoolToInt(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::bool_to_int, src, newTy); + } + + mlir::Value createBitcast(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::bitcast, src, newTy); + } + + mlir::Value createBitcast(mlir::Location loc, mlir::Value src, +mlir::Type newTy) { +return createCast(loc, cir::CastKind::bitcast, src, newTy); + } + + mlir::Value createPtrBitcast(mlir::Value src, mlir::Type newPointeeTy) { +assert(mlir::isa(src.getType()) && "expected ptr src"); +return createBitcast(src, getPointerTo(newPointeeTy)); + } + + mlir::Value createAddrSpaceCast(mlir::Location loc, mlir::Value src, mmha wrote: Correct. I removed these two functions again for now. https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -0,0 +1,58 @@ +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s + +unsigned char cxxstaticcast_0(unsigned int x) { + return static_cast(x); +} + +// CHECK: cir.func @cxxstaticcast_0 +// CHECK:%0 = cir.alloca !cir.int, !cir.ptr>, ["x", init] {alignment = 4 : i64} +// CHECK:cir.store %arg0, %0 : !cir.int, !cir.ptr> +// CHECK:%1 = cir.load %0 : !cir.ptr>, !cir.int +// CHECK:%2 = cir.cast(integral, %1 : !cir.int), !cir.int +// CHECK:cir.return %2 : !cir.int +// CHECK: } + + +int cStyleCasts_0(unsigned x1, int x2, float x3, short x4, double x5) { +// CHECK: cir.func @cStyleCasts_0 + + char a = (char)x1; // truncate + // CHECK: %{{[0-9]+}} = cir.cast(integral, %{{[0-9]+}} : !cir.int), !cir.int + + short b = (short)x2; // truncate with sign + // CHECK: %{{[0-9]+}} = cir.cast(integral, %{{[0-9]+}} : !cir.int), !cir.int + + long long c = (long long)x1; // zero extend + // CHECK: %{{[0-9]+}} = cir.cast(integral, %{{[0-9]+}} : !cir.int), !cir.int + + long long d = (long long)x2; // sign extend + // CHECK: %{{[0-9]+}} = cir.cast(integral, %{{[0-9]+}} : !cir.int), !cir.int + + unsigned ui = (unsigned)x2; // sign drop + // CHECK: %{{[0-9]+}} = cir.cast(integral, %{{[0-9]+}} : !cir.int), !cir.int + + int si = (int)x1; // sign add + // CHECK: %{{[0-9]+}} = cir.cast(integral, %{{[0-9]+}} : !cir.int), !cir.int + + unsigned uu = (unsigned)x1; // should not be generated + // CHECK-NOT: %{{[0-9]+}} = cir.cast(integral, %{{[0-9]+}} : !cir.int), !cir.int andykaylor wrote: I think this check is too specific for CHECK-NOT. Maybe move the cases that shouldn't cast to a separate function and just CHECK-NOT for `cir.cast` between the function start and end braces. https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -84,26 +96,266 @@ class ScalarExprEmitter : public StmtVisitor { } mlir::Value VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *e) { -mlir::Type type = cgf.convertType(e->getType()); +mlir::Type type = convertType(e->getType()); return builder.create( cgf.getLoc(e->getExprLoc()), type, builder.getCIRBoolAttr(e->getValue())); } - mlir::Value VisitCastExpr(CastExpr *E); + mlir::Value VisitCastExpr(CastExpr *e); + + mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { +return VisitCastExpr(e); + } + + /// Perform a pointer to boolean conversion. + mlir::Value emitPointerToBoolConversion(mlir::Value v, QualType qt) { +// TODO(cir): comparing the ptr to null is done when lowering CIR to LLVM. +// We might want to have a separate pass for these types of conversions. +return cgf.getBuilder().createPtrToBoolCast(v); + } + + mlir::Value emitFloatToBoolConversion(mlir::Value src, mlir::Location loc) { +auto boolTy = builder.getBoolTy(); +return builder.create(loc, boolTy, + cir::CastKind::float_to_bool, src); + } + + mlir::Value emitIntToBoolConversion(mlir::Value srcVal, mlir::Location loc) { +// Because of the type rules of C, we often end up computing a +// logical value, then zero extending it to int, then wanting it +// as a logical value again. +// TODO: optimize this common case here or leave it for later +// CIR passes? +mlir::Type boolTy = convertType(cgf.getContext().BoolTy); +return builder.create(loc, boolTy, cir::CastKind::int_to_bool, + srcVal); + } + + /// Convert the specified expression value to a boolean (!cir.bool) truth + /// value. This is equivalent to "Val != 0". + mlir::Value emitConversionToBool(mlir::Value src, QualType srcType, + mlir::Location loc) { +assert(srcType.isCanonical() && "EmitScalarConversion strips typedefs"); + +if (srcType->isRealFloatingType()) + return emitFloatToBoolConversion(src, loc); + +if ([[maybe_unused]] auto *mpt = llvm::dyn_cast(srcType)) + cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion"); + +if (srcType->isIntegerType()) + return emitIntToBoolConversion(src, loc); + +assert(::mlir::isa(src.getType())); +return emitPointerToBoolConversion(src, srcType); + } + + // Emit a conversion from the specified type to the specified destination + // type, both of which are CIR scalar types. + struct ScalarConversionOpts { +bool treatBooleanAsSigned; +bool emitImplicitIntegerTruncationChecks; +bool emitImplicitIntegerSignChangeChecks; + +ScalarConversionOpts() +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks(false), + emitImplicitIntegerSignChangeChecks(false) {} + +ScalarConversionOpts(clang::SanitizerSet sanOpts) +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks( + sanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation)), + emitImplicitIntegerSignChangeChecks( + sanOpts.has(SanitizerKind::ImplicitIntegerSignChange)) {} + }; + + // Conversion from bool, integral, or floating-point to integral or + // floating-point. Conversions involving other types are handled elsewhere. + // Conversion to bool is handled elsewhere because that's a comparison against + // zero, not a simple cast. This handles both individual scalars and vectors. + mlir::Value emitScalarCast(mlir::Value src, QualType srcType, + QualType dstType, mlir::Type srcTy, + mlir::Type dstTy, ScalarConversionOpts opts) { +assert(!srcType->isMatrixType() && !dstType->isMatrixType() && + "Internal error: matrix types not handled by this function."); +if (mlir::isa(srcTy) || +mlir::isa(dstTy)) + llvm_unreachable("Obsolete code. Don't use mlir::IntegerType with CIR."); + +mlir::Type fullDstTy = dstTy; +assert(!cir::MissingFeatures::vectorType()); + +std::optional castKind; + +if (mlir::isa(srcTy)) { + if (opts.treatBooleanAsSigned) +cgf.getCIRGenModule().errorNYI("signed bool"); + if (cgf.getBuilder().isInt(dstTy)) { +castKind = cir::CastKind::bool_to_int; + } else if (mlir::isa(dstTy)) { +castKind = cir::CastKind::bool_to_float; + } else { +llvm_unreachable("Internal error: Cast to unexpected type"); + } +} else if (cgf.getBuilder().isInt(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { +castKind = cir::CastKind::integral; + } else if (mlir::isa(dstTy)) { +castKind = cir::CastKind::int_to_float; + } else { +llvm_unreachable("Internal error: Cast to unexpected type"); + } +} else if (mlir::isa(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { +/
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -36,6 +36,18 @@ class ScalarExprEmitter : public StmtVisitor { bool ira = false) : cgf(cgf), builder(builder), ignoreResultAssign(ira) {} + //======// + // Utilities + //======// + + bool TestAndClearIgnoreResultAssign() { +bool i = ignoreResultAssign; +ignoreResultAssign = false; +return i; + } + + mlir::Type convertType(QualType t) { return cgf.convertType(t); } andykaylor wrote: As discussed earlier, let's get rid of wrapper functions like this. https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -0,0 +1,58 @@ +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir andykaylor wrote: Can you expand this test to cover all the cast types you're adding (or remove any that can't be supported yet)? https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -78,6 +79,67 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return create(loc, val, dst); } + //======// + // Cast/Conversion Operators + //======// + + mlir::Value createCast(mlir::Location loc, cir::CastKind kind, + mlir::Value src, mlir::Type newTy) { +if (newTy == src.getType()) + return src; +return create(loc, newTy, kind, src); + } + + mlir::Value createCast(cir::CastKind kind, mlir::Value src, + mlir::Type newTy) { +if (newTy == src.getType()) + return src; +return createCast(src.getLoc(), kind, src, newTy); + } + + mlir::Value createIntCast(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::integral, src, newTy); + } + + mlir::Value createIntToPtr(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::int_to_ptr, src, newTy); + } + + mlir::Value createPtrToInt(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::ptr_to_int, src, newTy); + } + + mlir::Value createPtrToBoolCast(mlir::Value v) { +return createCast(cir::CastKind::ptr_to_bool, v, getBoolTy()); + } + + mlir::Value createBoolToInt(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::bool_to_int, src, newTy); + } + + mlir::Value createBitcast(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::bitcast, src, newTy); + } + + mlir::Value createBitcast(mlir::Location loc, mlir::Value src, +mlir::Type newTy) { +return createCast(loc, cir::CastKind::bitcast, src, newTy); + } + + mlir::Value createPtrBitcast(mlir::Value src, mlir::Type newPointeeTy) { +assert(mlir::isa(src.getType()) && "expected ptr src"); +return createBitcast(src, getPointerTo(newPointeeTy)); + } + + mlir::Value createAddrSpaceCast(mlir::Location loc, mlir::Value src, andykaylor wrote: I'm guessing these aren't used either. I don't think we have upstreamed any address space support, have we? https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -36,6 +36,18 @@ class ScalarExprEmitter : public StmtVisitor { bool ira = false) : cgf(cgf), builder(builder), ignoreResultAssign(ira) {} + //======// + // Utilities + //======// + + bool TestAndClearIgnoreResultAssign() { andykaylor wrote: So, maybe replace calls to this with `std::exchange(ignoreResultAssign, false)`? That's just as long, but maybe a bit clearer. I notice there are several places (in the incubator and the classic codegen) that call this function and then don't use the result. I'm not sure why they're doing that rather than just `ignoreResultAssign = false;`. https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -84,26 +96,266 @@ class ScalarExprEmitter : public StmtVisitor { } mlir::Value VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *e) { -mlir::Type type = cgf.convertType(e->getType()); +mlir::Type type = convertType(e->getType()); return builder.create( cgf.getLoc(e->getExprLoc()), type, builder.getCIRBoolAttr(e->getValue())); } - mlir::Value VisitCastExpr(CastExpr *E); + mlir::Value VisitCastExpr(CastExpr *e); + + mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { +return VisitCastExpr(e); + } + + /// Perform a pointer to boolean conversion. + mlir::Value emitPointerToBoolConversion(mlir::Value v, QualType qt) { +// TODO(cir): comparing the ptr to null is done when lowering CIR to LLVM. +// We might want to have a separate pass for these types of conversions. +return cgf.getBuilder().createPtrToBoolCast(v); + } + + mlir::Value emitFloatToBoolConversion(mlir::Value src, mlir::Location loc) { +auto boolTy = builder.getBoolTy(); +return builder.create(loc, boolTy, + cir::CastKind::float_to_bool, src); + } + + mlir::Value emitIntToBoolConversion(mlir::Value srcVal, mlir::Location loc) { +// Because of the type rules of C, we often end up computing a +// logical value, then zero extending it to int, then wanting it +// as a logical value again. +// TODO: optimize this common case here or leave it for later +// CIR passes? +mlir::Type boolTy = convertType(cgf.getContext().BoolTy); +return builder.create(loc, boolTy, cir::CastKind::int_to_bool, + srcVal); + } + + /// Convert the specified expression value to a boolean (!cir.bool) truth + /// value. This is equivalent to "Val != 0". + mlir::Value emitConversionToBool(mlir::Value src, QualType srcType, + mlir::Location loc) { +assert(srcType.isCanonical() && "EmitScalarConversion strips typedefs"); + +if (srcType->isRealFloatingType()) + return emitFloatToBoolConversion(src, loc); + +if ([[maybe_unused]] auto *mpt = llvm::dyn_cast(srcType)) + cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion"); + +if (srcType->isIntegerType()) + return emitIntToBoolConversion(src, loc); + +assert(::mlir::isa(src.getType())); +return emitPointerToBoolConversion(src, srcType); + } + + // Emit a conversion from the specified type to the specified destination + // type, both of which are CIR scalar types. + struct ScalarConversionOpts { +bool treatBooleanAsSigned; +bool emitImplicitIntegerTruncationChecks; +bool emitImplicitIntegerSignChangeChecks; + +ScalarConversionOpts() +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks(false), + emitImplicitIntegerSignChangeChecks(false) {} + +ScalarConversionOpts(clang::SanitizerSet sanOpts) +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks( + sanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation)), + emitImplicitIntegerSignChangeChecks( + sanOpts.has(SanitizerKind::ImplicitIntegerSignChange)) {} + }; + + // Conversion from bool, integral, or floating-point to integral or + // floating-point. Conversions involving other types are handled elsewhere. + // Conversion to bool is handled elsewhere because that's a comparison against + // zero, not a simple cast. This handles both individual scalars and vectors. + mlir::Value emitScalarCast(mlir::Value src, QualType srcType, + QualType dstType, mlir::Type srcTy, + mlir::Type dstTy, ScalarConversionOpts opts) { +assert(!srcType->isMatrixType() && !dstType->isMatrixType() && + "Internal error: matrix types not handled by this function."); +if (mlir::isa(srcTy) || +mlir::isa(dstTy)) + llvm_unreachable("Obsolete code. Don't use mlir::IntegerType with CIR."); + +mlir::Type fullDstTy = dstTy; +assert(!cir::MissingFeatures::vectorType()); + +std::optional castKind; + +if (mlir::isa(srcTy)) { + if (opts.treatBooleanAsSigned) +cgf.getCIRGenModule().errorNYI("signed bool"); + if (cgf.getBuilder().isInt(dstTy)) { andykaylor wrote: We don't need braces with single-statement bodies. (Complete rules here: https://llvm.org/docs/CodingStandards.html#don-t-use-braces-on-simple-single-statement-bodies-of-if-else-loop-statements) https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
https://github.com/mmha updated https://github.com/llvm/llvm-project/pull/130690 >From b9a55d112998c468cce5cabff33939e4412e7ded Mon Sep 17 00:00:00 2001 From: Morris Hafner Date: Mon, 10 Mar 2025 16:18:34 -0700 Subject: [PATCH 1/2] [CIR] Upstream CastOp and scalar conversions This patch upstreams ClangIR's CastOp with the following exceptions: - No Fixed/FP conversions - No casts between value categories - No complex casts - No array_to_ptrdecay - No address_space - No casts involving record types (member pointers, base/derived casts) - No casts specific to ObjC or OpenCL --- .../CIR/Dialect/Builder/CIRBaseBuilder.h | 62 +++ clang/include/clang/CIR/Dialect/IR/CIROps.td | 105 + clang/include/clang/CIR/MissingFeatures.h | 14 +- clang/lib/CIR/CodeGen/CIRGenBuilder.h | 9 + clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp| 400 +- clang/lib/CIR/CodeGen/CIRGenFunction.h| 3 + clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 170 clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp| 27 ++ .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 207 + .../CIR/Lowering/DirectToLLVM/LowerToLLVM.h | 16 + clang/test/CIR/CodeGen/cast.cpp | 58 +++ clang/test/CIR/IR/cast.cir| 23 + clang/test/CIR/Lowering/cast.cir | 92 13 files changed, 1174 insertions(+), 12 deletions(-) create mode 100644 clang/test/CIR/CodeGen/cast.cpp create mode 100644 clang/test/CIR/IR/cast.cir create mode 100644 clang/test/CIR/Lowering/cast.cir diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index 017ae0c53a984..e5e8132e9f527 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -13,6 +13,7 @@ #include "clang/CIR/Dialect/IR/CIRAttrs.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/Dialect/IR/CIRTypes.h" +#include "llvm/Support/ErrorHandling.h" #include "mlir/IR/Builders.h" #include "mlir/IR/BuiltinTypes.h" @@ -78,6 +79,67 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return create(loc, val, dst); } + //======// + // Cast/Conversion Operators + //======// + + mlir::Value createCast(mlir::Location loc, cir::CastKind kind, + mlir::Value src, mlir::Type newTy) { +if (newTy == src.getType()) + return src; +return create(loc, newTy, kind, src); + } + + mlir::Value createCast(cir::CastKind kind, mlir::Value src, + mlir::Type newTy) { +if (newTy == src.getType()) + return src; +return createCast(src.getLoc(), kind, src, newTy); + } + + mlir::Value createIntCast(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::integral, src, newTy); + } + + mlir::Value createIntToPtr(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::int_to_ptr, src, newTy); + } + + mlir::Value createPtrToInt(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::ptr_to_int, src, newTy); + } + + mlir::Value createPtrToBoolCast(mlir::Value v) { +return createCast(cir::CastKind::ptr_to_bool, v, getBoolTy()); + } + + mlir::Value createBoolToInt(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::bool_to_int, src, newTy); + } + + mlir::Value createBitcast(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::bitcast, src, newTy); + } + + mlir::Value createBitcast(mlir::Location loc, mlir::Value src, +mlir::Type newTy) { +return createCast(loc, cir::CastKind::bitcast, src, newTy); + } + + mlir::Value createPtrBitcast(mlir::Value src, mlir::Type newPointeeTy) { +assert(mlir::isa(src.getType()) && "expected ptr src"); +return createBitcast(src, getPointerTo(newPointeeTy)); + } + + mlir::Value createAddrSpaceCast(mlir::Location loc, mlir::Value src, + mlir::Type newTy) { +return createCast(loc, cir::CastKind::address_space, src, newTy); + } + + mlir::Value createAddrSpaceCast(mlir::Value src, mlir::Type newTy) { +return createAddrSpaceCast(src.getLoc(), src, newTy); + } + // // Block handling helpers // -- diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index e2ab50c78ec2d..caef0947d0b16 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -78,6 +78,111 @@ class LLVMLoweringInfo { class CIR_Op traits = []> : Op, LLVMLoweringInfo; +//===--===// +// CastOp +//===--
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -36,6 +36,18 @@ class ScalarExprEmitter : public StmtVisitor { bool ira = false) : cgf(cgf), builder(builder), ignoreResultAssign(ira) {} + //======// + // Utilities + //======// + + bool TestAndClearIgnoreResultAssign() { mmha wrote: I noticed that the underlying variable is never read from, making this dead code. I removed it. https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -78,6 +79,67 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return create(loc, val, dst); } + //======// + // Cast/Conversion Operators + //======// + + mlir::Value createCast(mlir::Location loc, cir::CastKind kind, + mlir::Value src, mlir::Type newTy) { +if (newTy == src.getType()) + return src; +return create(loc, newTy, kind, src); + } + + mlir::Value createCast(cir::CastKind kind, mlir::Value src, + mlir::Type newTy) { +if (newTy == src.getType()) + return src; +return createCast(src.getLoc(), kind, src, newTy); + } + + mlir::Value createIntCast(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::integral, src, newTy); + } + + mlir::Value createIntToPtr(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::int_to_ptr, src, newTy); + } + + mlir::Value createPtrToInt(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::ptr_to_int, src, newTy); + } + + mlir::Value createPtrToBoolCast(mlir::Value v) { +return createCast(cir::CastKind::ptr_to_bool, v, getBoolTy()); + } + + mlir::Value createBoolToInt(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::bool_to_int, src, newTy); + } + + mlir::Value createBitcast(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::bitcast, src, newTy); + } + + mlir::Value createBitcast(mlir::Location loc, mlir::Value src, +mlir::Type newTy) { +return createCast(loc, cir::CastKind::bitcast, src, newTy); + } + + mlir::Value createPtrBitcast(mlir::Value src, mlir::Type newPointeeTy) { mmha wrote: This is a utility function used in a couple of other `create` or `emit` functions in the incubator, for example to cast the pointer type in `createLoad` and `createStore` in the incubator. Looking at it again this function isn't used in this patch and I copied this by accident. I'll remove it for now. https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -130,17 +389,136 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { QualType destTy = ce->getType(); CastKind kind = ce->getCastKind(); + // These cases are generally not written to ignore the result of evaluating + // their sub-expressions, so we clear this now. + [[maybe_unused]] bool ignored = TestAndClearIgnoreResultAssign(); erichkeane wrote: ```suggestion TestAndClearIgnoreResultAssign(); ``` More interestingly, why not just ignoreResultAssign = false; or something? https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -78,6 +79,67 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return create(loc, val, dst); } + //======// + // Cast/Conversion Operators + //======// + + mlir::Value createCast(mlir::Location loc, cir::CastKind kind, + mlir::Value src, mlir::Type newTy) { +if (newTy == src.getType()) + return src; +return create(loc, newTy, kind, src); + } + + mlir::Value createCast(cir::CastKind kind, mlir::Value src, + mlir::Type newTy) { +if (newTy == src.getType()) + return src; +return createCast(src.getLoc(), kind, src, newTy); + } + + mlir::Value createIntCast(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::integral, src, newTy); + } + + mlir::Value createIntToPtr(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::int_to_ptr, src, newTy); + } + + mlir::Value createPtrToInt(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::ptr_to_int, src, newTy); + } + + mlir::Value createPtrToBoolCast(mlir::Value v) { +return createCast(cir::CastKind::ptr_to_bool, v, getBoolTy()); + } + + mlir::Value createBoolToInt(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::bool_to_int, src, newTy); + } + + mlir::Value createBitcast(mlir::Value src, mlir::Type newTy) { +return createCast(cir::CastKind::bitcast, src, newTy); + } + + mlir::Value createBitcast(mlir::Location loc, mlir::Value src, +mlir::Type newTy) { +return createCast(loc, cir::CastKind::bitcast, src, newTy); + } + + mlir::Value createPtrBitcast(mlir::Value src, mlir::Type newPointeeTy) { erichkeane wrote: I'm not terribly sure what is going on here, can you share what the point of this function is? It seems like an odd operation to model specifically (that is, bitcast(&Thing)). https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -84,26 +96,266 @@ class ScalarExprEmitter : public StmtVisitor { } mlir::Value VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *e) { -mlir::Type type = cgf.convertType(e->getType()); +mlir::Type type = convertType(e->getType()); return builder.create( cgf.getLoc(e->getExprLoc()), type, builder.getCIRBoolAttr(e->getValue())); } - mlir::Value VisitCastExpr(CastExpr *E); + mlir::Value VisitCastExpr(CastExpr *e); + + mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { +return VisitCastExpr(e); + } + + /// Perform a pointer to boolean conversion. + mlir::Value emitPointerToBoolConversion(mlir::Value v, QualType qt) { +// TODO(cir): comparing the ptr to null is done when lowering CIR to LLVM. +// We might want to have a separate pass for these types of conversions. +return cgf.getBuilder().createPtrToBoolCast(v); + } + + mlir::Value emitFloatToBoolConversion(mlir::Value src, mlir::Location loc) { +auto boolTy = builder.getBoolTy(); +return builder.create(loc, boolTy, + cir::CastKind::float_to_bool, src); + } + + mlir::Value emitIntToBoolConversion(mlir::Value srcVal, mlir::Location loc) { +// Because of the type rules of C, we often end up computing a +// logical value, then zero extending it to int, then wanting it +// as a logical value again. +// TODO: optimize this common case here or leave it for later +// CIR passes? +mlir::Type boolTy = convertType(cgf.getContext().BoolTy); +return builder.create(loc, boolTy, cir::CastKind::int_to_bool, + srcVal); + } + + /// Convert the specified expression value to a boolean (!cir.bool) truth + /// value. This is equivalent to "Val != 0". + mlir::Value emitConversionToBool(mlir::Value src, QualType srcType, + mlir::Location loc) { +assert(srcType.isCanonical() && "EmitScalarConversion strips typedefs"); + +if (srcType->isRealFloatingType()) + return emitFloatToBoolConversion(src, loc); + +if ([[maybe_unused]] auto *mpt = llvm::dyn_cast(srcType)) + cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion"); + +if (srcType->isIntegerType()) + return emitIntToBoolConversion(src, loc); + +assert(::mlir::isa(src.getType())); +return emitPointerToBoolConversion(src, srcType); + } + + // Emit a conversion from the specified type to the specified destination + // type, both of which are CIR scalar types. + struct ScalarConversionOpts { +bool treatBooleanAsSigned; +bool emitImplicitIntegerTruncationChecks; +bool emitImplicitIntegerSignChangeChecks; + +ScalarConversionOpts() +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks(false), + emitImplicitIntegerSignChangeChecks(false) {} + +ScalarConversionOpts(clang::SanitizerSet sanOpts) +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks( + sanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation)), + emitImplicitIntegerSignChangeChecks( + sanOpts.has(SanitizerKind::ImplicitIntegerSignChange)) {} + }; + + // Conversion from bool, integral, or floating-point to integral or + // floating-point. Conversions involving other types are handled elsewhere. + // Conversion to bool is handled elsewhere because that's a comparison against + // zero, not a simple cast. This handles both individual scalars and vectors. + mlir::Value emitScalarCast(mlir::Value src, QualType srcType, + QualType dstType, mlir::Type srcTy, + mlir::Type dstTy, ScalarConversionOpts opts) { +assert(!srcType->isMatrixType() && !dstType->isMatrixType() && + "Internal error: matrix types not handled by this function."); +if (mlir::isa(srcTy) || +mlir::isa(dstTy)) + llvm_unreachable("Obsolete code. Don't use mlir::IntegerType with CIR."); + +mlir::Type fullDstTy = dstTy; +assert(!cir::MissingFeatures::vectorType()); + +std::optional castKind; + +if (mlir::isa(srcTy)) { + if (opts.treatBooleanAsSigned) +cgf.getCIRGenModule().errorNYI("signed bool"); + if (cgf.getBuilder().isInt(dstTy)) { +castKind = cir::CastKind::bool_to_int; + } else if (mlir::isa(dstTy)) { +castKind = cir::CastKind::bool_to_float; + } else { +llvm_unreachable("Internal error: Cast to unexpected type"); + } +} else if (cgf.getBuilder().isInt(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { +castKind = cir::CastKind::integral; + } else if (mlir::isa(dstTy)) { +castKind = cir::CastKind::int_to_float; + } else { +llvm_unreachable("Internal error: Cast to unexpected type"); + } +} else if (mlir::isa(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { +/
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -84,26 +96,266 @@ class ScalarExprEmitter : public StmtVisitor { } mlir::Value VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *e) { -mlir::Type type = cgf.convertType(e->getType()); +mlir::Type type = convertType(e->getType()); return builder.create( cgf.getLoc(e->getExprLoc()), type, builder.getCIRBoolAttr(e->getValue())); } - mlir::Value VisitCastExpr(CastExpr *E); + mlir::Value VisitCastExpr(CastExpr *e); + + mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { +return VisitCastExpr(e); + } + + /// Perform a pointer to boolean conversion. + mlir::Value emitPointerToBoolConversion(mlir::Value v, QualType qt) { +// TODO(cir): comparing the ptr to null is done when lowering CIR to LLVM. +// We might want to have a separate pass for these types of conversions. +return cgf.getBuilder().createPtrToBoolCast(v); + } + + mlir::Value emitFloatToBoolConversion(mlir::Value src, mlir::Location loc) { +auto boolTy = builder.getBoolTy(); +return builder.create(loc, boolTy, + cir::CastKind::float_to_bool, src); + } + + mlir::Value emitIntToBoolConversion(mlir::Value srcVal, mlir::Location loc) { +// Because of the type rules of C, we often end up computing a +// logical value, then zero extending it to int, then wanting it +// as a logical value again. +// TODO: optimize this common case here or leave it for later +// CIR passes? +mlir::Type boolTy = convertType(cgf.getContext().BoolTy); +return builder.create(loc, boolTy, cir::CastKind::int_to_bool, + srcVal); + } + + /// Convert the specified expression value to a boolean (!cir.bool) truth + /// value. This is equivalent to "Val != 0". + mlir::Value emitConversionToBool(mlir::Value src, QualType srcType, + mlir::Location loc) { +assert(srcType.isCanonical() && "EmitScalarConversion strips typedefs"); + +if (srcType->isRealFloatingType()) + return emitFloatToBoolConversion(src, loc); + +if ([[maybe_unused]] auto *mpt = llvm::dyn_cast(srcType)) + cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion"); + +if (srcType->isIntegerType()) + return emitIntToBoolConversion(src, loc); + +assert(::mlir::isa(src.getType())); +return emitPointerToBoolConversion(src, srcType); + } + + // Emit a conversion from the specified type to the specified destination + // type, both of which are CIR scalar types. + struct ScalarConversionOpts { +bool treatBooleanAsSigned; +bool emitImplicitIntegerTruncationChecks; +bool emitImplicitIntegerSignChangeChecks; + +ScalarConversionOpts() +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks(false), + emitImplicitIntegerSignChangeChecks(false) {} + +ScalarConversionOpts(clang::SanitizerSet sanOpts) +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks( + sanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation)), + emitImplicitIntegerSignChangeChecks( + sanOpts.has(SanitizerKind::ImplicitIntegerSignChange)) {} + }; + + // Conversion from bool, integral, or floating-point to integral or + // floating-point. Conversions involving other types are handled elsewhere. + // Conversion to bool is handled elsewhere because that's a comparison against + // zero, not a simple cast. This handles both individual scalars and vectors. + mlir::Value emitScalarCast(mlir::Value src, QualType srcType, + QualType dstType, mlir::Type srcTy, + mlir::Type dstTy, ScalarConversionOpts opts) { +assert(!srcType->isMatrixType() && !dstType->isMatrixType() && + "Internal error: matrix types not handled by this function."); +if (mlir::isa(srcTy) || +mlir::isa(dstTy)) + llvm_unreachable("Obsolete code. Don't use mlir::IntegerType with CIR."); + +mlir::Type fullDstTy = dstTy; +assert(!cir::MissingFeatures::vectorType()); + +std::optional castKind; + +if (mlir::isa(srcTy)) { + if (opts.treatBooleanAsSigned) +cgf.getCIRGenModule().errorNYI("signed bool"); + if (cgf.getBuilder().isInt(dstTy)) { +castKind = cir::CastKind::bool_to_int; + } else if (mlir::isa(dstTy)) { +castKind = cir::CastKind::bool_to_float; + } else { +llvm_unreachable("Internal error: Cast to unexpected type"); + } +} else if (cgf.getBuilder().isInt(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { +castKind = cir::CastKind::integral; + } else if (mlir::isa(dstTy)) { +castKind = cir::CastKind::int_to_float; + } else { +llvm_unreachable("Internal error: Cast to unexpected type"); + } +} else if (mlir::isa(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { +/
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
https://github.com/erichkeane edited https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -130,17 +389,136 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { QualType destTy = ce->getType(); CastKind kind = ce->getCastKind(); + // These cases are generally not written to ignore the result of evaluating + // their sub-expressions, so we clear this now. + [[maybe_unused]] bool ignored = TestAndClearIgnoreResultAssign(); + switch (kind) { + case clang::CK_Dependent: +llvm_unreachable("dependent cast kind in CIR gen!"); + case clang::CK_BuiltinFnToFnPtr: +llvm_unreachable("builtin functions are handled elsewhere"); + + case CK_CPointerToObjCPointerCast: + case CK_BlockPointerToObjCPointerCast: + case CK_AnyPointerToBlockPointerCast: + case CK_BitCast: { +auto src = Visit(const_cast(e)); erichkeane wrote: another place we can't use auto. https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -84,26 +96,266 @@ class ScalarExprEmitter : public StmtVisitor { } mlir::Value VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *e) { -mlir::Type type = cgf.convertType(e->getType()); +mlir::Type type = convertType(e->getType()); return builder.create( cgf.getLoc(e->getExprLoc()), type, builder.getCIRBoolAttr(e->getValue())); } - mlir::Value VisitCastExpr(CastExpr *E); + mlir::Value VisitCastExpr(CastExpr *e); + + mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { +return VisitCastExpr(e); + } + + /// Perform a pointer to boolean conversion. + mlir::Value emitPointerToBoolConversion(mlir::Value v, QualType qt) { +// TODO(cir): comparing the ptr to null is done when lowering CIR to LLVM. +// We might want to have a separate pass for these types of conversions. +return cgf.getBuilder().createPtrToBoolCast(v); + } + + mlir::Value emitFloatToBoolConversion(mlir::Value src, mlir::Location loc) { +auto boolTy = builder.getBoolTy(); +return builder.create(loc, boolTy, + cir::CastKind::float_to_bool, src); + } + + mlir::Value emitIntToBoolConversion(mlir::Value srcVal, mlir::Location loc) { +// Because of the type rules of C, we often end up computing a +// logical value, then zero extending it to int, then wanting it +// as a logical value again. +// TODO: optimize this common case here or leave it for later +// CIR passes? +mlir::Type boolTy = convertType(cgf.getContext().BoolTy); +return builder.create(loc, boolTy, cir::CastKind::int_to_bool, + srcVal); + } + + /// Convert the specified expression value to a boolean (!cir.bool) truth + /// value. This is equivalent to "Val != 0". + mlir::Value emitConversionToBool(mlir::Value src, QualType srcType, + mlir::Location loc) { +assert(srcType.isCanonical() && "EmitScalarConversion strips typedefs"); + +if (srcType->isRealFloatingType()) + return emitFloatToBoolConversion(src, loc); + +if ([[maybe_unused]] auto *mpt = llvm::dyn_cast(srcType)) + cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion"); + +if (srcType->isIntegerType()) + return emitIntToBoolConversion(src, loc); + +assert(::mlir::isa(src.getType())); +return emitPointerToBoolConversion(src, srcType); + } + + // Emit a conversion from the specified type to the specified destination + // type, both of which are CIR scalar types. + struct ScalarConversionOpts { +bool treatBooleanAsSigned; +bool emitImplicitIntegerTruncationChecks; +bool emitImplicitIntegerSignChangeChecks; + +ScalarConversionOpts() +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks(false), + emitImplicitIntegerSignChangeChecks(false) {} + +ScalarConversionOpts(clang::SanitizerSet sanOpts) +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks( + sanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation)), + emitImplicitIntegerSignChangeChecks( + sanOpts.has(SanitizerKind::ImplicitIntegerSignChange)) {} + }; + + // Conversion from bool, integral, or floating-point to integral or + // floating-point. Conversions involving other types are handled elsewhere. + // Conversion to bool is handled elsewhere because that's a comparison against + // zero, not a simple cast. This handles both individual scalars and vectors. + mlir::Value emitScalarCast(mlir::Value src, QualType srcType, + QualType dstType, mlir::Type srcTy, + mlir::Type dstTy, ScalarConversionOpts opts) { +assert(!srcType->isMatrixType() && !dstType->isMatrixType() && + "Internal error: matrix types not handled by this function."); +if (mlir::isa(srcTy) || +mlir::isa(dstTy)) + llvm_unreachable("Obsolete code. Don't use mlir::IntegerType with CIR."); + +mlir::Type fullDstTy = dstTy; +assert(!cir::MissingFeatures::vectorType()); + +std::optional castKind; + +if (mlir::isa(srcTy)) { + if (opts.treatBooleanAsSigned) +cgf.getCIRGenModule().errorNYI("signed bool"); + if (cgf.getBuilder().isInt(dstTy)) { +castKind = cir::CastKind::bool_to_int; + } else if (mlir::isa(dstTy)) { +castKind = cir::CastKind::bool_to_float; + } else { +llvm_unreachable("Internal error: Cast to unexpected type"); + } +} else if (cgf.getBuilder().isInt(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { +castKind = cir::CastKind::integral; + } else if (mlir::isa(dstTy)) { +castKind = cir::CastKind::int_to_float; + } else { +llvm_unreachable("Internal error: Cast to unexpected type"); + } +} else if (mlir::isa(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { +/
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -130,17 +389,136 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { QualType destTy = ce->getType(); CastKind kind = ce->getCastKind(); + // These cases are generally not written to ignore the result of evaluating + // their sub-expressions, so we clear this now. + [[maybe_unused]] bool ignored = TestAndClearIgnoreResultAssign(); + switch (kind) { + case clang::CK_Dependent: +llvm_unreachable("dependent cast kind in CIR gen!"); + case clang::CK_BuiltinFnToFnPtr: +llvm_unreachable("builtin functions are handled elsewhere"); + + case CK_CPointerToObjCPointerCast: + case CK_BlockPointerToObjCPointerCast: + case CK_AnyPointerToBlockPointerCast: + case CK_BitCast: { +auto src = Visit(const_cast(e)); +mlir::Type dstTy = convertType(destTy); + +assert(!cir::MissingFeatures::addressSpace()); + +if (cgf.sanOpts.has(SanitizerKind::CFIUnrelatedCast)) + cgf.getCIRGenModule().errorNYI(e->getSourceRange(), "sanitizer support"); + +if (cgf.cgm.getCodeGenOpts().StrictVTablePointers) + cgf.getCIRGenModule().errorNYI(e->getSourceRange(), + "strict vtable pointers"); + +// Update heapallocsite metadata when there is an explicit pointer cast. +assert(!cir::MissingFeatures::addHeapAllocSiteMetadata()); + +// If Src is a fixed vector and Dst is a scalable vector, and both have the +// same element type, use the llvm.vector.insert intrinsic to perform the +// bitcast. +assert(!cir::MissingFeatures::scalableVectors()); + +// If Src is a scalable vector and Dst is a fixed vector, and both have the +// same element type, use the llvm.vector.extract intrinsic to perform the +// bitcast. +assert(!cir::MissingFeatures::scalableVectors()); + +// Perform VLAT <-> VLST bitcast through memory. +// TODO: since the llvm.experimental.vector.{insert,extract} intrinsics +// require the element types of the vectors to be the same, we +// need to keep this around for bitcasts between VLAT <-> VLST where +// the element types of the vectors are not the same, until we figure +// out a better way of doing these casts. +assert(!cir::MissingFeatures::scalableVectors()); + +return cgf.getBuilder().createBitcast(cgf.getLoc(e->getSourceRange()), src, + dstTy); + } + + case CK_AtomicToNonAtomic: +cgf.getCIRGenModule().errorNYI(e->getSourceRange(), + "CastExpr: ", ce->getCastKindName()); +break; + case CK_NonAtomicToAtomic: + case CK_UserDefinedConversion: +return Visit(const_cast(e)); + case CK_NoOp: { +auto v = Visit(const_cast(e)); +if (v) { + // CK_NoOp can model a pointer qualification conversion, which can remove + // an array bound and change the IR type. + // FIXME: Once pointee types are removed from IR, remove this. + auto t = convertType(destTy); erichkeane wrote: not a place we can use `auto`. https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -84,26 +96,266 @@ class ScalarExprEmitter : public StmtVisitor { } mlir::Value VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *e) { -mlir::Type type = cgf.convertType(e->getType()); +mlir::Type type = convertType(e->getType()); return builder.create( cgf.getLoc(e->getExprLoc()), type, builder.getCIRBoolAttr(e->getValue())); } - mlir::Value VisitCastExpr(CastExpr *E); + mlir::Value VisitCastExpr(CastExpr *e); + + mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { +return VisitCastExpr(e); + } + + /// Perform a pointer to boolean conversion. + mlir::Value emitPointerToBoolConversion(mlir::Value v, QualType qt) { +// TODO(cir): comparing the ptr to null is done when lowering CIR to LLVM. +// We might want to have a separate pass for these types of conversions. +return cgf.getBuilder().createPtrToBoolCast(v); + } + + mlir::Value emitFloatToBoolConversion(mlir::Value src, mlir::Location loc) { +auto boolTy = builder.getBoolTy(); +return builder.create(loc, boolTy, + cir::CastKind::float_to_bool, src); + } + + mlir::Value emitIntToBoolConversion(mlir::Value srcVal, mlir::Location loc) { +// Because of the type rules of C, we often end up computing a +// logical value, then zero extending it to int, then wanting it +// as a logical value again. +// TODO: optimize this common case here or leave it for later +// CIR passes? +mlir::Type boolTy = convertType(cgf.getContext().BoolTy); +return builder.create(loc, boolTy, cir::CastKind::int_to_bool, + srcVal); + } + + /// Convert the specified expression value to a boolean (!cir.bool) truth + /// value. This is equivalent to "Val != 0". + mlir::Value emitConversionToBool(mlir::Value src, QualType srcType, + mlir::Location loc) { +assert(srcType.isCanonical() && "EmitScalarConversion strips typedefs"); + +if (srcType->isRealFloatingType()) + return emitFloatToBoolConversion(src, loc); + +if ([[maybe_unused]] auto *mpt = llvm::dyn_cast(srcType)) + cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion"); + +if (srcType->isIntegerType()) + return emitIntToBoolConversion(src, loc); + +assert(::mlir::isa(src.getType())); +return emitPointerToBoolConversion(src, srcType); + } + + // Emit a conversion from the specified type to the specified destination + // type, both of which are CIR scalar types. + struct ScalarConversionOpts { +bool treatBooleanAsSigned; +bool emitImplicitIntegerTruncationChecks; +bool emitImplicitIntegerSignChangeChecks; + +ScalarConversionOpts() +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks(false), + emitImplicitIntegerSignChangeChecks(false) {} + +ScalarConversionOpts(clang::SanitizerSet sanOpts) +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks( + sanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation)), + emitImplicitIntegerSignChangeChecks( + sanOpts.has(SanitizerKind::ImplicitIntegerSignChange)) {} + }; + + // Conversion from bool, integral, or floating-point to integral or + // floating-point. Conversions involving other types are handled elsewhere. + // Conversion to bool is handled elsewhere because that's a comparison against + // zero, not a simple cast. This handles both individual scalars and vectors. + mlir::Value emitScalarCast(mlir::Value src, QualType srcType, + QualType dstType, mlir::Type srcTy, + mlir::Type dstTy, ScalarConversionOpts opts) { +assert(!srcType->isMatrixType() && !dstType->isMatrixType() && + "Internal error: matrix types not handled by this function."); +if (mlir::isa(srcTy) || +mlir::isa(dstTy)) + llvm_unreachable("Obsolete code. Don't use mlir::IntegerType with CIR."); + +mlir::Type fullDstTy = dstTy; +assert(!cir::MissingFeatures::vectorType()); + +std::optional castKind; + +if (mlir::isa(srcTy)) { + if (opts.treatBooleanAsSigned) +cgf.getCIRGenModule().errorNYI("signed bool"); + if (cgf.getBuilder().isInt(dstTy)) { +castKind = cir::CastKind::bool_to_int; + } else if (mlir::isa(dstTy)) { +castKind = cir::CastKind::bool_to_float; + } else { +llvm_unreachable("Internal error: Cast to unexpected type"); + } +} else if (cgf.getBuilder().isInt(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { +castKind = cir::CastKind::integral; + } else if (mlir::isa(dstTy)) { +castKind = cir::CastKind::int_to_float; + } else { +llvm_unreachable("Internal error: Cast to unexpected type"); + } +} else if (mlir::isa(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { +/
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -84,26 +96,266 @@ class ScalarExprEmitter : public StmtVisitor { } mlir::Value VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *e) { -mlir::Type type = cgf.convertType(e->getType()); +mlir::Type type = convertType(e->getType()); return builder.create( cgf.getLoc(e->getExprLoc()), type, builder.getCIRBoolAttr(e->getValue())); } - mlir::Value VisitCastExpr(CastExpr *E); + mlir::Value VisitCastExpr(CastExpr *e); + + mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { +return VisitCastExpr(e); + } + + /// Perform a pointer to boolean conversion. + mlir::Value emitPointerToBoolConversion(mlir::Value v, QualType qt) { +// TODO(cir): comparing the ptr to null is done when lowering CIR to LLVM. +// We might want to have a separate pass for these types of conversions. +return cgf.getBuilder().createPtrToBoolCast(v); + } + + mlir::Value emitFloatToBoolConversion(mlir::Value src, mlir::Location loc) { +auto boolTy = builder.getBoolTy(); +return builder.create(loc, boolTy, + cir::CastKind::float_to_bool, src); + } + + mlir::Value emitIntToBoolConversion(mlir::Value srcVal, mlir::Location loc) { +// Because of the type rules of C, we often end up computing a +// logical value, then zero extending it to int, then wanting it +// as a logical value again. +// TODO: optimize this common case here or leave it for later +// CIR passes? +mlir::Type boolTy = convertType(cgf.getContext().BoolTy); +return builder.create(loc, boolTy, cir::CastKind::int_to_bool, + srcVal); + } + + /// Convert the specified expression value to a boolean (!cir.bool) truth + /// value. This is equivalent to "Val != 0". + mlir::Value emitConversionToBool(mlir::Value src, QualType srcType, + mlir::Location loc) { +assert(srcType.isCanonical() && "EmitScalarConversion strips typedefs"); + +if (srcType->isRealFloatingType()) + return emitFloatToBoolConversion(src, loc); + +if ([[maybe_unused]] auto *mpt = llvm::dyn_cast(srcType)) + cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion"); + +if (srcType->isIntegerType()) + return emitIntToBoolConversion(src, loc); + +assert(::mlir::isa(src.getType())); +return emitPointerToBoolConversion(src, srcType); + } + + // Emit a conversion from the specified type to the specified destination + // type, both of which are CIR scalar types. + struct ScalarConversionOpts { +bool treatBooleanAsSigned; +bool emitImplicitIntegerTruncationChecks; +bool emitImplicitIntegerSignChangeChecks; + +ScalarConversionOpts() +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks(false), + emitImplicitIntegerSignChangeChecks(false) {} + +ScalarConversionOpts(clang::SanitizerSet sanOpts) +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks( + sanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation)), + emitImplicitIntegerSignChangeChecks( + sanOpts.has(SanitizerKind::ImplicitIntegerSignChange)) {} + }; + + // Conversion from bool, integral, or floating-point to integral or + // floating-point. Conversions involving other types are handled elsewhere. + // Conversion to bool is handled elsewhere because that's a comparison against + // zero, not a simple cast. This handles both individual scalars and vectors. + mlir::Value emitScalarCast(mlir::Value src, QualType srcType, + QualType dstType, mlir::Type srcTy, + mlir::Type dstTy, ScalarConversionOpts opts) { +assert(!srcType->isMatrixType() && !dstType->isMatrixType() && + "Internal error: matrix types not handled by this function."); +if (mlir::isa(srcTy) || +mlir::isa(dstTy)) + llvm_unreachable("Obsolete code. Don't use mlir::IntegerType with CIR."); + +mlir::Type fullDstTy = dstTy; +assert(!cir::MissingFeatures::vectorType()); + +std::optional castKind; + +if (mlir::isa(srcTy)) { + if (opts.treatBooleanAsSigned) +cgf.getCIRGenModule().errorNYI("signed bool"); + if (cgf.getBuilder().isInt(dstTy)) { +castKind = cir::CastKind::bool_to_int; + } else if (mlir::isa(dstTy)) { +castKind = cir::CastKind::bool_to_float; + } else { +llvm_unreachable("Internal error: Cast to unexpected type"); + } +} else if (cgf.getBuilder().isInt(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { +castKind = cir::CastKind::integral; + } else if (mlir::isa(dstTy)) { +castKind = cir::CastKind::int_to_float; + } else { +llvm_unreachable("Internal error: Cast to unexpected type"); + } +} else if (mlir::isa(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { +/
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -84,26 +96,266 @@ class ScalarExprEmitter : public StmtVisitor { } mlir::Value VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *e) { -mlir::Type type = cgf.convertType(e->getType()); +mlir::Type type = convertType(e->getType()); return builder.create( cgf.getLoc(e->getExprLoc()), type, builder.getCIRBoolAttr(e->getValue())); } - mlir::Value VisitCastExpr(CastExpr *E); + mlir::Value VisitCastExpr(CastExpr *e); + + mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { +return VisitCastExpr(e); + } + + /// Perform a pointer to boolean conversion. + mlir::Value emitPointerToBoolConversion(mlir::Value v, QualType qt) { +// TODO(cir): comparing the ptr to null is done when lowering CIR to LLVM. +// We might want to have a separate pass for these types of conversions. +return cgf.getBuilder().createPtrToBoolCast(v); + } + + mlir::Value emitFloatToBoolConversion(mlir::Value src, mlir::Location loc) { +auto boolTy = builder.getBoolTy(); +return builder.create(loc, boolTy, + cir::CastKind::float_to_bool, src); + } + + mlir::Value emitIntToBoolConversion(mlir::Value srcVal, mlir::Location loc) { +// Because of the type rules of C, we often end up computing a +// logical value, then zero extending it to int, then wanting it +// as a logical value again. +// TODO: optimize this common case here or leave it for later +// CIR passes? +mlir::Type boolTy = convertType(cgf.getContext().BoolTy); +return builder.create(loc, boolTy, cir::CastKind::int_to_bool, + srcVal); + } + + /// Convert the specified expression value to a boolean (!cir.bool) truth + /// value. This is equivalent to "Val != 0". + mlir::Value emitConversionToBool(mlir::Value src, QualType srcType, + mlir::Location loc) { +assert(srcType.isCanonical() && "EmitScalarConversion strips typedefs"); + +if (srcType->isRealFloatingType()) + return emitFloatToBoolConversion(src, loc); + +if ([[maybe_unused]] auto *mpt = llvm::dyn_cast(srcType)) erichkeane wrote: ```suggestion if (llvm::isa(srcType)) ``` https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
https://github.com/erichkeane commented: We have to be mroe careful I think with uses of the `errorNYI` in a few places, we are using it too much like `assert` when it is more like `emitDiag`. Also, this patch uses `auto` a bunch in ways that are contrary to the coding standard. See : https://llvm.org/docs/CodingStandards.html#use-auto-type-deduction-to-make-code-more-readable `auto` is really only to be used (rule of thumb) when the type is already 'listed' on the RHS, or naming the type is so obnoxious as to be irrelevant (iterators/etc). https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -84,26 +96,266 @@ class ScalarExprEmitter : public StmtVisitor { } mlir::Value VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *e) { -mlir::Type type = cgf.convertType(e->getType()); +mlir::Type type = convertType(e->getType()); return builder.create( cgf.getLoc(e->getExprLoc()), type, builder.getCIRBoolAttr(e->getValue())); } - mlir::Value VisitCastExpr(CastExpr *E); + mlir::Value VisitCastExpr(CastExpr *e); + + mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { +return VisitCastExpr(e); + } + + /// Perform a pointer to boolean conversion. + mlir::Value emitPointerToBoolConversion(mlir::Value v, QualType qt) { +// TODO(cir): comparing the ptr to null is done when lowering CIR to LLVM. +// We might want to have a separate pass for these types of conversions. +return cgf.getBuilder().createPtrToBoolCast(v); + } + + mlir::Value emitFloatToBoolConversion(mlir::Value src, mlir::Location loc) { +auto boolTy = builder.getBoolTy(); +return builder.create(loc, boolTy, + cir::CastKind::float_to_bool, src); + } + + mlir::Value emitIntToBoolConversion(mlir::Value srcVal, mlir::Location loc) { +// Because of the type rules of C, we often end up computing a +// logical value, then zero extending it to int, then wanting it +// as a logical value again. +// TODO: optimize this common case here or leave it for later +// CIR passes? +mlir::Type boolTy = convertType(cgf.getContext().BoolTy); +return builder.create(loc, boolTy, cir::CastKind::int_to_bool, + srcVal); + } + + /// Convert the specified expression value to a boolean (!cir.bool) truth + /// value. This is equivalent to "Val != 0". + mlir::Value emitConversionToBool(mlir::Value src, QualType srcType, + mlir::Location loc) { +assert(srcType.isCanonical() && "EmitScalarConversion strips typedefs"); + +if (srcType->isRealFloatingType()) + return emitFloatToBoolConversion(src, loc); + +if ([[maybe_unused]] auto *mpt = llvm::dyn_cast(srcType)) + cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion"); + +if (srcType->isIntegerType()) + return emitIntToBoolConversion(src, loc); + +assert(::mlir::isa(src.getType())); +return emitPointerToBoolConversion(src, srcType); + } + + // Emit a conversion from the specified type to the specified destination + // type, both of which are CIR scalar types. + struct ScalarConversionOpts { +bool treatBooleanAsSigned; +bool emitImplicitIntegerTruncationChecks; +bool emitImplicitIntegerSignChangeChecks; + +ScalarConversionOpts() +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks(false), + emitImplicitIntegerSignChangeChecks(false) {} + +ScalarConversionOpts(clang::SanitizerSet sanOpts) +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks( + sanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation)), + emitImplicitIntegerSignChangeChecks( + sanOpts.has(SanitizerKind::ImplicitIntegerSignChange)) {} + }; + + // Conversion from bool, integral, or floating-point to integral or + // floating-point. Conversions involving other types are handled elsewhere. + // Conversion to bool is handled elsewhere because that's a comparison against + // zero, not a simple cast. This handles both individual scalars and vectors. + mlir::Value emitScalarCast(mlir::Value src, QualType srcType, + QualType dstType, mlir::Type srcTy, + mlir::Type dstTy, ScalarConversionOpts opts) { +assert(!srcType->isMatrixType() && !dstType->isMatrixType() && + "Internal error: matrix types not handled by this function."); +if (mlir::isa(srcTy) || +mlir::isa(dstTy)) + llvm_unreachable("Obsolete code. Don't use mlir::IntegerType with CIR."); + +mlir::Type fullDstTy = dstTy; +assert(!cir::MissingFeatures::vectorType()); + +std::optional castKind; + +if (mlir::isa(srcTy)) { + if (opts.treatBooleanAsSigned) +cgf.getCIRGenModule().errorNYI("signed bool"); + if (cgf.getBuilder().isInt(dstTy)) { +castKind = cir::CastKind::bool_to_int; + } else if (mlir::isa(dstTy)) { +castKind = cir::CastKind::bool_to_float; + } else { +llvm_unreachable("Internal error: Cast to unexpected type"); + } +} else if (cgf.getBuilder().isInt(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { +castKind = cir::CastKind::integral; + } else if (mlir::isa(dstTy)) { +castKind = cir::CastKind::int_to_float; + } else { +llvm_unreachable("Internal error: Cast to unexpected type"); + } +} else if (mlir::isa(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { +/
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -84,26 +96,266 @@ class ScalarExprEmitter : public StmtVisitor { } mlir::Value VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *e) { -mlir::Type type = cgf.convertType(e->getType()); +mlir::Type type = convertType(e->getType()); return builder.create( cgf.getLoc(e->getExprLoc()), type, builder.getCIRBoolAttr(e->getValue())); } - mlir::Value VisitCastExpr(CastExpr *E); + mlir::Value VisitCastExpr(CastExpr *e); + + mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { +return VisitCastExpr(e); + } + + /// Perform a pointer to boolean conversion. + mlir::Value emitPointerToBoolConversion(mlir::Value v, QualType qt) { +// TODO(cir): comparing the ptr to null is done when lowering CIR to LLVM. +// We might want to have a separate pass for these types of conversions. +return cgf.getBuilder().createPtrToBoolCast(v); + } + + mlir::Value emitFloatToBoolConversion(mlir::Value src, mlir::Location loc) { +auto boolTy = builder.getBoolTy(); +return builder.create(loc, boolTy, + cir::CastKind::float_to_bool, src); + } + + mlir::Value emitIntToBoolConversion(mlir::Value srcVal, mlir::Location loc) { +// Because of the type rules of C, we often end up computing a +// logical value, then zero extending it to int, then wanting it +// as a logical value again. +// TODO: optimize this common case here or leave it for later +// CIR passes? +mlir::Type boolTy = convertType(cgf.getContext().BoolTy); +return builder.create(loc, boolTy, cir::CastKind::int_to_bool, + srcVal); + } + + /// Convert the specified expression value to a boolean (!cir.bool) truth + /// value. This is equivalent to "Val != 0". + mlir::Value emitConversionToBool(mlir::Value src, QualType srcType, + mlir::Location loc) { +assert(srcType.isCanonical() && "EmitScalarConversion strips typedefs"); + +if (srcType->isRealFloatingType()) + return emitFloatToBoolConversion(src, loc); + +if ([[maybe_unused]] auto *mpt = llvm::dyn_cast(srcType)) + cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion"); + +if (srcType->isIntegerType()) + return emitIntToBoolConversion(src, loc); + +assert(::mlir::isa(src.getType())); +return emitPointerToBoolConversion(src, srcType); + } + + // Emit a conversion from the specified type to the specified destination + // type, both of which are CIR scalar types. + struct ScalarConversionOpts { +bool treatBooleanAsSigned; +bool emitImplicitIntegerTruncationChecks; +bool emitImplicitIntegerSignChangeChecks; + +ScalarConversionOpts() +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks(false), + emitImplicitIntegerSignChangeChecks(false) {} + +ScalarConversionOpts(clang::SanitizerSet sanOpts) +: treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks( + sanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation)), + emitImplicitIntegerSignChangeChecks( + sanOpts.has(SanitizerKind::ImplicitIntegerSignChange)) {} + }; + + // Conversion from bool, integral, or floating-point to integral or + // floating-point. Conversions involving other types are handled elsewhere. + // Conversion to bool is handled elsewhere because that's a comparison against + // zero, not a simple cast. This handles both individual scalars and vectors. + mlir::Value emitScalarCast(mlir::Value src, QualType srcType, + QualType dstType, mlir::Type srcTy, + mlir::Type dstTy, ScalarConversionOpts opts) { +assert(!srcType->isMatrixType() && !dstType->isMatrixType() && + "Internal error: matrix types not handled by this function."); +if (mlir::isa(srcTy) || +mlir::isa(dstTy)) + llvm_unreachable("Obsolete code. Don't use mlir::IntegerType with CIR."); + +mlir::Type fullDstTy = dstTy; +assert(!cir::MissingFeatures::vectorType()); + +std::optional castKind; + +if (mlir::isa(srcTy)) { + if (opts.treatBooleanAsSigned) +cgf.getCIRGenModule().errorNYI("signed bool"); + if (cgf.getBuilder().isInt(dstTy)) { +castKind = cir::CastKind::bool_to_int; + } else if (mlir::isa(dstTy)) { +castKind = cir::CastKind::bool_to_float; + } else { +llvm_unreachable("Internal error: Cast to unexpected type"); + } +} else if (cgf.getBuilder().isInt(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { +castKind = cir::CastKind::integral; + } else if (mlir::isa(dstTy)) { +castKind = cir::CastKind::int_to_float; + } else { +llvm_unreachable("Internal error: Cast to unexpected type"); + } +} else if (mlir::isa(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { +/
[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
@@ -36,6 +36,18 @@ class ScalarExprEmitter : public StmtVisitor { bool ira = false) : cgf(cgf), builder(builder), ignoreResultAssign(ira) {} + //======// + // Utilities + //======// + + bool TestAndClearIgnoreResultAssign() { erichkeane wrote: Oh boy this name is a bit of a word-soup :D This is actually more of an `exchange` in c++ parlance. https://github.com/llvm/llvm-project/pull/130690 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits