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

Erich Keane via cfe-commits cfe-commits at lists.llvm.org
Wed Mar 12 08:11:22 PDT 2025


================
@@ -84,26 +96,266 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
   }
 
   mlir::Value VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *e) {
-    mlir::Type type = cgf.convertType(e->getType());
+    mlir::Type type = convertType(e->getType());
     return builder.create<cir::ConstantOp>(
         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<cir::CastOp>(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<cir::CastOp>(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<MemberPointerType>(srcType))
+      cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion");
+
+    if (srcType->isIntegerType())
+      return emitIntToBoolConversion(src, loc);
+
+    assert(::mlir::isa<cir::PointerType>(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<mlir::IntegerType>(srcTy) ||
+        mlir::isa<mlir::IntegerType>(dstTy))
+      llvm_unreachable("Obsolete code. Don't use mlir::IntegerType with CIR.");
+
+    mlir::Type fullDstTy = dstTy;
+    assert(!cir::MissingFeatures::vectorType());
+
+    std::optional<cir::CastKind> castKind;
+
+    if (mlir::isa<cir::BoolType>(srcTy)) {
+      if (opts.treatBooleanAsSigned)
+        cgf.getCIRGenModule().errorNYI("signed bool");
+      if (cgf.getBuilder().isInt(dstTy)) {
+        castKind = cir::CastKind::bool_to_int;
+      } else if (mlir::isa<cir::CIRFPTypeInterface>(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<cir::CIRFPTypeInterface>(dstTy)) {
+        castKind = cir::CastKind::int_to_float;
+      } else {
+        llvm_unreachable("Internal error: Cast to unexpected type");
+      }
+    } else if (mlir::isa<cir::CIRFPTypeInterface>(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 float cast overflow");
+        assert(!cir::MissingFeatures::fpConstraints());
+        castKind = cir::CastKind::float_to_int;
+      } else if (mlir::isa<cir::CIRFPTypeInterface>(dstTy)) {
+        cgf.getCIRGenModule().errorNYI("floating point casts");
+      } else {
+        llvm_unreachable("Internal error: Cast to unexpected type");
+      }
+    } else {
+      llvm_unreachable("Internal error: Cast from unexpected type");
+    }
+
+    assert(castKind.has_value() && "Internal error: CastKind not set.");
+    return builder.create<cir::CastOp>(src.getLoc(), fullDstTy, *castKind, src);
+  }
 
   /// Emit a conversion from the specified type to the specified destination
   /// type, both of which are CIR scalar types.
   /// TODO: do we need ScalarConversionOpts here? Should be done in another
   /// pass.
-  mlir::Value emitScalarConversion(mlir::Value src, QualType srcType,
-                                   QualType dstType, SourceLocation loc) {
-    // No sort of type conversion is implemented yet, but the path for implicit
-    // paths goes through here even if the type isn't being changed.
+  mlir::Value
+  emitScalarConversion(mlir::Value src, QualType srcType, QualType dstType,
+                       SourceLocation loc,
+                       ScalarConversionOpts opts = ScalarConversionOpts()) {
+    // All conversions involving fixed point types should be handled by the
+    // emitFixedPoint family functions. This is done to prevent bloating up
+    // this function more, and although fixed point numbers are represented by
+    // integers, we do not want to follow any logic that assumes they should be
+    // treated as integers.
+    // TODO(leonardchan): When necessary, add another if statement checking for
+    // conversions to fixed point types from other types.
+    // conversions to fixed point types from other types.
+    if (srcType->isFixedPointType())
+      cgf.getCIRGenModule().errorNYI(loc, "fixed point conversions");
+    else if (dstType->isFixedPointType())
+      cgf.getCIRGenModule().errorNYI(loc, "fixed point conversions");
+
     srcType = srcType.getCanonicalType();
     dstType = dstType.getCanonicalType();
-    if (srcType == dstType)
+    if (srcType == dstType) {
+      if (opts.emitImplicitIntegerSignChangeChecks)
+        cgf.getCIRGenModule().errorNYI(loc,
+                                       "implicit integer sign change checks");
       return src;
+    }
+
+    if (dstType->isVoidType())
+      return nullptr;
+
+    mlir::Type srcTy = src.getType();
+
+    // Handle conversions to bool first, they are special: comparisons against
+    // 0.
+    if (dstType->isBooleanType())
+      return emitConversionToBool(src, srcType, cgf.getLoc(loc));
+
+    mlir::Type dstTy = convertType(dstType);
+
+    if (srcType->isHalfType() &&
+        !cgf.getContext().getLangOpts().NativeHalfType) {
+      // Cast to FP using the intrinsic if the half type itself isn't supported.
+      if (mlir::isa<cir::CIRFPTypeInterface>(dstTy)) {
+        if (cgf.getContext().getTargetInfo().useFP16ConversionIntrinsics())
+          cgf.getCIRGenModule().errorNYI(loc,
+                                         "cast via llvm.convert.from.fp16");
+      } else {
+        // Cast to other types through float, using either the intrinsic or
+        // FPExt, depending on whether the half type itself is supported (as
+        // opposed to operations on half, available with NativeHalfType).
+        if (cgf.getContext().getTargetInfo().useFP16ConversionIntrinsics()) {
+          cgf.getCIRGenModule().errorNYI(loc,
+                                         "cast via llvm.convert.from.fp16");
+        } else {
+          src = builder.createCast(cgf.getLoc(loc), cir::CastKind::floating,
+                                   src, cgf.FloatTy);
+        }
+        srcType = cgf.getContext().FloatTy;
+        srcTy = cgf.FloatTy;
+      }
+    }
+
+    // TODO(cir): LLVM codegen ignore conversions like int -> uint,
+    // is there anything to be done for CIR here?
+    if (srcTy == dstTy) {
+      if (opts.emitImplicitIntegerSignChangeChecks)
+        cgf.getCIRGenModule().errorNYI(loc,
+                                       "implicit integer sign change checks");
+      return src;
+    }
+
+    // Handle pointer conversions next: pointers can only be converted to/from
+    // other pointers and integers. Check for pointer types in terms of LLVM, as
+    // some native types (like Obj-C id) may map to a pointer type.
+    if (auto dstPT = dyn_cast<cir::PointerType>(dstTy)) {
+      cgf.getCIRGenModule().errorNYI(loc, "pointer casts");
----------------
erichkeane wrote:

If this falls through it could very well result in a pointer-to-pointer cast being done as pointer-to-int, which is likely to create nonsense and likely cause crashes later.  We should probably just create a constant of `dest` type here.

https://github.com/llvm/llvm-project/pull/130690


More information about the cfe-commits mailing list