https://github.com/mmha updated https://github.com/llvm/llvm-project/pull/130690
>From b9a55d112998c468cce5cabff33939e4412e7ded Mon Sep 17 00:00:00 2001 From: Morris Hafner <mhaf...@nvidia.com> Date: Mon, 10 Mar 2025 16:18:34 -0700 Subject: [PATCH 1/2] [CIR] Upstream CastOp and scalar conversions This patch upstreams ClangIR's CastOp with the following exceptions: - No Fixed/FP conversions - No casts between value categories - No complex casts - No array_to_ptrdecay - No address_space - No casts involving record types (member pointers, base/derived casts) - No casts specific to ObjC or OpenCL --- .../CIR/Dialect/Builder/CIRBaseBuilder.h | 62 +++ clang/include/clang/CIR/Dialect/IR/CIROps.td | 105 +++++ clang/include/clang/CIR/MissingFeatures.h | 14 +- clang/lib/CIR/CodeGen/CIRGenBuilder.h | 9 + clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 400 +++++++++++++++++- clang/lib/CIR/CodeGen/CIRGenFunction.h | 3 + clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 170 ++++++++ clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp | 27 ++ .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 207 +++++++++ .../CIR/Lowering/DirectToLLVM/LowerToLLVM.h | 16 + clang/test/CIR/CodeGen/cast.cpp | 58 +++ clang/test/CIR/IR/cast.cir | 23 + clang/test/CIR/Lowering/cast.cir | 92 ++++ 13 files changed, 1174 insertions(+), 12 deletions(-) create mode 100644 clang/test/CIR/CodeGen/cast.cpp create mode 100644 clang/test/CIR/IR/cast.cir create mode 100644 clang/test/CIR/Lowering/cast.cir diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index 017ae0c53a984..e5e8132e9f527 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -13,6 +13,7 @@ #include "clang/CIR/Dialect/IR/CIRAttrs.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/Dialect/IR/CIRTypes.h" +#include "llvm/Support/ErrorHandling.h" #include "mlir/IR/Builders.h" #include "mlir/IR/BuiltinTypes.h" @@ -78,6 +79,67 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return create<cir::StoreOp>(loc, val, dst); } + //===--------------------------------------------------------------------===// + // Cast/Conversion Operators + //===--------------------------------------------------------------------===// + + mlir::Value createCast(mlir::Location loc, cir::CastKind kind, + mlir::Value src, mlir::Type newTy) { + if (newTy == src.getType()) + return src; + return create<cir::CastOp>(loc, newTy, kind, src); + } + + mlir::Value createCast(cir::CastKind kind, mlir::Value src, + mlir::Type newTy) { + if (newTy == src.getType()) + return src; + return createCast(src.getLoc(), kind, src, newTy); + } + + mlir::Value createIntCast(mlir::Value src, mlir::Type newTy) { + return createCast(cir::CastKind::integral, src, newTy); + } + + mlir::Value createIntToPtr(mlir::Value src, mlir::Type newTy) { + return createCast(cir::CastKind::int_to_ptr, src, newTy); + } + + mlir::Value createPtrToInt(mlir::Value src, mlir::Type newTy) { + return createCast(cir::CastKind::ptr_to_int, src, newTy); + } + + mlir::Value createPtrToBoolCast(mlir::Value v) { + return createCast(cir::CastKind::ptr_to_bool, v, getBoolTy()); + } + + mlir::Value createBoolToInt(mlir::Value src, mlir::Type newTy) { + return createCast(cir::CastKind::bool_to_int, src, newTy); + } + + mlir::Value createBitcast(mlir::Value src, mlir::Type newTy) { + return createCast(cir::CastKind::bitcast, src, newTy); + } + + mlir::Value createBitcast(mlir::Location loc, mlir::Value src, + mlir::Type newTy) { + return createCast(loc, cir::CastKind::bitcast, src, newTy); + } + + mlir::Value createPtrBitcast(mlir::Value src, mlir::Type newPointeeTy) { + assert(mlir::isa<cir::PointerType>(src.getType()) && "expected ptr src"); + return createBitcast(src, getPointerTo(newPointeeTy)); + } + + mlir::Value createAddrSpaceCast(mlir::Location loc, mlir::Value src, + mlir::Type newTy) { + return createCast(loc, cir::CastKind::address_space, src, newTy); + } + + mlir::Value createAddrSpaceCast(mlir::Value src, mlir::Type newTy) { + return createAddrSpaceCast(src.getLoc(), src, newTy); + } + // // Block handling helpers // ---------------------- diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index e2ab50c78ec2d..caef0947d0b16 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -78,6 +78,111 @@ class LLVMLoweringInfo { class CIR_Op<string mnemonic, list<Trait> traits = []> : Op<CIR_Dialect, mnemonic, traits>, LLVMLoweringInfo; +//===----------------------------------------------------------------------===// +// CastOp +//===----------------------------------------------------------------------===// + +// The enumaration value isn't in sync with clang. +def CK_IntegralToBoolean : I32EnumAttrCase<"int_to_bool", 1>; +def CK_ArrayToPointerDecay : I32EnumAttrCase<"array_to_ptrdecay", 2>; +def CK_IntegralCast : I32EnumAttrCase<"integral", 3>; +def CK_BitCast : I32EnumAttrCase<"bitcast", 4>; +def CK_FloatingCast : I32EnumAttrCase<"floating", 5>; +def CK_PtrToBoolean : I32EnumAttrCase<"ptr_to_bool", 6>; +def CK_FloatToIntegral : I32EnumAttrCase<"float_to_int", 7>; +def CK_IntegralToPointer : I32EnumAttrCase<"int_to_ptr", 8>; +def CK_PointerToIntegral : I32EnumAttrCase<"ptr_to_int", 9>; +def CK_FloatToBoolean : I32EnumAttrCase<"float_to_bool", 10>; +def CK_BooleanToIntegral : I32EnumAttrCase<"bool_to_int", 11>; +def CK_IntegralToFloat : I32EnumAttrCase<"int_to_float", 12>; +def CK_BooleanToFloat : I32EnumAttrCase<"bool_to_float", 13>; +def CK_AddressSpaceConversion : I32EnumAttrCase<"address_space", 14>; +def CK_FloatToComplex : I32EnumAttrCase<"float_to_complex", 15>; +def CK_IntegralToComplex : I32EnumAttrCase<"int_to_complex", 16>; +def CK_FloatComplexToReal : I32EnumAttrCase<"float_complex_to_real", 17>; +def CK_IntegralComplexToReal : I32EnumAttrCase<"int_complex_to_real", 18>; +def CK_FloatComplexToBoolean : I32EnumAttrCase<"float_complex_to_bool", 19>; +def CK_IntegralComplexToBoolean : I32EnumAttrCase<"int_complex_to_bool", 20>; +def CK_FloatComplexCast : I32EnumAttrCase<"float_complex", 21>; +def CK_FloatComplexToIntegralComplex + : I32EnumAttrCase<"float_complex_to_int_complex", 22>; +def CK_IntegralComplexCast : I32EnumAttrCase<"int_complex", 23>; +def CK_IntegralComplexToFloatComplex + : I32EnumAttrCase<"int_complex_to_float_complex", 24>; +def CK_MemberPtrToBoolean : I32EnumAttrCase<"member_ptr_to_bool", 25>; + +def CastKind : I32EnumAttr< + "CastKind", + "cast kind", + [CK_IntegralToBoolean, CK_ArrayToPointerDecay, CK_IntegralCast, + CK_BitCast, CK_FloatingCast, CK_PtrToBoolean, CK_FloatToIntegral, + CK_IntegralToPointer, CK_PointerToIntegral, CK_FloatToBoolean, + CK_BooleanToIntegral, CK_IntegralToFloat, CK_BooleanToFloat, + CK_AddressSpaceConversion, CK_FloatToComplex, CK_IntegralToComplex, + CK_FloatComplexToReal, CK_IntegralComplexToReal, CK_FloatComplexToBoolean, + CK_IntegralComplexToBoolean, CK_FloatComplexCast, + CK_FloatComplexToIntegralComplex, CK_IntegralComplexCast, + CK_IntegralComplexToFloatComplex, CK_MemberPtrToBoolean]> { + let cppNamespace = "::cir"; +} + +def CastOp : CIR_Op<"cast", + [Pure, + DeclareOpInterfaceMethods<PromotableOpInterface>]> { + // FIXME: not all conversions are free of side effects. + let summary = "Conversion between values of different types"; + let description = [{ + Apply C/C++ usual conversions rules between values. Currently supported kinds: + + - `array_to_ptrdecay` + - `bitcast` + - `integral` + - `int_to_bool` + - `int_to_float` + - `floating` + - `float_to_int` + - `float_to_bool` + - `ptr_to_int` + - `ptr_to_bool` + - `bool_to_int` + - `bool_to_float` + - `address_space` + - `float_to_complex` + - `int_to_complex` + - `float_complex_to_real` + - `int_complex_to_real` + - `float_complex_to_bool` + - `int_complex_to_bool` + - `float_complex` + - `float_complex_to_int_complex` + - `int_complex` + - `int_complex_to_float_complex` + + This is effectively a subset of the rules from + `llvm-project/clang/include/clang/AST/OperationKinds.def`; but note that some + of the conversions aren't implemented in terms of `cir.cast`, `lvalue-to-rvalue` + for instance is modeled as a regular `cir.load`. + + ```mlir + %4 = cir.cast (int_to_bool, %3 : i32), !cir.bool + ... + %x = cir.cast(array_to_ptrdecay, %0 : !cir.ptr<!cir.array<i32 x 10>>), !cir.ptr<i32> + ``` + }]; + + let arguments = (ins CastKind:$kind, CIR_AnyType:$src); + let results = (outs CIR_AnyType:$result); + + let assemblyFormat = [{ + `(` $kind `,` $src `:` type($src) `)` + `,` type($result) attr-dict + }]; + + // The input and output types should match the cast kind. + let hasVerifier = 1; + let hasFolder = 1; +} + //===----------------------------------------------------------------------===// // ConstantOp //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index d20cd0560a7c1..5f1ed97f4940a 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -73,15 +73,27 @@ struct MissingFeatures { static bool opFuncVisibility() { return false; } // Misc - static bool scalarConversionOpts() { return false; } + static bool cxxABI() { return false; } static bool tryEmitAsConstant() { return false; } static bool constructABIArgDirectExtend() { return false; } static bool opGlobalViewAttr() { return false; } static bool lowerModeOptLevel() { return false; } static bool opTBAA() { return false; } + static bool opCmp() { return false; } static bool objCLifetime() { return false; } static bool emitNullabilityCheck() { return false; } static bool astVarDeclInterface() { return false; } + static bool scalableVectors() { return false; } + static bool fpConstraints() { return false; } + static bool sanitizers() { return false; } + static bool addHeapAllocSiteMetadata() { return false; } + static bool targetCodeGenInfoGetNullPointer() { return false; } + + // Missing types + static bool dataMemberType() { return false; } + static bool methodType() { return false; } + static bool matrixType() { return false; } + static bool vectorType() { return false; } }; } // namespace cir diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index 01d56963883cc..76e6c53d9b6e2 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -10,6 +10,7 @@ #define LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENBUILDER_H #include "CIRGenTypeCache.h" +#include "clang/CIR/MissingFeatures.h" #include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h" @@ -33,6 +34,14 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy { llvm_unreachable("NYI: PPC double-double format for long double"); llvm_unreachable("Unsupported format for long double"); } + + bool isInt(mlir::Type i) { return mlir::isa<cir::IntType>(i); } + + // Creates constant nullptr for pointer type ty. + cir::ConstantOp getNullPtr(mlir::Type ty, mlir::Location loc) { + assert(!cir::MissingFeatures::targetCodeGenInfoGetNullPointer()); + return create<cir::ConstantOp>(loc, ty, getConstPtrAttr(ty, 0)); + } }; } // namespace clang::CIRGen diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index b9e56dc4123d6..df9447841800a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -36,6 +36,18 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { bool ira = false) : cgf(cgf), builder(builder), ignoreResultAssign(ira) {} + //===--------------------------------------------------------------------===// + // Utilities + //===--------------------------------------------------------------------===// + + bool TestAndClearIgnoreResultAssign() { + bool i = ignoreResultAssign; + ignoreResultAssign = false; + return i; + } + + mlir::Type convertType(QualType t) { return cgf.convertType(t); } + //===--------------------------------------------------------------------===// // Visitor Methods //===--------------------------------------------------------------------===// @@ -68,14 +80,14 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { } mlir::Value VisitIntegerLiteral(const IntegerLiteral *e) { - mlir::Type type = cgf.convertType(e->getType()); + mlir::Type type = convertType(e->getType()); return builder.create<cir::ConstantOp>( cgf.getLoc(e->getExprLoc()), type, builder.getAttr<cir::IntAttr>(type, e->getValue())); } mlir::Value VisitFloatingLiteral(const FloatingLiteral *e) { - mlir::Type type = cgf.convertType(e->getType()); + mlir::Type type = convertType(e->getType()); assert(mlir::isa<cir::CIRFPTypeInterface>(type) && "expect floating-point type"); return builder.create<cir::ConstantOp>( @@ -84,26 +96,266 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { } mlir::Value VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *e) { - mlir::Type type = cgf.convertType(e->getType()); + mlir::Type type = convertType(e->getType()); return builder.create<cir::ConstantOp>( cgf.getLoc(e->getExprLoc()), type, builder.getCIRBoolAttr(e->getValue())); } - mlir::Value VisitCastExpr(CastExpr *E); + mlir::Value VisitCastExpr(CastExpr *e); + + mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { + return VisitCastExpr(e); + } + + /// Perform a pointer to boolean conversion. + mlir::Value emitPointerToBoolConversion(mlir::Value v, QualType qt) { + // TODO(cir): comparing the ptr to null is done when lowering CIR to LLVM. + // We might want to have a separate pass for these types of conversions. + return cgf.getBuilder().createPtrToBoolCast(v); + } + + mlir::Value emitFloatToBoolConversion(mlir::Value src, mlir::Location loc) { + auto boolTy = builder.getBoolTy(); + return builder.create<cir::CastOp>(loc, boolTy, + cir::CastKind::float_to_bool, src); + } + + mlir::Value emitIntToBoolConversion(mlir::Value srcVal, mlir::Location loc) { + // Because of the type rules of C, we often end up computing a + // logical value, then zero extending it to int, then wanting it + // as a logical value again. + // TODO: optimize this common case here or leave it for later + // CIR passes? + mlir::Type boolTy = convertType(cgf.getContext().BoolTy); + return builder.create<cir::CastOp>(loc, boolTy, cir::CastKind::int_to_bool, + srcVal); + } + + /// Convert the specified expression value to a boolean (!cir.bool) truth + /// value. This is equivalent to "Val != 0". + mlir::Value emitConversionToBool(mlir::Value src, QualType srcType, + mlir::Location loc) { + assert(srcType.isCanonical() && "EmitScalarConversion strips typedefs"); + + if (srcType->isRealFloatingType()) + return emitFloatToBoolConversion(src, loc); + + if ([[maybe_unused]] auto *mpt = llvm::dyn_cast<MemberPointerType>(srcType)) + cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion"); + + if (srcType->isIntegerType()) + return emitIntToBoolConversion(src, loc); + + assert(::mlir::isa<cir::PointerType>(src.getType())); + return emitPointerToBoolConversion(src, srcType); + } + + // Emit a conversion from the specified type to the specified destination + // type, both of which are CIR scalar types. + struct ScalarConversionOpts { + bool treatBooleanAsSigned; + bool emitImplicitIntegerTruncationChecks; + bool emitImplicitIntegerSignChangeChecks; + + ScalarConversionOpts() + : treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks(false), + emitImplicitIntegerSignChangeChecks(false) {} + + ScalarConversionOpts(clang::SanitizerSet sanOpts) + : treatBooleanAsSigned(false), + emitImplicitIntegerTruncationChecks( + sanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation)), + emitImplicitIntegerSignChangeChecks( + sanOpts.has(SanitizerKind::ImplicitIntegerSignChange)) {} + }; + + // Conversion from bool, integral, or floating-point to integral or + // floating-point. Conversions involving other types are handled elsewhere. + // Conversion to bool is handled elsewhere because that's a comparison against + // zero, not a simple cast. This handles both individual scalars and vectors. + mlir::Value emitScalarCast(mlir::Value src, QualType srcType, + QualType dstType, mlir::Type srcTy, + mlir::Type dstTy, ScalarConversionOpts opts) { + assert(!srcType->isMatrixType() && !dstType->isMatrixType() && + "Internal error: matrix types not handled by this function."); + if (mlir::isa<mlir::IntegerType>(srcTy) || + mlir::isa<mlir::IntegerType>(dstTy)) + llvm_unreachable("Obsolete code. Don't use mlir::IntegerType with CIR."); + + mlir::Type fullDstTy = dstTy; + assert(!cir::MissingFeatures::vectorType()); + + std::optional<cir::CastKind> castKind; + + if (mlir::isa<cir::BoolType>(srcTy)) { + if (opts.treatBooleanAsSigned) + cgf.getCIRGenModule().errorNYI("signed bool"); + if (cgf.getBuilder().isInt(dstTy)) { + castKind = cir::CastKind::bool_to_int; + } else if (mlir::isa<cir::CIRFPTypeInterface>(dstTy)) { + castKind = cir::CastKind::bool_to_float; + } else { + llvm_unreachable("Internal error: Cast to unexpected type"); + } + } else if (cgf.getBuilder().isInt(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { + castKind = cir::CastKind::integral; + } else if (mlir::isa<cir::CIRFPTypeInterface>(dstTy)) { + castKind = cir::CastKind::int_to_float; + } else { + llvm_unreachable("Internal error: Cast to unexpected type"); + } + } else if (mlir::isa<cir::CIRFPTypeInterface>(srcTy)) { + if (cgf.getBuilder().isInt(dstTy)) { + // If we can't recognize overflow as undefined behavior, assume that + // overflow saturates. This protects against normal optimizations if we + // are compiling with non-standard FP semantics. + if (!cgf.cgm.getCodeGenOpts().StrictFloatCastOverflow) + cgf.getCIRGenModule().errorNYI("strict float cast overflow"); + assert(!cir::MissingFeatures::fpConstraints()); + castKind = cir::CastKind::float_to_int; + } else if (mlir::isa<cir::CIRFPTypeInterface>(dstTy)) { + cgf.getCIRGenModule().errorNYI("floating point casts"); + } else { + llvm_unreachable("Internal error: Cast to unexpected type"); + } + } else { + llvm_unreachable("Internal error: Cast from unexpected type"); + } + + assert(castKind.has_value() && "Internal error: CastKind not set."); + return builder.create<cir::CastOp>(src.getLoc(), fullDstTy, *castKind, src); + } /// Emit a conversion from the specified type to the specified destination /// type, both of which are CIR scalar types. /// TODO: do we need ScalarConversionOpts here? Should be done in another /// pass. - mlir::Value emitScalarConversion(mlir::Value src, QualType srcType, - QualType dstType, SourceLocation loc) { - // No sort of type conversion is implemented yet, but the path for implicit - // paths goes through here even if the type isn't being changed. + mlir::Value + emitScalarConversion(mlir::Value src, QualType srcType, QualType dstType, + SourceLocation loc, + ScalarConversionOpts opts = ScalarConversionOpts()) { + // All conversions involving fixed point types should be handled by the + // emitFixedPoint family functions. This is done to prevent bloating up + // this function more, and although fixed point numbers are represented by + // integers, we do not want to follow any logic that assumes they should be + // treated as integers. + // TODO(leonardchan): When necessary, add another if statement checking for + // conversions to fixed point types from other types. + // conversions to fixed point types from other types. + if (srcType->isFixedPointType()) + cgf.getCIRGenModule().errorNYI(loc, "fixed point conversions"); + else if (dstType->isFixedPointType()) + cgf.getCIRGenModule().errorNYI(loc, "fixed point conversions"); + srcType = srcType.getCanonicalType(); dstType = dstType.getCanonicalType(); - if (srcType == dstType) + if (srcType == dstType) { + if (opts.emitImplicitIntegerSignChangeChecks) + cgf.getCIRGenModule().errorNYI(loc, + "implicit integer sign change checks"); return src; + } + + if (dstType->isVoidType()) + return nullptr; + + mlir::Type srcTy = src.getType(); + + // Handle conversions to bool first, they are special: comparisons against + // 0. + if (dstType->isBooleanType()) + return emitConversionToBool(src, srcType, cgf.getLoc(loc)); + + mlir::Type dstTy = convertType(dstType); + + if (srcType->isHalfType() && + !cgf.getContext().getLangOpts().NativeHalfType) { + // Cast to FP using the intrinsic if the half type itself isn't supported. + if (mlir::isa<cir::CIRFPTypeInterface>(dstTy)) { + if (cgf.getContext().getTargetInfo().useFP16ConversionIntrinsics()) + cgf.getCIRGenModule().errorNYI(loc, + "cast via llvm.convert.from.fp16"); + } else { + // Cast to other types through float, using either the intrinsic or + // FPExt, depending on whether the half type itself is supported (as + // opposed to operations on half, available with NativeHalfType). + if (cgf.getContext().getTargetInfo().useFP16ConversionIntrinsics()) { + cgf.getCIRGenModule().errorNYI(loc, + "cast via llvm.convert.from.fp16"); + } else { + src = builder.createCast(cgf.getLoc(loc), cir::CastKind::floating, + src, cgf.FloatTy); + } + srcType = cgf.getContext().FloatTy; + srcTy = cgf.FloatTy; + } + } + + // TODO(cir): LLVM codegen ignore conversions like int -> uint, + // is there anything to be done for CIR here? + if (srcTy == dstTy) { + if (opts.emitImplicitIntegerSignChangeChecks) + cgf.getCIRGenModule().errorNYI(loc, + "implicit integer sign change checks"); + return src; + } + + // Handle pointer conversions next: pointers can only be converted to/from + // other pointers and integers. Check for pointer types in terms of LLVM, as + // some native types (like Obj-C id) may map to a pointer type. + if (auto dstPT = dyn_cast<cir::PointerType>(dstTy)) { + cgf.getCIRGenModule().errorNYI(loc, "pointer casts"); + } + + if (isa<cir::PointerType>(srcTy)) { + // Must be an ptr to int cast. + assert(isa<cir::IntType>(dstTy) && "not ptr->int?"); + return builder.createPtrToInt(src, dstTy); + } + + // A scalar can be splatted to an extended vector of the same element type + if (dstType->isExtVectorType() && !srcType->isVectorType()) { + // Sema should add casts to make sure that the source expression's type + // is the same as the vector's element type (sans qualifiers) + assert(dstType->castAs<ExtVectorType>()->getElementType().getTypePtr() == + srcType.getTypePtr() && + "Splatted expr doesn't match with vector element type?"); + + llvm_unreachable("not implemented"); + } + + if (srcType->isMatrixType() && dstType->isMatrixType()) + cgf.getCIRGenModule().errorNYI(loc, + "matrix type to matrix type conversion"); + assert(!srcType->isMatrixType() && !dstType->isMatrixType() && + "Internal error: conversion between matrix type and scalar type"); + + // Finally, we have the arithmetic types or vectors of arithmetic types. + mlir::Value res = nullptr; + mlir::Type resTy = dstTy; + + res = emitScalarCast(src, srcType, dstType, srcTy, dstTy, opts); + + if (dstTy != resTy) { + if (cgf.getContext().getTargetInfo().useFP16ConversionIntrinsics()) { + cgf.getCIRGenModule().errorNYI(loc, "cast via llvm.convert.to.fp16"); + } else { + res = builder.createCast(cgf.getLoc(loc), cir::CastKind::floating, res, + resTy); + } + } + + if (opts.emitImplicitIntegerTruncationChecks) + cgf.getCIRGenModule().errorNYI(loc, "implicit integer truncation checks"); + + if (opts.emitImplicitIntegerSignChangeChecks) + cgf.getCIRGenModule().errorNYI(loc, + "implicit integer sign change checks"); + + return res; cgf.getCIRGenModule().errorNYI(loc, "emitScalarConversion for unequal types"); @@ -121,6 +373,13 @@ mlir::Value CIRGenFunction::emitScalarExpr(const Expr *e) { return ScalarExprEmitter(*this, builder).Visit(const_cast<Expr *>(e)); } +[[maybe_unused]] static bool MustVisitNullValue(const Expr *e) { + // If a null pointer expression's type is the C++0x nullptr_t, then + // it's not necessarily a simple constant and it must be evaluated + // for its potential side effects. + return e->getType()->isNullPtrType(); +} + // Emit code for an explicit or implicit cast. Implicit // casts have to handle a more broad range of conversions than explicit // casts, as they handle things like function to ptr-to-function decay @@ -130,17 +389,136 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { QualType destTy = ce->getType(); CastKind kind = ce->getCastKind(); + // These cases are generally not written to ignore the result of evaluating + // their sub-expressions, so we clear this now. + [[maybe_unused]] bool ignored = TestAndClearIgnoreResultAssign(); + switch (kind) { + case clang::CK_Dependent: + llvm_unreachable("dependent cast kind in CIR gen!"); + case clang::CK_BuiltinFnToFnPtr: + llvm_unreachable("builtin functions are handled elsewhere"); + + case CK_CPointerToObjCPointerCast: + case CK_BlockPointerToObjCPointerCast: + case CK_AnyPointerToBlockPointerCast: + case CK_BitCast: { + auto src = Visit(const_cast<Expr *>(e)); + mlir::Type dstTy = convertType(destTy); + + assert(!cir::MissingFeatures::addressSpace()); + + if (cgf.sanOpts.has(SanitizerKind::CFIUnrelatedCast)) + cgf.getCIRGenModule().errorNYI(e->getSourceRange(), "sanitizer support"); + + if (cgf.cgm.getCodeGenOpts().StrictVTablePointers) + cgf.getCIRGenModule().errorNYI(e->getSourceRange(), + "strict vtable pointers"); + + // Update heapallocsite metadata when there is an explicit pointer cast. + assert(!cir::MissingFeatures::addHeapAllocSiteMetadata()); + + // If Src is a fixed vector and Dst is a scalable vector, and both have the + // same element type, use the llvm.vector.insert intrinsic to perform the + // bitcast. + assert(!cir::MissingFeatures::scalableVectors()); + + // If Src is a scalable vector and Dst is a fixed vector, and both have the + // same element type, use the llvm.vector.extract intrinsic to perform the + // bitcast. + assert(!cir::MissingFeatures::scalableVectors()); + + // Perform VLAT <-> VLST bitcast through memory. + // TODO: since the llvm.experimental.vector.{insert,extract} intrinsics + // require the element types of the vectors to be the same, we + // need to keep this around for bitcasts between VLAT <-> VLST where + // the element types of the vectors are not the same, until we figure + // out a better way of doing these casts. + assert(!cir::MissingFeatures::scalableVectors()); + + return cgf.getBuilder().createBitcast(cgf.getLoc(e->getSourceRange()), src, + dstTy); + } + + case CK_AtomicToNonAtomic: + cgf.getCIRGenModule().errorNYI(e->getSourceRange(), + "CastExpr: ", ce->getCastKindName()); + break; + case CK_NonAtomicToAtomic: + case CK_UserDefinedConversion: + return Visit(const_cast<Expr *>(e)); + case CK_NoOp: { + auto v = Visit(const_cast<Expr *>(e)); + if (v) { + // CK_NoOp can model a pointer qualification conversion, which can remove + // an array bound and change the IR type. + // FIXME: Once pointee types are removed from IR, remove this. + auto t = convertType(destTy); + if (t != v.getType()) + cgf.getCIRGenModule().errorNYI("pointer qualification conversion"); + } + return v; + } + + case CK_NullToPointer: { + if (MustVisitNullValue(e)) + cgf.getCIRGenModule().errorNYI( + e->getSourceRange(), "ignored expression on null to pointer cast"); + + // Note that DestTy is used as the MLIR type instead of a custom + // nullptr type. + mlir::Type ty = convertType(destTy); + return builder.getNullPtr(ty, cgf.getLoc(e->getExprLoc())); + } + case CK_LValueToRValue: assert(cgf.getContext().hasSameUnqualifiedType(e->getType(), destTy)); assert(e->isGLValue() && "lvalue-to-rvalue applied to r-value!"); return Visit(const_cast<Expr *>(e)); case CK_IntegralCast: { - assert(!cir::MissingFeatures::scalarConversionOpts()); + ScalarConversionOpts opts; + if (auto *ice = dyn_cast<ImplicitCastExpr>(ce)) { + if (!ice->isPartOfExplicitCast()) + opts = ScalarConversionOpts(cgf.sanOpts); + } return emitScalarConversion(Visit(e), e->getType(), destTy, - ce->getExprLoc()); + ce->getExprLoc(), opts); + } + + case CK_FloatingRealToComplex: + case CK_FloatingComplexCast: + case CK_IntegralRealToComplex: + case CK_IntegralComplexCast: + case CK_IntegralComplexToFloatingComplex: + case CK_FloatingComplexToIntegralComplex: + llvm_unreachable("scalar cast to non-scalar value"); + + case CK_PointerToIntegral: { + assert(!destTy->isBooleanType() && "bool should use PointerToBool"); + if (cgf.cgm.getCodeGenOpts().StrictVTablePointers) + llvm_unreachable("NYI"); + return builder.createPtrToInt(Visit(e), convertType(destTy)); + } + case CK_ToVoid: + cgf.getCIRGenModule().errorNYI(e->getSourceRange(), + "ignored expression on void cast"); + return nullptr; + + case CK_IntegralToBoolean: + return emitIntToBoolConversion(Visit(e), cgf.getLoc(ce->getSourceRange())); + + case CK_PointerToBoolean: + return emitPointerToBoolConversion(Visit(e), e->getType()); + case CK_FloatingToBoolean: + return emitFloatToBoolConversion(Visit(e), cgf.getLoc(e->getExprLoc())); + case CK_MemberPointerToBoolean: { + mlir::Value memPtr = Visit(e); + return builder.createCast(cgf.getLoc(ce->getSourceRange()), + cir::CastKind::member_ptr_to_bool, memPtr, + cgf.convertType(destTy)); } + return nullptr; default: cgf.getCIRGenModule().errorNYI(e->getSourceRange(), diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 72445f62232a4..87890d14b4d05 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -73,6 +73,9 @@ class CIRGenFunction : public CIRGenTypeCache { return &fn.getRegion().front(); } + /// Sanitizers enabled for this function. + clang::SanitizerSet sanOpts; + mlir::Type convertTypeForMem(QualType T); mlir::Type convertType(clang::QualType T); diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 5ad369b40cda1..d03189ce0e210 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -182,6 +182,176 @@ OpFoldResult cir::ConstantOp::fold(FoldAdaptor /*adaptor*/) { return getValue(); } +//===----------------------------------------------------------------------===// +// CastOp +//===----------------------------------------------------------------------===// + +LogicalResult cir::CastOp::verify() { + auto resType = getResult().getType(); + auto srcType = getSrc().getType(); + + switch (getKind()) { + case cir::CastKind::int_to_bool: { + if (!mlir::isa<cir::BoolType>(resType)) + return emitOpError() << "requires !cir.bool type for result"; + if (!mlir::isa<cir::IntType>(srcType)) + return emitOpError() << "requires !cir.int type for source"; + return success(); + } + case cir::CastKind::ptr_to_bool: { + if (!mlir::isa<cir::BoolType>(resType)) + return emitOpError() << "requires !cir.bool type for result"; + if (!mlir::isa<cir::PointerType>(srcType)) + return emitOpError() << "requires !cir.ptr type for source"; + return success(); + } + case cir::CastKind::integral: { + if (!mlir::isa<cir::IntType>(resType)) + return emitOpError() << "requires !cir.int type for result"; + if (!mlir::isa<cir::IntType>(srcType)) + return emitOpError() << "requires !cir.int type for source"; + return success(); + } + case cir::CastKind::bitcast: { + // Handle the pointer types first. + auto srcPtrTy = mlir::dyn_cast<cir::PointerType>(srcType); + auto resPtrTy = mlir::dyn_cast<cir::PointerType>(resType); + + if (srcPtrTy && resPtrTy) { + return success(); + } + + return success(); + } + case cir::CastKind::floating: { + if (!mlir::isa<cir::CIRFPTypeInterface>(srcType) || + !mlir::isa<cir::CIRFPTypeInterface>(resType)) + return emitOpError() << "requires !cir.float type for source and result"; + return success(); + } + case cir::CastKind::float_to_int: { + if (!mlir::isa<cir::CIRFPTypeInterface>(srcType)) + return emitOpError() << "requires !cir.float type for source"; + if (!mlir::dyn_cast<cir::IntType>(resType)) + return emitOpError() << "requires !cir.int type for result"; + return success(); + } + case cir::CastKind::int_to_ptr: { + if (!mlir::dyn_cast<cir::IntType>(srcType)) + return emitOpError() << "requires !cir.int type for source"; + if (!mlir::dyn_cast<cir::PointerType>(resType)) + return emitOpError() << "requires !cir.ptr type for result"; + return success(); + } + case cir::CastKind::ptr_to_int: { + if (!mlir::dyn_cast<cir::PointerType>(srcType)) + return emitOpError() << "requires !cir.ptr type for source"; + if (!mlir::dyn_cast<cir::IntType>(resType)) + return emitOpError() << "requires !cir.int type for result"; + return success(); + } + case cir::CastKind::float_to_bool: { + if (!mlir::isa<cir::CIRFPTypeInterface>(srcType)) + return emitOpError() << "requires !cir.float type for source"; + if (!mlir::isa<cir::BoolType>(resType)) + return emitOpError() << "requires !cir.bool type for result"; + return success(); + } + case cir::CastKind::bool_to_int: { + if (!mlir::isa<cir::BoolType>(srcType)) + return emitOpError() << "requires !cir.bool type for source"; + if (!mlir::isa<cir::IntType>(resType)) + return emitOpError() << "requires !cir.int type for result"; + return success(); + } + case cir::CastKind::int_to_float: { + if (!mlir::isa<cir::IntType>(srcType)) + return emitOpError() << "requires !cir.int type for source"; + if (!mlir::isa<cir::CIRFPTypeInterface>(resType)) + return emitOpError() << "requires !cir.float type for result"; + return success(); + } + case cir::CastKind::bool_to_float: { + if (!mlir::isa<cir::BoolType>(srcType)) + return emitOpError() << "requires !cir.bool type for source"; + if (!mlir::isa<cir::CIRFPTypeInterface>(resType)) + return emitOpError() << "requires !cir.float type for result"; + return success(); + } + case cir::CastKind::address_space: { + auto srcPtrTy = mlir::dyn_cast<cir::PointerType>(srcType); + auto resPtrTy = mlir::dyn_cast<cir::PointerType>(resType); + if (!srcPtrTy || !resPtrTy) + return emitOpError() << "requires !cir.ptr type for source and result"; + if (srcPtrTy.getPointee() != resPtrTy.getPointee()) + return emitOpError() << "requires two types differ in addrspace only"; + return success(); + } + } + + llvm_unreachable("Unknown CastOp kind?"); +} + +static bool isIntOrBoolCast(cir::CastOp op) { + auto kind = op.getKind(); + return kind == cir::CastKind::bool_to_int || + kind == cir::CastKind::int_to_bool || kind == cir::CastKind::integral; +} + +static Value tryFoldCastChain(cir::CastOp op) { + cir::CastOp head = op, tail = op; + + while (op) { + if (!isIntOrBoolCast(op)) + break; + head = op; + op = dyn_cast_or_null<cir::CastOp>(head.getSrc().getDefiningOp()); + } + + if (head == tail) + return {}; + + // if bool_to_int -> ... -> int_to_bool: take the bool + // as we had it was before all casts + if (head.getKind() == cir::CastKind::bool_to_int && + tail.getKind() == cir::CastKind::int_to_bool) + return head.getSrc(); + + // if int_to_bool -> ... -> int_to_bool: take the result + // of the first one, as no other casts (and ext casts as well) + // don't change the first result + if (head.getKind() == cir::CastKind::int_to_bool && + tail.getKind() == cir::CastKind::int_to_bool) + return head.getResult(); + + return {}; +} + +OpFoldResult cir::CastOp::fold(FoldAdaptor adaptor) { + if (getSrc().getType() == getResult().getType()) { + switch (getKind()) { + case cir::CastKind::integral: { + // TODO: for sign differences, it's possible in certain conditions to + // create a new attribute that's capable of representing the source. + llvm::SmallVector<mlir::OpFoldResult, 1> foldResults; + auto foldOrder = getSrc().getDefiningOp()->fold(foldResults); + if (foldOrder.succeeded() && mlir::isa<mlir::Attribute>(foldResults[0])) + return mlir::cast<mlir::Attribute>(foldResults[0]); + return {}; + } + case cir::CastKind::bitcast: + case cir::CastKind::address_space: + case cir::CastKind::float_complex: + case cir::CastKind::int_complex: { + return getSrc(); + } + default: + return {}; + } + } + return tryFoldCastChain(*this); +} + //===----------------------------------------------------------------------===// // ReturnOp //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp b/clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp index 5e44837979af3..9cd5c54e6c19e 100644 --- a/clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp @@ -15,6 +15,15 @@ using namespace mlir; +/// Conditions the deletion of the operation to the removal of all its uses. +static bool forwardToUsers(Operation *op, + SmallVectorImpl<OpOperand *> &newBlockingUses) { + for (Value result : op->getResults()) + for (OpOperand &use : result.getUses()) + newBlockingUses.push_back(&use); + return true; +} + //===----------------------------------------------------------------------===// // Interfaces for AllocaOp //===----------------------------------------------------------------------===// @@ -108,3 +117,21 @@ DeletionKind cir::StoreOp::removeBlockingUses( const DataLayout &dataLayout) { return DeletionKind::Delete; } + +//===----------------------------------------------------------------------===// +// Interfaces for CastOp +//===----------------------------------------------------------------------===// + +bool cir::CastOp::canUsesBeRemoved( + const SmallPtrSetImpl<OpOperand *> &blockingUses, + SmallVectorImpl<OpOperand *> &newBlockingUses, + const DataLayout &dataLayout) { + if (getKind() == cir::CastKind::bitcast) + return forwardToUsers(*this, newBlockingUses); + return false; +} + +DeletionKind cir::CastOp::removeBlockingUses( + const SmallPtrSetImpl<OpOperand *> &blockingUses, OpBuilder &builder) { + return DeletionKind::Delete; +} diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 3200527bd03af..9a02a023d731f 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -38,6 +38,21 @@ using namespace llvm; namespace cir { namespace direct { +//===----------------------------------------------------------------------===// +// Helper Methods +//===----------------------------------------------------------------------===// + +namespace { +/// If the given type is a vector type, return the vector's element type. +/// Otherwise return the given type unchanged. +// TODO(cir): Return the vector element type once we have support for vectors +// instead of the identity type. +mlir::Type elementTypeIfVector(mlir::Type type) { + assert(!cir::MissingFeatures::vectorType()); + return type; +} +} // namespace + /// Given a type convertor and a data layout, convert the given type to a type /// that is suitable for memory operations. For example, this can be used to /// lower cir.bool accesses to i8. @@ -137,6 +152,24 @@ mlir::LLVM::Linkage convertLinkage(cir::GlobalLinkageKind linkage) { }; } +static mlir::Value getLLVMIntCast(mlir::ConversionPatternRewriter &rewriter, + mlir::Value llvmSrc, mlir::Type llvmDstIntTy, + bool isUnsigned, uint64_t cirSrcWidth, + uint64_t cirDstIntWidth) { + if (cirSrcWidth == cirDstIntWidth) + return llvmSrc; + + auto loc = llvmSrc.getLoc(); + if (cirSrcWidth < cirDstIntWidth) { + if (isUnsigned) + return rewriter.create<mlir::LLVM::ZExtOp>(loc, llvmDstIntTy, llvmSrc); + return rewriter.create<mlir::LLVM::SExtOp>(loc, llvmDstIntTy, llvmSrc); + } + + // Otherwise truncate + return rewriter.create<mlir::LLVM::TruncOp>(loc, llvmDstIntTy, llvmSrc); +} + class CIRAttrToValue { public: CIRAttrToValue(mlir::Operation *parentOp, @@ -242,6 +275,179 @@ struct ConvertCIRToLLVMPass StringRef getArgument() const override { return "cir-flat-to-llvm"; } }; +mlir::Type CIRToLLVMCastOpLowering::convertTy(mlir::Type ty) const { + return getTypeConverter()->convertType(ty); +} + +mlir::LogicalResult CIRToLLVMCastOpLowering::matchAndRewrite( + cir::CastOp castOp, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + // For arithmetic conversions, LLVM IR uses the same instruction to convert + // both individual scalars and entire vectors. This lowering pass handles + // both situations. + + switch (castOp.getKind()) { + case cir::CastKind::array_to_ptrdecay: { + const auto ptrTy = mlir::cast<cir::PointerType>(castOp.getType()); + auto sourceValue = adaptor.getOperands().front(); + auto targetType = convertTy(ptrTy); + auto elementTy = convertTypeForMemory(*getTypeConverter(), dataLayout, + ptrTy.getPointee()); + auto offset = llvm::SmallVector<mlir::LLVM::GEPArg>{0}; + rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>( + castOp, targetType, elementTy, sourceValue, offset); + break; + } + case cir::CastKind::int_to_bool: + assert(!cir::MissingFeatures::opCmp()); + break; + case cir::CastKind::integral: { + auto srcType = castOp.getSrc().getType(); + auto dstType = castOp.getResult().getType(); + auto llvmSrcVal = adaptor.getOperands().front(); + auto llvmDstType = getTypeConverter()->convertType(dstType); + cir::IntType srcIntType = + mlir::cast<cir::IntType>(elementTypeIfVector(srcType)); + cir::IntType dstIntType = + mlir::cast<cir::IntType>(elementTypeIfVector(dstType)); + rewriter.replaceOp(castOp, getLLVMIntCast(rewriter, llvmSrcVal, llvmDstType, + srcIntType.isUnsigned(), + srcIntType.getWidth(), + dstIntType.getWidth())); + break; + } + case cir::CastKind::floating: { + auto llvmSrcVal = adaptor.getOperands().front(); + auto llvmDstTy = + getTypeConverter()->convertType(castOp.getResult().getType()); + + auto srcTy = elementTypeIfVector(castOp.getSrc().getType()); + auto dstTy = elementTypeIfVector(castOp.getResult().getType()); + + if (!mlir::isa<cir::CIRFPTypeInterface>(dstTy) || + !mlir::isa<cir::CIRFPTypeInterface>(srcTy)) + return castOp.emitError() << "NYI cast from " << srcTy << " to " << dstTy; + + auto getFloatWidth = [](mlir::Type ty) -> unsigned { + return mlir::cast<cir::CIRFPTypeInterface>(ty).getWidth(); + }; + + if (getFloatWidth(srcTy) > getFloatWidth(dstTy)) + rewriter.replaceOpWithNewOp<mlir::LLVM::FPTruncOp>(castOp, llvmDstTy, + llvmSrcVal); + else + rewriter.replaceOpWithNewOp<mlir::LLVM::FPExtOp>(castOp, llvmDstTy, + llvmSrcVal); + return mlir::success(); + } + case cir::CastKind::int_to_ptr: { + auto dstTy = mlir::cast<cir::PointerType>(castOp.getType()); + auto llvmSrcVal = adaptor.getOperands().front(); + auto llvmDstTy = getTypeConverter()->convertType(dstTy); + rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(castOp, llvmDstTy, + llvmSrcVal); + return mlir::success(); + } + case cir::CastKind::ptr_to_int: { + auto dstTy = mlir::cast<cir::IntType>(castOp.getType()); + auto llvmSrcVal = adaptor.getOperands().front(); + auto llvmDstTy = getTypeConverter()->convertType(dstTy); + rewriter.replaceOpWithNewOp<mlir::LLVM::PtrToIntOp>(castOp, llvmDstTy, + llvmSrcVal); + return mlir::success(); + } + case cir::CastKind::float_to_bool: { + auto llvmSrcVal = adaptor.getOperands().front(); + auto kind = mlir::LLVM::FCmpPredicate::une; + + // Check if float is not equal to zero. + auto zeroFloat = rewriter.create<mlir::LLVM::ConstantOp>( + castOp.getLoc(), llvmSrcVal.getType(), + mlir::FloatAttr::get(llvmSrcVal.getType(), 0.0)); + + // Extend comparison result to either bool (C++) or int (C). + rewriter.replaceOpWithNewOp<mlir::LLVM::FCmpOp>(castOp, kind, llvmSrcVal, + zeroFloat); + + return mlir::success(); + } + case cir::CastKind::bool_to_int: { + auto dstTy = mlir::cast<cir::IntType>(castOp.getType()); + auto llvmSrcVal = adaptor.getOperands().front(); + auto llvmSrcTy = mlir::cast<mlir::IntegerType>(llvmSrcVal.getType()); + auto llvmDstTy = + mlir::cast<mlir::IntegerType>(getTypeConverter()->convertType(dstTy)); + if (llvmSrcTy.getWidth() == llvmDstTy.getWidth()) + rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(castOp, llvmDstTy, + llvmSrcVal); + else + rewriter.replaceOpWithNewOp<mlir::LLVM::ZExtOp>(castOp, llvmDstTy, + llvmSrcVal); + return mlir::success(); + } + case cir::CastKind::bool_to_float: { + auto dstTy = castOp.getType(); + auto llvmSrcVal = adaptor.getOperands().front(); + auto llvmDstTy = getTypeConverter()->convertType(dstTy); + rewriter.replaceOpWithNewOp<mlir::LLVM::UIToFPOp>(castOp, llvmDstTy, + llvmSrcVal); + return mlir::success(); + } + case cir::CastKind::int_to_float: { + auto dstTy = castOp.getType(); + auto llvmSrcVal = adaptor.getOperands().front(); + auto llvmDstTy = getTypeConverter()->convertType(dstTy); + if (mlir::cast<cir::IntType>(elementTypeIfVector(castOp.getSrc().getType())) + .isSigned()) + rewriter.replaceOpWithNewOp<mlir::LLVM::SIToFPOp>(castOp, llvmDstTy, + llvmSrcVal); + else + rewriter.replaceOpWithNewOp<mlir::LLVM::UIToFPOp>(castOp, llvmDstTy, + llvmSrcVal); + return mlir::success(); + } + case cir::CastKind::float_to_int: { + auto dstTy = castOp.getType(); + auto llvmSrcVal = adaptor.getOperands().front(); + auto llvmDstTy = getTypeConverter()->convertType(dstTy); + if (mlir::cast<cir::IntType>( + elementTypeIfVector(castOp.getResult().getType())) + .isSigned()) + rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(castOp, llvmDstTy, + llvmSrcVal); + else + rewriter.replaceOpWithNewOp<mlir::LLVM::FPToUIOp>(castOp, llvmDstTy, + llvmSrcVal); + return mlir::success(); + } + case cir::CastKind::bitcast: + assert(!MissingFeatures::cxxABI()); + assert(!MissingFeatures::dataMemberType()); + break; + case cir::CastKind::ptr_to_bool: + assert(!cir::MissingFeatures::opCmp()); + break; + case cir::CastKind::address_space: { + auto dstTy = castOp.getType(); + auto llvmSrcVal = adaptor.getOperands().front(); + auto llvmDstTy = getTypeConverter()->convertType(dstTy); + rewriter.replaceOpWithNewOp<mlir::LLVM::AddrSpaceCastOp>(castOp, llvmDstTy, + llvmSrcVal); + break; + } + case cir::CastKind::member_ptr_to_bool: + assert(!MissingFeatures::cxxABI()); + assert(!MissingFeatures::methodType()); + break; + default: { + return castOp.emitError("Unhandled cast kind: ") + << castOp.getKindAttrName(); + } + } + + return mlir::success(); +} + mlir::LogicalResult CIRToLLVMAllocaOpLowering::matchAndRewrite( cir::AllocaOp op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const { @@ -628,6 +834,7 @@ void ConvertCIRToLLVMPass::runOnOperation() { patterns.add<CIRToLLVMLoadOpLowering>(converter, patterns.getContext(), dl); patterns.add<CIRToLLVMStoreOpLowering>(converter, patterns.getContext(), dl); patterns.add<CIRToLLVMGlobalOpLowering>(converter, patterns.getContext(), dl); + patterns.add<CIRToLLVMCastOpLowering>(converter, patterns.getContext(), dl); patterns.add<CIRToLLVMConstantOpLowering>(converter, patterns.getContext(), dl); patterns.add<CIRToLLVMFuncOpLowering>(converter, patterns.getContext()); diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h index a694047e3616b..f7b4ef409874a 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h @@ -22,6 +22,22 @@ namespace direct { mlir::LLVM::Linkage convertLinkage(cir::GlobalLinkageKind linkage); +class CIRToLLVMCastOpLowering : public mlir::OpConversionPattern<cir::CastOp> { + mlir::DataLayout const &dataLayout; + + mlir::Type convertTy(mlir::Type ty) const; + +public: + CIRToLLVMCastOpLowering(const mlir::TypeConverter &typeConverter, + mlir::MLIRContext *context, + mlir::DataLayout const &dataLayout) + : OpConversionPattern(typeConverter, context), dataLayout(dataLayout) {} + + mlir::LogicalResult + matchAndRewrite(cir::CastOp op, OpAdaptor, + mlir::ConversionPatternRewriter &) const override; +}; + class CIRToLLVMReturnOpLowering : public mlir::OpConversionPattern<cir::ReturnOp> { public: diff --git a/clang/test/CIR/CodeGen/cast.cpp b/clang/test/CIR/CodeGen/cast.cpp new file mode 100644 index 0000000000000..29d34e87c398d --- /dev/null +++ b/clang/test/CIR/CodeGen/cast.cpp @@ -0,0 +1,58 @@ +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s + +unsigned char cxxstaticcast_0(unsigned int x) { + return static_cast<unsigned char>(x); +} + +// CHECK: cir.func @cxxstaticcast_0 +// CHECK: %0 = cir.alloca !cir.int<u, 32>, !cir.ptr<!cir.int<u, 32>>, ["x", init] {alignment = 4 : i64} +// CHECK: cir.store %arg0, %0 : !cir.int<u, 32>, !cir.ptr<!cir.int<u, 32>> +// CHECK: %1 = cir.load %0 : !cir.ptr<!cir.int<u, 32>>, !cir.int<u, 32> +// CHECK: %2 = cir.cast(integral, %1 : !cir.int<u, 32>), !cir.int<u, 8> +// CHECK: cir.return %2 : !cir.int<u, 8> +// CHECK: } + + +int cStyleCasts_0(unsigned x1, int x2, float x3, short x4, double x5) { +// CHECK: cir.func @cStyleCasts_0 + + char a = (char)x1; // truncate + // CHECK: %{{[0-9]+}} = cir.cast(integral, %{{[0-9]+}} : !cir.int<u, 32>), !cir.int<s, 8> + + short b = (short)x2; // truncate with sign + // CHECK: %{{[0-9]+}} = cir.cast(integral, %{{[0-9]+}} : !cir.int<s, 32>), !cir.int<s, 16> + + long long c = (long long)x1; // zero extend + // CHECK: %{{[0-9]+}} = cir.cast(integral, %{{[0-9]+}} : !cir.int<u, 32>), !cir.int<s, 64> + + long long d = (long long)x2; // sign extend + // CHECK: %{{[0-9]+}} = cir.cast(integral, %{{[0-9]+}} : !cir.int<s, 32>), !cir.int<s, 64> + + unsigned ui = (unsigned)x2; // sign drop + // CHECK: %{{[0-9]+}} = cir.cast(integral, %{{[0-9]+}} : !cir.int<s, 32>), !cir.int<u, 32> + + int si = (int)x1; // sign add + // CHECK: %{{[0-9]+}} = cir.cast(integral, %{{[0-9]+}} : !cir.int<u, 32>), !cir.int<s, 32> + + unsigned uu = (unsigned)x1; // should not be generated + // CHECK-NOT: %{{[0-9]+}} = cir.cast(integral, %{{[0-9]+}} : !cir.int<u, 32>), !cir.int<u, 32> + + bool ib = (bool)x1; // No checking, because this isn't a regular cast. + + int bi = (int)ib; // bool to int + // CHECK: %{{[0-9]+}} = cir.cast(bool_to_int, %{{[0-9]+}} : !cir.bool), !cir.int<s, 32> + + return 0; +} + +bool cptr(void *d) { + bool x = d; + return x; +} + +// CHECK: cir.func @cptr(%arg0: !cir.ptr<!cir.void> +// CHECK: %0 = cir.alloca !cir.ptr<!cir.void>, !cir.ptr<!cir.ptr<!cir.void>>, ["d", init] {alignment = 8 : i64} + +// CHECK: %2 = cir.load %0 : !cir.ptr<!cir.ptr<!cir.void>>, !cir.ptr<!cir.void> +// CHECK: %3 = cir.cast(ptr_to_bool, %2 : !cir.ptr<!cir.void>), !cir.bool diff --git a/clang/test/CIR/IR/cast.cir b/clang/test/CIR/IR/cast.cir new file mode 100644 index 0000000000000..de3cc37467eff --- /dev/null +++ b/clang/test/CIR/IR/cast.cir @@ -0,0 +1,23 @@ +// RUN: cir-opt %s | cir-opt | FileCheck %s +!s32i = !cir.int<s, 32> + +module { + cir.func @yolo(%arg0 : !s32i) { + %a = cir.cast (int_to_bool, %arg0 : !s32i), !cir.bool + + %0 = cir.const #cir.int<0> : !s32i + cir.return + } + + cir.func @bitcast(%p: !cir.ptr<!s32i>) { + %0 = cir.cast(bitcast, %p : !cir.ptr<!s32i>), !cir.ptr<f32> + cir.return + } +} + +// CHECK: cir.func @yolo(%arg0: !cir.int<s, 32>) +// CHECK: %0 = cir.cast(int_to_bool, %arg0 : !cir.int<s, 32>), !cir.bool +// CHECK: %1 = cir.const #cir.int<0> : !cir.int<s, 32> + +// CHECK: cir.func @bitcast +// CHECK: %0 = cir.cast(bitcast, %arg0 : !cir.ptr<!cir.int<s, 32>>), !cir.ptr<f32> diff --git a/clang/test/CIR/Lowering/cast.cir b/clang/test/CIR/Lowering/cast.cir new file mode 100644 index 0000000000000..b2d0a6d42eeb1 --- /dev/null +++ b/clang/test/CIR/Lowering/cast.cir @@ -0,0 +1,92 @@ +// RUN: cir-opt %s -cir-to-llvm -o %t.cir +// RUN: FileCheck %s --input-file=%t.cir + +!s16i = !cir.int<s, 16> +!s32i = !cir.int<s, 32> +!s64i = !cir.int<s, 64> +!s8i = !cir.int<s, 8> +!u32i = !cir.int<u, 32> +!u8i = !cir.int<u, 8> +!u64i = !cir.int<u, 64> + +module { + cir.func @cStyleCasts(%arg0: !u32i, %arg1: !s32i, %arg2: !cir.float, %arg3: !cir.double) -> !s32i { + // CHECK: llvm.func @cStyleCasts + %0 = cir.alloca !u32i, !cir.ptr<!u32i>, ["x1", init] {alignment = 4 : i64} + %1 = cir.alloca !s32i, !cir.ptr<!s32i>, ["x2", init] {alignment = 4 : i64} + %20 = cir.alloca !s16i, !cir.ptr<!s16i>, ["x4", init] {alignment = 2 : i64} + %2 = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"] {alignment = 4 : i64} + %3 = cir.alloca !s8i, !cir.ptr<!s8i>, ["a", init] {alignment = 1 : i64} + %4 = cir.alloca !s16i, !cir.ptr<!s16i>, ["b", init] {alignment = 2 : i64} + %5 = cir.alloca !s64i, !cir.ptr<!s64i>, ["c", init] {alignment = 8 : i64} + %6 = cir.alloca !s64i, !cir.ptr<!s64i>, ["d", init] {alignment = 8 : i64} + %8 = cir.alloca !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>, ["e", init] {alignment = 8 : i64} + cir.store %arg0, %0 : !u32i, !cir.ptr<!u32i> + cir.store %arg1, %1 : !s32i, !cir.ptr<!s32i> + + // Integer casts. + %9 = cir.load %0 : !cir.ptr<!u32i>, !u32i + %10 = cir.cast(integral, %9 : !u32i), !s8i + // CHECK: %{{[0-9]+}} = llvm.trunc %{{[0-9]+}} : i32 to i8 + cir.store %10, %3 : !s8i, !cir.ptr<!s8i> + %11 = cir.load %1 : !cir.ptr<!s32i>, !s32i + %12 = cir.cast(integral, %11 : !s32i), !s16i + // CHECK: %{{[0-9]+}} = llvm.trunc %{{[0-9]+}} : i32 to i16 + cir.store %12, %4 : !s16i, !cir.ptr<!s16i> + %13 = cir.load %0 : !cir.ptr<!u32i>, !u32i + %14 = cir.cast(integral, %13 : !u32i), !s64i + // CHECK: %{{[0-9]+}} = llvm.zext %{{[0-9]+}} : i32 to i64 + cir.store %14, %5 : !s64i, !cir.ptr<!s64i> + %15 = cir.load %1 : !cir.ptr<!s32i>, !s32i + %16 = cir.cast(integral, %15 : !s32i), !s64i + // CHECK: %{{[0-9]+}} = llvm.sext %{{[0-9]+}} : i32 to i64 + %30 = cir.cast(integral, %arg1 : !s32i), !u32i + // Should not produce a cast. + %32 = cir.cast(integral, %arg0 : !u32i), !s32i + // Should not produce a cast. + %21 = cir.load %20 : !cir.ptr<!s16i>, !s16i + %22 = cir.cast(integral, %21 : !s16i), !u64i + // CHECK: %[[TMP:[0-9]+]] = llvm.sext %{{[0-9]+}} : i16 to i64 + + // Pointer casts. + cir.store %16, %6 : !s64i, !cir.ptr<!s64i> + %23 = cir.cast(int_to_ptr, %22 : !u64i), !cir.ptr<!u8i> + // CHECK: %[[TMP2:[0-9]+]] = llvm.inttoptr %[[TMP]] : i64 to !llvm.ptr + %24 = cir.cast(ptr_to_int, %23 : !cir.ptr<!u8i>), !s32i + // CHECK: %{{[0-9]+}} = llvm.ptrtoint %[[TMP2]] : !llvm.ptr to i32 + %29 = cir.cast(ptr_to_bool, %23 : !cir.ptr<!u8i>), !cir.bool + + // Floating point casts. + %25 = cir.cast(int_to_float, %arg1 : !s32i), !cir.float + // CHECK: %{{.+}} = llvm.sitofp %{{.+}} : i32 to f32 + %26 = cir.cast(int_to_float, %arg0 : !u32i), !cir.float + // CHECK: %{{.+}} = llvm.uitofp %{{.+}} : i32 to f32 + %27 = cir.cast(float_to_int, %arg2 : !cir.float), !s32i + // CHECK: %{{.+}} = llvm.fptosi %{{.+}} : f32 to i32 + %28 = cir.cast(float_to_int, %arg2 : !cir.float), !u32i + // CHECK: %{{.+}} = llvm.fptoui %{{.+}} : f32 to i32 + %18 = cir.const #cir.int<0> : !s32i + // CHECK: %{{.+}} = llvm.fptrunc %{{.+}} : f64 to f32 + %34 = cir.cast(floating, %arg3 : !cir.double), !cir.float + + cir.store %18, %2 : !s32i, !cir.ptr<!s32i> + %19 = cir.load %2 : !cir.ptr<!s32i>, !s32i + cir.return %19 : !s32i + } + + cir.func @testBoolToIntCast(%arg0: !cir.bool) { + // CHECK: llvm.func @testBoolToIntCast + %0 = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["bl", init] {alignment = 1 : i64} + %1 = cir.alloca !u8i, !cir.ptr<!u8i>, ["y", init] {alignment = 1 : i64} + cir.store %arg0, %0 : !cir.bool, !cir.ptr<!cir.bool> + + %2 = cir.load %0 : !cir.ptr<!cir.bool>, !cir.bool + %3 = cir.cast(bool_to_int, %2 : !cir.bool), !u8i + // CHECK: %[[LOAD_BOOL:.*]] = llvm.load %{{.*}} : !llvm.ptr -> i8 + // CHECK: %[[TRUNC:.*]] = llvm.trunc %[[LOAD_BOOL]] : i8 to i1 + // CHECK: %[[EXT:.*]] = llvm.zext %[[TRUNC]] : i1 to i8 + + cir.store %3, %1 : !u8i, !cir.ptr<!u8i> + cir.return + } +} >From 91ae8f963035a28cdc429ae7079e929e886704bd Mon Sep 17 00:00:00 2001 From: Morris Hafner <m...@users.noreply.github.com> Date: Tue, 11 Mar 2025 11:12:54 -0700 Subject: [PATCH 2/2] Update clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp Co-authored-by: Erich Keane <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()) _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits