diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index 017ae0c53a984..32d1af677c62b 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -10,9 +10,11 @@ #define LLVM_CLANG_CIR_DIALECT_BUILDER_CIRBASEBUILDER_H #include "clang/AST/CharUnits.h" +#include "clang/AST/Type.h" #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 +80,60 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return create(loc, val, dst); } + mlir::Value createDummyValue(mlir::Location loc, mlir::Type type, + clang::CharUnits alignment) { + auto addr = createAlloca(loc, getPointerTo(type), type, {}, + getSizeFromCharUnits(getContext(), alignment)); + return createLoad(loc, addr); + } + + //===--------------------------------------------------------------------===// + // 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(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); + } + // // Block handling helpers // ---------------------- diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index c914fe99371ab..2a5caf1bf1f63 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -78,6 +78,156 @@ class LLVMLoweringInfo { class CIR_Op traits = []> : Op, LLVMLoweringInfo; +//===----------------------------------------------------------------------===// +// CastOp +//===----------------------------------------------------------------------===// + +// 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_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"; +} + +def CastOp : CIR_Op<"cast", + [Pure, + DeclareOpInterfaceMethods]> { + // 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.ptr + ``` + }]; + + 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 fcbb2ae3db6aa..6adff30f5c91a 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -77,23 +77,31 @@ struct MissingFeatures { static bool opUnaryPromotionType() { 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 aggValueSlot() { return false; } - static bool unsizedTypes() { return false; } + static bool fpConstraints() { return false; } static bool sanitizers() { return false; } + static bool addHeapAllocSiteMetadata() { return false; } + static bool targetCodeGenInfoGetNullPointer() { return false; } static bool CGFPOptionsRAII() { return false; } // Missing types + static bool dataMemberType() { return false; } + static bool matrixType() { return false; } + static bool methodType() { return false; } + static bool scalableVectors() { return false; } + static bool unsizedTypes() { return false; } static bool vectorType() { return false; } }; diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index 260ee25719be1..85f6a00e596a7 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" #include "clang/CIR/MissingFeatures.h" @@ -43,6 +44,14 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy { assert(!cir::MissingFeatures::unsizedTypes()); return false; } + + bool isInt(mlir::Type i) { return mlir::isa(i); } + + // Creates constant nullptr for pointer type ty. + cir::ConstantOp getNullPtr(mlir::Type ty, mlir::Location loc) { + assert(!cir::MissingFeatures::targetCodeGenInfoGetNullPointer()); + return create(loc, ty, getConstPtrAttr(ty, 0)); + } }; } // namespace clang::CIRGen diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 24c0c8a18efd8..1c529b9efc84b 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -264,6 +264,13 @@ mlir::Value CIRGenFunction::emitAlloca(StringRef name, mlir::Type ty, return addr; } +mlir::Value CIRGenFunction::createDummyValue(mlir::Location loc, + clang::QualType qt) { + mlir::Type t = convertType(qt); + CharUnits alignment = getContext().getTypeAlignInChars(qt); + return builder.createDummyValue(loc, t, alignment); +} + /// This creates an alloca and inserts it at the current insertion point of the /// builder. Address CIRGenFunction::createTempAlloca(mlir::Type ty, CharUnits align, diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index a0fe456d4ff71..726062b805775 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -32,9 +32,8 @@ class ScalarExprEmitter : public StmtVisitor { bool ignoreResultAssign; public: - ScalarExprEmitter(CIRGenFunction &cgf, CIRGenBuilderTy &builder, - bool ira = false) - : cgf(cgf), builder(builder), ignoreResultAssign(ira) {} + ScalarExprEmitter(CIRGenFunction &cgf, CIRGenBuilderTy &builder) + : cgf(cgf), builder(builder) {} //===--------------------------------------------------------------------===// // Visitor Methods @@ -90,7 +89,135 @@ class ScalarExprEmitter : public StmtVisitor { 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) { + cir::BoolType boolTy = builder.getBoolTy(); + return builder.create(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? + cir::BoolType boolTy = builder.getBoolTy(); + return builder.create(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 (llvm::isa(srcType)) { + cgf.getCIRGenModule().errorNYI(loc, "member pointer to bool conversion"); + mlir::Type boolType = builder.getBoolTy(); + return builder.create(loc, boolType, + builder.getCIRBoolAttr(false)); + } + + if (srcType->isIntegerType()) + return emitIntToBoolConversion(src, loc); + + assert(::mlir::isa(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."); + assert(!(mlir::isa(srcTy) || + mlir::isa(dstTy)) && + "Obsolete code. Don't use mlir::IntegerType with CIR."); + + mlir::Type fullDstTy = dstTy; + assert(!cir::MissingFeatures::vectorType()); + + std::optional castKind; + + if (mlir::isa(srcTy)) { + if (opts.treatBooleanAsSigned) + cgf.getCIRGenModule().errorNYI("signed bool"); + if (cgf.getBuilder().isInt(dstTy)) + castKind = cir::CastKind::bool_to_int; + else if (mlir::isa(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(dstTy)) + castKind = cir::CastKind::int_to_float; + else + llvm_unreachable("Internal error: Cast to unexpected type"); + } else if (mlir::isa(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(dstTy)) { + cgf.getCIRGenModule().errorNYI("floating point casts"); + return cgf.createDummyValue(src.getLoc(), dstType); + } 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(src.getLoc(), fullDstTy, *castKind, src); + } mlir::Value VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *e); @@ -302,18 +429,134 @@ class ScalarExprEmitter : public StmtVisitor { /// 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() || dstType->isFixedPointType()) { + cgf.getCIRGenModule().errorNYI(loc, "fixed point conversions"); + return {}; + } + 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; + } - cgf.getCIRGenModule().errorNYI(loc, - "emitScalarConversion for unequal types"); - return {}; + if (dstType->isVoidType()) + return {}; + + 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 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(mlirDstType)) { + 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"); + // 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; + mlirSrcType = cgf.FloatTy; + } + } + + // TODO(cir): LLVM codegen ignore conversions like int -> uint, + // is there anything to be done for CIR here? + if (mlirSrcType == mlirDstType) { + 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(mlirDstType)) { + cgf.getCIRGenModule().errorNYI(loc, "pointer casts"); + return builder.getNullPtr(dstPT, src.getLoc()); + } + + if (isa(mlirSrcType)) { + // Must be an ptr to int cast. + assert(isa(mlirDstType) && "not ptr->int?"); + return builder.createPtrToInt(src, mlirDstType); + } + + // 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()->getElementType().getTypePtr() == + srcType.getTypePtr() && + "Splatted expr doesn't match with vector element type?"); + + cgf.getCIRGenModule().errorNYI(loc, "vector splatting"); + return {}; + } + + if (srcType->isMatrixType() && dstType->isMatrixType()) { + cgf.getCIRGenModule().errorNYI(loc, + "matrix type to matrix type conversion"); + return {}; + } + 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 = mlirDstType; + + res = emitScalarCast(src, srcType, dstType, mlirSrcType, mlirDstType, opts); + + if (mlirDstType != resTy) { + if (cgf.getContext().getTargetInfo().useFP16ConversionIntrinsics()) { + cgf.getCIRGenModule().errorNYI(loc, "cast via llvm.convert.to.fp16"); + } + // 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) + cgf.getCIRGenModule().errorNYI(loc, "implicit integer truncation checks"); + + if (opts.emitImplicitIntegerSignChangeChecks) + cgf.getCIRGenModule().errorNYI(loc, + "implicit integer sign change checks"); + + return res; } }; @@ -327,29 +570,173 @@ mlir::Value CIRGenFunction::emitScalarExpr(const Expr *e) { return ScalarExprEmitter(*this, builder).Visit(const_cast(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 // 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. + ignoreResultAssign = false; + 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: { + mlir::Value src = Visit(const_cast(subExpr)); + mlir::Type dstTy = cgf.convertType(destTy); + + assert(!cir::MissingFeatures::addressSpace()); + + if (cgf.sanOpts.has(SanitizerKind::CFIUnrelatedCast)) + cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(), + "sanitizer support"); + + if (cgf.cgm.getCodeGenOpts().StrictVTablePointers) + cgf.getCIRGenModule().errorNYI(subExpr->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(subExpr->getSourceRange()), + src, dstTy); + } + + case CK_AtomicToNonAtomic: { + cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(), + "CastExpr: ", ce->getCastKindName()); + mlir::Location loc = cgf.getLoc(subExpr->getSourceRange()); + return cgf.createDummyValue(loc, destTy); + } + case CK_NonAtomicToAtomic: + case CK_UserDefinedConversion: + return Visit(const_cast(subExpr)); + case CK_NoOp: { + auto v = Visit(const_cast(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. + mlir::Type t = cgf.convertType(destTy); + if (t != v.getType()) + cgf.getCIRGenModule().errorNYI("pointer qualification conversion"); + } + return v; + } + + case CK_NullToPointer: { + if (MustVisitNullValue(subExpr)) + cgf.emitIgnoredExpr(subExpr); + + // Note that DestTy is used as the MLIR type instead of a custom + // nullptr type. + 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(e)); + assert(cgf.getContext().hasSameUnqualifiedType(subExpr->getType(), destTy)); + assert(subExpr->isGLValue() && "lvalue-to-rvalue applied to r-value!"); + return Visit(const_cast(subExpr)); case CK_IntegralCast: { - assert(!cir::MissingFeatures::scalarConversionOpts()); - return emitScalarConversion(Visit(e), e->getType(), destTy, + ScalarConversionOpts opts; + if (auto *ice = dyn_cast(ce)) { + if (!ice->isPartOfExplicitCast()) + opts = ScalarConversionOpts(cgf.sanOpts); + } + return emitScalarConversion(Visit(subExpr), subExpr->getType(), destTy, + 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) + cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(), + "strict vtable pointers"); + return builder.createPtrToInt(Visit(subExpr), cgf.convertType(destTy)); + } + case CK_ToVoid: + cgf.emitIgnoredExpr(subExpr); + return {}; + + 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())); + + case CK_PointerToBoolean: + return emitPointerToBoolConversion(Visit(subExpr), subExpr->getType()); + case CK_FloatingToBoolean: + return emitFloatToBoolConversion(Visit(subExpr), + cgf.getLoc(subExpr->getExprLoc())); + case CK_MemberPointerToBoolean: { + mlir::Value memPtr = Visit(subExpr); + return builder.createCast(cgf.getLoc(ce->getSourceRange()), + cir::CastKind::member_ptr_to_bool, memPtr, + cgf.convertType(destTy)); + } + default: - cgf.getCIRGenModule().errorNYI(e->getSourceRange(), + cgf.getCIRGenModule().errorNYI(subExpr->getSourceRange(), "CastExpr: ", ce->getCastKindName()); } return {}; diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 3542b6cafbc9c..f931da32d51db 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); @@ -106,6 +109,8 @@ class CIRGenFunction : public CIRGenTypeCache { mlir::Value emitAlloca(llvm::StringRef name, mlir::Type ty, mlir::Location loc, clang::CharUnits alignment); + mlir::Value createDummyValue(mlir::Location loc, clang::QualType qt); + private: // Track current variable initialization (if there's one) const clang::VarDecl *currVarDecl = nullptr; diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index faf8996434f74..56247e2466350 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(resType)) + return emitOpError() << "requires !cir.bool type for result"; + if (!mlir::isa(srcType)) + return emitOpError() << "requires !cir.int type for source"; + return success(); + } + case cir::CastKind::ptr_to_bool: { + if (!mlir::isa(resType)) + return emitOpError() << "requires !cir.bool type for result"; + if (!mlir::isa(srcType)) + return emitOpError() << "requires !cir.ptr type for source"; + return success(); + } + case cir::CastKind::integral: { + if (!mlir::isa(resType)) + return emitOpError() << "requires !cir.int type for result"; + if (!mlir::isa(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(srcType); + auto resPtrTy = mlir::dyn_cast(resType); + + if (srcPtrTy && resPtrTy) { + return success(); + } + + return success(); + } + case cir::CastKind::floating: { + if (!mlir::isa(srcType) || + !mlir::isa(resType)) + return emitOpError() << "requires !cir.float type for source and result"; + return success(); + } + case cir::CastKind::float_to_int: { + if (!mlir::isa(srcType)) + return emitOpError() << "requires !cir.float type for source"; + if (!mlir::dyn_cast(resType)) + return emitOpError() << "requires !cir.int type for result"; + return success(); + } + case cir::CastKind::int_to_ptr: { + if (!mlir::dyn_cast(srcType)) + return emitOpError() << "requires !cir.int type for source"; + if (!mlir::dyn_cast(resType)) + return emitOpError() << "requires !cir.ptr type for result"; + return success(); + } + case cir::CastKind::ptr_to_int: { + if (!mlir::dyn_cast(srcType)) + return emitOpError() << "requires !cir.ptr type for source"; + if (!mlir::dyn_cast(resType)) + return emitOpError() << "requires !cir.int type for result"; + return success(); + } + case cir::CastKind::float_to_bool: { + if (!mlir::isa(srcType)) + return emitOpError() << "requires !cir.float type for source"; + if (!mlir::isa(resType)) + return emitOpError() << "requires !cir.bool type for result"; + return success(); + } + case cir::CastKind::bool_to_int: { + if (!mlir::isa(srcType)) + return emitOpError() << "requires !cir.bool type for source"; + if (!mlir::isa(resType)) + return emitOpError() << "requires !cir.int type for result"; + return success(); + } + case cir::CastKind::int_to_float: { + if (!mlir::isa(srcType)) + return emitOpError() << "requires !cir.int type for source"; + if (!mlir::isa(resType)) + return emitOpError() << "requires !cir.float type for result"; + return success(); + } + case cir::CastKind::bool_to_float: { + if (!mlir::isa(srcType)) + return emitOpError() << "requires !cir.bool type for source"; + if (!mlir::isa(resType)) + return emitOpError() << "requires !cir.float type for result"; + return success(); + } + case cir::CastKind::address_space: { + auto srcPtrTy = mlir::dyn_cast(srcType); + auto resPtrTy = mlir::dyn_cast(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(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 foldResults; + auto foldOrder = getSrc().getDefiningOp()->fold(foldResults); + if (foldOrder.succeeded() && mlir::isa(foldResults[0])) + return mlir::cast(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 &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 &blockingUses, + SmallVectorImpl &newBlockingUses, + const DataLayout &dataLayout) { + if (getKind() == cir::CastKind::bitcast) + return forwardToUsers(*this, newBlockingUses); + return false; +} + +DeletionKind cir::CastOp::removeBlockingUses( + const SmallPtrSetImpl &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 3bc2d89852500..93d83f2e822ca 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(loc, llvmDstIntTy, llvmSrc); + return rewriter.create(loc, llvmDstIntTy, llvmSrc); + } + + // Otherwise truncate + return rewriter.create(loc, llvmDstIntTy, llvmSrc); +} + class CIRAttrToValue { public: CIRAttrToValue(mlir::Operation *parentOp, @@ -247,6 +280,185 @@ 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(castOp.getType()); + mlir::Value sourceValue = adaptor.getOperands().front(); + mlir::Type targetType = convertTy(ptrTy); + mlir::Type elementTy = convertTypeForMemory(*getTypeConverter(), dataLayout, + ptrTy.getPointee()); + llvm::SmallVector offset{0}; + rewriter.replaceOpWithNewOp( + castOp, targetType, elementTy, sourceValue, offset); + break; + } + case cir::CastKind::int_to_bool: { + assert(!cir::MissingFeatures::opCmp()); + mlir::Type dstType = castOp.getResult().getType(); + mlir::Type llvmDstType = getTypeConverter()->convertType(dstType); + auto zeroBool = rewriter.create( + castOp.getLoc(), llvmDstType, mlir::BoolAttr::get(getContext(), false)); + rewriter.replaceOp(castOp, zeroBool); + return castOp.emitError() << "NYI int_to_bool cast"; + } + case cir::CastKind::integral: { + mlir::Type srcType = castOp.getSrc().getType(); + mlir::Type dstType = castOp.getResult().getType(); + mlir::Value llvmSrcVal = adaptor.getOperands().front(); + mlir::Type llvmDstType = getTypeConverter()->convertType(dstType); + cir::IntType srcIntType = + mlir::cast(elementTypeIfVector(srcType)); + cir::IntType dstIntType = + mlir::cast(elementTypeIfVector(dstType)); + rewriter.replaceOp(castOp, getLLVMIntCast(rewriter, llvmSrcVal, llvmDstType, + srcIntType.isUnsigned(), + srcIntType.getWidth(), + dstIntType.getWidth())); + break; + } + case cir::CastKind::floating: { + mlir::Value llvmSrcVal = adaptor.getOperands().front(); + mlir::Type llvmDstTy = + getTypeConverter()->convertType(castOp.getResult().getType()); + + mlir::Type srcTy = elementTypeIfVector(castOp.getSrc().getType()); + mlir::Type dstTy = elementTypeIfVector(castOp.getResult().getType()); + + if (!mlir::isa(dstTy) || + !mlir::isa(srcTy)) + return castOp.emitError() << "NYI cast from " << srcTy << " to " << dstTy; + + auto getFloatWidth = [](mlir::Type ty) -> unsigned { + return mlir::cast(ty).getWidth(); + }; + + if (getFloatWidth(srcTy) > getFloatWidth(dstTy)) + rewriter.replaceOpWithNewOp(castOp, llvmDstTy, + llvmSrcVal); + else + rewriter.replaceOpWithNewOp(castOp, llvmDstTy, + llvmSrcVal); + return mlir::success(); + } + case cir::CastKind::int_to_ptr: { + auto dstTy = mlir::cast(castOp.getType()); + mlir::Value llvmSrcVal = adaptor.getOperands().front(); + mlir::Type llvmDstTy = getTypeConverter()->convertType(dstTy); + rewriter.replaceOpWithNewOp(castOp, llvmDstTy, + llvmSrcVal); + return mlir::success(); + } + case cir::CastKind::ptr_to_int: { + auto dstTy = mlir::cast(castOp.getType()); + mlir::Value llvmSrcVal = adaptor.getOperands().front(); + mlir::Type llvmDstTy = getTypeConverter()->convertType(dstTy); + rewriter.replaceOpWithNewOp(castOp, llvmDstTy, + llvmSrcVal); + return mlir::success(); + } + case cir::CastKind::float_to_bool: { + mlir::Value llvmSrcVal = adaptor.getOperands().front(); + auto kind = mlir::LLVM::FCmpPredicate::une; + + // Check if float is not equal to zero. + auto zeroFloat = rewriter.create( + castOp.getLoc(), llvmSrcVal.getType(), + mlir::FloatAttr::get(llvmSrcVal.getType(), 0.0)); + + // Extend comparison result to either bool (C++) or int (C). + rewriter.replaceOpWithNewOp(castOp, kind, llvmSrcVal, + zeroFloat); + + return mlir::success(); + } + case cir::CastKind::bool_to_int: { + auto dstTy = mlir::cast(castOp.getType()); + mlir::Value llvmSrcVal = adaptor.getOperands().front(); + auto llvmSrcTy = mlir::cast(llvmSrcVal.getType()); + auto llvmDstTy = + mlir::cast(getTypeConverter()->convertType(dstTy)); + if (llvmSrcTy.getWidth() == llvmDstTy.getWidth()) + rewriter.replaceOpWithNewOp(castOp, llvmDstTy, + llvmSrcVal); + else + rewriter.replaceOpWithNewOp(castOp, llvmDstTy, + llvmSrcVal); + return mlir::success(); + } + case cir::CastKind::bool_to_float: { + mlir::Type dstTy = castOp.getType(); + mlir::Value llvmSrcVal = adaptor.getOperands().front(); + mlir::Type llvmDstTy = getTypeConverter()->convertType(dstTy); + rewriter.replaceOpWithNewOp(castOp, llvmDstTy, + llvmSrcVal); + return mlir::success(); + } + case cir::CastKind::int_to_float: { + mlir::Type dstTy = castOp.getType(); + mlir::Value llvmSrcVal = adaptor.getOperands().front(); + mlir::Type llvmDstTy = getTypeConverter()->convertType(dstTy); + if (mlir::cast(elementTypeIfVector(castOp.getSrc().getType())) + .isSigned()) + rewriter.replaceOpWithNewOp(castOp, llvmDstTy, + llvmSrcVal); + else + rewriter.replaceOpWithNewOp(castOp, llvmDstTy, + llvmSrcVal); + return mlir::success(); + } + case cir::CastKind::float_to_int: { + mlir::Type dstTy = castOp.getType(); + mlir::Value llvmSrcVal = adaptor.getOperands().front(); + mlir::Type llvmDstTy = getTypeConverter()->convertType(dstTy); + if (mlir::cast( + elementTypeIfVector(castOp.getResult().getType())) + .isSigned()) + rewriter.replaceOpWithNewOp(castOp, llvmDstTy, + llvmSrcVal); + else + rewriter.replaceOpWithNewOp(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: { + mlir::Type dstTy = castOp.getType(); + mlir::Value llvmSrcVal = adaptor.getOperands().front(); + mlir::Type llvmDstTy = getTypeConverter()->convertType(dstTy); + rewriter.replaceOpWithNewOp(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 { @@ -833,6 +1045,7 @@ void ConvertCIRToLLVMPass::runOnOperation() { patterns.add(converter, patterns.getContext(), dl); patterns.add(converter, patterns.getContext(), dl); patterns.add(converter, patterns.getContext(), dl); + patterns.add(converter, patterns.getContext(), dl); patterns.add(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 60518e55348e1..b96e16113abe0 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 { + 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 { public: diff --git a/clang/test/CIR/CodeGen/cast.cpp b/clang/test/CIR/CodeGen/cast.cpp new file mode 100644 index 0000000000000..b25a0cdb4b055 --- /dev/null +++ b/clang/test/CIR/CodeGen/cast.cpp @@ -0,0 +1,100 @@ +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -DCIR_ONLY %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM + +unsigned char cxxstaticcast_0(unsigned int x) { + return static_cast(x); +} + +// CIR: cir.func @cxxstaticcast_0 +// CIR: %[[XPTR:[0-9]+]] = cir.alloca !cir.int, !cir.ptr>, ["x", init] {alignment = 4 : i64} +// CIR: cir.store %arg0, %[[XPTR]] : !cir.int, !cir.ptr> +// CIR: %[[XVAL:[0-9]+]] = cir.load %[[XPTR]] : !cir.ptr>, !cir.int +// CIR: %[[CASTED:[0-9]+]] = cir.cast(integral, %[[XVAL]] : !cir.int), !cir.int +// CIR: cir.return %[[CASTED]] : !cir.int +// CIR: } + +// LLVM: define i8 @cxxstaticcast_0(i32 %{{[0-9]+}}) +// LLVM: %[[LOAD:[0-9]+]] = load i32, ptr %{{[0-9]+}}, align 4 +// LLVM: %[[TRUNC:[0-9]+]] = trunc i32 %[[LOAD]] to i8 +// LLVM: ret i8 %[[TRUNC]] + + +int cStyleCasts_0(unsigned x1, int x2, float x3, short x4, double x5) { +// CIR: cir.func @cStyleCasts_0 +// LLVM: define i32 @cStyleCasts_0 + + char a = (char)x1; // truncate + // CIR: %{{[0-9]+}} = cir.cast(integral, %{{[0-9]+}} : !cir.int), !cir.int + // LLVM: %{{[0-9]+}} = trunc i32 %{{[0-9]+}} to i8 + + short b = (short)x2; // truncate with sign + // CIR: %{{[0-9]+}} = cir.cast(integral, %{{[0-9]+}} : !cir.int), !cir.int + // LLVM: %{{[0-9]+}} = trunc i32 %{{[0-9]+}} to i16 + + long long c = (long long)x1; // zero extend + // CIR: %{{[0-9]+}} = cir.cast(integral, %{{[0-9]+}} : !cir.int), !cir.int + // LLVM: %{{[0-9]+}} = zext i32 %{{[0-9]+}} to i64 + + long long d = (long long)x2; // sign extend + // CIR: %{{[0-9]+}} = cir.cast(integral, %{{[0-9]+}} : !cir.int), !cir.int + // LLVM: %{{[0-9]+}} = sext i32 %{{[0-9]+}} to i64 + + unsigned ui = (unsigned)x2; // sign drop + // CIR: %{{[0-9]+}} = cir.cast(integral, %{{[0-9]+}} : !cir.int), !cir.int + + int si = (int)x1; // sign add + // CIR: %{{[0-9]+}} = cir.cast(integral, %{{[0-9]+}} : !cir.int), !cir.int + + bool ib; + int bi = (int)ib; // bool to int + // CIR: %{{[0-9]+}} = cir.cast(bool_to_int, %{{[0-9]+}} : !cir.bool), !cir.int + // LLVM: %{{[0-9]+}} = zext i1 %{{[0-9]+}} to i32 + + #ifdef CIR_ONLY + bool b2 = x2; // int to bool + // CIR: %{{[0-9]+}} = cir.cast(int_to_bool, %{{[0-9]+}} : !cir.int), !cir.bool + #endif + + #ifdef CIR_ONLY + void *p; + bool b3 = p; // ptr to bool + // CIR: %{{[0-9]+}} = cir.cast(ptr_to_bool, %{{[0-9]+}} : !cir.ptr), !cir.bool + #endif + + float f; + bool b4 = f; // float to bool + // CIR: %{{[0-9]+}} = cir.cast(float_to_bool, %{{[0-9]+}} : !cir.float), !cir.bool + // LLVM: %{{[0-9]+}} = fcmp une float %{{[0-9]+}}, 0.000000e+00 + // LLVM: %{{[0-9]+}} = zext i1 %{{[0-9]+}} to i8 + + return 0; +} + +#ifdef CIR_ONLY +bool cptr(void *d) { + bool x = d; + return x; +} + +// CIR: cir.func @cptr(%arg0: !cir.ptr +// CIR: %[[DPTR:[0-9]+]] = cir.alloca !cir.ptr, !cir.ptr>, ["d", init] {alignment = 8 : i64} + +// CIR: %[[DVAL:[0-9]+]] = cir.load %[[DPTR]] : !cir.ptr>, !cir.ptr +// CIR: %{{[0-9]+}} = cir.cast(ptr_to_bool, %[[DVAL]] : !cir.ptr), !cir.bool +#endif + +void should_not_cast() { + unsigned x1; + unsigned uu = (unsigned)x1; // identity + + bool x2; + bool ib = (bool)x2; // identity + + (void) ib; // void cast +} + +// CIR: cir.func @should_not_cast +// CIR-NOT: cir.cast +// CIR: cir.return 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 + +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) { + %0 = cir.cast(bitcast, %p : !cir.ptr), !cir.ptr + cir.return + } +} + +// CHECK: cir.func @yolo(%arg0: !cir.int) +// CHECK: %0 = cir.cast(int_to_bool, %arg0 : !cir.int), !cir.bool +// CHECK: %1 = cir.const #cir.int<0> : !cir.int + +// CHECK: cir.func @bitcast +// CHECK: %0 = cir.cast(bitcast, %arg0 : !cir.ptr>), !cir.ptr 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 +!s32i = !cir.int +!s64i = !cir.int +!s8i = !cir.int +!u32i = !cir.int +!u8i = !cir.int +!u64i = !cir.int + +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, ["x1", init] {alignment = 4 : i64} + %1 = cir.alloca !s32i, !cir.ptr, ["x2", init] {alignment = 4 : i64} + %20 = cir.alloca !s16i, !cir.ptr, ["x4", init] {alignment = 2 : i64} + %2 = cir.alloca !s32i, !cir.ptr, ["__retval"] {alignment = 4 : i64} + %3 = cir.alloca !s8i, !cir.ptr, ["a", init] {alignment = 1 : i64} + %4 = cir.alloca !s16i, !cir.ptr, ["b", init] {alignment = 2 : i64} + %5 = cir.alloca !s64i, !cir.ptr, ["c", init] {alignment = 8 : i64} + %6 = cir.alloca !s64i, !cir.ptr, ["d", init] {alignment = 8 : i64} + %8 = cir.alloca !cir.ptr, !cir.ptr>, ["e", init] {alignment = 8 : i64} + cir.store %arg0, %0 : !u32i, !cir.ptr + cir.store %arg1, %1 : !s32i, !cir.ptr + + // Integer casts. + %9 = cir.load %0 : !cir.ptr, !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 + %11 = cir.load %1 : !cir.ptr, !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 + %13 = cir.load %0 : !cir.ptr, !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 + %15 = cir.load %1 : !cir.ptr, !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 + %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 + %23 = cir.cast(int_to_ptr, %22 : !u64i), !cir.ptr + // CHECK: %[[TMP2:[0-9]+]] = llvm.inttoptr %[[TMP]] : i64 to !llvm.ptr + %24 = cir.cast(ptr_to_int, %23 : !cir.ptr), !s32i + // CHECK: %{{[0-9]+}} = llvm.ptrtoint %[[TMP2]] : !llvm.ptr to i32 + %29 = cir.cast(ptr_to_bool, %23 : !cir.ptr), !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 + %19 = cir.load %2 : !cir.ptr, !s32i + cir.return %19 : !s32i + } + + cir.func @testBoolToIntCast(%arg0: !cir.bool) { + // CHECK: llvm.func @testBoolToIntCast + %0 = cir.alloca !cir.bool, !cir.ptr, ["bl", init] {alignment = 1 : i64} + %1 = cir.alloca !u8i, !cir.ptr, ["y", init] {alignment = 1 : i64} + cir.store %arg0, %0 : !cir.bool, !cir.ptr + + %2 = cir.load %0 : !cir.ptr, !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 + cir.return + } +}