[clang] [CIR] Upstream CastOp and scalar conversions (PR #130690)
Erich Keane via cfe-commits
cfe-commits at lists.llvm.org
Wed Mar 12 08:11:19 PDT 2025
================
@@ -84,26 +96,266 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
}
mlir::Value VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *e) {
- mlir::Type type = cgf.convertType(e->getType());
+ mlir::Type type = convertType(e->getType());
return builder.create<cir::ConstantOp>(
cgf.getLoc(e->getExprLoc()), type,
builder.getCIRBoolAttr(e->getValue()));
}
- mlir::Value VisitCastExpr(CastExpr *E);
+ mlir::Value VisitCastExpr(CastExpr *e);
+
+ mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) {
+ return VisitCastExpr(e);
+ }
+
+ /// Perform a pointer to boolean conversion.
+ mlir::Value emitPointerToBoolConversion(mlir::Value v, QualType qt) {
+ // TODO(cir): comparing the ptr to null is done when lowering CIR to LLVM.
+ // We might want to have a separate pass for these types of conversions.
+ return cgf.getBuilder().createPtrToBoolCast(v);
+ }
+
+ mlir::Value emitFloatToBoolConversion(mlir::Value src, mlir::Location loc) {
+ auto boolTy = builder.getBoolTy();
+ return builder.create<cir::CastOp>(loc, boolTy,
+ cir::CastKind::float_to_bool, src);
+ }
+
+ mlir::Value emitIntToBoolConversion(mlir::Value srcVal, mlir::Location loc) {
+ // Because of the type rules of C, we often end up computing a
+ // logical value, then zero extending it to int, then wanting it
+ // as a logical value again.
+ // TODO: optimize this common case here or leave it for later
+ // CIR passes?
+ mlir::Type boolTy = convertType(cgf.getContext().BoolTy);
+ return builder.create<cir::CastOp>(loc, boolTy, cir::CastKind::int_to_bool,
+ srcVal);
+ }
+
+ /// Convert the specified expression value to a boolean (!cir.bool) truth
+ /// value. This is equivalent to "Val != 0".
+ mlir::Value emitConversionToBool(mlir::Value src, QualType srcType,
+ mlir::Location loc) {
+ assert(srcType.isCanonical() && "EmitScalarConversion strips typedefs");
+
+ if (srcType->isRealFloatingType())
+ return emitFloatToBoolConversion(src, loc);
+
+ if ([[maybe_unused]] auto *mpt = llvm::dyn_cast<MemberPointerType>(srcType))
+ cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion");
+
+ if (srcType->isIntegerType())
+ return emitIntToBoolConversion(src, loc);
+
+ assert(::mlir::isa<cir::PointerType>(src.getType()));
+ return emitPointerToBoolConversion(src, srcType);
+ }
+
+ // Emit a conversion from the specified type to the specified destination
+ // type, both of which are CIR scalar types.
+ struct ScalarConversionOpts {
+ bool treatBooleanAsSigned;
+ bool emitImplicitIntegerTruncationChecks;
+ bool emitImplicitIntegerSignChangeChecks;
+
+ ScalarConversionOpts()
+ : treatBooleanAsSigned(false),
+ emitImplicitIntegerTruncationChecks(false),
+ emitImplicitIntegerSignChangeChecks(false) {}
+
+ ScalarConversionOpts(clang::SanitizerSet sanOpts)
+ : treatBooleanAsSigned(false),
+ emitImplicitIntegerTruncationChecks(
+ sanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation)),
+ emitImplicitIntegerSignChangeChecks(
+ sanOpts.has(SanitizerKind::ImplicitIntegerSignChange)) {}
+ };
+
+ // Conversion from bool, integral, or floating-point to integral or
+ // floating-point. Conversions involving other types are handled elsewhere.
+ // Conversion to bool is handled elsewhere because that's a comparison against
+ // zero, not a simple cast. This handles both individual scalars and vectors.
+ mlir::Value emitScalarCast(mlir::Value src, QualType srcType,
+ QualType dstType, mlir::Type srcTy,
+ mlir::Type dstTy, ScalarConversionOpts opts) {
+ assert(!srcType->isMatrixType() && !dstType->isMatrixType() &&
+ "Internal error: matrix types not handled by this function.");
+ if (mlir::isa<mlir::IntegerType>(srcTy) ||
+ mlir::isa<mlir::IntegerType>(dstTy))
+ llvm_unreachable("Obsolete code. Don't use mlir::IntegerType with CIR.");
+
+ mlir::Type fullDstTy = dstTy;
+ assert(!cir::MissingFeatures::vectorType());
+
+ std::optional<cir::CastKind> castKind;
+
+ if (mlir::isa<cir::BoolType>(srcTy)) {
+ if (opts.treatBooleanAsSigned)
+ cgf.getCIRGenModule().errorNYI("signed bool");
+ if (cgf.getBuilder().isInt(dstTy)) {
+ castKind = cir::CastKind::bool_to_int;
+ } else if (mlir::isa<cir::CIRFPTypeInterface>(dstTy)) {
+ castKind = cir::CastKind::bool_to_float;
+ } else {
+ llvm_unreachable("Internal error: Cast to unexpected type");
+ }
+ } else if (cgf.getBuilder().isInt(srcTy)) {
+ if (cgf.getBuilder().isInt(dstTy)) {
+ castKind = cir::CastKind::integral;
+ } else if (mlir::isa<cir::CIRFPTypeInterface>(dstTy)) {
+ castKind = cir::CastKind::int_to_float;
+ } else {
+ llvm_unreachable("Internal error: Cast to unexpected type");
+ }
+ } else if (mlir::isa<cir::CIRFPTypeInterface>(srcTy)) {
+ if (cgf.getBuilder().isInt(dstTy)) {
+ // If we can't recognize overflow as undefined behavior, assume that
+ // overflow saturates. This protects against normal optimizations if we
+ // are compiling with non-standard FP semantics.
+ if (!cgf.cgm.getCodeGenOpts().StrictFloatCastOverflow)
+ cgf.getCIRGenModule().errorNYI("strict float cast overflow");
+ assert(!cir::MissingFeatures::fpConstraints());
+ castKind = cir::CastKind::float_to_int;
+ } else if (mlir::isa<cir::CIRFPTypeInterface>(dstTy)) {
+ cgf.getCIRGenModule().errorNYI("floating point casts");
+ } else {
+ llvm_unreachable("Internal error: Cast to unexpected type");
+ }
+ } else {
+ llvm_unreachable("Internal error: Cast from unexpected type");
+ }
+
+ assert(castKind.has_value() && "Internal error: CastKind not set.");
+ return builder.create<cir::CastOp>(src.getLoc(), fullDstTy, *castKind, src);
+ }
/// Emit a conversion from the specified type to the specified destination
/// type, both of which are CIR scalar types.
/// TODO: do we need ScalarConversionOpts here? Should be done in another
/// pass.
- mlir::Value emitScalarConversion(mlir::Value src, QualType srcType,
- QualType dstType, SourceLocation loc) {
- // No sort of type conversion is implemented yet, but the path for implicit
- // paths goes through here even if the type isn't being changed.
+ mlir::Value
+ emitScalarConversion(mlir::Value src, QualType srcType, QualType dstType,
+ SourceLocation loc,
+ ScalarConversionOpts opts = ScalarConversionOpts()) {
+ // All conversions involving fixed point types should be handled by the
+ // emitFixedPoint family functions. This is done to prevent bloating up
+ // this function more, and although fixed point numbers are represented by
+ // integers, we do not want to follow any logic that assumes they should be
+ // treated as integers.
+ // TODO(leonardchan): When necessary, add another if statement checking for
+ // conversions to fixed point types from other types.
+ // conversions to fixed point types from other types.
+ if (srcType->isFixedPointType())
+ cgf.getCIRGenModule().errorNYI(loc, "fixed point conversions");
+ else if (dstType->isFixedPointType())
+ cgf.getCIRGenModule().errorNYI(loc, "fixed point conversions");
+
srcType = srcType.getCanonicalType();
dstType = dstType.getCanonicalType();
- if (srcType == dstType)
+ if (srcType == dstType) {
+ if (opts.emitImplicitIntegerSignChangeChecks)
+ cgf.getCIRGenModule().errorNYI(loc,
+ "implicit integer sign change checks");
return src;
+ }
+
+ if (dstType->isVoidType())
----------------
erichkeane wrote:
The explicit ignoring of a value shows a level of intent that just ignoring the return value does not. Whether this permits some sort of intuition to happen as an optimization opportunity is perhaps beyond my level of creativity, but one that I still wonder if could have some value in CIR. This is more of a `we should think about this` than a `we should change something` here.
https://github.com/llvm/llvm-project/pull/130690
More information about the cfe-commits
mailing list