https://github.com/mmha updated https://github.com/llvm/llvm-project/pull/130690
>From a945e21869e5276c66ac979acd893d9bd9afe2cc Mon Sep 17 00:00:00 2001 From: Morris Hafner <mhaf...@nvidia.com> Date: Mon, 10 Mar 2025 16:18:34 -0700 Subject: [PATCH 1/4] [CIR] Upstream CastOp and scalar conversions This patch upstreams ClangIR's CastOp with the following exceptions: - No Fixed/FP conversions - No casts between value categories - No complex casts - No array_to_ptrdecay - No address_space - No casts involving record types (member pointers, base/derived casts) - No casts specific to ObjC or OpenCL --- .../CIR/Dialect/Builder/CIRBaseBuilder.h | 62 +++ clang/include/clang/CIR/Dialect/IR/CIROps.td | 105 +++++ clang/include/clang/CIR/MissingFeatures.h | 14 +- clang/lib/CIR/CodeGen/CIRGenBuilder.h | 9 + clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 400 +++++++++++++++++- clang/lib/CIR/CodeGen/CIRGenFunction.h | 3 + clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 170 ++++++++ clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp | 27 ++ .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 207 +++++++++ .../CIR/Lowering/DirectToLLVM/LowerToLLVM.h | 16 + clang/test/CIR/CodeGen/cast.cpp | 58 +++ clang/test/CIR/IR/cast.cir | 23 + clang/test/CIR/Lowering/cast.cir | 92 ++++ 13 files changed, 1174 insertions(+), 12 deletions(-) create mode 100644 clang/test/CIR/CodeGen/cast.cpp create mode 100644 clang/test/CIR/IR/cast.cir create mode 100644 clang/test/CIR/Lowering/cast.cir diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index 017ae0c53a984..e5e8132e9f527 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -13,6 +13,7 @@ #include "clang/CIR/Dialect/IR/CIRAttrs.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/Dialect/IR/CIRTypes.h" +#include "llvm/Support/ErrorHandling.h" #include "mlir/IR/Builders.h" #include "mlir/IR/BuiltinTypes.h" @@ -78,6 +79,67 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return create<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 77c43e5ace64a..9797960e00867 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -78,6 +78,111 @@ class LLVMLoweringInfo { class CIR_Op<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 ddfe654009644..8eb90d895d3d6 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -73,16 +73,28 @@ 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 stackSaveOp() { 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 d041791770d82..6e45303a4f287 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 79ec0696eb180..4125525583f33 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -42,6 +42,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. @@ -142,6 +157,24 @@ mlir::LLVM::Linkage convertLinkage(cir::GlobalLinkageKind linkage) { llvm_unreachable("Unknown CIR linkage type"); } +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, @@ -247,6 +280,179 @@ struct ConvertCIRToLLVMPass StringRef getArgument() const override { return "cir-flat-to-llvm"; } }; +mlir::Type CIRToLLVMCastOpLowering::convertTy(mlir::Type ty) const { + return getTypeConverter()->convertType(ty); +} + +mlir::LogicalResult CIRToLLVMCastOpLowering::matchAndRewrite( + cir::CastOp castOp, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + // For arithmetic conversions, LLVM IR uses the same instruction to convert + // both individual scalars and entire vectors. This lowering pass handles + // both situations. + + switch (castOp.getKind()) { + case cir::CastKind::array_to_ptrdecay: { + const auto ptrTy = mlir::cast<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 { @@ -696,6 +902,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< diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h index d090bbe4f2e10..5fc248778e9bc 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 1b86b9efab6395d5477d2c450957d1f59c9aaaa0 Mon Sep 17 00:00:00 2001 From: Morris Hafner <m...@users.noreply.github.com> Date: Tue, 11 Mar 2025 11:12:54 -0700 Subject: [PATCH 2/4] Update clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp Co-authored-by: Erich Keane <eke...@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()) >From c1cf00e0acc76624e492880b58c7609b4afdb06a Mon Sep 17 00:00:00 2001 From: Morris Hafner <mhaf...@nvidia.com> Date: Tue, 11 Mar 2025 17:13:53 -0700 Subject: [PATCH 3/4] Address some review feedback - Remove dead functions - Remove convertType helper function - rename some variables - change __fp16 conversion codegen to emit cast even if intrinsic is requested - Move some CHECK-NOT tests out into a separate function --- .../CIR/Dialect/Builder/CIRBaseBuilder.h | 14 -- clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 128 ++++++++---------- clang/test/CIR/CodeGen/cast.cpp | 18 ++- 3 files changed, 72 insertions(+), 88 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index e5e8132e9f527..f88862d514731 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -126,20 +126,6 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { 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/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 3d5862949b51f..60a1f465b645b 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -32,21 +32,8 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { bool ignoreResultAssign; public: - ScalarExprEmitter(CIRGenFunction &cgf, CIRGenBuilderTy &builder, - 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); } + ScalarExprEmitter(CIRGenFunction &cgf, CIRGenBuilderTy &builder) + : cgf(cgf), builder(builder) {} //===--------------------------------------------------------------------===// // Visitor Methods @@ -80,14 +67,14 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { } mlir::Value VisitIntegerLiteral(const IntegerLiteral *e) { - mlir::Type type = convertType(e->getType()); + mlir::Type type = cgf.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 = convertType(e->getType()); + mlir::Type type = cgf.convertType(e->getType()); assert(mlir::isa<cir::CIRFPTypeInterface>(type) && "expect floating-point type"); return builder.create<cir::ConstantOp>( @@ -96,7 +83,7 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { } mlir::Value VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *e) { - mlir::Type type = convertType(e->getType()); + mlir::Type type = cgf.convertType(e->getType()); return builder.create<cir::ConstantOp>( cgf.getLoc(e->getExprLoc()), type, builder.getCIRBoolAttr(e->getValue())); @@ -127,7 +114,7 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { // 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); + mlir::Type boolTy = cgf.convertType(cgf.getContext().BoolTy); return builder.create<cir::CastOp>(loc, boolTy, cir::CastKind::int_to_bool, srcVal); } @@ -192,21 +179,19 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { if (mlir::isa<cir::BoolType>(srcTy)) { if (opts.treatBooleanAsSigned) cgf.getCIRGenModule().errorNYI("signed bool"); - if (cgf.getBuilder().isInt(dstTy)) { + if (cgf.getBuilder().isInt(dstTy)) castKind = cir::CastKind::bool_to_int; - } else if (mlir::isa<cir::CIRFPTypeInterface>(dstTy)) { + else if (mlir::isa<cir::CIRFPTypeInterface>(dstTy)) castKind = cir::CastKind::bool_to_float; - } else { + else llvm_unreachable("Internal error: Cast to unexpected type"); - } } else if (cgf.getBuilder().isInt(srcTy)) { - if (cgf.getBuilder().isInt(dstTy)) { + if (cgf.getBuilder().isInt(dstTy)) castKind = cir::CastKind::integral; - } else if (mlir::isa<cir::CIRFPTypeInterface>(dstTy)) { + else if (mlir::isa<cir::CIRFPTypeInterface>(dstTy)) castKind = cir::CastKind::int_to_float; - } else { + 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 @@ -262,19 +247,19 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { if (dstType->isVoidType()) return nullptr; - mlir::Type srcTy = src.getType(); + mlir::Type mlirSrcType = 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); + mlir::Type mlirDstType = cgf.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 (mlir::isa<cir::CIRFPTypeInterface>(mlirDstType)) { if (cgf.getContext().getTargetInfo().useFP16ConversionIntrinsics()) cgf.getCIRGenModule().errorNYI(loc, "cast via llvm.convert.from.fp16"); @@ -285,18 +270,19 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { 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); + // FIXME(cir): For now lets pretend we shouldn't use the conversion + // intrinsics and insert a cast here unconditionally. } + src = builder.createCast(cgf.getLoc(loc), cir::CastKind::floating, src, + cgf.FloatTy); srcType = cgf.getContext().FloatTy; - srcTy = cgf.FloatTy; + mlirSrcType = cgf.FloatTy; } } // TODO(cir): LLVM codegen ignore conversions like int -> uint, // is there anything to be done for CIR here? - if (srcTy == dstTy) { + if (mlirSrcType == mlirDstType) { if (opts.emitImplicitIntegerSignChangeChecks) cgf.getCIRGenModule().errorNYI(loc, "implicit integer sign change checks"); @@ -306,14 +292,14 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { // 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)) { + if (auto dstPT = dyn_cast<cir::PointerType>(mlirDstType)) { cgf.getCIRGenModule().errorNYI(loc, "pointer casts"); } - if (isa<cir::PointerType>(srcTy)) { + if (isa<cir::PointerType>(mlirSrcType)) { // Must be an ptr to int cast. - assert(isa<cir::IntType>(dstTy) && "not ptr->int?"); - return builder.createPtrToInt(src, dstTy); + assert(isa<cir::IntType>(mlirDstType) && "not ptr->int?"); + return builder.createPtrToInt(src, mlirDstType); } // A scalar can be splatted to an extended vector of the same element type @@ -335,11 +321,11 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { // Finally, we have the arithmetic types or vectors of arithmetic types. mlir::Value res = nullptr; - mlir::Type resTy = dstTy; + mlir::Type resTy = mlirDstType; - res = emitScalarCast(src, srcType, dstType, srcTy, dstTy, opts); + res = emitScalarCast(src, srcType, dstType, mlirSrcType, mlirDstType, opts); - if (dstTy != resTy) { + if (mlirDstType != resTy) { if (cgf.getContext().getTargetInfo().useFP16ConversionIntrinsics()) { cgf.getCIRGenModule().errorNYI(loc, "cast via llvm.convert.to.fp16"); } else { @@ -385,13 +371,13 @@ mlir::Value CIRGenFunction::emitScalarExpr(const Expr *e) { // casts, as they handle things like function to ptr-to-function decay // etc. mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { - Expr *e = ce->getSubExpr(); + Expr *subExpr = ce->getSubExpr(); QualType destTy = ce->getType(); CastKind kind = ce->getCastKind(); // These cases are generally not written to ignore the result of evaluating // their sub-expressions, so we clear this now. - [[maybe_unused]] bool ignored = TestAndClearIgnoreResultAssign(); + ignoreResultAssign = false; switch (kind) { case clang::CK_Dependent: @@ -403,16 +389,17 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { case CK_BlockPointerToObjCPointerCast: case CK_AnyPointerToBlockPointerCast: case CK_BitCast: { - auto src = Visit(const_cast<Expr *>(e)); - mlir::Type dstTy = convertType(destTy); + mlir::Value src = Visit(const_cast<Expr *>(subExpr)); + mlir::Type dstTy = cgf.convertType(destTy); assert(!cir::MissingFeatures::addressSpace()); if (cgf.sanOpts.has(SanitizerKind::CFIUnrelatedCast)) - cgf.getCIRGenModule().errorNYI(e->getSourceRange(), "sanitizer support"); + cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(), + "sanitizer support"); if (cgf.cgm.getCodeGenOpts().StrictVTablePointers) - cgf.getCIRGenModule().errorNYI(e->getSourceRange(), + cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(), "strict vtable pointers"); // Update heapallocsite metadata when there is an explicit pointer cast. @@ -436,24 +423,24 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { // out a better way of doing these casts. assert(!cir::MissingFeatures::scalableVectors()); - return cgf.getBuilder().createBitcast(cgf.getLoc(e->getSourceRange()), src, - dstTy); + return cgf.getBuilder().createBitcast(cgf.getLoc(subExpr->getSourceRange()), + src, dstTy); } case CK_AtomicToNonAtomic: - cgf.getCIRGenModule().errorNYI(e->getSourceRange(), + cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(), "CastExpr: ", ce->getCastKindName()); break; case CK_NonAtomicToAtomic: case CK_UserDefinedConversion: - return Visit(const_cast<Expr *>(e)); + return Visit(const_cast<Expr *>(subExpr)); case CK_NoOp: { - auto v = Visit(const_cast<Expr *>(e)); + auto v = Visit(const_cast<Expr *>(subExpr)); if (v) { // CK_NoOp can model a pointer qualification conversion, which can remove // an array bound and change the IR type. // FIXME: Once pointee types are removed from IR, remove this. - auto t = convertType(destTy); + mlir::Type t = cgf.convertType(destTy); if (t != v.getType()) cgf.getCIRGenModule().errorNYI("pointer qualification conversion"); } @@ -461,20 +448,21 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { } case CK_NullToPointer: { - if (MustVisitNullValue(e)) + if (MustVisitNullValue(subExpr)) cgf.getCIRGenModule().errorNYI( - e->getSourceRange(), "ignored expression on null to pointer cast"); + subExpr->getSourceRange(), + "ignored expression on null to pointer cast"); // Note that DestTy is used as the MLIR type instead of a custom // nullptr type. - mlir::Type ty = convertType(destTy); - return builder.getNullPtr(ty, cgf.getLoc(e->getExprLoc())); + mlir::Type ty = cgf.convertType(destTy); + return builder.getNullPtr(ty, cgf.getLoc(subExpr->getExprLoc())); } case CK_LValueToRValue: - assert(cgf.getContext().hasSameUnqualifiedType(e->getType(), destTy)); - assert(e->isGLValue() && "lvalue-to-rvalue applied to r-value!"); - return Visit(const_cast<Expr *>(e)); + assert(cgf.getContext().hasSameUnqualifiedType(subExpr->getType(), destTy)); + assert(subExpr->isGLValue() && "lvalue-to-rvalue applied to r-value!"); + return Visit(const_cast<Expr *>(subExpr)); case CK_IntegralCast: { ScalarConversionOpts opts; @@ -482,7 +470,7 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { if (!ice->isPartOfExplicitCast()) opts = ScalarConversionOpts(cgf.sanOpts); } - return emitScalarConversion(Visit(e), e->getType(), destTy, + return emitScalarConversion(Visit(subExpr), subExpr->getType(), destTy, ce->getExprLoc(), opts); } @@ -498,22 +486,24 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { assert(!destTy->isBooleanType() && "bool should use PointerToBool"); if (cgf.cgm.getCodeGenOpts().StrictVTablePointers) llvm_unreachable("NYI"); - return builder.createPtrToInt(Visit(e), convertType(destTy)); + return builder.createPtrToInt(Visit(subExpr), cgf.convertType(destTy)); } case CK_ToVoid: - cgf.getCIRGenModule().errorNYI(e->getSourceRange(), + cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(), "ignored expression on void cast"); return nullptr; case CK_IntegralToBoolean: - return emitIntToBoolConversion(Visit(e), cgf.getLoc(ce->getSourceRange())); + return emitIntToBoolConversion(Visit(subExpr), + cgf.getLoc(ce->getSourceRange())); case CK_PointerToBoolean: - return emitPointerToBoolConversion(Visit(e), e->getType()); + return emitPointerToBoolConversion(Visit(subExpr), subExpr->getType()); case CK_FloatingToBoolean: - return emitFloatToBoolConversion(Visit(e), cgf.getLoc(e->getExprLoc())); + return emitFloatToBoolConversion(Visit(subExpr), + cgf.getLoc(subExpr->getExprLoc())); case CK_MemberPointerToBoolean: { - mlir::Value memPtr = Visit(e); + mlir::Value memPtr = Visit(subExpr); return builder.createCast(cgf.getLoc(ce->getSourceRange()), cir::CastKind::member_ptr_to_bool, memPtr, cgf.convertType(destTy)); @@ -521,7 +511,7 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { return nullptr; default: - cgf.getCIRGenModule().errorNYI(e->getSourceRange(), + cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(), "CastExpr: ", ce->getCastKindName()); } return {}; diff --git a/clang/test/CIR/CodeGen/cast.cpp b/clang/test/CIR/CodeGen/cast.cpp index 29d34e87c398d..d8bf7ca66b6a3 100644 --- a/clang/test/CIR/CodeGen/cast.cpp +++ b/clang/test/CIR/CodeGen/cast.cpp @@ -35,11 +35,7 @@ int cStyleCasts_0(unsigned x1, int x2, float x3, short x4, double x5) { 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. - + bool ib; int bi = (int)ib; // bool to int // CHECK: %{{[0-9]+}} = cir.cast(bool_to_int, %{{[0-9]+}} : !cir.bool), !cir.int<s, 32> @@ -56,3 +52,15 @@ bool cptr(void *d) { // 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 + +void should_not_cast() { + unsigned x1; + + unsigned uu = (unsigned)x1; + bool ib = (bool)x1; + return (void) x1; +} + +// CHECK: cir.func @should_not_cast +// CHECK-NOT: cir.cast +// CHECK: } >From 92bf5f7b8a9850a88a49c7d9ecea00218240ddab Mon Sep 17 00:00:00 2001 From: Morris Hafner <mhaf...@nvidia.com> Date: Wed, 12 Mar 2025 14:26:10 -0700 Subject: [PATCH 4/4] More review feedback changes - CastKind enum now aligns with classic codegen CastKind - Try to emit a value of the expected type after errorNYI()s --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 119 +++++++++++++------ clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 59 ++++++--- clang/test/CIR/CodeGen/cast.cpp | 1 - 3 files changed, 127 insertions(+), 52 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 9797960e00867..fb76ce6b51f1d 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -82,47 +82,92 @@ class CIR_Op<string mnemonic, list<Trait> traits = []> : // 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>; +// CK_Dependent +def CK_BitCast : I32EnumAttrCase<"bitcast", 1>; +// CK_LValueBitCast +// CK_LValueToRValueBitCast +// CK_LValueToRValue +// CK_NoOp +// CK_BaseToDerived +// CK_DerivedToBase +// CK_UncheckedDerivedToBase +// CK_Dynamic +// CK_ToUnion +def CK_ArrayToPointerDecay : I32EnumAttrCase<"array_to_ptrdecay", 11>; +// CK_FunctionToPointerDecay +// CK_NullToPointer +// CK_NullToMemberPointer +// CK_BaseToDerivedMemberPointer +// CK_DerivedToBaseMemberPointer +def CK_MemberPointerToBoolean : I32EnumAttrCase<"member_ptr_to_bool", 17>; +// CK_ReinterpretMemberPointer +// CK_UserDefinedConversion +// CK_ConstructorConversion +def CK_IntegralToPointer : I32EnumAttrCase<"int_to_ptr", 21>; +def CK_PointerToIntegral : I32EnumAttrCase<"ptr_to_int", 22>; +def CK_PointerToBoolean : I32EnumAttrCase<"ptr_to_bool", 23>; +// CK_ToVoid +// CK_MatrixCast +// CK_VectorSplat +def CK_IntegralCast : I32EnumAttrCase<"integral", 27>; +def CK_IntegralToBoolean : I32EnumAttrCase<"int_to_bool", 28>; +def CK_IntegralToFloating : I32EnumAttrCase<"int_to_float", 29>; +// CK_FloatingToFixedPoint +// CK_FixedPointToFloating +// CK_FixedPointCast +// CK_FixedPointToIntegral +// CK_IntegralToFixedPoint +// CK_FixedPointToBoolean +def CK_FloatingToIntegral : I32EnumAttrCase<"float_to_int", 36>; +def CK_FloatingToBoolean : I32EnumAttrCase<"float_to_bool", 37>; +def CK_BooleanToSignedIntegral : I32EnumAttrCase<"bool_to_int", 38>; +def CK_FloatingCast : I32EnumAttrCase<"floating", 39>; +// CK_CPointerToObjCPointerCast +// CK_BlockPointerToObjCPointerCast +// CK_AnyPointerToBlockPointerCast +// CK_ObjCObjectLValueCast +// CK_FloatingRealToComplex +// CK_FloatingComplexToReal +// CK_FloatingComplexToBoolean +def CK_FloatingComplexCast : I32EnumAttrCase<"float_complex", 47>; +// CK_FloatingComplexToIntegralComplex +// CK_IntegralRealToComplex +def CK_IntegralComplexToReal : I32EnumAttrCase<"int_complex_to_real", 50>; +def CK_IntegralComplexToBoolean : I32EnumAttrCase<"int_complex_to_bool", 51>; +def CK_IntegralComplexCast : I32EnumAttrCase<"int_complex", 52>; +def CK_IntegralComplexToFloatingComplex + : I32EnumAttrCase<"int_complex_to_float_complex", 53>; +// CK_ARCProduceObject +// CK_ARCConsumeObject +// CK_ARCReclaimReturnedObject +// CK_ARCExtendBlockObject +// CK_AtomicToNonAtomic +// CK_NonAtomicToAtomic +// CK_CopyAndAutoreleaseBlockObject +// CK_BuiltinFnToFnPtr +// CK_ZeroToOCLOpaqueType +def CK_AddressSpaceConversion : I32EnumAttrCase<"address_space", 63>; +// CK_IntToOCLSampler +// CK_HLSLVectorTruncation +// CK_HLSLArrayRValue +// CK_HLSLElementwiseCast +// CK_HLSLAggregateSplatCast + +// Enums below are specific to CIR and don't have a correspondence to classic +// codegen: +def CK_BooleanToFloat : I32EnumAttrCase<"bool_to_float", 1000>; 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]> { + [CK_BitCast, CK_ArrayToPointerDecay, CK_MemberPointerToBoolean, + CK_IntegralToPointer, CK_PointerToIntegral, CK_PointerToBoolean, + CK_IntegralCast, CK_IntegralToBoolean, CK_IntegralToFloating, + CK_FloatingToIntegral, CK_FloatingToBoolean, CK_BooleanToSignedIntegral, + CK_FloatingCast, CK_FloatingComplexCast, CK_IntegralComplexToReal, + CK_IntegralComplexToBoolean, CK_IntegralComplexCast, + CK_IntegralComplexToFloatingComplex, CK_AddressSpaceConversion, + CK_BooleanToFloat]> { let cppNamespace = "::cir"; } diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 60a1f465b645b..cebf190ffc81f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -128,8 +128,16 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { if (srcType->isRealFloatingType()) return emitFloatToBoolConversion(src, loc); - if (llvm::isa<MemberPointerType>(srcType)) + if (llvm::isa<MemberPointerType>(srcType)) { cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion"); + auto boolType = cgf.getContext().getBOOLType(); + auto cirBoolType = cgf.convertType(boolType); + CharUnits alignment = cgf.getContext().getTypeAlignInChars(boolType); + auto addr = + builder.createAlloca(loc, builder.getPointerTo(cirBoolType), + cirBoolType, {}, cgf.cgm.getSize(alignment)); + return builder.createLoad(loc, addr); + } if (srcType->isIntegerType()) return emitIntToBoolConversion(src, loc); @@ -203,6 +211,11 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { castKind = cir::CastKind::float_to_int; } else if (mlir::isa<cir::CIRFPTypeInterface>(dstTy)) { cgf.getCIRGenModule().errorNYI("floating point casts"); + CharUnits alignment = cgf.getContext().getTypeAlignInChars(dstType); + auto addr = + builder.createAlloca(src.getLoc(), builder.getPointerTo(dstTy), + dstTy, {}, cgf.cgm.getSize(alignment)); + return builder.createLoad(src.getLoc(), addr); } else { llvm_unreachable("Internal error: Cast to unexpected type"); } @@ -230,10 +243,10 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { // 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()) + if (srcType->isFixedPointType() || dstType->isFixedPointType()) { cgf.getCIRGenModule().errorNYI(loc, "fixed point conversions"); + return nullptr; + } srcType = srcType.getCanonicalType(); dstType = dstType.getCanonicalType(); @@ -294,6 +307,11 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { // some native types (like Obj-C id) may map to a pointer type. if (auto dstPT = dyn_cast<cir::PointerType>(mlirDstType)) { cgf.getCIRGenModule().errorNYI(loc, "pointer casts"); + CharUnits alignment = cgf.getContext().getTypeAlignInChars(dstType); + auto addr = + builder.createAlloca(src.getLoc(), builder.getPointerTo(dstPT), dstPT, + {}, cgf.cgm.getSize(alignment)); + return builder.createLoad(src.getLoc(), addr); } if (isa<cir::PointerType>(mlirSrcType)) { @@ -310,7 +328,8 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { srcType.getTypePtr() && "Splatted expr doesn't match with vector element type?"); - llvm_unreachable("not implemented"); + cgf.getCIRGenModule().errorNYI(loc, "vector splatting"); + return nullptr; } if (srcType->isMatrixType() && dstType->isMatrixType()) @@ -328,10 +347,11 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { if (mlirDstType != 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); } + // FIXME(cir): For now we never use FP16 conversion intrinsics even if + // required by the target. Change that once this is implemented + res = builder.createCast(cgf.getLoc(loc), cir::CastKind::floating, res, + resTy); } if (opts.emitImplicitIntegerTruncationChecks) @@ -342,10 +362,6 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { "implicit integer sign change checks"); return res; - - cgf.getCIRGenModule().errorNYI(loc, - "emitScalarConversion for unequal types"); - return {}; } }; @@ -485,7 +501,8 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { case CK_PointerToIntegral: { assert(!destTy->isBooleanType() && "bool should use PointerToBool"); if (cgf.cgm.getCodeGenOpts().StrictVTablePointers) - llvm_unreachable("NYI"); + cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(), + "strict vtable pointers"); return builder.createPtrToInt(Visit(subExpr), cgf.convertType(destTy)); } case CK_ToVoid: @@ -493,6 +510,21 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { "ignored expression on void cast"); return nullptr; + case CK_IntegralToFloating: + case CK_FloatingToIntegral: + case CK_FloatingCast: + case CK_FixedPointToFloating: + case CK_FloatingToFixedPoint: { + if (kind == CK_FixedPointToFloating || kind == CK_FloatingToFixedPoint) { + cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(), + "fixed point casts"); + return {}; + } + cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(), "fp options"); + return emitScalarConversion(Visit(subExpr), subExpr->getType(), destTy, + ce->getExprLoc()); + } + case CK_IntegralToBoolean: return emitIntToBoolConversion(Visit(subExpr), cgf.getLoc(ce->getSourceRange())); @@ -508,7 +540,6 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { cir::CastKind::member_ptr_to_bool, memPtr, cgf.convertType(destTy)); } - return nullptr; default: cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(), diff --git a/clang/test/CIR/CodeGen/cast.cpp b/clang/test/CIR/CodeGen/cast.cpp index d8bf7ca66b6a3..9fd42e6611aa0 100644 --- a/clang/test/CIR/CodeGen/cast.cpp +++ b/clang/test/CIR/CodeGen/cast.cpp @@ -58,7 +58,6 @@ void should_not_cast() { unsigned uu = (unsigned)x1; bool ib = (bool)x1; - return (void) x1; } // CHECK: cir.func @should_not_cast _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits