[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