[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)

2025-03-15 Thread Morris Hafner via cfe-commits


@@ -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)

2025-03-15 Thread Andy Kaylor via cfe-commits


@@ -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)

2025-03-15 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-15 Thread Andy Kaylor via cfe-commits


@@ -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)

2025-03-14 Thread Andy Kaylor via cfe-commits

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)

2025-03-14 Thread Andy Kaylor via cfe-commits


@@ -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)

2025-03-14 Thread Andy Kaylor via cfe-commits

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)

2025-03-14 Thread Morris Hafner via cfe-commits


@@ -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)

2025-03-14 Thread Morris Hafner via cfe-commits


@@ -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)

2025-03-14 Thread Andy Kaylor via cfe-commits


@@ -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)

2025-03-14 Thread Andy Kaylor via cfe-commits


@@ -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)

2025-03-13 Thread Bruno Cardoso Lopes via cfe-commits


@@ -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)

2025-03-13 Thread Bruno Cardoso Lopes via cfe-commits

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)

2025-03-13 Thread Bruno Cardoso Lopes via cfe-commits

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)

2025-03-13 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-13 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-13 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-13 Thread Erich Keane via cfe-commits

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)

2025-03-13 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-13 Thread Erich Keane via cfe-commits

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)

2025-03-13 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-12 Thread Morris Hafner via cfe-commits

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)

2025-03-12 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-12 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-12 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-12 Thread Morris Hafner via cfe-commits


@@ -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)

2025-03-12 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-12 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-12 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-12 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-12 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-12 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-12 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-12 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-12 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-12 Thread Morris Hafner via cfe-commits

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)

2025-03-12 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-12 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-12 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-12 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-12 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-12 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-12 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-12 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-12 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-11 Thread Morris Hafner via cfe-commits


@@ -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)

2025-03-11 Thread Andy Kaylor via cfe-commits


@@ -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)

2025-03-11 Thread Andy Kaylor via cfe-commits


@@ -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)

2025-03-11 Thread Andy Kaylor via cfe-commits


@@ -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)

2025-03-11 Thread Andy Kaylor via cfe-commits


@@ -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)

2025-03-11 Thread Andy Kaylor via cfe-commits


@@ -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)

2025-03-11 Thread Andy Kaylor via cfe-commits


@@ -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)

2025-03-11 Thread Andy Kaylor via cfe-commits


@@ -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)

2025-03-11 Thread Morris Hafner via cfe-commits

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)

2025-03-11 Thread Morris Hafner via cfe-commits


@@ -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)

2025-03-11 Thread Morris Hafner via cfe-commits


@@ -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)

2025-03-11 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-11 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-11 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-11 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-11 Thread Erich Keane via cfe-commits

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)

2025-03-11 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-11 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-11 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-11 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-11 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-11 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-11 Thread Erich Keane via cfe-commits

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)

2025-03-11 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-11 Thread Erich Keane via cfe-commits


@@ -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)

2025-03-11 Thread Erich Keane via cfe-commits


@@ -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