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

Morris Hafner via cfe-commits cfe-commits at lists.llvm.org
Tue Mar 11 11:13:02 PDT 2025


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 <mhafner at nvidia.com>
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<cir::StoreOp>(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<cir::CastOp>(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<cir::PointerType>(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<string mnemonic, list<Trait> traits = []> :
     Op<CIR_Dialect, mnemonic, traits>, LLVMLoweringInfo;
 
+//===----------------------------------------------------------------------===//
+// CastOp
+//===----------------------------------------------------------------------===//
+
+// The enumaration value isn't in sync with clang.
+def CK_IntegralToBoolean : I32EnumAttrCase<"int_to_bool", 1>;
+def CK_ArrayToPointerDecay : I32EnumAttrCase<"array_to_ptrdecay", 2>;
+def CK_IntegralCast : I32EnumAttrCase<"integral", 3>;
+def CK_BitCast : I32EnumAttrCase<"bitcast", 4>;
+def CK_FloatingCast : I32EnumAttrCase<"floating", 5>;
+def CK_PtrToBoolean : I32EnumAttrCase<"ptr_to_bool", 6>;
+def CK_FloatToIntegral : I32EnumAttrCase<"float_to_int", 7>;
+def CK_IntegralToPointer : I32EnumAttrCase<"int_to_ptr", 8>;
+def CK_PointerToIntegral : I32EnumAttrCase<"ptr_to_int", 9>;
+def CK_FloatToBoolean : I32EnumAttrCase<"float_to_bool", 10>;
+def CK_BooleanToIntegral : I32EnumAttrCase<"bool_to_int", 11>;
+def CK_IntegralToFloat : I32EnumAttrCase<"int_to_float", 12>;
+def CK_BooleanToFloat : I32EnumAttrCase<"bool_to_float", 13>;
+def CK_AddressSpaceConversion : I32EnumAttrCase<"address_space", 14>;
+def CK_FloatToComplex : I32EnumAttrCase<"float_to_complex", 15>;
+def CK_IntegralToComplex : I32EnumAttrCase<"int_to_complex", 16>;
+def CK_FloatComplexToReal : I32EnumAttrCase<"float_complex_to_real", 17>;
+def CK_IntegralComplexToReal : I32EnumAttrCase<"int_complex_to_real", 18>;
+def CK_FloatComplexToBoolean : I32EnumAttrCase<"float_complex_to_bool", 19>;
+def CK_IntegralComplexToBoolean : I32EnumAttrCase<"int_complex_to_bool", 20>;
+def CK_FloatComplexCast : I32EnumAttrCase<"float_complex", 21>;
+def CK_FloatComplexToIntegralComplex
+    : I32EnumAttrCase<"float_complex_to_int_complex", 22>;
+def CK_IntegralComplexCast : I32EnumAttrCase<"int_complex", 23>;
+def CK_IntegralComplexToFloatComplex
+    : I32EnumAttrCase<"int_complex_to_float_complex", 24>;
+def CK_MemberPtrToBoolean : I32EnumAttrCase<"member_ptr_to_bool", 25>;
+
+def CastKind : I32EnumAttr<
+    "CastKind",
+    "cast kind",
+    [CK_IntegralToBoolean, CK_ArrayToPointerDecay, CK_IntegralCast,
+     CK_BitCast, CK_FloatingCast, CK_PtrToBoolean, CK_FloatToIntegral,
+     CK_IntegralToPointer, CK_PointerToIntegral, CK_FloatToBoolean,
+     CK_BooleanToIntegral, CK_IntegralToFloat, CK_BooleanToFloat,
+     CK_AddressSpaceConversion, CK_FloatToComplex, CK_IntegralToComplex,
+     CK_FloatComplexToReal, CK_IntegralComplexToReal, CK_FloatComplexToBoolean,
+     CK_IntegralComplexToBoolean, CK_FloatComplexCast,
+     CK_FloatComplexToIntegralComplex, CK_IntegralComplexCast,
+     CK_IntegralComplexToFloatComplex, CK_MemberPtrToBoolean]> {
+  let cppNamespace = "::cir";
+}
+
+def CastOp : CIR_Op<"cast",
+             [Pure,
+              DeclareOpInterfaceMethods<PromotableOpInterface>]> {
+  // FIXME: not all conversions are free of side effects.
+  let summary = "Conversion between values of different types";
+  let description = [{
+    Apply C/C++ usual conversions rules between values. Currently supported kinds:
+
+    - `array_to_ptrdecay`
+    - `bitcast`
+    - `integral`
+    - `int_to_bool`
+    - `int_to_float`
+    - `floating`
+    - `float_to_int`
+    - `float_to_bool`
+    - `ptr_to_int`
+    - `ptr_to_bool`
+    - `bool_to_int`
+    - `bool_to_float`
+    - `address_space`
+    - `float_to_complex`
+    - `int_to_complex`
+    - `float_complex_to_real`
+    - `int_complex_to_real`
+    - `float_complex_to_bool`
+    - `int_complex_to_bool`
+    - `float_complex`
+    - `float_complex_to_int_complex`
+    - `int_complex`
+    - `int_complex_to_float_complex`
+
+    This is effectively a subset of the rules from
+    `llvm-project/clang/include/clang/AST/OperationKinds.def`; but note that some
+    of the conversions aren't implemented in terms of `cir.cast`, `lvalue-to-rvalue`
+    for instance is modeled as a regular `cir.load`.
+
+    ```mlir
+    %4 = cir.cast (int_to_bool, %3 : i32), !cir.bool
+    ...
+    %x = cir.cast(array_to_ptrdecay, %0 : !cir.ptr<!cir.array<i32 x 10>>), !cir.ptr<i32>
+    ```
+  }];
+
+  let arguments = (ins CastKind:$kind, CIR_AnyType:$src);
+  let results = (outs CIR_AnyType:$result);
+
+  let assemblyFormat = [{
+    `(` $kind `,` $src `:` type($src) `)`
+    `,` type($result) attr-dict
+  }];
+
+  // The input and output types should match the cast kind.
+  let hasVerifier = 1;
+  let hasFolder = 1;
+}
+
 //===----------------------------------------------------------------------===//
 // ConstantOp
 //===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index d20cd0560a7c1..5f1ed97f4940a 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -73,15 +73,27 @@ struct MissingFeatures {
   static bool opFuncVisibility() { return false; }
 
   // Misc
-  static bool scalarConversionOpts() { return false; }
+  static bool cxxABI() { return false; }
   static bool tryEmitAsConstant() { return false; }
   static bool constructABIArgDirectExtend() { return false; }
   static bool opGlobalViewAttr() { return false; }
   static bool lowerModeOptLevel() { return false; }
   static bool opTBAA() { return false; }
+  static bool opCmp() { return false; }
   static bool objCLifetime() { return false; }
   static bool emitNullabilityCheck() { return false; }
   static bool astVarDeclInterface() { return false; }
+  static bool scalableVectors() { return false; }
+  static bool fpConstraints() { return false; }
+  static bool sanitizers() { return false; }
+  static bool addHeapAllocSiteMetadata() { return false; }
+  static bool targetCodeGenInfoGetNullPointer() { return false; }
+
+  // Missing types
+  static bool dataMemberType() { return false; }
+  static bool methodType() { return false; }
+  static bool matrixType() { return false; }
+  static bool vectorType() { return false; }
 };
 
 } // namespace cir
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index 01d56963883cc..76e6c53d9b6e2 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -10,6 +10,7 @@
 #define LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENBUILDER_H
 
 #include "CIRGenTypeCache.h"
+#include "clang/CIR/MissingFeatures.h"
 
 #include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h"
 
@@ -33,6 +34,14 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
       llvm_unreachable("NYI: PPC double-double format for long double");
     llvm_unreachable("Unsupported format for long double");
   }
+
+  bool isInt(mlir::Type i) { return mlir::isa<cir::IntType>(i); }
+
+  // Creates constant nullptr for pointer type ty.
+  cir::ConstantOp getNullPtr(mlir::Type ty, mlir::Location loc) {
+    assert(!cir::MissingFeatures::targetCodeGenInfoGetNullPointer());
+    return create<cir::ConstantOp>(loc, ty, getConstPtrAttr(ty, 0));
+  }
 };
 
 } // namespace clang::CIRGen
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index b9e56dc4123d6..df9447841800a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -36,6 +36,18 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
                     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); }
+
   //===--------------------------------------------------------------------===//
   //                            Visitor Methods
   //===--------------------------------------------------------------------===//
@@ -68,14 +80,14 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
   }
 
   mlir::Value VisitIntegerLiteral(const IntegerLiteral *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.getAttr<cir::IntAttr>(type, e->getValue()));
   }
 
   mlir::Value VisitFloatingLiteral(const FloatingLiteral *e) {
-    mlir::Type type = cgf.convertType(e->getType());
+    mlir::Type type = convertType(e->getType());
     assert(mlir::isa<cir::CIRFPTypeInterface>(type) &&
            "expect floating-point type");
     return builder.create<cir::ConstantOp>(
@@ -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");
+    }
+
+    if (isa<cir::PointerType>(srcTy)) {
+      // Must be an ptr to int cast.
+      assert(isa<cir::IntType>(dstTy) && "not ptr->int?");
+      return builder.createPtrToInt(src, dstTy);
+    }
+
+    // A scalar can be splatted to an extended vector of the same element type
+    if (dstType->isExtVectorType() && !srcType->isVectorType()) {
+      // Sema should add casts to make sure that the source expression's type
+      // is the same as the vector's element type (sans qualifiers)
+      assert(dstType->castAs<ExtVectorType>()->getElementType().getTypePtr() ==
+                 srcType.getTypePtr() &&
+             "Splatted expr doesn't match with vector element type?");
+
+      llvm_unreachable("not implemented");
+    }
+
+    if (srcType->isMatrixType() && dstType->isMatrixType())
+      cgf.getCIRGenModule().errorNYI(loc,
+                                     "matrix type to matrix type conversion");
+    assert(!srcType->isMatrixType() && !dstType->isMatrixType() &&
+           "Internal error: conversion between matrix type and scalar type");
+
+    // Finally, we have the arithmetic types or vectors of arithmetic types.
+    mlir::Value res = nullptr;
+    mlir::Type resTy = dstTy;
+
+    res = emitScalarCast(src, srcType, dstType, srcTy, dstTy, opts);
+
+    if (dstTy != resTy) {
+      if (cgf.getContext().getTargetInfo().useFP16ConversionIntrinsics()) {
+        cgf.getCIRGenModule().errorNYI(loc, "cast via llvm.convert.to.fp16");
+      } else {
+        res = builder.createCast(cgf.getLoc(loc), cir::CastKind::floating, res,
+                                 resTy);
+      }
+    }
+
+    if (opts.emitImplicitIntegerTruncationChecks)
+      cgf.getCIRGenModule().errorNYI(loc, "implicit integer truncation checks");
+
+    if (opts.emitImplicitIntegerSignChangeChecks)
+      cgf.getCIRGenModule().errorNYI(loc,
+                                     "implicit integer sign change checks");
+
+    return res;
 
     cgf.getCIRGenModule().errorNYI(loc,
                                    "emitScalarConversion for unequal types");
@@ -121,6 +373,13 @@ mlir::Value CIRGenFunction::emitScalarExpr(const Expr *e) {
   return ScalarExprEmitter(*this, builder).Visit(const_cast<Expr *>(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
@@ -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<Expr *>(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<Expr *>(e));
+  case CK_NoOp: {
+    auto v = Visit(const_cast<Expr *>(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);
+      if (t != v.getType())
+        cgf.getCIRGenModule().errorNYI("pointer qualification conversion");
+    }
+    return v;
+  }
+
+  case CK_NullToPointer: {
+    if (MustVisitNullValue(e))
+      cgf.getCIRGenModule().errorNYI(
+          e->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 = convertType(destTy);
+    return builder.getNullPtr(ty, cgf.getLoc(e->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<Expr *>(e));
 
   case CK_IntegralCast: {
-    assert(!cir::MissingFeatures::scalarConversionOpts());
+    ScalarConversionOpts opts;
+    if (auto *ice = dyn_cast<ImplicitCastExpr>(ce)) {
+      if (!ice->isPartOfExplicitCast())
+        opts = ScalarConversionOpts(cgf.sanOpts);
+    }
     return emitScalarConversion(Visit(e), e->getType(), destTy,
-                                ce->getExprLoc());
+                                ce->getExprLoc(), opts);
+  }
+
+  case CK_FloatingRealToComplex:
+  case CK_FloatingComplexCast:
+  case CK_IntegralRealToComplex:
+  case CK_IntegralComplexCast:
+  case CK_IntegralComplexToFloatingComplex:
+  case CK_FloatingComplexToIntegralComplex:
+    llvm_unreachable("scalar cast to non-scalar value");
+
+  case CK_PointerToIntegral: {
+    assert(!destTy->isBooleanType() && "bool should use PointerToBool");
+    if (cgf.cgm.getCodeGenOpts().StrictVTablePointers)
+      llvm_unreachable("NYI");
+    return builder.createPtrToInt(Visit(e), convertType(destTy));
+  }
+  case CK_ToVoid:
+    cgf.getCIRGenModule().errorNYI(e->getSourceRange(),
+                                   "ignored expression on void cast");
+    return nullptr;
+
+  case CK_IntegralToBoolean:
+    return emitIntToBoolConversion(Visit(e), cgf.getLoc(ce->getSourceRange()));
+
+  case CK_PointerToBoolean:
+    return emitPointerToBoolConversion(Visit(e), e->getType());
+  case CK_FloatingToBoolean:
+    return emitFloatToBoolConversion(Visit(e), cgf.getLoc(e->getExprLoc()));
+  case CK_MemberPointerToBoolean: {
+    mlir::Value memPtr = Visit(e);
+    return builder.createCast(cgf.getLoc(ce->getSourceRange()),
+                              cir::CastKind::member_ptr_to_bool, memPtr,
+                              cgf.convertType(destTy));
   }
+    return nullptr;
 
   default:
     cgf.getCIRGenModule().errorNYI(e->getSourceRange(),
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 72445f62232a4..87890d14b4d05 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -73,6 +73,9 @@ class CIRGenFunction : public CIRGenTypeCache {
     return &fn.getRegion().front();
   }
 
+  /// Sanitizers enabled for this function.
+  clang::SanitizerSet sanOpts;
+
   mlir::Type convertTypeForMem(QualType T);
 
   mlir::Type convertType(clang::QualType T);
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 5ad369b40cda1..d03189ce0e210 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -182,6 +182,176 @@ OpFoldResult cir::ConstantOp::fold(FoldAdaptor /*adaptor*/) {
   return getValue();
 }
 
+//===----------------------------------------------------------------------===//
+// CastOp
+//===----------------------------------------------------------------------===//
+
+LogicalResult cir::CastOp::verify() {
+  auto resType = getResult().getType();
+  auto srcType = getSrc().getType();
+
+  switch (getKind()) {
+  case cir::CastKind::int_to_bool: {
+    if (!mlir::isa<cir::BoolType>(resType))
+      return emitOpError() << "requires !cir.bool type for result";
+    if (!mlir::isa<cir::IntType>(srcType))
+      return emitOpError() << "requires !cir.int type for source";
+    return success();
+  }
+  case cir::CastKind::ptr_to_bool: {
+    if (!mlir::isa<cir::BoolType>(resType))
+      return emitOpError() << "requires !cir.bool type for result";
+    if (!mlir::isa<cir::PointerType>(srcType))
+      return emitOpError() << "requires !cir.ptr type for source";
+    return success();
+  }
+  case cir::CastKind::integral: {
+    if (!mlir::isa<cir::IntType>(resType))
+      return emitOpError() << "requires !cir.int type for result";
+    if (!mlir::isa<cir::IntType>(srcType))
+      return emitOpError() << "requires !cir.int type for source";
+    return success();
+  }
+  case cir::CastKind::bitcast: {
+    // Handle the pointer types first.
+    auto srcPtrTy = mlir::dyn_cast<cir::PointerType>(srcType);
+    auto resPtrTy = mlir::dyn_cast<cir::PointerType>(resType);
+
+    if (srcPtrTy && resPtrTy) {
+      return success();
+    }
+
+    return success();
+  }
+  case cir::CastKind::floating: {
+    if (!mlir::isa<cir::CIRFPTypeInterface>(srcType) ||
+        !mlir::isa<cir::CIRFPTypeInterface>(resType))
+      return emitOpError() << "requires !cir.float type for source and result";
+    return success();
+  }
+  case cir::CastKind::float_to_int: {
+    if (!mlir::isa<cir::CIRFPTypeInterface>(srcType))
+      return emitOpError() << "requires !cir.float type for source";
+    if (!mlir::dyn_cast<cir::IntType>(resType))
+      return emitOpError() << "requires !cir.int type for result";
+    return success();
+  }
+  case cir::CastKind::int_to_ptr: {
+    if (!mlir::dyn_cast<cir::IntType>(srcType))
+      return emitOpError() << "requires !cir.int type for source";
+    if (!mlir::dyn_cast<cir::PointerType>(resType))
+      return emitOpError() << "requires !cir.ptr type for result";
+    return success();
+  }
+  case cir::CastKind::ptr_to_int: {
+    if (!mlir::dyn_cast<cir::PointerType>(srcType))
+      return emitOpError() << "requires !cir.ptr type for source";
+    if (!mlir::dyn_cast<cir::IntType>(resType))
+      return emitOpError() << "requires !cir.int type for result";
+    return success();
+  }
+  case cir::CastKind::float_to_bool: {
+    if (!mlir::isa<cir::CIRFPTypeInterface>(srcType))
+      return emitOpError() << "requires !cir.float type for source";
+    if (!mlir::isa<cir::BoolType>(resType))
+      return emitOpError() << "requires !cir.bool type for result";
+    return success();
+  }
+  case cir::CastKind::bool_to_int: {
+    if (!mlir::isa<cir::BoolType>(srcType))
+      return emitOpError() << "requires !cir.bool type for source";
+    if (!mlir::isa<cir::IntType>(resType))
+      return emitOpError() << "requires !cir.int type for result";
+    return success();
+  }
+  case cir::CastKind::int_to_float: {
+    if (!mlir::isa<cir::IntType>(srcType))
+      return emitOpError() << "requires !cir.int type for source";
+    if (!mlir::isa<cir::CIRFPTypeInterface>(resType))
+      return emitOpError() << "requires !cir.float type for result";
+    return success();
+  }
+  case cir::CastKind::bool_to_float: {
+    if (!mlir::isa<cir::BoolType>(srcType))
+      return emitOpError() << "requires !cir.bool type for source";
+    if (!mlir::isa<cir::CIRFPTypeInterface>(resType))
+      return emitOpError() << "requires !cir.float type for result";
+    return success();
+  }
+  case cir::CastKind::address_space: {
+    auto srcPtrTy = mlir::dyn_cast<cir::PointerType>(srcType);
+    auto resPtrTy = mlir::dyn_cast<cir::PointerType>(resType);
+    if (!srcPtrTy || !resPtrTy)
+      return emitOpError() << "requires !cir.ptr type for source and result";
+    if (srcPtrTy.getPointee() != resPtrTy.getPointee())
+      return emitOpError() << "requires two types differ in addrspace only";
+    return success();
+  }
+  }
+
+  llvm_unreachable("Unknown CastOp kind?");
+}
+
+static bool isIntOrBoolCast(cir::CastOp op) {
+  auto kind = op.getKind();
+  return kind == cir::CastKind::bool_to_int ||
+         kind == cir::CastKind::int_to_bool || kind == cir::CastKind::integral;
+}
+
+static Value tryFoldCastChain(cir::CastOp op) {
+  cir::CastOp head = op, tail = op;
+
+  while (op) {
+    if (!isIntOrBoolCast(op))
+      break;
+    head = op;
+    op = dyn_cast_or_null<cir::CastOp>(head.getSrc().getDefiningOp());
+  }
+
+  if (head == tail)
+    return {};
+
+  // if bool_to_int -> ...  -> int_to_bool: take the bool
+  // as we had it was before all casts
+  if (head.getKind() == cir::CastKind::bool_to_int &&
+      tail.getKind() == cir::CastKind::int_to_bool)
+    return head.getSrc();
+
+  // if int_to_bool -> ...  -> int_to_bool: take the result
+  // of the first one, as no other casts (and ext casts as well)
+  // don't change the first result
+  if (head.getKind() == cir::CastKind::int_to_bool &&
+      tail.getKind() == cir::CastKind::int_to_bool)
+    return head.getResult();
+
+  return {};
+}
+
+OpFoldResult cir::CastOp::fold(FoldAdaptor adaptor) {
+  if (getSrc().getType() == getResult().getType()) {
+    switch (getKind()) {
+    case cir::CastKind::integral: {
+      // TODO: for sign differences, it's possible in certain conditions to
+      // create a new attribute that's capable of representing the source.
+      llvm::SmallVector<mlir::OpFoldResult, 1> foldResults;
+      auto foldOrder = getSrc().getDefiningOp()->fold(foldResults);
+      if (foldOrder.succeeded() && mlir::isa<mlir::Attribute>(foldResults[0]))
+        return mlir::cast<mlir::Attribute>(foldResults[0]);
+      return {};
+    }
+    case cir::CastKind::bitcast:
+    case cir::CastKind::address_space:
+    case cir::CastKind::float_complex:
+    case cir::CastKind::int_complex: {
+      return getSrc();
+    }
+    default:
+      return {};
+    }
+  }
+  return tryFoldCastChain(*this);
+}
+
 //===----------------------------------------------------------------------===//
 // ReturnOp
 //===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp b/clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp
index 5e44837979af3..9cd5c54e6c19e 100644
--- a/clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp
@@ -15,6 +15,15 @@
 
 using namespace mlir;
 
+/// Conditions the deletion of the operation to the removal of all its uses.
+static bool forwardToUsers(Operation *op,
+                           SmallVectorImpl<OpOperand *> &newBlockingUses) {
+  for (Value result : op->getResults())
+    for (OpOperand &use : result.getUses())
+      newBlockingUses.push_back(&use);
+  return true;
+}
+
 //===----------------------------------------------------------------------===//
 // Interfaces for AllocaOp
 //===----------------------------------------------------------------------===//
@@ -108,3 +117,21 @@ DeletionKind cir::StoreOp::removeBlockingUses(
     const DataLayout &dataLayout) {
   return DeletionKind::Delete;
 }
+
+//===----------------------------------------------------------------------===//
+// Interfaces for CastOp
+//===----------------------------------------------------------------------===//
+
+bool cir::CastOp::canUsesBeRemoved(
+    const SmallPtrSetImpl<OpOperand *> &blockingUses,
+    SmallVectorImpl<OpOperand *> &newBlockingUses,
+    const DataLayout &dataLayout) {
+  if (getKind() == cir::CastKind::bitcast)
+    return forwardToUsers(*this, newBlockingUses);
+  return false;
+}
+
+DeletionKind cir::CastOp::removeBlockingUses(
+    const SmallPtrSetImpl<OpOperand *> &blockingUses, OpBuilder &builder) {
+  return DeletionKind::Delete;
+}
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 3200527bd03af..9a02a023d731f 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -38,6 +38,21 @@ using namespace llvm;
 namespace cir {
 namespace direct {
 
+//===----------------------------------------------------------------------===//
+// Helper Methods
+//===----------------------------------------------------------------------===//
+
+namespace {
+/// If the given type is a vector type, return the vector's element type.
+/// Otherwise return the given type unchanged.
+// TODO(cir): Return the vector element type once we have support for vectors
+// instead of the identity type.
+mlir::Type elementTypeIfVector(mlir::Type type) {
+  assert(!cir::MissingFeatures::vectorType());
+  return type;
+}
+} // namespace
+
 /// Given a type convertor and a data layout, convert the given type to a type
 /// that is suitable for memory operations. For example, this can be used to
 /// lower cir.bool accesses to i8.
@@ -137,6 +152,24 @@ mlir::LLVM::Linkage convertLinkage(cir::GlobalLinkageKind linkage) {
   };
 }
 
+static mlir::Value getLLVMIntCast(mlir::ConversionPatternRewriter &rewriter,
+                                  mlir::Value llvmSrc, mlir::Type llvmDstIntTy,
+                                  bool isUnsigned, uint64_t cirSrcWidth,
+                                  uint64_t cirDstIntWidth) {
+  if (cirSrcWidth == cirDstIntWidth)
+    return llvmSrc;
+
+  auto loc = llvmSrc.getLoc();
+  if (cirSrcWidth < cirDstIntWidth) {
+    if (isUnsigned)
+      return rewriter.create<mlir::LLVM::ZExtOp>(loc, llvmDstIntTy, llvmSrc);
+    return rewriter.create<mlir::LLVM::SExtOp>(loc, llvmDstIntTy, llvmSrc);
+  }
+
+  // Otherwise truncate
+  return rewriter.create<mlir::LLVM::TruncOp>(loc, llvmDstIntTy, llvmSrc);
+}
+
 class CIRAttrToValue {
 public:
   CIRAttrToValue(mlir::Operation *parentOp,
@@ -242,6 +275,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<cir::PointerType>(castOp.getType());
+    auto sourceValue = adaptor.getOperands().front();
+    auto targetType = convertTy(ptrTy);
+    auto elementTy = convertTypeForMemory(*getTypeConverter(), dataLayout,
+                                          ptrTy.getPointee());
+    auto offset = llvm::SmallVector<mlir::LLVM::GEPArg>{0};
+    rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(
+        castOp, targetType, elementTy, sourceValue, offset);
+    break;
+  }
+  case cir::CastKind::int_to_bool:
+    assert(!cir::MissingFeatures::opCmp());
+    break;
+  case cir::CastKind::integral: {
+    auto srcType = castOp.getSrc().getType();
+    auto dstType = castOp.getResult().getType();
+    auto llvmSrcVal = adaptor.getOperands().front();
+    auto llvmDstType = getTypeConverter()->convertType(dstType);
+    cir::IntType srcIntType =
+        mlir::cast<cir::IntType>(elementTypeIfVector(srcType));
+    cir::IntType dstIntType =
+        mlir::cast<cir::IntType>(elementTypeIfVector(dstType));
+    rewriter.replaceOp(castOp, getLLVMIntCast(rewriter, llvmSrcVal, llvmDstType,
+                                              srcIntType.isUnsigned(),
+                                              srcIntType.getWidth(),
+                                              dstIntType.getWidth()));
+    break;
+  }
+  case cir::CastKind::floating: {
+    auto llvmSrcVal = adaptor.getOperands().front();
+    auto llvmDstTy =
+        getTypeConverter()->convertType(castOp.getResult().getType());
+
+    auto srcTy = elementTypeIfVector(castOp.getSrc().getType());
+    auto dstTy = elementTypeIfVector(castOp.getResult().getType());
+
+    if (!mlir::isa<cir::CIRFPTypeInterface>(dstTy) ||
+        !mlir::isa<cir::CIRFPTypeInterface>(srcTy))
+      return castOp.emitError() << "NYI cast from " << srcTy << " to " << dstTy;
+
+    auto getFloatWidth = [](mlir::Type ty) -> unsigned {
+      return mlir::cast<cir::CIRFPTypeInterface>(ty).getWidth();
+    };
+
+    if (getFloatWidth(srcTy) > getFloatWidth(dstTy))
+      rewriter.replaceOpWithNewOp<mlir::LLVM::FPTruncOp>(castOp, llvmDstTy,
+                                                         llvmSrcVal);
+    else
+      rewriter.replaceOpWithNewOp<mlir::LLVM::FPExtOp>(castOp, llvmDstTy,
+                                                       llvmSrcVal);
+    return mlir::success();
+  }
+  case cir::CastKind::int_to_ptr: {
+    auto dstTy = mlir::cast<cir::PointerType>(castOp.getType());
+    auto llvmSrcVal = adaptor.getOperands().front();
+    auto llvmDstTy = getTypeConverter()->convertType(dstTy);
+    rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(castOp, llvmDstTy,
+                                                        llvmSrcVal);
+    return mlir::success();
+  }
+  case cir::CastKind::ptr_to_int: {
+    auto dstTy = mlir::cast<cir::IntType>(castOp.getType());
+    auto llvmSrcVal = adaptor.getOperands().front();
+    auto llvmDstTy = getTypeConverter()->convertType(dstTy);
+    rewriter.replaceOpWithNewOp<mlir::LLVM::PtrToIntOp>(castOp, llvmDstTy,
+                                                        llvmSrcVal);
+    return mlir::success();
+  }
+  case cir::CastKind::float_to_bool: {
+    auto llvmSrcVal = adaptor.getOperands().front();
+    auto kind = mlir::LLVM::FCmpPredicate::une;
+
+    // Check if float is not equal to zero.
+    auto zeroFloat = rewriter.create<mlir::LLVM::ConstantOp>(
+        castOp.getLoc(), llvmSrcVal.getType(),
+        mlir::FloatAttr::get(llvmSrcVal.getType(), 0.0));
+
+    // Extend comparison result to either bool (C++) or int (C).
+    rewriter.replaceOpWithNewOp<mlir::LLVM::FCmpOp>(castOp, kind, llvmSrcVal,
+                                                    zeroFloat);
+
+    return mlir::success();
+  }
+  case cir::CastKind::bool_to_int: {
+    auto dstTy = mlir::cast<cir::IntType>(castOp.getType());
+    auto llvmSrcVal = adaptor.getOperands().front();
+    auto llvmSrcTy = mlir::cast<mlir::IntegerType>(llvmSrcVal.getType());
+    auto llvmDstTy =
+        mlir::cast<mlir::IntegerType>(getTypeConverter()->convertType(dstTy));
+    if (llvmSrcTy.getWidth() == llvmDstTy.getWidth())
+      rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(castOp, llvmDstTy,
+                                                         llvmSrcVal);
+    else
+      rewriter.replaceOpWithNewOp<mlir::LLVM::ZExtOp>(castOp, llvmDstTy,
+                                                      llvmSrcVal);
+    return mlir::success();
+  }
+  case cir::CastKind::bool_to_float: {
+    auto dstTy = castOp.getType();
+    auto llvmSrcVal = adaptor.getOperands().front();
+    auto llvmDstTy = getTypeConverter()->convertType(dstTy);
+    rewriter.replaceOpWithNewOp<mlir::LLVM::UIToFPOp>(castOp, llvmDstTy,
+                                                      llvmSrcVal);
+    return mlir::success();
+  }
+  case cir::CastKind::int_to_float: {
+    auto dstTy = castOp.getType();
+    auto llvmSrcVal = adaptor.getOperands().front();
+    auto llvmDstTy = getTypeConverter()->convertType(dstTy);
+    if (mlir::cast<cir::IntType>(elementTypeIfVector(castOp.getSrc().getType()))
+            .isSigned())
+      rewriter.replaceOpWithNewOp<mlir::LLVM::SIToFPOp>(castOp, llvmDstTy,
+                                                        llvmSrcVal);
+    else
+      rewriter.replaceOpWithNewOp<mlir::LLVM::UIToFPOp>(castOp, llvmDstTy,
+                                                        llvmSrcVal);
+    return mlir::success();
+  }
+  case cir::CastKind::float_to_int: {
+    auto dstTy = castOp.getType();
+    auto llvmSrcVal = adaptor.getOperands().front();
+    auto llvmDstTy = getTypeConverter()->convertType(dstTy);
+    if (mlir::cast<cir::IntType>(
+            elementTypeIfVector(castOp.getResult().getType()))
+            .isSigned())
+      rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(castOp, llvmDstTy,
+                                                        llvmSrcVal);
+    else
+      rewriter.replaceOpWithNewOp<mlir::LLVM::FPToUIOp>(castOp, llvmDstTy,
+                                                        llvmSrcVal);
+    return mlir::success();
+  }
+  case cir::CastKind::bitcast:
+    assert(!MissingFeatures::cxxABI());
+    assert(!MissingFeatures::dataMemberType());
+    break;
+  case cir::CastKind::ptr_to_bool:
+    assert(!cir::MissingFeatures::opCmp());
+    break;
+  case cir::CastKind::address_space: {
+    auto dstTy = castOp.getType();
+    auto llvmSrcVal = adaptor.getOperands().front();
+    auto llvmDstTy = getTypeConverter()->convertType(dstTy);
+    rewriter.replaceOpWithNewOp<mlir::LLVM::AddrSpaceCastOp>(castOp, llvmDstTy,
+                                                             llvmSrcVal);
+    break;
+  }
+  case cir::CastKind::member_ptr_to_bool:
+    assert(!MissingFeatures::cxxABI());
+    assert(!MissingFeatures::methodType());
+    break;
+  default: {
+    return castOp.emitError("Unhandled cast kind: ")
+           << castOp.getKindAttrName();
+  }
+  }
+
+  return mlir::success();
+}
+
 mlir::LogicalResult CIRToLLVMAllocaOpLowering::matchAndRewrite(
     cir::AllocaOp op, OpAdaptor adaptor,
     mlir::ConversionPatternRewriter &rewriter) const {
@@ -628,6 +834,7 @@ void ConvertCIRToLLVMPass::runOnOperation() {
   patterns.add<CIRToLLVMLoadOpLowering>(converter, patterns.getContext(), dl);
   patterns.add<CIRToLLVMStoreOpLowering>(converter, patterns.getContext(), dl);
   patterns.add<CIRToLLVMGlobalOpLowering>(converter, patterns.getContext(), dl);
+  patterns.add<CIRToLLVMCastOpLowering>(converter, patterns.getContext(), dl);
   patterns.add<CIRToLLVMConstantOpLowering>(converter, patterns.getContext(),
                                             dl);
   patterns.add<CIRToLLVMFuncOpLowering>(converter, patterns.getContext());
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
index a694047e3616b..f7b4ef409874a 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -22,6 +22,22 @@ namespace direct {
 
 mlir::LLVM::Linkage convertLinkage(cir::GlobalLinkageKind linkage);
 
+class CIRToLLVMCastOpLowering : public mlir::OpConversionPattern<cir::CastOp> {
+  mlir::DataLayout const &dataLayout;
+
+  mlir::Type convertTy(mlir::Type ty) const;
+
+public:
+  CIRToLLVMCastOpLowering(const mlir::TypeConverter &typeConverter,
+                          mlir::MLIRContext *context,
+                          mlir::DataLayout const &dataLayout)
+      : OpConversionPattern(typeConverter, context), dataLayout(dataLayout) {}
+
+  mlir::LogicalResult
+  matchAndRewrite(cir::CastOp op, OpAdaptor,
+                  mlir::ConversionPatternRewriter &) const override;
+};
+
 class CIRToLLVMReturnOpLowering
     : public mlir::OpConversionPattern<cir::ReturnOp> {
 public:
diff --git a/clang/test/CIR/CodeGen/cast.cpp b/clang/test/CIR/CodeGen/cast.cpp
new file mode 100644
index 0000000000000..29d34e87c398d
--- /dev/null
+++ b/clang/test/CIR/CodeGen/cast.cpp
@@ -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<unsigned char>(x);
+}
+
+// CHECK: cir.func @cxxstaticcast_0
+// CHECK:    %0 = cir.alloca !cir.int<u, 32>, !cir.ptr<!cir.int<u, 32>>, ["x", init] {alignment = 4 : i64}
+// CHECK:    cir.store %arg0, %0 : !cir.int<u, 32>, !cir.ptr<!cir.int<u, 32>>
+// CHECK:    %1 = cir.load %0 : !cir.ptr<!cir.int<u, 32>>, !cir.int<u, 32>
+// CHECK:    %2 = cir.cast(integral, %1 : !cir.int<u, 32>), !cir.int<u, 8>
+// CHECK:    cir.return %2 : !cir.int<u, 8>
+// 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<u, 32>), !cir.int<s, 8>
+
+  short b = (short)x2; // truncate with sign
+  // CHECK: %{{[0-9]+}} = cir.cast(integral, %{{[0-9]+}} : !cir.int<s, 32>), !cir.int<s, 16>
+
+  long long c = (long long)x1; // zero extend
+  // CHECK: %{{[0-9]+}} = cir.cast(integral, %{{[0-9]+}} : !cir.int<u, 32>), !cir.int<s, 64>
+
+  long long d = (long long)x2; // sign extend
+  // CHECK: %{{[0-9]+}} = cir.cast(integral, %{{[0-9]+}} : !cir.int<s, 32>), !cir.int<s, 64>
+
+  unsigned ui = (unsigned)x2; // sign drop
+  // CHECK: %{{[0-9]+}} = cir.cast(integral, %{{[0-9]+}} : !cir.int<s, 32>), !cir.int<u, 32>
+
+  int si = (int)x1; // sign add
+  // CHECK: %{{[0-9]+}} = cir.cast(integral, %{{[0-9]+}} : !cir.int<u, 32>), !cir.int<s, 32>
+
+  unsigned uu = (unsigned)x1; // should not be generated
+  // CHECK-NOT: %{{[0-9]+}} = cir.cast(integral, %{{[0-9]+}} : !cir.int<u, 32>), !cir.int<u, 32>
+
+  bool ib = (bool)x1; // No checking, because this isn't a regular cast.
+
+  int bi = (int)ib; // bool to int
+  // CHECK: %{{[0-9]+}} = cir.cast(bool_to_int, %{{[0-9]+}} : !cir.bool), !cir.int<s, 32>
+
+  return 0;
+}
+
+bool cptr(void *d) {
+  bool x = d;
+  return x;
+}
+
+// CHECK: cir.func @cptr(%arg0: !cir.ptr<!cir.void>
+// CHECK:   %0 = cir.alloca !cir.ptr<!cir.void>, !cir.ptr<!cir.ptr<!cir.void>>, ["d", init] {alignment = 8 : i64}
+
+// CHECK:   %2 = cir.load %0 : !cir.ptr<!cir.ptr<!cir.void>>, !cir.ptr<!cir.void>
+// CHECK:   %3 = cir.cast(ptr_to_bool, %2 : !cir.ptr<!cir.void>), !cir.bool
diff --git a/clang/test/CIR/IR/cast.cir b/clang/test/CIR/IR/cast.cir
new file mode 100644
index 0000000000000..de3cc37467eff
--- /dev/null
+++ b/clang/test/CIR/IR/cast.cir
@@ -0,0 +1,23 @@
+// RUN: cir-opt %s | cir-opt | FileCheck %s
+!s32i = !cir.int<s, 32>
+
+module  {
+  cir.func @yolo(%arg0 : !s32i) {
+    %a = cir.cast (int_to_bool, %arg0 : !s32i), !cir.bool
+
+    %0 = cir.const #cir.int<0> : !s32i
+    cir.return
+  }
+
+  cir.func @bitcast(%p: !cir.ptr<!s32i>) {
+    %0 = cir.cast(bitcast, %p : !cir.ptr<!s32i>), !cir.ptr<f32>
+    cir.return
+  }
+}
+
+// CHECK: cir.func @yolo(%arg0: !cir.int<s, 32>)
+// CHECK: %0 = cir.cast(int_to_bool, %arg0 : !cir.int<s, 32>), !cir.bool
+// CHECK: %1 = cir.const #cir.int<0> : !cir.int<s, 32>
+
+// CHECK: cir.func @bitcast
+// CHECK: %0 = cir.cast(bitcast, %arg0 : !cir.ptr<!cir.int<s, 32>>), !cir.ptr<f32>
diff --git a/clang/test/CIR/Lowering/cast.cir b/clang/test/CIR/Lowering/cast.cir
new file mode 100644
index 0000000000000..b2d0a6d42eeb1
--- /dev/null
+++ b/clang/test/CIR/Lowering/cast.cir
@@ -0,0 +1,92 @@
+// RUN: cir-opt %s -cir-to-llvm -o %t.cir
+// RUN: FileCheck %s --input-file=%t.cir
+
+!s16i = !cir.int<s, 16>
+!s32i = !cir.int<s, 32>
+!s64i = !cir.int<s, 64>
+!s8i = !cir.int<s, 8>
+!u32i = !cir.int<u, 32>
+!u8i = !cir.int<u, 8>
+!u64i = !cir.int<u, 64>
+
+module {
+  cir.func @cStyleCasts(%arg0: !u32i, %arg1: !s32i, %arg2: !cir.float, %arg3: !cir.double) -> !s32i {
+  // CHECK: llvm.func @cStyleCasts
+    %0 = cir.alloca !u32i, !cir.ptr<!u32i>, ["x1", init] {alignment = 4 : i64}
+    %1 = cir.alloca !s32i, !cir.ptr<!s32i>, ["x2", init] {alignment = 4 : i64}
+    %20 = cir.alloca !s16i, !cir.ptr<!s16i>, ["x4", init] {alignment = 2 : i64}
+    %2 = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"] {alignment = 4 : i64}
+    %3 = cir.alloca !s8i, !cir.ptr<!s8i>, ["a", init] {alignment = 1 : i64}
+    %4 = cir.alloca !s16i, !cir.ptr<!s16i>, ["b", init] {alignment = 2 : i64}
+    %5 = cir.alloca !s64i, !cir.ptr<!s64i>, ["c", init] {alignment = 8 : i64}
+    %6 = cir.alloca !s64i, !cir.ptr<!s64i>, ["d", init] {alignment = 8 : i64}
+    %8 = cir.alloca !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>, ["e", init] {alignment = 8 : i64}
+    cir.store %arg0, %0 : !u32i, !cir.ptr<!u32i>
+    cir.store %arg1, %1 : !s32i, !cir.ptr<!s32i>
+
+    // Integer casts.
+    %9 = cir.load %0 : !cir.ptr<!u32i>, !u32i
+    %10 = cir.cast(integral, %9 : !u32i), !s8i
+    // CHECK: %{{[0-9]+}} = llvm.trunc %{{[0-9]+}} : i32 to i8
+    cir.store %10, %3 : !s8i, !cir.ptr<!s8i>
+    %11 = cir.load %1 : !cir.ptr<!s32i>, !s32i
+    %12 = cir.cast(integral, %11 : !s32i), !s16i
+    // CHECK: %{{[0-9]+}} = llvm.trunc %{{[0-9]+}} : i32 to i16
+    cir.store %12, %4 : !s16i, !cir.ptr<!s16i>
+    %13 = cir.load %0 : !cir.ptr<!u32i>, !u32i
+    %14 = cir.cast(integral, %13 : !u32i), !s64i
+    // CHECK: %{{[0-9]+}} = llvm.zext %{{[0-9]+}} : i32 to i64
+    cir.store %14, %5 : !s64i, !cir.ptr<!s64i>
+    %15 = cir.load %1 : !cir.ptr<!s32i>, !s32i
+    %16 = cir.cast(integral, %15 : !s32i), !s64i
+    // CHECK: %{{[0-9]+}} = llvm.sext %{{[0-9]+}} : i32 to i64
+    %30 = cir.cast(integral, %arg1 : !s32i), !u32i
+    // Should not produce a cast.
+    %32 = cir.cast(integral, %arg0 : !u32i), !s32i
+    // Should not produce a cast.
+    %21 = cir.load %20 : !cir.ptr<!s16i>, !s16i
+    %22 = cir.cast(integral, %21 : !s16i), !u64i
+    // CHECK: %[[TMP:[0-9]+]] = llvm.sext %{{[0-9]+}} : i16 to i64
+
+    // Pointer casts.
+    cir.store %16, %6 : !s64i, !cir.ptr<!s64i>
+    %23 = cir.cast(int_to_ptr, %22 : !u64i), !cir.ptr<!u8i>
+    // CHECK: %[[TMP2:[0-9]+]] = llvm.inttoptr %[[TMP]] : i64 to !llvm.ptr
+    %24 = cir.cast(ptr_to_int, %23 : !cir.ptr<!u8i>), !s32i
+    // CHECK: %{{[0-9]+}} = llvm.ptrtoint %[[TMP2]] : !llvm.ptr to i32
+    %29 = cir.cast(ptr_to_bool, %23 : !cir.ptr<!u8i>), !cir.bool
+
+    // Floating point casts.
+    %25 = cir.cast(int_to_float, %arg1 : !s32i), !cir.float
+    // CHECK: %{{.+}} = llvm.sitofp %{{.+}} : i32 to f32
+    %26 = cir.cast(int_to_float, %arg0 : !u32i), !cir.float
+    // CHECK: %{{.+}} = llvm.uitofp %{{.+}} : i32 to f32
+    %27 = cir.cast(float_to_int, %arg2 : !cir.float), !s32i
+    // CHECK: %{{.+}} = llvm.fptosi %{{.+}} : f32 to i32
+    %28 = cir.cast(float_to_int, %arg2 : !cir.float), !u32i
+    // CHECK: %{{.+}} = llvm.fptoui %{{.+}} : f32 to i32
+    %18 = cir.const #cir.int<0> : !s32i
+    // CHECK: %{{.+}} = llvm.fptrunc %{{.+}} : f64 to f32
+    %34 = cir.cast(floating, %arg3 : !cir.double), !cir.float
+
+    cir.store %18, %2 : !s32i, !cir.ptr<!s32i>
+    %19 = cir.load %2 : !cir.ptr<!s32i>, !s32i
+    cir.return %19 : !s32i
+  }
+
+  cir.func @testBoolToIntCast(%arg0: !cir.bool)  {
+  // CHECK: llvm.func @testBoolToIntCast
+    %0 = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["bl", init] {alignment = 1 : i64}
+    %1 = cir.alloca !u8i, !cir.ptr<!u8i>, ["y", init] {alignment = 1 : i64}
+    cir.store %arg0, %0 : !cir.bool, !cir.ptr<!cir.bool>
+
+    %2 = cir.load %0 : !cir.ptr<!cir.bool>, !cir.bool
+    %3 = cir.cast(bool_to_int, %2 : !cir.bool), !u8i
+    // CHECK: %[[LOAD_BOOL:.*]] = llvm.load %{{.*}} : !llvm.ptr -> i8
+    // CHECK: %[[TRUNC:.*]] = llvm.trunc %[[LOAD_BOOL]] : i8 to i1
+    // CHECK: %[[EXT:.*]] = llvm.zext %[[TRUNC]] : i1 to i8
+
+    cir.store %3, %1 : !u8i, !cir.ptr<!u8i>
+    cir.return
+  }
+}

>From 91ae8f963035a28cdc429ae7079e929e886704bd Mon Sep 17 00:00:00 2001
From: Morris Hafner <mmha at users.noreply.github.com>
Date: Tue, 11 Mar 2025 11:12:54 -0700
Subject: [PATCH 2/2] Update clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

Co-authored-by: Erich Keane <ekeane at nvidia.com>
---
 clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index df9447841800a..3d5862949b51f 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -141,7 +141,7 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
     if (srcType->isRealFloatingType())
       return emitFloatToBoolConversion(src, loc);
 
-    if ([[maybe_unused]] auto *mpt = llvm::dyn_cast<MemberPointerType>(srcType))
+    if (llvm::isa<MemberPointerType>(srcType))
       cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion");
 
     if (srcType->isIntegerType())



More information about the cfe-commits mailing list