diff --git a/clang/docs/HLSL/ExpectedDifferences.rst b/clang/docs/HLSL/ExpectedDifferences.rst index d1b6010f10f43..a29b6348e0b8e 100644 --- a/clang/docs/HLSL/ExpectedDifferences.rst +++ b/clang/docs/HLSL/ExpectedDifferences.rst @@ -67,12 +67,16 @@ behavior between Clang and DXC. Some examples include: void takesDoubles(double, double, double); cbuffer CB { + bool B; uint U; int I; float X, Y, Z; double3 A, B; } + void twoParams(int, int); + void twoParams(float, float); + export void call() { halfOrInt16(U); // DXC: Fails with call ambiguous between int16_t and uint16_t overloads // Clang: Resolves to halfOrInt16(uint16_t). @@ -98,6 +102,13 @@ behavior between Clang and DXC. Some examples include: // FXC: Expands to compute double dot product with fmul/fadd // Clang: Resolves to dot(float3, float3), emits conversion warnings. + #ifndef IGNORE_ERRORS + tan(B); // DXC: resolves to tan(float). + // Clang: Fails to resolve, ambiguous between integer types. + + twoParams(I, X); // DXC: resolves twoParams(int, int). + // Clang: Fails to resolve ambiguous conversions. + #endif } .. note:: diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h index 4a5c9e8ca1229..9d8b797af6663 100644 --- a/clang/include/clang/Sema/Overload.h +++ b/clang/include/clang/Sema/Overload.h @@ -201,6 +201,9 @@ class Sema; /// HLSL non-decaying array rvalue cast. ICK_HLSL_Array_RValue, + // HLSL vector splat from scalar or boolean type. + ICK_HLSL_Vector_Splat, + /// The number of conversion kinds ICK_Num_Conversion_Kinds, }; @@ -213,15 +216,27 @@ class Sema; /// Exact Match ICR_Exact_Match = 0, + /// HLSL Scalar Widening + ICR_HLSL_Scalar_Widening, + /// Promotion ICR_Promotion, + /// HLSL Scalar Widening with promotion + ICR_HLSL_Scalar_Widening_Promotion, + + /// HLSL Matching Dimension Reduction + ICR_HLSL_Dimension_Reduction, + /// Conversion ICR_Conversion, /// OpenCL Scalar Widening ICR_OCL_Scalar_Widening, + /// HLSL Scalar Widening with conversion + ICR_HLSL_Scalar_Widening_Conversion, + /// Complex <-> Real conversion ICR_Complex_Real_Conversion, @@ -233,11 +248,21 @@ class Sema; /// Conversion not allowed by the C standard, but that we accept as an /// extension anyway. - ICR_C_Conversion_Extension + ICR_C_Conversion_Extension, + + /// HLSL Dimension reduction with promotion + ICR_HLSL_Dimension_Reduction_Promotion, + + /// HLSL Dimension reduction with conversion + ICR_HLSL_Dimension_Reduction_Conversion, }; ImplicitConversionRank GetConversionRank(ImplicitConversionKind Kind); + ImplicitConversionRank + GetDimensionConversionRank(ImplicitConversionRank Base, + ImplicitConversionKind Dimension); + /// NarrowingKind - The kind of narrowing conversion being performed by a /// standard conversion sequence according to C++11 [dcl.init.list]p7. enum NarrowingKind { @@ -277,11 +302,10 @@ class Sema; /// pointer-to-member conversion, or boolean conversion. ImplicitConversionKind Second : 8; - /// Element - Between the second and third conversion a vector or matrix - /// element conversion may occur. If this is not ICK_Identity this - /// conversion is applied element-wise to each element in the vector or - /// matrix. - ImplicitConversionKind Element : 8; + /// Dimension - Between the second and third conversion a vector or matrix + /// dimension conversion may occur. If this is not ICK_Identity this + /// conversion truncates the vector or matrix, or extends a scalar. + ImplicitConversionKind Dimension : 8; /// Third - The third conversion can be a qualification conversion /// or a function conversion. @@ -379,7 +403,7 @@ class Sema; void setAsIdentityConversion(); bool isIdentityConversion() const { - return Second == ICK_Identity && Element == ICK_Identity && + return Second == ICK_Identity && Dimension == ICK_Identity && Third == ICK_Identity; } diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index d0e963441d79a..bef7da239e6e5 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -4294,6 +4294,20 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, return From; } +// adjustVectorType - Compute the intermediate cast type casting elements of the +// from type to the elements of the to type without resizing the vector. +static QualType adjustVectorType(ASTContext &Context, QualType FromTy, + QualType ToType, QualType *ElTy = nullptr) { + auto *ToVec = ToType->castAs(); + QualType ElType = ToVec->getElementType(); + if (ElTy) + *ElTy = ElType; + if (!FromTy->isVectorType()) + return ElType; + auto *FromVec = FromTy->castAs(); + return Context.getExtVectorType(ElType, FromVec->getNumElements()); +} + ExprResult Sema::PerformImplicitConversion(Expr *From, QualType ToType, const StandardConversionSequence& SCS, @@ -4443,27 +4457,36 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, break; case ICK_Integral_Promotion: - case ICK_Integral_Conversion: - if (ToType->isBooleanType()) { + case ICK_Integral_Conversion: { + QualType ElTy = ToType; + QualType StepTy = ToType; + if (ToType->isVectorType()) + StepTy = adjustVectorType(Context, FromType, ToType, &ElTy); + if (ElTy->isBooleanType()) { assert(FromType->castAs()->getDecl()->isFixed() && SCS.Second == ICK_Integral_Promotion && "only enums with fixed underlying type can promote to bool"); - From = ImpCastExprToType(From, ToType, CK_IntegralToBoolean, VK_PRValue, + From = ImpCastExprToType(From, StepTy, CK_IntegralToBoolean, VK_PRValue, /*BasePath=*/nullptr, CCK) .get(); } else { - From = ImpCastExprToType(From, ToType, CK_IntegralCast, VK_PRValue, + From = ImpCastExprToType(From, StepTy, CK_IntegralCast, VK_PRValue, /*BasePath=*/nullptr, CCK) .get(); } break; + } case ICK_Floating_Promotion: - case ICK_Floating_Conversion: - From = ImpCastExprToType(From, ToType, CK_FloatingCast, VK_PRValue, + case ICK_Floating_Conversion: { + QualType StepTy = ToType; + if (ToType->isVectorType()) + StepTy = adjustVectorType(Context, FromType, ToType); + From = ImpCastExprToType(From, StepTy, CK_FloatingCast, VK_PRValue, /*BasePath=*/nullptr, CCK) .get(); break; + } case ICK_Complex_Promotion: case ICK_Complex_Conversion: { @@ -4486,16 +4509,21 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, break; } - case ICK_Floating_Integral: - if (ToType->isRealFloatingType()) - From = ImpCastExprToType(From, ToType, CK_IntegralToFloating, VK_PRValue, + case ICK_Floating_Integral: { + QualType ElTy = ToType; + QualType StepTy = ToType; + if (ToType->isVectorType()) + StepTy = adjustVectorType(Context, FromType, ToType, &ElTy); + if (ElTy->isRealFloatingType()) + From = ImpCastExprToType(From, StepTy, CK_IntegralToFloating, VK_PRValue, /*BasePath=*/nullptr, CCK) .get(); else - From = ImpCastExprToType(From, ToType, CK_FloatingToIntegral, VK_PRValue, + From = ImpCastExprToType(From, StepTy, CK_FloatingToIntegral, VK_PRValue, /*BasePath=*/nullptr, CCK) .get(); break; + } case ICK_Fixed_Point_Conversion: assert((FromType->isFixedPointType() || ToType->isFixedPointType()) && @@ -4617,18 +4645,26 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, break; } - case ICK_Boolean_Conversion: + case ICK_Boolean_Conversion: { // Perform half-to-boolean conversion via float. if (From->getType()->isHalfType()) { From = ImpCastExprToType(From, Context.FloatTy, CK_FloatingCast).get(); FromType = Context.FloatTy; } + QualType ElTy = FromType; + QualType StepTy = ToType; + if (FromType->isVectorType()) { + if (getLangOpts().HLSL) + StepTy = adjustVectorType(Context, FromType, ToType); + ElTy = FromType->castAs()->getElementType(); + } - From = ImpCastExprToType(From, Context.BoolTy, - ScalarTypeToBooleanCastKind(FromType), VK_PRValue, + From = ImpCastExprToType(From, StepTy, ScalarTypeToBooleanCastKind(ElTy), + VK_PRValue, /*BasePath=*/nullptr, CCK) .get(); break; + } case ICK_Derived_To_Base: { CXXCastPath BasePath; @@ -4754,22 +4790,6 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, CK_ZeroToOCLOpaqueType, From->getValueKind()).get(); break; - case ICK_HLSL_Vector_Truncation: { - // Note: HLSL built-in vectors are ExtVectors. Since this truncates a vector - // to a smaller vector, this can only operate on arguments where the source - // and destination types are ExtVectors. - assert(From->getType()->isExtVectorType() && ToType->isExtVectorType() && - "HLSL vector truncation should only apply to ExtVectors"); - auto *FromVec = From->getType()->castAs(); - auto *ToVec = ToType->castAs(); - QualType ElType = FromVec->getElementType(); - QualType TruncTy = - Context.getExtVectorType(ElType, ToVec->getNumElements()); - From = ImpCastExprToType(From, TruncTy, CK_HLSLVectorTruncation, - From->getValueKind()) - .get(); - break; - } case ICK_Lvalue_To_Rvalue: case ICK_Array_To_Pointer: @@ -4780,73 +4800,45 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, case ICK_C_Only_Conversion: case ICK_Incompatible_Pointer_Conversion: case ICK_HLSL_Array_RValue: + case ICK_HLSL_Vector_Truncation: + case ICK_HLSL_Vector_Splat: llvm_unreachable("Improper second standard conversion"); } - if (SCS.Element != ICK_Identity) { + if (SCS.Dimension != ICK_Identity) { // If SCS.Element is not ICK_Identity the To and From types must be HLSL // vectors or matrices. // TODO: Support HLSL matrices. assert((!From->getType()->isMatrixType() && !ToType->isMatrixType()) && - "Element conversion for matrix types is not implemented yet."); - assert(From->getType()->isVectorType() && ToType->isVectorType() && - "Element conversion is only supported for vector types."); - assert(From->getType()->getAs()->getNumElements() == - ToType->getAs()->getNumElements() && - "Element conversion is only supported for vectors with the same " - "element counts."); - QualType FromElTy = From->getType()->getAs()->getElementType(); - unsigned NumElts = ToType->getAs()->getNumElements(); - switch (SCS.Element) { - case ICK_Boolean_Conversion: - // Perform half-to-boolean conversion via float. - if (FromElTy->isHalfType()) { - QualType FPExtType = Context.getExtVectorType(FromElTy, NumElts); - From = ImpCastExprToType(From, FPExtType, CK_FloatingCast).get(); - FromType = FPExtType; - } - - From = - ImpCastExprToType(From, ToType, ScalarTypeToBooleanCastKind(FromElTy), - VK_PRValue, - /*BasePath=*/nullptr, CCK) - .get(); - break; - case ICK_Integral_Promotion: - case ICK_Integral_Conversion: - if (ToType->isBooleanType()) { - assert(FromType->castAs()->getDecl()->isFixed() && - SCS.Second == ICK_Integral_Promotion && - "only enums with fixed underlying type can promote to bool"); - From = ImpCastExprToType(From, ToType, CK_IntegralToBoolean, VK_PRValue, - /*BasePath=*/nullptr, CCK) - .get(); - } else { - From = ImpCastExprToType(From, ToType, CK_IntegralCast, VK_PRValue, - /*BasePath=*/nullptr, CCK) - .get(); - } - break; - - case ICK_Floating_Promotion: - case ICK_Floating_Conversion: - From = ImpCastExprToType(From, ToType, CK_FloatingCast, VK_PRValue, + "Dimension conversion for matrix types is not implemented yet."); + assert(ToType->isVectorType() && + "Dimension conversion is only supported for vector types."); + switch (SCS.Dimension) { + case ICK_HLSL_Vector_Splat: { + // Vector splat from any arithmetic type to a vector. + Expr *Elem = prepareVectorSplat(ToType, From).get(); + From = ImpCastExprToType(Elem, ToType, CK_VectorSplat, VK_PRValue, /*BasePath=*/nullptr, CCK) .get(); break; - case ICK_Floating_Integral: - if (ToType->hasFloatingRepresentation()) - From = - ImpCastExprToType(From, ToType, CK_IntegralToFloating, VK_PRValue, - /*BasePath=*/nullptr, CCK) - .get(); - else - From = - ImpCastExprToType(From, ToType, CK_FloatingToIntegral, VK_PRValue, - /*BasePath=*/nullptr, CCK) - .get(); + } + case ICK_HLSL_Vector_Truncation: { + // Note: HLSL built-in vectors are ExtVectors. Since this truncates a + // vector to a smaller vector, this can only operate on arguments where + // the source and destination types are ExtVectors. + assert(From->getType()->isExtVectorType() && ToType->isExtVectorType() && + "HLSL vector truncation should only apply to ExtVectors"); + auto *FromVec = From->getType()->castAs(); + auto *ToVec = ToType->castAs(); + QualType ElType = FromVec->getElementType(); + QualType TruncTy = + Context.getExtVectorType(ElType, ToVec->getNumElements()); + From = ImpCastExprToType(From, TruncTy, CK_HLSLVectorTruncation, + From->getValueKind()) + .get(); break; + } case ICK_Identity: default: llvm_unreachable("Improper element standard conversion"); diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index f49635aa445a6..d4a48858ec419 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -164,13 +164,33 @@ ImplicitConversionRank clang::GetConversionRank(ImplicitConversionKind Kind) { ICR_C_Conversion, ICR_C_Conversion_Extension, ICR_Conversion, + ICR_HLSL_Dimension_Reduction, ICR_Conversion, - ICR_Conversion, + ICR_HLSL_Scalar_Widening, }; static_assert(std::size(Rank) == (int)ICK_Num_Conversion_Kinds); return Rank[(int)Kind]; } +ImplicitConversionRank +clang::GetDimensionConversionRank(ImplicitConversionRank Base, + ImplicitConversionKind Dimension) { + ImplicitConversionRank Rank = GetConversionRank(Dimension); + if (Rank == ICR_HLSL_Scalar_Widening) { + if (Base == ICR_Promotion) + return ICR_HLSL_Scalar_Widening_Promotion; + if (Base == ICR_Conversion) + return ICR_HLSL_Scalar_Widening_Conversion; + } + if (Rank == ICR_HLSL_Dimension_Reduction) { + if (Base == ICR_Promotion) + return ICR_HLSL_Dimension_Reduction_Promotion; + if (Base == ICR_Conversion) + return ICR_HLSL_Dimension_Reduction_Conversion; + } + return Rank; +} + /// GetImplicitConversionName - Return the name of this kind of /// implicit conversion. static const char *GetImplicitConversionName(ImplicitConversionKind Kind) { @@ -208,6 +228,7 @@ static const char *GetImplicitConversionName(ImplicitConversionKind Kind) { "Fixed point conversion", "HLSL vector truncation", "Non-decaying array conversion", + "HLSL vector splat", }; static_assert(std::size(Name) == (int)ICK_Num_Conversion_Kinds); return Name[Kind]; @@ -218,7 +239,7 @@ static const char *GetImplicitConversionName(ImplicitConversionKind Kind) { void StandardConversionSequence::setAsIdentityConversion() { First = ICK_Identity; Second = ICK_Identity; - Element = ICK_Identity; + Dimension = ICK_Identity; Third = ICK_Identity; DeprecatedStringLiteralToCharPtr = false; QualificationIncludesObjCLifetime = false; @@ -241,8 +262,8 @@ ImplicitConversionRank StandardConversionSequence::getRank() const { Rank = GetConversionRank(First); if (GetConversionRank(Second) > Rank) Rank = GetConversionRank(Second); - if (GetConversionRank(Element) > Rank) - Rank = GetConversionRank(Element); + if (GetDimensionConversionRank(Rank, Dimension) > Rank) + Rank = GetDimensionConversionRank(Rank, Dimension); if (GetConversionRank(Third) > Rank) Rank = GetConversionRank(Third); return Rank; @@ -1970,15 +1991,15 @@ static bool IsVectorConversion(Sema &S, QualType FromType, QualType ToType, if (FromElts < ToElts) return false; if (FromElts == ToElts) - ICK = ICK_Identity; + ElConv = ICK_Identity; else - ICK = ICK_HLSL_Vector_Truncation; + ElConv = ICK_HLSL_Vector_Truncation; QualType FromElTy = FromExtType->getElementType(); QualType ToElTy = ToExtType->getElementType(); if (S.Context.hasSameUnqualifiedType(FromElTy, ToElTy)) return true; - return IsVectorElementConversion(S, FromElTy, ToElTy, ElConv, From); + return IsVectorElementConversion(S, FromElTy, ToElTy, ICK, From); } // There are no conversions between extended vector types other than the // identity conversion. @@ -1987,6 +2008,11 @@ static bool IsVectorConversion(Sema &S, QualType FromType, QualType ToType, // Vector splat from any arithmetic type to a vector. if (FromType->isArithmeticType()) { + if (S.getLangOpts().HLSL) { + ElConv = ICK_HLSL_Vector_Splat; + QualType ToElTy = ToExtType->getElementType(); + return IsVectorElementConversion(S, FromType, ToElTy, ICK, From); + } ICK = ICK_Vector_Splat; return true; } @@ -2201,7 +2227,7 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, // conversion. bool IncompatibleObjC = false; ImplicitConversionKind SecondICK = ICK_Identity; - ImplicitConversionKind ElementICK = ICK_Identity; + ImplicitConversionKind DimensionICK = ICK_Identity; if (S.Context.hasSameUnqualifiedType(FromType, ToType)) { // The unqualified versions of the types are the same: there's no // conversion to do. @@ -2267,10 +2293,10 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, InOverloadResolution, FromType)) { // Pointer to member conversions (4.11). SCS.Second = ICK_Pointer_Member; - } else if (IsVectorConversion(S, FromType, ToType, SecondICK, ElementICK, + } else if (IsVectorConversion(S, FromType, ToType, SecondICK, DimensionICK, From, InOverloadResolution, CStyle)) { SCS.Second = SecondICK; - SCS.Element = ElementICK; + SCS.Dimension = DimensionICK; FromType = ToType.getUnqualifiedType(); } else if (!S.getLangOpts().CPlusPlus && S.Context.typesAreCompatible(ToType, FromType)) { @@ -4257,24 +4283,6 @@ getFixedEnumPromtion(Sema &S, const StandardConversionSequence &SCS) { return FixedEnumPromotion::ToPromotedUnderlyingType; } -static ImplicitConversionSequence::CompareKind -HLSLCompareFloatingRank(QualType LHS, QualType RHS) { - assert(LHS->isVectorType() == RHS->isVectorType() && - "Either both elements should be vectors or neither should."); - if (const auto *VT = LHS->getAs()) - LHS = VT->getElementType(); - - if (const auto *VT = RHS->getAs()) - RHS = VT->getElementType(); - - const auto L = LHS->castAs()->getKind(); - const auto R = RHS->castAs()->getKind(); - if (L == R) - return ImplicitConversionSequence::Indistinguishable; - return L < R ? ImplicitConversionSequence::Better - : ImplicitConversionSequence::Worse; -} - /// CompareStandardConversionSequences - Compare two standard /// conversion sequences to determine whether one is better than the /// other or if they are indistinguishable (C++ 13.3.3.2p3). @@ -4515,22 +4523,6 @@ CompareStandardConversionSequences(Sema &S, SourceLocation Loc, ? ImplicitConversionSequence::Better : ImplicitConversionSequence::Worse; } - - if (S.getLangOpts().HLSL) { - // On a promotion we prefer the lower rank to disambiguate. - if ((SCS1.Second == ICK_Floating_Promotion && - SCS2.Second == ICK_Floating_Promotion) || - (SCS1.Element == ICK_Floating_Promotion && - SCS2.Element == ICK_Floating_Promotion)) - return HLSLCompareFloatingRank(SCS1.getToType(2), SCS2.getToType(2)); - // On a conversion we prefer the higher rank to disambiguate. - if ((SCS1.Second == ICK_Floating_Conversion && - SCS2.Second == ICK_Floating_Conversion) || - (SCS1.Element == ICK_Floating_Conversion && - SCS2.Element == ICK_Floating_Conversion)) - return HLSLCompareFloatingRank(SCS2.getToType(2), SCS1.getToType(2)); - } - return ImplicitConversionSequence::Indistinguishable; } @@ -5074,7 +5066,7 @@ TryReferenceInit(Sema &S, Expr *Init, QualType DeclType, : (RefConv & Sema::ReferenceConversions::ObjC) ? ICK_Compatible_Conversion : ICK_Identity; - ICS.Standard.Element = ICK_Identity; + ICS.Standard.Dimension = ICK_Identity; // FIXME: As a speculative fix to a defect introduced by CWG2352, we rank // a reference binding that performs a non-top-level qualification // conversion as a qualification conversion, not as an identity conversion. @@ -5990,6 +5982,7 @@ static bool CheckConvertedConstantConversions(Sema &S, case ICK_Vector_Conversion: case ICK_SVE_Vector_Conversion: case ICK_RVV_Vector_Conversion: + case ICK_HLSL_Vector_Splat: case ICK_Vector_Splat: case ICK_Complex_Real: case ICK_Block_Pointer_Conversion: @@ -6276,7 +6269,7 @@ Sema::EvaluateConvertedConstantExpression(Expr *E, QualType T, APValue &Value, static void dropPointerConversion(StandardConversionSequence &SCS) { if (SCS.Second == ICK_Pointer_Conversion) { SCS.Second = ICK_Identity; - SCS.Element = ICK_Identity; + SCS.Dimension = ICK_Identity; SCS.Third = ICK_Identity; SCS.ToTypePtrs[2] = SCS.ToTypePtrs[1] = SCS.ToTypePtrs[0]; } diff --git a/clang/test/CodeGenHLSL/BasicFeatures/standard_conversion_sequences.hlsl b/clang/test/CodeGenHLSL/BasicFeatures/standard_conversion_sequences.hlsl index 06e3cc5af87e1..5d751be6dae06 100644 --- a/clang/test/CodeGenHLSL/BasicFeatures/standard_conversion_sequences.hlsl +++ b/clang/test/CodeGenHLSL/BasicFeatures/standard_conversion_sequences.hlsl @@ -30,8 +30,8 @@ void f3_to_f2() { // CHECK: [[f2:%.*]] = alloca <2 x float> // CHECK: store <4 x double> , ptr [[d4]] // CHECK: [[vecd4:%.*]] = load <4 x double>, ptr [[d4]] -// CHECK: [[vecd2:%.*]] = shufflevector <4 x double> [[vecd4]], <4 x double> poison, <2 x i32> -// CHECK: [[vecf2:%.*]] = fptrunc <2 x double> [[vecd2]] to <2 x float> +// CHECK: [[vecf4:%.*]] = fptrunc <4 x double> [[vecd4]] to <4 x float> +// CHECK: [[vecf2:%.*]] = shufflevector <4 x float> [[vecf4]], <4 x float> poison, <2 x i32> // CHECK: store <2 x float> [[vecf2]], ptr [[f2]] void d4_to_f2() { vector d4 = 3.0; @@ -55,8 +55,8 @@ void f2_to_i2() { // CHECK: [[i2:%.*]] = alloca <2 x i32> // CHECK: store <4 x double> , ptr [[d4]] // CHECK: [[vecd4:%.*]] = load <4 x double>, ptr [[d4]] -// CHECK: [[vecd2:%.*]] = shufflevector <4 x double> [[vecd4]], <4 x double> poison, <2 x i32> -// CHECK: [[veci2]] = fptosi <2 x double> [[vecd2]] to <2 x i32> +// CHECK: [[veci4:%.*]] = fptosi <4 x double> [[vecd4]] to <4 x i32> +// CHECK: [[veci2:%.*]] = shufflevector <4 x i32> [[veci4]], <4 x i32> poison, <2 x i32> // CHECK: store <2 x i32> [[veci2]], ptr [[i2]] void d4_to_i2() { vector d4 = 5.0; @@ -81,8 +81,8 @@ void d4_to_l4() { // CHECK: [[i2:%.*]] = alloca <2 x i32> // CHECK: store <4 x i64> , ptr [[l4]] // CHECK: [[vecl4:%.*]] = load <4 x i64>, ptr [[l4]] -// CHECK: [[vecl2:%.*]] = shufflevector <4 x i64> [[vecl4]], <4 x i64> poison, <2 x i32> -// CHECK: [[veci2:%.*]] = trunc <2 x i64> [[vecl2]] to <2 x i32> +// CHECK: [[veci4:%.*]] = trunc <4 x i64> [[vecl4]] to <4 x i32> +// CHECK: [[veci2:%.*]] = shufflevector <4 x i32> [[veci4]], <4 x i32> poison, <2 x i32> // CHECK: store <2 x i32> [[veci2]], ptr [[i2]] void l4_to_i2() { vector l4 = 7; @@ -108,9 +108,9 @@ void i2_to_b2() { // CHECK: [[b2:%.*]] = alloca i8 // CHECK: store <4 x double> , ptr [[d4]] // CHECK: [[vecd4:%.*]] = load <4 x double>, ptr [[d4]] -// CHECK: [[vecd2:%.*]] = shufflevector <4 x double> [[vecd4]], <4 x double> poison, <2 x i32> -// CHECK: [[vecb2:%.*]] = fcmp une <2 x double> [[vecd2]], zeroinitializer -// CHECK: [[vecb8:%.*]] = shufflevector <2 x i1> [[vecb2]], <2 x i1> poison, <8 x i32> +// CHECK: [[vecb4:%.*]] = fcmp une <4 x double> [[vecd4]], zeroinitializer +// CHECK: [[vecd2:%.*]] = shufflevector <4 x i1> [[vecb4]], <4 x i1> poison, <2 x i32> +// CHECK: [[vecb8:%.*]] = shufflevector <2 x i1> [[vecd2]], <2 x i1> poison, <8 x i32> // CHECK: [[i8:%.*]] = bitcast <8 x i1> [[vecb8]] to i8 // CHECK: store i8 [[i8]], ptr [[b2]] void d4_to_b2() { diff --git a/clang/test/CodeGenHLSL/builtins/dot.hlsl b/clang/test/CodeGenHLSL/builtins/dot.hlsl index 307d71cce3cb6..ae6e45c3f9482 100644 --- a/clang/test/CodeGenHLSL/builtins/dot.hlsl +++ b/clang/test/CodeGenHLSL/builtins/dot.hlsl @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \ // RUN: dxil-pc-shadermodel6.3-library %s -fnative-half-type \ -// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s \ +// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s \ // RUN: --check-prefixes=CHECK,NATIVE_HALF // RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \ // RUN: dxil-pc-shadermodel6.3-library %s -emit-llvm -disable-llvm-passes \ @@ -156,38 +156,6 @@ float test_dot_float3_splat(float p0, float3 p1) { return dot(p0, p1); } // CHECK: ret float %dx.dot float test_dot_float4_splat(float p0, float4 p1) { return dot(p0, p1); } -// CHECK: %conv = sitofp i32 %1 to float -// CHECK: %splat.splatinsert = insertelement <2 x float> poison, float %conv, i64 0 -// CHECK: %splat.splat = shufflevector <2 x float> %splat.splatinsert, <2 x float> poison, <2 x i32> zeroinitializer -// CHECK: %dx.dot = call float @llvm.dx.dot2.v2f32(<2 x float> %0, <2 x float> %splat.splat) -// CHECK: ret float %dx.dot -float test_builtin_dot_float2_int_splat(float2 p0, int p1) { - return dot(p0, p1); -} - -// CHECK: %conv = sitofp i32 %1 to float -// CHECK: %splat.splatinsert = insertelement <3 x float> poison, float %conv, i64 0 -// CHECK: %splat.splat = shufflevector <3 x float> %splat.splatinsert, <3 x float> poison, <3 x i32> zeroinitializer -// CHECK: %dx.dot = call float @llvm.dx.dot3.v3f32(<3 x float> %0, <3 x float> %splat.splat) -// CHECK: ret float %dx.dot -float test_builtin_dot_float3_int_splat(float3 p0, int p1) { - return dot(p0, p1); -} - // CHECK: %dx.dot = fmul double %0, %1 // CHECK: ret double %dx.dot double test_dot_double(double p0, double p1) { return dot(p0, p1); } - -// CHECK: %conv = zext i1 %tobool to i32 -// CHECK: %dx.dot = mul i32 %conv, %1 -// CHECK: ret i32 %dx.dot -int test_dot_bool_scalar_arg0_type_promotion(bool p0, int p1) { - return dot(p0, p1); -} - -// CHECK: %conv = zext i1 %tobool to i32 -// CHECK: %dx.dot = mul i32 %0, %conv -// CHECK: ret i32 %dx.dot -int test_dot_bool_scalar_arg1_type_promotion(int p0, bool p1) { - return dot(p0, p1); -} diff --git a/clang/test/CodeGenHLSL/builtins/lerp.hlsl b/clang/test/CodeGenHLSL/builtins/lerp.hlsl index bbb419acaf3ba..53ac24dd45693 100644 --- a/clang/test/CodeGenHLSL/builtins/lerp.hlsl +++ b/clang/test/CodeGenHLSL/builtins/lerp.hlsl @@ -86,27 +86,3 @@ float3 test_lerp_float3_splat(float p0, float3 p1) { return lerp(p0, p1, p1); } // SPIR_CHECK: %hlsl.lerp = call <4 x float> @llvm.spv.lerp.v4f32(<4 x float> %splat.splat, <4 x float> %[[b]], <4 x float> %[[c]]) // CHECK: ret <4 x float> %hlsl.lerp float4 test_lerp_float4_splat(float p0, float4 p1) { return lerp(p0, p1, p1); } - -// CHECK: %[[a:.*]] = load <2 x float>, ptr %p0.addr, align 8 -// CHECK: %[[b:.*]] = load <2 x float>, ptr %p0.addr, align 8 -// CHECK: %conv = sitofp i32 {{.*}} to float -// CHECK: %splat.splatinsert = insertelement <2 x float> poison, float %conv, i64 0 -// CHECK: %splat.splat = shufflevector <2 x float> %splat.splatinsert, <2 x float> poison, <2 x i32> zeroinitializer -// DXIL_CHECK: %hlsl.lerp = call <2 x float> @llvm.dx.lerp.v2f32(<2 x float> %[[a]], <2 x float> %[[b]], <2 x float> %splat.splat) -// SPIR_CHECK: %hlsl.lerp = call <2 x float> @llvm.spv.lerp.v2f32(<2 x float> %[[a]], <2 x float> %[[b]], <2 x float> %splat.splat) -// CHECK: ret <2 x float> %hlsl.lerp -float2 test_lerp_float2_int_splat(float2 p0, int p1) { - return lerp(p0, p0, p1); -} - -// CHECK: %[[a:.*]] = load <3 x float>, ptr %p0.addr, align 16 -// CHECK: %[[b:.*]] = load <3 x float>, ptr %p0.addr, align 16 -// CHECK: %conv = sitofp i32 {{.*}} to float -// CHECK: %splat.splatinsert = insertelement <3 x float> poison, float %conv, i64 0 -// CHECK: %splat.splat = shufflevector <3 x float> %splat.splatinsert, <3 x float> poison, <3 x i32> zeroinitializer -// DXIL_CHECK: %hlsl.lerp = call <3 x float> @llvm.dx.lerp.v3f32(<3 x float> %[[a]], <3 x float> %[[b]], <3 x float> %splat.splat) -// SPIR_CHECK: %hlsl.lerp = call <3 x float> @llvm.spv.lerp.v3f32(<3 x float> %[[a]], <3 x float> %[[b]], <3 x float> %splat.splat) -// CHECK: ret <3 x float> %hlsl.lerp -float3 test_lerp_float3_int_splat(float3 p0, int p1) { - return lerp(p0, p0, p1); -} diff --git a/clang/test/CodeGenHLSL/builtins/mad.hlsl b/clang/test/CodeGenHLSL/builtins/mad.hlsl index 559e1d1dd3903..449a793caf93b 100644 --- a/clang/test/CodeGenHLSL/builtins/mad.hlsl +++ b/clang/test/CodeGenHLSL/builtins/mad.hlsl @@ -281,25 +281,3 @@ float3 test_mad_float3_splat(float p0, float3 p1, float3 p2) { return mad(p0, p1 // CHECK: %hlsl.fmad = call <4 x float> @llvm.fmuladd.v4f32(<4 x float> %splat.splat, <4 x float> %[[p1]], <4 x float> %[[p2]]) // CHECK: ret <4 x float> %hlsl.fmad float4 test_mad_float4_splat(float p0, float4 p1, float4 p2) { return mad(p0, p1, p2); } - -// CHECK: %[[p0:.*]] = load <2 x float>, ptr %p0.addr, align 8 -// CHECK: %[[p1:.*]] = load <2 x float>, ptr %p1.addr, align 8 -// CHECK: %conv = sitofp i32 %{{.*}} to float -// CHECK: %splat.splatinsert = insertelement <2 x float> poison, float %conv, i64 0 -// CHECK: %splat.splat = shufflevector <2 x float> %splat.splatinsert, <2 x float> poison, <2 x i32> zeroinitializer -// CHECK: %hlsl.fmad = call <2 x float> @llvm.fmuladd.v2f32(<2 x float> %[[p0]], <2 x float> %[[p1]], <2 x float> %splat.splat) -// CHECK: ret <2 x float> %hlsl.fmad -float2 test_mad_float2_int_splat(float2 p0, float2 p1, int p2) { - return mad(p0, p1, p2); -} - -// CHECK: %[[p0:.*]] = load <3 x float>, ptr %p0.addr, align 16 -// CHECK: %[[p1:.*]] = load <3 x float>, ptr %p1.addr, align 16 -// CHECK: %conv = sitofp i32 %{{.*}} to float -// CHECK: %splat.splatinsert = insertelement <3 x float> poison, float %conv, i64 0 -// CHECK: %splat.splat = shufflevector <3 x float> %splat.splatinsert, <3 x float> poison, <3 x i32> zeroinitializer -// CHECK: %hlsl.fmad = call <3 x float> @llvm.fmuladd.v3f32(<3 x float> %[[p0]], <3 x float> %[[p1]], <3 x float> %splat.splat) -// CHECK: ret <3 x float> %hlsl.fmad -float3 test_mad_float3_int_splat(float3 p0, float3 p1, int p2) { - return mad(p0, p1, p2); -} diff --git a/clang/test/SemaHLSL/OverloadResolutionBugs.hlsl b/clang/test/SemaHLSL/OverloadResolutionBugs.hlsl deleted file mode 100644 index 30de00063f542..0000000000000 --- a/clang/test/SemaHLSL/OverloadResolutionBugs.hlsl +++ /dev/null @@ -1,63 +0,0 @@ -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -o - -fsyntax-only %s -verify -// XFAIL: * - -// https://github.com/llvm/llvm-project/issues/81047 - -// expected-no-diagnostics -void Fn4(int64_t2 L); -void Fn4(int2 I); - -void Call4(int16_t H) { Fn4(H); } - -int test_builtin_dot_bool_type_promotion(bool p0, bool p1) { - return dot(p0, p1); -} - -float test_dot_scalar_mismatch(float p0, int p1) { return dot(p0, p1); } - -float test_dot_element_type_mismatch(int2 p0, float2 p1) { return dot(p0, p1); } - -float test_builtin_dot_vec_int_to_float_promotion(int2 p0, float2 p1) { - return dot(p0, p1); -} - -int64_t test_builtin_dot_vec_int_to_int64_promotion(int64_t2 p0, int2 p1) { - return dot(p0, p1); -} - -float test_builtin_dot_vec_half_to_float_promotion(float2 p0, half2 p1) { - return dot(p0, p1); -} - -float test_builtin_dot_vec_int16_to_float_promotion(float2 p0, int16_t2 p1) { - return dot(p0, p1); -} - -half test_builtin_dot_vec_int16_to_half_promotion(half2 p0, int16_t2 p1) { - return dot(p0, p1); -} - -int test_builtin_dot_vec_int16_to_int_promotion(int2 p0, int16_t2 p1) { - return dot(p0, p1); -} - -int64_t test_builtin_dot_vec_int16_to_int64_promotion(int64_t2 p0, - int16_t2 p1) { - return dot(p0, p1); -} - -float4 test_frac_int4(int4 p0) { return frac(p0); } - -float test_frac_int(int p0) { return frac(p0); } - -float test_frac_bool(bool p0) { return frac(p0); } - -// This resolves the wrong overload. In clang this converts down to an int, in -// DXC it extends the scalar to a vector. -void Fn(int) {} -void Fn(vector) {} - -void Call() { - int64_t V; - Fn(V); -} diff --git a/clang/test/SemaHLSL/ScalarOverloadResolution.hlsl b/clang/test/SemaHLSL/ScalarOverloadResolution.hlsl index d1a47af228e24..77090b7fda257 100644 --- a/clang/test/SemaHLSL/ScalarOverloadResolution.hlsl +++ b/clang/test/SemaHLSL/ScalarOverloadResolution.hlsl @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -fnative-half-type -finclude-default-header -Wconversion -verify -o - %s +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -fnative-half-type -finclude-default-header -Wconversion -verify -o - -DERROR=1 %s // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -fnative-half-type -finclude-default-header -ast-dump %s | FileCheck %s // This test verifies floating point type implicit conversion ranks for overload @@ -19,8 +19,8 @@ void HalfFloatDouble(half H); // CHECK: FunctionDecl {{.*}} used HalfFloatDouble 'void (float)' // CHECK: FunctionDecl {{.*}} used HalfFloatDouble 'void (half)' -void FloatDouble(double D); -void FloatDouble(float F); +void FloatDouble(double D); // expected-note{{candidate function}} +void FloatDouble(float F); // expected-note{{candidate function}} // CHECK: FunctionDecl {{.*}} used FloatDouble 'void (double)' // CHECK: FunctionDecl {{.*}} used FloatDouble 'void (float)' @@ -31,8 +31,8 @@ void HalfDouble(half H); // CHECK: FunctionDecl {{.*}} used HalfDouble 'void (double)' // CHECK: FunctionDecl {{.*}} used HalfDouble 'void (half)' -void HalfFloat(float F); -void HalfFloat(half H); +void HalfFloat(float F); // expected-note{{candidate function}} +void HalfFloat(half H); // expected-note{{candidate function}} // CHECK: FunctionDecl {{.*}} used HalfFloat 'void (float)' // CHECK: FunctionDecl {{.*}} used HalfFloat 'void (half)' @@ -72,9 +72,9 @@ void Case1(half H, float F, double D) { HalfFloatDouble(D); } -// Case 2: A function declared with double and float overloads. -// (a) When called with half, it will resolve to float because float is lower -// ranked than double. +// Case 2: A function declared with double and float overlaods. +// (a) When called with half, it will fail to resolve because it cannot +// disambiguate the promotions. // (b) When called with float it will resolve to float because float is an // exact match. // (c) When called with double it will resolve to double because it is an @@ -82,10 +82,9 @@ void Case1(half H, float F, double D) { // CHECK-LABEL: FunctionDecl {{.*}} Case2 'void (half, float, double)' void Case2(half H, float F, double D) { - // CHECK: CallExpr {{.*}} 'void' - // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float)' - // CHECK-NEXT: DeclRefExpr {{.*}} 'void (float)' lvalue Function {{.*}} 'FloatDouble' 'void (float)' - FloatDouble(H); + #if ERROR + FloatDouble(H); // expected-error{{call to 'FloatDouble' is ambiguous}} + #endif // CHECK: CallExpr {{.*}} 'void' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float)' @@ -144,10 +143,9 @@ void Case4(half H, float F, double D) { // CHECK-NEXT: DeclRefExpr {{.*}} 'void (float)' lvalue Function {{.*}} 'HalfFloat' 'void (float)' HalfFloat(F); - // CHECK: CallExpr {{.*}} 'void' - // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float)' - // CHECK-NEXT: DeclRefExpr {{.*}} 'void (float)' lvalue Function {{.*}} 'HalfFloat' 'void (float)' - HalfFloat(D); // expected-warning{{implicit conversion loses floating-point precision: 'double' to 'float'}} + #if ERROR + HalfFloat(D); // expected-error{{call to 'HalfFloat' is ambiguous}} + #endif } // Case 5: A function declared with only a double overload. diff --git a/clang/test/SemaHLSL/SplatOverloadResolution.hlsl b/clang/test/SemaHLSL/SplatOverloadResolution.hlsl new file mode 100644 index 0000000000000..f0798dfc72497 --- /dev/null +++ b/clang/test/SemaHLSL/SplatOverloadResolution.hlsl @@ -0,0 +1,166 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -fnative-half-type -finclude-default-header -fsyntax-only %s -DERROR=1 -verify +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -fnative-half-type -finclude-default-header -ast-dump %s | FileCheck %s + + +// Case 1: Prioritize splat without conversion over conversion. In this case the +// called functions have valid overloads for each type, however one of the +// overloads is a vector rather than scalar. Each call should resolve to the +// same type, and the vector should splat. +void HalfFloatDoubleV(double2 D); +void HalfFloatDoubleV(float F); +void HalfFloatDoubleV(half H); + +void HalfFloatVDouble(double D); +void HalfFloatVDouble(float2 F); +void HalfFloatVDouble(half H); + +void HalfVFloatDouble(double D); +void HalfVFloatDouble(float F); +void HalfVFloatDouble(half2 H); + + +// CHECK-LABEL: FunctionDecl {{.*}} Case1 'void (half, float, double)' +void Case1(half H, float F, double D) { + // CHECK: CallExpr {{.*}} 'void' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(half)' + // CHECK-NEXT: DeclRefExpr {{.*}} 'void (half)' lvalue Function {{.*}} 'HalfFloatDoubleV' 'void (half)' + HalfFloatDoubleV(H); + + // CHECK: CallExpr {{.*}} 'void' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float)' + // CHECK-NEXT: DeclRefExpr {{.*}} 'void (float)' lvalue Function {{.*}} 'HalfFloatDoubleV' 'void (float)' + HalfFloatDoubleV(F); + + // CHECK: CallExpr {{.*}} 'void' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(double2)' + // CHECK-NEXT: DeclRefExpr {{.*}} 'void (double2)' lvalue Function {{.*}} 'HalfFloatDoubleV' 'void (double2)' + HalfFloatDoubleV(D); + + // CHECK: CallExpr {{.*}} 'void' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(half)' + // CHECK-NEXT: DeclRefExpr {{.*}} 'void (half)' lvalue Function {{.*}} 'HalfFloatVDouble' 'void (half)' + HalfFloatVDouble(H); + + // CHECK: CallExpr {{.*}} 'void' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float2)' + // CHECK-NEXT: DeclRefExpr {{.*}} 'void (float2)' lvalue Function {{.*}} 'HalfFloatVDouble' 'void (float2)' + HalfFloatVDouble(F); + + // CHECK: CallExpr {{.*}} 'void' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(double)' + // CHECK-NEXT: DeclRefExpr {{.*}} 'void (double)' lvalue Function {{.*}} 'HalfFloatVDouble' 'void (double)' + HalfFloatVDouble(D); + + // CHECK: CallExpr {{.*}} 'void' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(half2)' + // CHECK-NEXT: DeclRefExpr {{.*}} 'void (half2)' lvalue Function {{.*}} 'HalfVFloatDouble' 'void (half2)' + HalfVFloatDouble(H); + + // CHECK: CallExpr {{.*}} 'void' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float)' + // CHECK-NEXT: DeclRefExpr {{.*}} 'void (float)' lvalue Function {{.*}} 'HalfVFloatDouble' 'void (float)' + HalfVFloatDouble(F); + + // CHECK: CallExpr {{.*}} 'void' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(double)' + // CHECK-NEXT: DeclRefExpr {{.*}} 'void (double)' lvalue Function {{.*}} 'HalfVFloatDouble' 'void (double)' + HalfVFloatDouble(D); +} + +// Case 2: Prefer splat+promotion over conversion. In this case the overloads +// require a splat+promotion or a conversion. The call will resolve to the +// splat+promotion. +void HalfDoubleV(double2 D); +void HalfDoubleV(half H); + +// CHECK-LABEL: FunctionDecl {{.*}} Case2 'void (float)' +void Case2(float F) { + // CHECK: CallExpr {{.*}} 'void' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(double2)' + // CHECK-NEXT: DeclRefExpr {{.*}} 'void (double2)' lvalue Function {{.*}} 'HalfDoubleV' 'void (double2)' + HalfDoubleV(F); +} + +// Case 3: Prefer promotion or conversion without splat over the splat. In this +// case the scalar value will overload to the scalar function. +void DoubleV(double D); +void DoubleV(double2 V); + +void HalfV(half D); +void HalfV(half2 V); + +// CHECK-LABEL: FunctionDecl {{.*}} Case3 'void (float)' +void Case3(float F) { + // CHECK: CallExpr {{.*}} 'void' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(double)' + // CHECK-NEXT: DeclRefExpr {{.*}} 'void (double)' lvalue Function {{.*}} 'DoubleV' 'void (double)' + DoubleV(F); + + // CHECK: CallExpr {{.*}} 'void' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(half)' + // CHECK-NEXT: DeclRefExpr {{.*}} 'void (half)' lvalue Function {{.*}} 'HalfV' 'void (half)' + HalfV(F); +} + +#if ERROR +// Case 4: It is ambiguous to resolve two splat+conversion or splat+promotion +// functions. In all the calls below an error occurs. +void FloatVDoubleV(float2 F); // expected-note {{candidate function}} +void FloatVDoubleV(double2 D); // expected-note {{candidate function}} + +void HalfVFloatV(half2 H); // expected-note {{candidate function}} +void HalfVFloatV(float2 F); // expected-note {{candidate function}} + +void Case4(half H, double D) { + FloatVDoubleV(H); // expected-error {{call to 'FloatVDoubleV' is ambiguous}} + + HalfVFloatV(D); // expected-error {{call to 'HalfVFloatV' is ambiguous}} +} + +// Case 5: It is ambiguous to resolve two splats of different lengths. +void FloatV(float2 V); // expected-note {{candidate function}} expected-note {{candidate function}} expected-note {{candidate function}} +void FloatV(float4 V); // expected-note {{candidate function}} expected-note {{candidate function}} expected-note {{candidate function}} + +void Case5(half H, float F, double D) { + FloatV(H); // expected-error {{call to 'FloatV' is ambiguous}} + FloatV(F); // expected-error {{call to 'FloatV' is ambiguous}} + FloatV(D); // expected-error {{call to 'FloatV' is ambiguous}} +} +#endif + +// Case 5: Vectors truncate or match, but don't extend. +void FloatV24(float2 V); +void FloatV24(float4 V); + +// CHECK-LABEL: FunctionDecl {{.*}} Case5 'void (half3, float3, double3, half4, float4, double4)' +void Case5(half3 H3, float3 F3, double3 D3, half4 H4, float4 F4, double4 D4) { + // CHECK: CallExpr {{.*}} 'void' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float2)' + // CHECK-NEXT: DeclRefExpr {{.*}} 'void (float2)' lvalue Function {{.*}} 'FloatV24' 'void (float2)' + FloatV24(H3); // expected-warning{{implicit conversion truncates vector: 'half3' (aka 'vector') to 'vector' (vector of 2 'float' values)}} + + // CHECK: CallExpr {{.*}} 'void' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float2)' + // CHECK-NEXT: DeclRefExpr {{.*}} 'void (float2)' lvalue Function {{.*}} 'FloatV24' 'void (float2)' + FloatV24(F3); // expected-warning{{implicit conversion truncates vector: 'float3' (aka 'vector') to 'vector' (vector of 2 'float' values)}} + + // CHECK: CallExpr {{.*}} 'void' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float2)' + // CHECK-NEXT: DeclRefExpr {{.*}} 'void (float2)' lvalue Function {{.*}} 'FloatV24' 'void (float2)' + FloatV24(D3); // expected-warning{{implicit conversion truncates vector: 'double3' (aka 'vector') to 'vector' (vector of 2 'float' values)}} + + // CHECK: CallExpr {{.*}} 'void' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float4)' + // CHECK-NEXT: DeclRefExpr {{.*}} 'void (float4)' lvalue Function {{.*}} 'FloatV24' 'void (float4)' + FloatV24(H4); + + // CHECK: CallExpr {{.*}} 'void' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float4)' + // CHECK-NEXT: DeclRefExpr {{.*}} 'void (float4)' lvalue Function {{.*}} 'FloatV24' 'void (float4)' + FloatV24(F4); + + // CHECK: CallExpr {{.*}} 'void' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float4)' + // CHECK-NEXT: DeclRefExpr {{.*}} 'void (float4)' lvalue Function {{.*}} 'FloatV24' 'void (float4)' + FloatV24(D4); +} diff --git a/clang/test/SemaHLSL/TruncationOverloadResolution.hlsl b/clang/test/SemaHLSL/TruncationOverloadResolution.hlsl new file mode 100644 index 0000000000000..f8cfe22372e88 --- /dev/null +++ b/clang/test/SemaHLSL/TruncationOverloadResolution.hlsl @@ -0,0 +1,68 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -fnative-half-type -finclude-default-header -fsyntax-only %s -DERROR=1 -verify +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -fnative-half-type -finclude-default-header -ast-dump %s | FileCheck %s + +// Case 1: Prefer exact-match truncation over conversion. +void Half4Float4Double2(double2 D); +void Half4Float4Double2(float4 D); +void Half4Float4Double2(half4 D); + +void Half4Float2(float2 D); +void Half4Float2(half4 D); + +void Case1(float4 F, double4 D) { + // CHECK: CallExpr {{.*}} 'void' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(double2)' + // CHECK-NEXT: DeclRefExpr {{.*}} 'void (double2)' lvalue Function {{.*}} 'Half4Float4Double2' 'void (double2)' + Half4Float4Double2(D); // expected-warning{{implicit conversion truncates vector: 'double4' (aka 'vector') to 'vector' (vector of 2 'double' values)}} + + // CHECK: CallExpr {{.*}} 'void' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float2)' + // CHECK-NEXT: DeclRefExpr {{.*}} 'void (float2)' lvalue Function {{.*}} 'Half4Float2' 'void (float2)' + Half4Float2(F); // expected-warning{{implicit conversion truncates vector: 'float4' (aka 'vector') to 'vector' (vector of 2 'float' values)}} +} + +// Case 2: Prefer promotions over conversions when truncating. +void Half2Double2(double2 D); +void Half2Double2(half2 H); + +void Case2(float4 F) { + // CHECK: CallExpr {{.*}} 'void' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(double2)' + // CHECK-NEXT: DeclRefExpr {{.*}} 'void (double2)' lvalue Function {{.*}} 'Half2Double2' 'void (double2)' + Half2Double2(F); // expected-warning{{implicit conversion truncates vector: 'float4' (aka 'vector') to 'vector' (vector of 2 'double' values)}} +} + +#if ERROR +// Case 3: Two promotions or two conversions are ambiguous. +void Float2Double2(double2 D); // expected-note{{candidate function}} +void Float2Double2(float2 D); // expected-note{{candidate function}} + +void Half2Float2(float2 D); // expected-note{{candidate function}} +void Half2Float2(half2 D); // expected-note{{candidate function}} + +void Half2Half3(half3 D); // expected-note{{candidate function}} expected-note{{candidate function}} expected-note{{candidate function}} +void Half2Half3(half2 D); // expected-note{{candidate function}} expected-note{{candidate function}} expected-note{{candidate function}} + +void Double2Double3(double3 D); // expected-note{{candidate function}} expected-note{{candidate function}} expected-note{{candidate function}} +void Double2Double3(double2 D); // expected-note{{candidate function}} expected-note{{candidate function}} expected-note{{candidate function}} + +void Case1(half4 H, float4 F, double4 D) { + Float2Double2(H); // expected-error {{call to 'Float2Double2' is ambiguous}} + + Half2Float2(D); // expected-error {{call to 'Half2Float2' is ambiguous}} + + Half2Half3(H); // expected-error {{call to 'Half2Half3' is ambiguous}} + Half2Half3(F); // expected-error {{call to 'Half2Half3' is ambiguous}} + Half2Half3(D); // expected-error {{call to 'Half2Half3' is ambiguous}} + Half2Half3(H.xyz); + Half2Half3(F.xyz); + Half2Half3(D.xyz); + + Double2Double3(H); // expected-error {{call to 'Double2Double3' is ambiguous}} + Double2Double3(F); // expected-error {{call to 'Double2Double3' is ambiguous}} + Double2Double3(D); // expected-error {{call to 'Double2Double3' is ambiguous}} + Double2Double3(D.xyz); + Double2Double3(F.xyz); + Double2Double3(H.xyz); +} +#endif diff --git a/clang/test/SemaHLSL/Types/BuiltinVector/ScalarSwizzles.hlsl b/clang/test/SemaHLSL/Types/BuiltinVector/ScalarSwizzles.hlsl index 683c05b20c34e..78ff54987a5bf 100644 --- a/clang/test/SemaHLSL/Types/BuiltinVector/ScalarSwizzles.hlsl +++ b/clang/test/SemaHLSL/Types/BuiltinVector/ScalarSwizzles.hlsl @@ -113,7 +113,7 @@ int64_t4 HooBoy() { // list with float truncation casts. // CHECK-LABEL: AllRighty -// CHECK: ImplicitCastExpr {{.*}} 'float3':'vector' +// CHECK: ImplicitCastExpr {{.*}} 'vector' // CHECK-NEXT: ExtVectorElementExpr {{.*}} 'vector' rrr // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' // CHECK-NEXT: FloatingLiteral {{.*}} 'double' 1.000000e+00 diff --git a/clang/test/SemaHLSL/VectorElementOverloadResolution.hlsl b/clang/test/SemaHLSL/VectorElementOverloadResolution.hlsl index bbf8d3b5e102c..a980cbd252965 100644 --- a/clang/test/SemaHLSL/VectorElementOverloadResolution.hlsl +++ b/clang/test/SemaHLSL/VectorElementOverloadResolution.hlsl @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -fnative-half-type -finclude-default-header -Wconversion -verify -o - %s +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -fnative-half-type -finclude-default-header -Wconversion -verify -o - %s -DERROR=1 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -fnative-half-type -finclude-default-header -Wno-conversion -ast-dump %s | FileCheck %s // This test verifies floating point type implicit conversion ranks for overload @@ -19,8 +19,8 @@ void HalfFloatDouble(half2 H); // CHECK: FunctionDecl {{.*}} used HalfFloatDouble 'void (float2)' // CHECK: FunctionDecl {{.*}} used HalfFloatDouble 'void (half2)' -void FloatDouble(double2 D); -void FloatDouble(float2 F); +void FloatDouble(double2 D); // expected-note {{candidate function}} +void FloatDouble(float2 F); // expected-note {{candidate function}} // CHECK: FunctionDecl {{.*}} used FloatDouble 'void (double2)' // CHECK: FunctionDecl {{.*}} used FloatDouble 'void (float2)' @@ -31,8 +31,8 @@ void HalfDouble(half2 H); // CHECK: FunctionDecl {{.*}} used HalfDouble 'void (double2)' // CHECK: FunctionDecl {{.*}} used HalfDouble 'void (half2)' -void HalfFloat(float2 F); -void HalfFloat(half2 H); +void HalfFloat(float2 F); // expected-note {{candidate function}} +void HalfFloat(half2 H); // expected-note {{candidate function}} // CHECK: FunctionDecl {{.*}} used HalfFloat 'void (float2)' // CHECK: FunctionDecl {{.*}} used HalfFloat 'void (half2)' @@ -71,9 +71,8 @@ void Case1(half2 H, float2 F, double2 D) { HalfFloatDouble(D); } -// Case 2: A function declared with double and float overloads. -// (a) When called with half, it will resolve to float because float is lower -// ranked than double. +// Case 2: A function declared with double and float overlaods. +// (a) When called with half, it fails to resulve the ambiguous promotion. // (b) When called with float it will resolve to float because float is an // exact match. // (c) When called with double it will resolve to double because it is an @@ -81,10 +80,9 @@ void Case1(half2 H, float2 F, double2 D) { // CHECK-LABEL: FunctionDecl {{.*}} Case2 'void (half2, float2, double2)' void Case2(half2 H, float2 F, double2 D) { - // CHECK: CallExpr {{.*}} 'void' - // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float2)' - // CHECK-NEXT: DeclRefExpr {{.*}} 'void (float2)' lvalue Function {{.*}} 'FloatDouble' 'void (float2)' - FloatDouble(H); +#if ERROR + FloatDouble(H); // expected-error {{call to 'FloatDouble' is ambiguous}} +#endif // CHECK: CallExpr {{.*}} 'void' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float2)' @@ -128,8 +126,7 @@ void Case3(half2 H, float2 F, double2 D) { // match. // (b) When called with float it will resolve to float because float is an // exact match. -// (c) When called with double it will resolve to float because it is the -// float is higher rank than half. +// (c) When called with double it fails to resolve the ambigjuous conversion. // CHECK-LABEL: FunctionDecl {{.*}} Case4 'void (half2, float2, double2)' void Case4(half2 H, float2 F, double2 D) { @@ -143,10 +140,9 @@ void Case4(half2 H, float2 F, double2 D) { // CHECK-NEXT: DeclRefExpr {{.*}} 'void (float2)' lvalue Function {{.*}} 'HalfFloat' 'void (float2)' HalfFloat(F); - // CHECK: CallExpr {{.*}} 'void' - // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float2)' - // CHECK-NEXT: DeclRefExpr {{.*}} 'void (float2)' lvalue Function {{.*}} 'HalfFloat' 'void (float2)' - HalfFloat(D); // expected-warning{{implicit conversion loses floating-point precision: 'double2' (aka 'vector') to 'float2' (aka 'vector')}} +#if ERROR + HalfFloat(D); // expected-error{{call to 'HalfFloat' is ambiguous}} +#endif } // Case 5: A function declared with only a double overload. @@ -198,7 +194,7 @@ void Case6(half2 H, float2 F, double2 D) { // CHECK: CallExpr {{.*}} 'void' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float2)' // CHECK-NEXT: DeclRefExpr {{.*}} 'void (float2)' lvalue Function {{.*}} 'Float' 'void (float2)' - Float(D); // expected-warning{{implicit conversion loses floating-point precision: 'double2' (aka 'vector') to 'float2' (aka 'vector')}} + Float(D); // expected-warning{{implicit conversion loses floating-point precision: 'double2' (aka 'vector') to 'vector' (vector of 2 'float' values)}} } // Case 7: A function declared with only a half overload. @@ -219,10 +215,10 @@ void Case7(half2 H, float2 F, double2 D) { // CHECK: CallExpr {{.*}} 'void' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(half2)' // CHECK-NEXT: DeclRefExpr {{.*}} 'void (half2)' lvalue Function {{.*}} 'Half' 'void (half2)' - Half(F); // expected-warning{{implicit conversion loses floating-point precision: 'float2' (aka 'vector') to 'half2' (aka 'vector')}} + Half(F); // expected-warning{{implicit conversion loses floating-point precision: 'float2' (aka 'vector') to 'vector' (vector of 2 'half' values)}} // CHECK: CallExpr {{.*}} 'void' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(half2)' // CHECK-NEXT: DeclRefExpr {{.*}} 'void (half2)' lvalue Function {{.*}} 'Half' 'void (half2)' - Half(D); // expected-warning{{implicit conversion loses floating-point precision: 'double2' (aka 'vector') to 'half2' (aka 'vector')}} + Half(D); // expected-warning{{implicit conversion loses floating-point precision: 'double2' (aka 'vector') to 'vector' (vector of 2 'half' values)}} } diff --git a/clang/test/SemaHLSL/VectorOverloadResolution.hlsl b/clang/test/SemaHLSL/VectorOverloadResolution.hlsl index 485094fd09b3c..37d5068c3067c 100644 --- a/clang/test/SemaHLSL/VectorOverloadResolution.hlsl +++ b/clang/test/SemaHLSL/VectorOverloadResolution.hlsl @@ -7,7 +7,7 @@ void Fn(half2 H); // CHECK: CallExpr {{.*}}'void' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(double2)' // CHECK-NEXT: DeclRefExpr {{.*}}'void (double2)' lvalue Function {{.*}} 'Fn' 'void (double2)' -// CHECK-NEXT: ImplicitCastExpr {{.*}} 'double2':'vector' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'float2':'vector' // CHECK-NEXT: DeclRefExpr {{.*}} 'float2':'vector' lvalue ParmVar {{.*}} 'F' 'float2':'vector' @@ -22,7 +22,7 @@ void Fn2(int16_t2 S); // CHECK: CallExpr {{.*}} 'void' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(int64_t2)' // CHECK-NEXT: DeclRefExpr {{.*}} 'void (int64_t2)' lvalue Function {{.*}} 'Fn2' 'void (int64_t2)' -// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int64_t2':'vector' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'int2':'vector' // CHECK-NEXT: DeclRefExpr {{.*}} 'int2':'vector' lvalue ParmVar {{.*}} 'I' 'int2':'vector' @@ -36,7 +36,7 @@ void Fn3( int64_t2 p0); // CHECK: CallExpr {{.*}} 'void' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(int64_t2)' // CHECK-NEXT: DeclRefExpr {{.*}} 'void (int64_t2)' lvalue Function {{.*}} 'Fn3' 'void (int64_t2)' -// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int64_t2':'vector' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'half2':'vector' // CHECK-NEXT: DeclRefExpr {{.*}} 'half2':'vector' lvalue ParmVar {{.*}} 'p0' 'half2':'vector' // CHECKIR-LABEL: Call3 @@ -49,7 +49,7 @@ void Call3(half2 p0) { // CHECK: CallExpr {{.*}} 'void' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(int64_t2)' // CHECK-NEXT: DeclRefExpr {{.*}} 'void (int64_t2)' lvalue Function {{.*}} 'Fn3' 'void (int64_t2)' -// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int64_t2':'vector' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'float2':'vector' // CHECK-NEXT: DeclRefExpr {{.*}} 'float2':'vector' lvalue ParmVar {{.*}} 'p0' 'float2':'vector' // CHECKIR-LABEL: Call4 @@ -64,7 +64,7 @@ void Fn4( float2 p0); // CHECK: CallExpr {{.*}} 'void' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float2)' // CHECK-NEXT: DeclRefExpr {{.*}} 'void (float2)' lvalue Function {{.*}} 'Fn4' 'void (float2)' -// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float2':'vector' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'int64_t2':'vector' // CHECK-NEXT: DeclRefExpr {{.*}} 'int64_t2':'vector' lvalue ParmVar {{.*}} 'p0' 'int64_t2':'vector' // CHECKIR-LABEL: Call5 diff --git a/clang/test/SemaHLSL/standard_conversion_sequences.hlsl b/clang/test/SemaHLSL/standard_conversion_sequences.hlsl index c8d9f2c156e31..59779708d9137 100644 --- a/clang/test/SemaHLSL/standard_conversion_sequences.hlsl +++ b/clang/test/SemaHLSL/standard_conversion_sequences.hlsl @@ -23,8 +23,8 @@ void test() { vector f2 = f3; // #f2 // CHECK: VarDecl {{.*}} f2_2 'vector' cinit - // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' - // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' // CHECK-NEXT: DeclRefExpr {{.*}} 'vector' lvalue Var {{.*}} 'd4' 'vector' // expected-warning@#f2_2{{implicit conversion truncates vector: 'vector' (vector of 4 'double' values) to 'vector' (vector of 2 'float' values)}} @@ -39,8 +39,8 @@ void test() { vector i2 = f2; // #i2 // CHECK: VarDecl {{.*}} i2_2 'vector' cinit - // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' - // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' // CHECK-NEXT: DeclRefExpr {{.*}} 'vector' lvalue Var {{.*}} 'd4' 'vector' // expected-warning@#i2_2{{implicit conversion truncates vector: 'vector' (vector of 4 'double' values) to 'vector' (vector of 2 'int' values)}} @@ -56,8 +56,8 @@ void test() { vector i64_4 = d4; // #i64_4 // CHECK: VarDecl {{.*}} used i2_3 'vector' cinit - // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' - // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' // CHECK-NEXT: DeclRefExpr {{.*}} 'vector' lvalue Var {{.*}} 'i64_4' 'vector' // expected-warning@#i2_3{{implicit conversion loses integer precision: 'vector' (vector of 4 'long' values) to 'vector' (vector of 2 'int' values)}} @@ -71,8 +71,8 @@ void test() { vector b2 = i2_3; // No warning for integer to bool conversion. // CHECK: VarDecl {{.*}} b2_2 'vector' cinit - // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' - // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' // CHECK-NEXT: DeclRefExpr {{.*}} 'vector' lvalue Var {{.*}} 'd4' 'vector' // expected-warning@#b2_2{{implicit conversion truncates vector: 'vector' (vector of 4 'double' values) to 'vector' (vector of 2 'bool' values)}}