[clang] [HLSL] Vector standard conversions (PR #71098)

Chris B via cfe-commits cfe-commits at lists.llvm.org
Tue Feb 13 08:04:28 PST 2024


https://github.com/llvm-beanz updated https://github.com/llvm/llvm-project/pull/71098

>From 91e8d9d9f63fe2ac481bb01549e3d69ac59d68f8 Mon Sep 17 00:00:00 2001
From: Chris Bieneman <chris.bieneman at me.com>
Date: Wed, 1 Nov 2023 12:18:43 -0500
Subject: [PATCH 01/11] [HLSL] Vector vector standard conversions

HLSL supports vector truncation and element conversions as part of
standard conversion sequences. The vector truncation conversion is a C++
second conversion in the conversion sequence. If a vector truncation is
in a conversion sequence an element conversion may occur after it before
the standard C++ third conversion.

Vector element conversions can be boolean conversions, floating point or
integral conversions or promotions.

[HLSL Draft
Specification](https://microsoft.github.io/hlsl-specs/specs/hlsl.pdf)

../clang/test/CodeGenHLSL/BasicFeatures/standard_conversion_sequences.hl
sl
../clang/test/SemaHLSL/BuiltIns/vector-constructors-erros.hlsl
../clang/test/SemaHLSL/standard_conversion_sequences.hlsl
---
 clang/include/clang/AST/OperationKinds.def    |   3 +
 .../clang/Basic/DiagnosticSemaKinds.td        |   3 +
 clang/include/clang/Sema/Overload.h           |  12 +-
 clang/lib/AST/Expr.cpp                        |   1 +
 clang/lib/AST/ExprConstant.cpp                |   2 +
 clang/lib/CodeGen/CGExpr.cpp                  |   2 +
 clang/lib/CodeGen/CGExprAgg.cpp               |   2 +
 clang/lib/CodeGen/CGExprComplex.cpp           |   1 +
 clang/lib/CodeGen/CGExprConstant.cpp          |   1 +
 clang/lib/CodeGen/CGExprScalar.cpp            |  23 +-
 clang/lib/Edit/RewriteObjCFoundationAPI.cpp   |   4 +
 clang/lib/Sema/SemaChecking.cpp               |  11 +-
 clang/lib/Sema/SemaExprCXX.cpp                |  84 ++++++
 clang/lib/Sema/SemaInit.cpp                   |   2 +-
 clang/lib/Sema/SemaOverload.cpp               | 281 +++++++++++-------
 clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp |   3 +-
 .../standard_conversion_sequences.hlsl        | 119 ++++++++
 .../BuiltIns/vector-constructors-erros.hlsl   |   2 +-
 .../standard_conversion_sequences.hlsl        |  92 ++++++
 19 files changed, 536 insertions(+), 112 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/BasicFeatures/standard_conversion_sequences.hlsl
 create mode 100644 clang/test/SemaHLSL/standard_conversion_sequences.hlsl

diff --git a/clang/include/clang/AST/OperationKinds.def b/clang/include/clang/AST/OperationKinds.def
index 8dd98730dff742..e497fe4d1f93ff 100644
--- a/clang/include/clang/AST/OperationKinds.def
+++ b/clang/include/clang/AST/OperationKinds.def
@@ -361,6 +361,9 @@ CAST_OPERATION(AddressSpaceConversion)
 // Convert an integer initializer to an OpenCL sampler.
 CAST_OPERATION(IntToOCLSampler)
 
+// Truncate a vector type (HLSL only).
+CAST_OPERATION(VectorTruncation)
+
 //===- Binary Operations  -------------------------------------------------===//
 // Operators listed in order of precedence.
 // Note that additions to this should also update the StmtVisitor class,
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d164177251e4db..6f21674d21fa54 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12023,6 +12023,9 @@ def err_hlsl_operator_unsupported : Error<
 def err_hlsl_param_qualifier_mismatch :
   Error<"conflicting parameter qualifier %0 on parameter %1">;
 
+def warn_hlsl_impcast_vector_truncation : Warning<
+  "implicit conversion truncates vector: %0 to %1">, InGroup<Conversion>;
+
 // Layout randomization diagnostics.
 def err_non_designated_init_used : Error<
   "a randomized struct can only be initialized with a designated initializer">;
diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h
index 6ccabad3af5446..2f93fee4d9806a 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -195,6 +195,9 @@ class Sema;
     /// Fixed point type conversions according to N1169.
     ICK_Fixed_Point_Conversion,
 
+    /// HLSL vector truncation.
+    ICK_HLSL_Vector_Truncation,
+
     /// The number of conversion kinds
     ICK_Num_Conversion_Kinds,
   };
@@ -271,6 +274,12 @@ 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;
+
     /// Third - The third conversion can be a qualification conversion
     /// or a function conversion.
     ImplicitConversionKind Third : 8;
@@ -357,7 +366,8 @@ class Sema;
     void setAsIdentityConversion();
 
     bool isIdentityConversion() const {
-      return Second == ICK_Identity && Third == ICK_Identity;
+      return Second == ICK_Identity && Element == ICK_Identity &&
+             Third == ICK_Identity;
     }
 
     ImplicitConversionRank getRank() const;
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 55c6b732b7081f..bfbdb6cf0f929d 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -1898,6 +1898,7 @@ bool CastExpr::CastConsistency() const {
   case CK_FixedPointToIntegral:
   case CK_IntegralToFixedPoint:
   case CK_MatrixCast:
+  case CK_VectorTruncation:
     assert(!getType()->isBooleanType() && "unheralded conversion to bool");
     goto CheckNoBasePath;
 
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 16697e5f076a8f..24c12cd0fd939d 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -13875,6 +13875,7 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) {
   case CK_FixedPointCast:
   case CK_IntegralToFixedPoint:
   case CK_MatrixCast:
+  case CK_VectorTruncation:
     llvm_unreachable("invalid cast kind for integral value");
 
   case CK_BitCast:
@@ -14713,6 +14714,7 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) {
   case CK_FixedPointToIntegral:
   case CK_IntegralToFixedPoint:
   case CK_MatrixCast:
+  case CK_VectorTruncation:
     llvm_unreachable("invalid cast kind for complex value");
 
   case CK_LValueToRValue:
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 9d1f1a58f9e1c5..9101f81c718b71 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -4933,6 +4933,7 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) {
   case CK_FixedPointToIntegral:
   case CK_IntegralToFixedPoint:
   case CK_MatrixCast:
+  case CK_VectorTruncation:
     return EmitUnsupportedLValue(E, "unexpected cast lvalue");
 
   case CK_Dependent:
@@ -5060,6 +5061,7 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) {
     return MakeAddrLValue(V, E->getType(), LV.getBaseInfo(),
                           CGM.getTBAAInfoForSubobject(LV, E->getType()));
   }
+
   case CK_ZeroToOCLOpaqueType:
     llvm_unreachable("NULL to OpenCL opaque type lvalue cast is not valid");
 
diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp
index 810b28f25fa18b..f9278133e5ddc6 100644
--- a/clang/lib/CodeGen/CGExprAgg.cpp
+++ b/clang/lib/CodeGen/CGExprAgg.cpp
@@ -930,6 +930,7 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) {
   case CK_BuiltinFnToFnPtr:
   case CK_ZeroToOCLOpaqueType:
   case CK_MatrixCast:
+  case CK_VectorTruncation:
 
   case CK_IntToOCLSampler:
   case CK_FloatingToFixedPoint:
@@ -1454,6 +1455,7 @@ static bool castPreservesZero(const CastExpr *CE) {
   case CK_MatrixCast:
   case CK_NonAtomicToAtomic:
   case CK_AtomicToNonAtomic:
+  case CK_VectorTruncation:
     return true;
 
   case CK_BaseToDerivedMemberPointer:
diff --git a/clang/lib/CodeGen/CGExprComplex.cpp b/clang/lib/CodeGen/CGExprComplex.cpp
index f3cbd1d0451ebe..45862701d59bb4 100644
--- a/clang/lib/CodeGen/CGExprComplex.cpp
+++ b/clang/lib/CodeGen/CGExprComplex.cpp
@@ -556,6 +556,7 @@ ComplexPairTy ComplexExprEmitter::EmitCast(CastKind CK, Expr *Op,
   case CK_FixedPointToIntegral:
   case CK_IntegralToFixedPoint:
   case CK_MatrixCast:
+  case CK_VectorTruncation:
     llvm_unreachable("invalid cast kind for complex value");
 
   case CK_FloatingRealToComplex:
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index 604e3958161db2..577eee2ee1ff0f 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -1225,6 +1225,7 @@ class ConstExprEmitter :
     case CK_IntegralToFixedPoint:
     case CK_ZeroToOCLOpaqueType:
     case CK_MatrixCast:
+    case CK_VectorTruncation:
       return nullptr;
     }
     llvm_unreachable("Invalid CastKind");
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 378437364767f6..7b57f33865c03b 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -610,6 +610,8 @@ class ScalarExprEmitter
   llvm::Value *EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV,
                                        bool isInc, bool isPre);
 
+  llvm::Value *EmitVectorElementConversion(QualType SrcType, QualType DstType,
+                                           llvm::Value *Src);
 
   Value *VisitUnaryAddrOf(const UnaryOperator *E) {
     if (isa<MemberPointerType>(E->getType())) // never sugared
@@ -1422,6 +1424,9 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType,
     return Builder.CreateVectorSplat(NumElements, Src, "splat");
   }
 
+  if (SrcType->isExtVectorType() && DstType->isExtVectorType())
+    return EmitVectorElementConversion(SrcType, DstType, Src);
+
   if (SrcType->isMatrixType() && DstType->isMatrixType())
     return EmitScalarCast(Src, SrcType, DstType, SrcTy, DstTy, Opts);
 
@@ -1701,10 +1706,14 @@ Value *ScalarExprEmitter::VisitShuffleVectorExpr(ShuffleVectorExpr *E) {
 }
 
 Value *ScalarExprEmitter::VisitConvertVectorExpr(ConvertVectorExpr *E) {
-  QualType SrcType = E->getSrcExpr()->getType(),
-           DstType = E->getType();
+  QualType SrcType = E->getSrcExpr()->getType(), DstType = E->getType();
+  Value *Src = CGF.EmitScalarExpr(E->getSrcExpr());
+  return EmitVectorElementConversion(SrcType, DstType, Src);
+}
 
-  Value *Src  = CGF.EmitScalarExpr(E->getSrcExpr());
+llvm::Value *ScalarExprEmitter::EmitVectorElementConversion(QualType SrcType,
+                                                            QualType DstType,
+                                                            Value *Src) {
 
   SrcType = CGF.getContext().getCanonicalType(SrcType);
   DstType = CGF.getContext().getCanonicalType(DstType);
@@ -2466,6 +2475,14 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
   case CK_IntToOCLSampler:
     return CGF.CGM.createOpenCLIntToSamplerConversion(E, CGF);
 
+  case CK_VectorTruncation: {
+    assert(DestTy->isVectorType() && "Expected dest type to be vector type");
+    Value *Vec = Visit(const_cast<Expr *>(E));
+    SmallVector<int, 16> Mask;
+    Mask.insert(Mask.begin(), DestTy->getAs<VectorType>()->getNumElements(), 0);
+    return Builder.CreateShuffleVector(Vec, Mask, "trunc");
+  }
+
   } // end of switch
 
   llvm_unreachable("unknown scalar cast");
diff --git a/clang/lib/Edit/RewriteObjCFoundationAPI.cpp b/clang/lib/Edit/RewriteObjCFoundationAPI.cpp
index adb34eba497030..d42532cc15d27e 100644
--- a/clang/lib/Edit/RewriteObjCFoundationAPI.cpp
+++ b/clang/lib/Edit/RewriteObjCFoundationAPI.cpp
@@ -1087,6 +1087,10 @@ static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
     case CK_BooleanToSignedIntegral:
       llvm_unreachable("OpenCL-specific cast in Objective-C?");
 
+    case CK_VectorTruncation:
+      llvm_unreachable("HLSL-specific cast in Objective-C?");
+      break;
+
     case CK_FloatingToFixedPoint:
     case CK_FixedPointToFloating:
     case CK_FixedPointCast:
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 9dfff132cd88db..4d35dc35139a32 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -15433,11 +15433,18 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
       if (S.SourceMgr.isInSystemMacro(CC))
         return;
       return DiagnoseImpCast(S, E, T, CC, diag::warn_impcast_vector_scalar);
+    } else if (S.getLangOpts().HLSL &&
+               Target->getAs<VectorType>()->getNumElements() <
+                   Source->getAs<VectorType>()->getNumElements()) {
+      // Diagnose vector truncation but don't return. We may also want to
+      // diagnose an element conversion.
+      DiagnoseImpCast(S, E, T, CC, diag::warn_hlsl_impcast_vector_truncation);
     }
 
     // If the vector cast is cast between two vectors of the same size, it is
-    // a bitcast, not a conversion.
-    if (S.Context.getTypeSize(Source) == S.Context.getTypeSize(Target))
+    // a bitcast, not a conversion, except under HLSL where it is a conversion.
+    if (!S.getLangOpts().HLSL &&
+        S.Context.getTypeSize(Source) == S.Context.getTypeSize(Target))
       return;
 
     Source = cast<VectorType>(Source)->getElementType().getTypePtr();
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 081b568762ae22..5052af7905ca14 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -4763,6 +4763,20 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
                              CK_ZeroToOCLOpaqueType,
                              From->getValueKind()).get();
     break;
+  case ICK_HLSL_Vector_Truncation: {
+    // Note: HLSL 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.
+    auto *FromVec = From->getType()->castAs<ExtVectorType>();
+    auto *ToVec = ToType->castAs<ExtVectorType>();
+    QualType ElType = FromVec->getElementType();
+    QualType TruncTy =
+        Context.getExtVectorType(ElType, ToVec->getNumElements());
+    From = ImpCastExprToType(From, TruncTy, CK_VectorTruncation,
+                             From->getValueKind())
+               .get();
+    break;
+  }
 
   case ICK_Lvalue_To_Rvalue:
   case ICK_Array_To_Pointer:
@@ -4775,6 +4789,76 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
     llvm_unreachable("Improper second standard conversion");
   }
 
+  if (SCS.Element != ICK_Identity) {
+    // if SCS.Element is not ICK_Identity the To and From types must be HLSL
+    // vectors or matrices. HLSL matrices aren't yet supported so this code only
+    // handles vectors for now.
+
+    assert(From->getType()->isVectorType() && ToType->isVectorType() &&
+           "Element conversion is only supported for vector types.");
+    assert(From->getType()->getAs<VectorType>()->getNumElements() ==
+               ToType->getAs<VectorType>()->getNumElements() &&
+           "Element conversion is only supported for vectors with the same "
+           "element counts.");
+    QualType FromElTy = From->getType()->getAs<VectorType>()->getElementType();
+    unsigned NumElts = ToType->getAs<VectorType>()->getNumElements();
+    switch (SCS.Element) {
+    case ICK_Identity:
+      // Nothing to do.
+      break;
+    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<EnumType>()->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,
+                               /*BasePath=*/nullptr, CCK)
+                 .get();
+      break;
+    case ICK_Floating_Integral:
+      if (ToType->isRealFloatingType())
+        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();
+      break;
+    default:
+      llvm_unreachable("Improper element standard conversion");
+    }
+  }
+
   switch (SCS.Third) {
   case ICK_Identity:
     // Nothing to do.
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 50ee0a5acb5586..b2f7eb65be3f13 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -6427,7 +6427,7 @@ void InitializationSequence::InitializeFrom(Sema &S,
   // For HLSL ext vector types we allow list initialization behavior for C++
   // constructor syntax. This is accomplished by converting initialization
   // arguments an InitListExpr late.
-  if (S.getLangOpts().HLSL && DestType->isExtVectorType() &&
+  if (S.getLangOpts().HLSL && Args.size() > 1 && DestType->isExtVectorType() &&
       (SourceType.isNull() ||
        !Context.hasSameUnqualifiedType(SourceType, DestType))) {
 
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 3a3e9234469d39..e3509546acc0c7 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -120,87 +120,88 @@ CompareDerivedToBaseConversions(Sema &S, SourceLocation Loc,
                                 const StandardConversionSequence& SCS1,
                                 const StandardConversionSequence& SCS2);
 
+// clang-format off - disable clang format for this block.
 /// GetConversionRank - Retrieve the implicit conversion rank
 /// corresponding to the given implicit conversion kind.
 ImplicitConversionRank clang::GetConversionRank(ImplicitConversionKind Kind) {
-  static const ImplicitConversionRank
-    Rank[] = {
-    ICR_Exact_Match,
-    ICR_Exact_Match,
-    ICR_Exact_Match,
-    ICR_Exact_Match,
-    ICR_Exact_Match,
-    ICR_Exact_Match,
-    ICR_Promotion,
-    ICR_Promotion,
-    ICR_Promotion,
-    ICR_Conversion,
-    ICR_Conversion,
-    ICR_Conversion,
-    ICR_Conversion,
-    ICR_Conversion,
-    ICR_Conversion,
-    ICR_Conversion,
-    ICR_Conversion,
-    ICR_Conversion,
-    ICR_Conversion,
-    ICR_Conversion,
-    ICR_Conversion,
-    ICR_OCL_Scalar_Widening,
-    ICR_Complex_Real_Conversion,
-    ICR_Conversion,
-    ICR_Conversion,
-    ICR_Writeback_Conversion,
-    ICR_Exact_Match, // NOTE(gbiv): This may not be completely right --
-                     // it was omitted by the patch that added
-                     // ICK_Zero_Event_Conversion
-    ICR_Exact_Match, // NOTE(ctopper): This may not be completely right --
-                     // it was omitted by the patch that added
-                     // ICK_Zero_Queue_Conversion
-    ICR_C_Conversion,
-    ICR_C_Conversion_Extension,
-    ICR_Conversion,
+  static const ImplicitConversionRank Rank[] = {
+      ICR_Exact_Match,
+      ICR_Exact_Match,
+      ICR_Exact_Match,
+      ICR_Exact_Match,
+      ICR_Exact_Match,
+      ICR_Exact_Match,
+      ICR_Promotion,
+      ICR_Promotion,
+      ICR_Promotion,
+      ICR_Conversion,
+      ICR_Conversion,
+      ICR_Conversion,
+      ICR_Conversion,
+      ICR_Conversion,
+      ICR_Conversion,
+      ICR_Conversion,
+      ICR_Conversion,
+      ICR_Conversion,
+      ICR_Conversion,
+      ICR_Conversion,
+      ICR_Conversion,
+      ICR_OCL_Scalar_Widening,
+      ICR_Complex_Real_Conversion,
+      ICR_Conversion,
+      ICR_Conversion,
+      ICR_Writeback_Conversion,
+      ICR_Exact_Match, // NOTE(gbiv): This may not be completely right --
+                       // it was omitted by the patch that added
+                       // ICK_Zero_Event_Conversion
+      ICR_Exact_Match, // NOTE(ctopper): This may not be completely right --
+                       // it was omitted by the patch that added
+                       // ICK_Zero_Queue_Conversion
+      ICR_C_Conversion,
+      ICR_C_Conversion_Extension,
+      ICR_Conversion,
+      ICR_Conversion,
   };
   static_assert(std::size(Rank) == (int)ICK_Num_Conversion_Kinds);
   return Rank[(int)Kind];
 }
+// clang-format on
 
 /// GetImplicitConversionName - Return the name of this kind of
 /// implicit conversion.
-static const char* GetImplicitConversionName(ImplicitConversionKind Kind) {
-  static const char* const Name[] = {
-    "No conversion",
-    "Lvalue-to-rvalue",
-    "Array-to-pointer",
-    "Function-to-pointer",
-    "Function pointer conversion",
-    "Qualification",
-    "Integral promotion",
-    "Floating point promotion",
-    "Complex promotion",
-    "Integral conversion",
-    "Floating conversion",
-    "Complex conversion",
-    "Floating-integral conversion",
-    "Pointer conversion",
-    "Pointer-to-member conversion",
-    "Boolean conversion",
-    "Compatible-types conversion",
-    "Derived-to-base conversion",
-    "Vector conversion",
-    "SVE Vector conversion",
-    "RVV Vector conversion",
-    "Vector splat",
-    "Complex-real conversion",
-    "Block Pointer conversion",
-    "Transparent Union Conversion",
-    "Writeback conversion",
-    "OpenCL Zero Event Conversion",
-    "OpenCL Zero Queue Conversion",
-    "C specific type conversion",
-    "Incompatible pointer conversion",
-    "Fixed point conversion",
-  };
+static const char *GetImplicitConversionName(ImplicitConversionKind Kind) {
+  static const char *const Name[] = {"No conversion",
+                                     "Lvalue-to-rvalue",
+                                     "Array-to-pointer",
+                                     "Function-to-pointer",
+                                     "Function pointer conversion",
+                                     "Qualification",
+                                     "Integral promotion",
+                                     "Floating point promotion",
+                                     "Complex promotion",
+                                     "Integral conversion",
+                                     "Floating conversion",
+                                     "Complex conversion",
+                                     "Floating-integral conversion",
+                                     "Pointer conversion",
+                                     "Pointer-to-member conversion",
+                                     "Boolean conversion",
+                                     "Compatible-types conversion",
+                                     "Derived-to-base conversion",
+                                     "Vector conversion",
+                                     "SVE Vector conversion",
+                                     "RVV Vector conversion",
+                                     "Vector splat",
+                                     "Complex-real conversion",
+                                     "Block Pointer conversion",
+                                     "Transparent Union Conversion",
+                                     "Writeback conversion",
+                                     "OpenCL Zero Event Conversion",
+                                     "OpenCL Zero Queue Conversion",
+                                     "C specific type conversion",
+                                     "Incompatible pointer conversion",
+                                     "Fixed point conversion",
+                                     "HLSL vector truncation"};
   static_assert(std::size(Name) == (int)ICK_Num_Conversion_Kinds);
   return Name[Kind];
 }
@@ -210,6 +211,7 @@ static const char* GetImplicitConversionName(ImplicitConversionKind Kind) {
 void StandardConversionSequence::setAsIdentityConversion() {
   First = ICK_Identity;
   Second = ICK_Identity;
+  Element = ICK_Identity;
   Third = ICK_Identity;
   DeprecatedStringLiteralToCharPtr = false;
   QualificationIncludesObjCLifetime = false;
@@ -1843,13 +1845,86 @@ bool Sema::IsFunctionConversion(QualType FromType, QualType ToType,
   return true;
 }
 
+/// Determine whether the conversion from FromType to ToType is a valid
+/// floating point conversion.
+///
+static bool IsFloatingPointConversion(Sema &S, QualType FromType,
+                                      QualType ToType) {
+  if (!FromType->isRealFloatingType() || !ToType->isRealFloatingType())
+    return false;
+  // FIXME: disable conversions between long double, __ibm128 and __float128
+  // if their representation is different until there is back end support
+  // We of course allow this conversion if long double is really double.
+
+  // Conversions between bfloat16 and float16 are currently not supported.
+  if ((FromType->isBFloat16Type() &&
+       (ToType->isFloat16Type() || ToType->isHalfType())) ||
+      (ToType->isBFloat16Type() &&
+       (FromType->isFloat16Type() || FromType->isHalfType())))
+    return false;
+
+  // Conversions between IEEE-quad and IBM-extended semantics are not
+  // permitted.
+  const llvm::fltSemantics &FromSem = S.Context.getFloatTypeSemantics(FromType);
+  const llvm::fltSemantics &ToSem = S.Context.getFloatTypeSemantics(ToType);
+  if ((&FromSem == &llvm::APFloat::PPCDoubleDouble() &&
+       &ToSem == &llvm::APFloat::IEEEquad()) ||
+      (&FromSem == &llvm::APFloat::IEEEquad() &&
+       &ToSem == &llvm::APFloat::PPCDoubleDouble()))
+    return false;
+  return true;
+}
+
+static bool IsVectorElementConversion(Sema &S, QualType FromType,
+                                      QualType ToType,
+                                      ImplicitConversionKind &ICK, Expr *From) {
+  if (S.Context.hasSameUnqualifiedType(FromType, ToType))
+    return true;
+
+  if (IsFloatingPointConversion(S, FromType, ToType)) {
+    ICK = ICK_Floating_Conversion;
+    return true;
+  }
+
+  if (S.IsFloatingPointPromotion(FromType, ToType)) {
+    ICK = ICK_Floating_Promotion;
+    return true;
+  }
+
+  if (ToType->isBooleanType() && FromType->isArithmeticType()) {
+    ICK = ICK_Boolean_Conversion;
+    return true;
+  }
+
+  if (FromType->isIntegralOrUnscopedEnumerationType() &&
+      ToType->isIntegralType(S.Context)) {
+    ICK = ICK_Integral_Conversion;
+    return true;
+  }
+
+  if (S.IsIntegralPromotion(From, FromType, ToType)) {
+    ICK = ICK_Integral_Promotion;
+    return true;
+  }
+
+  if ((FromType->isRealFloatingType() && ToType->isIntegralType(S.Context)) ||
+      (FromType->isIntegralOrUnscopedEnumerationType() &&
+       ToType->isRealFloatingType())) {
+    ICK = ICK_Floating_Integral;
+    return true;
+  }
+
+  return false;
+}
+
 /// Determine whether the conversion from FromType to ToType is a valid
 /// vector conversion.
 ///
 /// \param ICK Will be set to the vector conversion kind, if this is a vector
 /// conversion.
 static bool IsVectorConversion(Sema &S, QualType FromType, QualType ToType,
-                               ImplicitConversionKind &ICK, Expr *From,
+                               ImplicitConversionKind &ICK,
+                               ImplicitConversionKind &ElConv, Expr *From,
                                bool InOverloadResolution, bool CStyle) {
   // We need at least one of these types to be a vector type to have a vector
   // conversion.
@@ -1862,10 +1937,28 @@ static bool IsVectorConversion(Sema &S, QualType FromType, QualType ToType,
 
   // There are no conversions between extended vector types, only identity.
   if (ToType->isExtVectorType()) {
-    // There are no conversions between extended vector types other than the
-    // identity conversion.
-    if (FromType->isExtVectorType())
+    if (FromType->isExtVectorType()) {
+      // HLSL allows implicit truncation of vector types.
+      if (S.getLangOpts().HLSL) {
+        unsigned FromElts = FromType->getAs<VectorType>()->getNumElements();
+        unsigned ToElts = ToType->getAs<VectorType>()->getNumElements();
+        if (FromElts < ToElts)
+          return false;
+        if (FromElts == ToElts)
+          ICK = ICK_Identity;
+        else
+          ICK = ICK_HLSL_Vector_Truncation;
+
+        QualType FromElTy = FromType->getAs<VectorType>()->getElementType();
+        QualType ToElTy = ToType->getAs<VectorType>()->getElementType();
+        if (S.Context.hasSameUnqualifiedType(FromElTy, ToElTy))
+          return true;
+        return IsVectorElementConversion(S, FromElTy, ToElTy, ElConv, From);
+      }
+      // There are no conversions between extended vector types other than the
+      // identity conversion.
       return false;
+    }
 
     // Vector splat from any arithmetic type to a vector.
     if (FromType->isArithmeticType()) {
@@ -2071,6 +2164,7 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
   // conversion.
   bool IncompatibleObjC = false;
   ImplicitConversionKind SecondICK = ICK_Identity;
+  ImplicitConversionKind ElementICK = ICK_Identity;
   if (S.Context.hasSameUnqualifiedType(FromType, ToType)) {
     // The unqualified versions of the types are the same: there's no
     // conversion to do.
@@ -2109,29 +2203,7 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
     // Complex-real conversions (C99 6.3.1.7)
     SCS.Second = ICK_Complex_Real;
     FromType = ToType.getUnqualifiedType();
-  } else if (FromType->isRealFloatingType() && ToType->isRealFloatingType()) {
-    // FIXME: disable conversions between long double, __ibm128 and __float128
-    // if their representation is different until there is back end support
-    // We of course allow this conversion if long double is really double.
-
-    // Conversions between bfloat16 and float16 are currently not supported.
-    if ((FromType->isBFloat16Type() &&
-         (ToType->isFloat16Type() || ToType->isHalfType())) ||
-        (ToType->isBFloat16Type() &&
-         (FromType->isFloat16Type() || FromType->isHalfType())))
-      return false;
-
-    // Conversions between IEEE-quad and IBM-extended semantics are not
-    // permitted.
-    const llvm::fltSemantics &FromSem =
-        S.Context.getFloatTypeSemantics(FromType);
-    const llvm::fltSemantics &ToSem = S.Context.getFloatTypeSemantics(ToType);
-    if ((&FromSem == &llvm::APFloat::PPCDoubleDouble() &&
-         &ToSem == &llvm::APFloat::IEEEquad()) ||
-        (&FromSem == &llvm::APFloat::IEEEquad() &&
-         &ToSem == &llvm::APFloat::PPCDoubleDouble()))
-      return false;
-
+  } else if (IsFloatingPointConversion(S, FromType, ToType)) {
     // Floating point conversions (C++ 4.8).
     SCS.Second = ICK_Floating_Conversion;
     FromType = ToType.getUnqualifiedType();
@@ -2158,18 +2230,18 @@ 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, From,
-                                InOverloadResolution, CStyle)) {
+  } else if (IsVectorConversion(S, FromType, ToType, SecondICK, ElementICK,
+                                From, InOverloadResolution, CStyle)) {
     SCS.Second = SecondICK;
+    SCS.Element = ElementICK;
     FromType = ToType.getUnqualifiedType();
   } else if (!S.getLangOpts().CPlusPlus &&
              S.Context.typesAreCompatible(ToType, FromType)) {
     // Compatible conversions (Clang extension for C function overloading)
     SCS.Second = ICK_Compatible_Conversion;
     FromType = ToType.getUnqualifiedType();
-  } else if (IsTransparentUnionStandardConversion(S, From, ToType,
-                                             InOverloadResolution,
-                                             SCS, CStyle)) {
+  } else if (IsTransparentUnionStandardConversion(
+                 S, From, ToType, InOverloadResolution, SCS, CStyle)) {
     SCS.Second = ICK_TransparentUnionConversion;
     FromType = ToType;
   } else if (tryAtomicConversion(S, From, ToType, InOverloadResolution, SCS,
@@ -5054,6 +5126,7 @@ TryReferenceInit(Sema &S, Expr *Init, QualType DeclType,
                               : (RefConv & Sema::ReferenceConversions::ObjC)
                                     ? ICK_Compatible_Conversion
                                     : ICK_Identity;
+    ICS.Standard.Element = 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.
@@ -5975,6 +6048,7 @@ static bool CheckConvertedConstantConversions(Sema &S,
   case ICK_C_Only_Conversion:
   case ICK_Incompatible_Pointer_Conversion:
   case ICK_Fixed_Point_Conversion:
+  case ICK_HLSL_Vector_Truncation:
     return false;
 
   case ICK_Lvalue_To_Rvalue:
@@ -6233,6 +6307,7 @@ ExprResult Sema::CheckConvertedConstantExpression(Expr *From, QualType T,
 static void dropPointerConversion(StandardConversionSequence &SCS) {
   if (SCS.Second == ICK_Pointer_Conversion) {
     SCS.Second = ICK_Identity;
+    SCS.Element = ICK_Identity;
     SCS.Third = ICK_Identity;
     SCS.ToTypePtrs[2] = SCS.ToTypePtrs[1] = SCS.ToTypePtrs[0];
   }
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index 7e431f7e598c4c..c90423784a3425 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -520,7 +520,8 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
       // Various C++ casts that are not handled yet.
       case CK_ToUnion:
       case CK_MatrixCast:
-      case CK_VectorSplat: {
+      case CK_VectorSplat:
+      case CK_VectorTruncation: {
         QualType resultType = CastE->getType();
         if (CastE->isGLValue())
           resultType = getContext().getPointerType(resultType);
diff --git a/clang/test/CodeGenHLSL/BasicFeatures/standard_conversion_sequences.hlsl b/clang/test/CodeGenHLSL/BasicFeatures/standard_conversion_sequences.hlsl
new file mode 100644
index 00000000000000..85b8eed4dafbe5
--- /dev/null
+++ b/clang/test/CodeGenHLSL/BasicFeatures/standard_conversion_sequences.hlsl
@@ -0,0 +1,119 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s
+
+// CHECK-LABEL: f3_to_d4
+// CHECK: [[f3:%.*]] = alloca <3 x float>
+// CHECK: [[d4:%.*]] = alloca <4 x double>
+// CHECK: store <3 x float> <float 1.000000e+00, float 1.000000e+00, float 1.000000e+00>, ptr [[f3]]
+// CHECK: [[vecf3:%.*]] = load <3 x float>, ptr [[f3]]
+// CHECK: [[vecf4:%.*]] = shufflevector <3 x float> [[vecf3]], <3 x float> poison, <4 x i32> <i32 0, i32 1, i32 2, i32 0>
+// CHECK: [[vecd4:%.*]] = fpext <4 x float> [[vecf4]] to <4 x double>
+// CHECK: store <4 x double> [[vecd4]], ptr [[d4]]
+void f3_to_d4() {
+  vector<float,3> f3 = 1.0;
+  vector<double,4> d4 = f3.xyzx;
+}
+
+// CHECK-LABEL: f3_to_f2
+// CHECK: [[f3:%.*]] = alloca <3 x float>
+// CHECK: [[f2:%.*]] = alloca <2 x float>
+// CHECK: store <3 x float> <float 2.000000e+00, float 2.000000e+00, float 2.000000e+00>, ptr [[f3]]
+// CHECK: [[vecf3:%.*]] = load <3 x float>, ptr [[f3]]
+// CHECK: [[vecf2:%.*]] = shufflevector <3 x float> [[vecf3]], <3 x float> poison, <2 x i32> zeroinitializer
+// CHECK: store <2 x float> [[vecf2]], ptr [[f2]]
+void f3_to_f2() {
+  vector<float,3> f3 = 2.0;
+  vector<float,2> f2 = f3;
+}
+
+// CHECK-LABEL: d4_to_f2
+// CHECK: [[d4:%.*]] = alloca <4 x double>
+// CHECK: [[f2:%.*]] = alloca <2 x float>
+// CHECK: store <4 x double> <double 3.000000e+00, double 3.000000e+00, double 3.000000e+00, double 3.000000e+00>, ptr [[d4]]
+// CHECK: [[vecd4:%.*]] = load <4 x double>, ptr [[d4]]
+// CHECK: [[vecd2:%.*]] = shufflevector <4 x double> [[vecd4]], <4 x double> poison, <2 x i32> zeroinitializer
+// CHECK: [[vecf2:%.*]] = fptrunc <2 x double> [[vecd2]] to <2 x float>
+// CHECK: store <2 x float> [[vecf2]], ptr [[f2]]
+void d4_to_f2() {
+  vector<double,4> d4 = 3.0;
+  vector<float,2> f2 = d4;
+}
+
+// CHECK-LABEL: f2_to_i2
+// CHECK: [[f2:%.*]] = alloca <2 x float>
+// CHECK: [[i2:%.*]] = alloca <2 x i32>
+// CHECK: store <2 x float> <float 4.000000e+00, float 4.000000e+00>, ptr [[f2]]
+// CHECK: [[vecf2:%.*]] = load <2 x float>, ptr [[f2]]
+// CHECK: [[veci2:%.*]] = fptosi <2 x float> [[vecf2]] to <2 x i32>
+// CHECK: store <2 x i32> [[veci2]], ptr [[i2]]
+void f2_to_i2() {
+  vector<float,2> f2 = 4.0;
+  vector<int,2> i2 = f2;
+}
+
+// CHECK-LABEL: d4_to_i2
+// CHECK: [[f4:%.*]] = alloca <4 x double>
+// CHECK: [[i2:%.*]] = alloca <2 x i32>
+// CHECK: store <4 x double> <double 5.000000e+00, double 5.000000e+00, double 5.000000e+00, double 5.000000e+00>, ptr [[d4]]
+// CHECK: [[vecd4:%.*]] = load <4 x double>, ptr [[d4]]
+// CHECK: [[vecd2:%.*]] = shufflevector <4 x double> [[vecd4]], <4 x double> poison, <2 x i32> zeroinitializer
+// CHECK: [[veci2]] = fptosi <2 x double> [[vecd2]] to <2 x i32>
+// CHECK: store <2 x i32> [[veci2]], ptr [[i2]]
+void d4_to_i2() {
+  vector<double,4> d4 = 5.0;
+  vector<int,2> i2 = d4;
+}
+
+// CHECK-LABEL: d4_to_l4
+// CHECK: [[d4:%.*]] = alloca <4 x double>
+// CHECK: [[l4:%.*]] = alloca <4 x i64>
+// CHECK: store <4 x double> <double 6.000000e+00, double 6.000000e+00, double 6.000000e+00, double 6.000000e+00>, ptr [[d4]]
+// CHECK: [[vecd4:%.*]] = load <4 x double>, ptr [[d4]]
+// CHECK: [[vecl4:%.*]] = fptosi <4 x double> [[vecd4]] to <4 x i64>
+// CHECK: store <4 x i64> [[vecl4]], ptr [[l4]]
+void d4_to_l4() {
+  vector<double,4> d4 = 6.0;
+  vector<long,4> l4 = d4;
+}
+
+
+// CHECK-LABEL: l4_to_i2
+// CHECK: [[l4:%.*]] = alloca <4 x i64>
+// CHECK: [[i2:%.*]] = alloca <2 x i32>
+// CHECK: store <4 x i64> <i64 7, i64 7, i64 7, i64 7>, ptr [[l4]]
+// CHECK: [[vecl4:%.*]] = load <4 x i64>, ptr [[l4]]
+// CHECK: [[vecl2:%.*]] = shufflevector <4 x i64> [[vecl4]], <4 x i64> poison, <2 x i32> zeroinitializer
+// CHECK: [[veci2:%.*]] = trunc <2 x i64> [[vecl2]] to <2 x i32>
+// CHECK: store <2 x i32> [[veci2]], ptr [[i2]]
+void l4_to_i2() {
+  vector<long, 4> l4 = 7;
+  vector<int,2> i2 = l4;
+}
+
+// CHECK-LABEL: i2_to_b2
+// CHECK: [[l2:%.*]] = alloca <2 x i32>
+// CHECK: [[b2:%.*]] = alloca i8
+// CHECK: store <2 x i32> <i32 8, i32 8>, ptr [[i2]]
+// CHECK: [[veci2:%.*]] = load <2 x i32>, ptr [[i2]]
+// CHECK: [[vecb2:%.*]] = icmp ne <2 x i32> [[veci2]], zeroinitializer
+// CHECK: [[vecb8:%.*]] = shufflevector <2 x i1> [[vecb2]], <2 x i1> poison, <8 x i32> <i32 0, i32 1, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+// CHECK: [[i8:%.*]] = bitcast <8 x i1> [[vecb8]] to i8
+// CHECK: store i8 [[i8]], ptr [[b2]]
+void i2_to_b2() {
+  vector<int, 2> i2 = 8;
+  vector<bool, 2> b2 = i2;
+}
+
+// CHECK-LABEL: d4_to_b2
+// CHECK: [[d4:%.*]] = alloca <4 x double>
+// CHECK: [[b2:%.*]] = alloca i8
+// CHECK: store <4 x double> <double 9.000000e+00, double 9.000000e+00, double 9.000000e+00, double 9.000000e+00>, ptr [[d4]]
+// CHECK: [[vecd4:%.*]] = load <4 x double>, ptr [[d4]]
+// CHECK: [[vecd2:%.*]] = shufflevector <4 x double> [[vecd4]], <4 x double> poison, <2 x i32> zeroinitializer
+// CHECK: [[vecb2:%.*]] = fcmp une <2 x double> [[vecd2]], zeroinitializer
+// CHECK: [[vecb8:%.*]] = shufflevector <2 x i1> [[vecb2]], <2 x i1> poison, <8 x i32> <i32 0, i32 1, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+// CHECK: [[i8:%.*]] = bitcast <8 x i1> [[vecb8]] to i8
+// CHECK: store i8 [[i8]], ptr [[b2]]
+void d4_to_b2() {
+  vector<double,4> d4 = 9.0;
+  vector<bool, 2> b2 = d4;
+}
diff --git a/clang/test/SemaHLSL/BuiltIns/vector-constructors-erros.hlsl b/clang/test/SemaHLSL/BuiltIns/vector-constructors-erros.hlsl
index 73b5a192793e0c..7f6bdc7e67836b 100644
--- a/clang/test/SemaHLSL/BuiltIns/vector-constructors-erros.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/vector-constructors-erros.hlsl
@@ -18,5 +18,5 @@ void entry() {
   S s;
   float2 GettingStrange = float2(s, s); // expected-error{{no viable conversion from 'S' to 'float'}} expected-error{{no viable conversion from 'S' to 'float'}}
   S2 s2;
-  float2 EvenStranger = float2(s2); // expected-error{{no viable conversion from 'S2' to 'float'}} expected-error{{too few elements in vector initialization (expected 2 elements, have 1)}}
+  float2 EvenStranger = float2(s2); // expected-error{{cannot convert 'S2' to 'float2' (vector of 2 'float' values) without a conversion operator}}
 }
diff --git a/clang/test/SemaHLSL/standard_conversion_sequences.hlsl b/clang/test/SemaHLSL/standard_conversion_sequences.hlsl
new file mode 100644
index 00000000000000..ca9f2a372c0261
--- /dev/null
+++ b/clang/test/SemaHLSL/standard_conversion_sequences.hlsl
@@ -0,0 +1,92 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -Wconversion -verify -o - %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -Wno-conversion -DNO_ERR -ast-dump %s | FileCheck %s
+
+void test() {
+  
+  // CHECK: VarDecl {{.*}} used f3 'vector<float, 3>':'float __attribute__((ext_vector_type(3)))' cinit
+  // CHECK-NEXt: ImplicitCastExpr {{.*}} 'vector<float, 3>':'float __attribute__((ext_vector_type(3)))' <VectorSplat>
+  // CHECK-NEXt: ImplicitCastExpr {{.*}} 'float' <FloatingCast>
+  // CHECK-NEXt: FloatingLiteral {{.*}} 'double' 1.000000e+00
+  vector<float,3> f3 = 1.0; // No warning for splatting to a vector from a literal.
+
+
+  // CHECK: VarDecl {{.*}} used d4 'vector<double, 4>':'double __attribute__((ext_vector_type(4)))' cinit
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<double, 4>':'double __attribute__((ext_vector_type(4)))' <FloatingCast>
+  // CHECK-NEXT: ExtVectorElementExpr {{.*}} 'float __attribute__((ext_vector_type(4)))' xyzx
+  // CHECK-NEXT: DeclRefExpr {{.*}} 'vector<float, 3>':'float __attribute__((ext_vector_type(3)))' lvalue Var {{.*}} 'f3' 'vector<float, 3>':'float __attribute__((ext_vector_type(3)))'
+  vector<double,4> d4 = f3.xyzx; // No warnings for promotion or explicit extension.
+
+  // CHECK: VarDecl {{.*}} used f2 'vector<float, 2>':'float __attribute__((ext_vector_type(2)))' cinit
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'float __attribute__((ext_vector_type(2)))' <VectorTruncation>
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<float, 3>':'float __attribute__((ext_vector_type(3)))' <LValueToRValue>
+  // CHECK-NEXT: DeclRefExpr {{.*}} 'vector<float, 3>':'float __attribute__((ext_vector_type(3)))' lvalue Var {{.*}} 'f3' 'vector<float, 3>':'float __attribute__((ext_vector_type(3)))'
+  // expected-warning@#f2{{implicit conversion truncates vector: 'vector<float, 3>' (vector of 3 'float' values) to 'float __attribute__((ext_vector_type(2)))' (vector of 2 'float' values)}}
+  vector<float,2> f2 = f3; // #f2
+
+  // CHECK: VarDecl {{.*}} f2_2 'vector<float, 2>':'float __attribute__((ext_vector_type(2)))' cinit
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<float, 2>':'float __attribute__((ext_vector_type(2)))' <FloatingCast>
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'double __attribute__((ext_vector_type(2)))' <VectorTruncation>
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<double, 4>':'double __attribute__((ext_vector_type(4)))' <LValueToRValue>
+  // CHECK-NEXT: DeclRefExpr {{.*}} 'vector<double, 4>':'double __attribute__((ext_vector_type(4)))' lvalue Var {{.*}} 'd4' 'vector<double, 4>':'double __attribute__((ext_vector_type(4)))'
+  // expected-warning@#f2_2{{implicit conversion truncates vector: 'vector<double, 4>' (vector of 4 'double' values) to 'vector<float, 2>' (vector of 2 'float' values)}}
+  // expected-warning@#f2_2{{implicit conversion loses floating-point precision: 'vector<double, 4>' (vector of 4 'double' values) to 'vector<float, 2>' (vector of 2 'float' values)}}
+  vector<float,2> f2_2 = d4; // #f2_2
+
+  // CHECK: VarDecl {{.*}} i2 'vector<int, 2>':'int __attribute__((ext_vector_type(2)))' cinit
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<int, 2>':'int __attribute__((ext_vector_type(2)))' <FloatingToIntegral>
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<float, 2>':'float __attribute__((ext_vector_type(2)))' <LValueToRValue>
+  // CHECK-NEXT: DeclRefExpr {{.*}} 'vector<float, 2>':'float __attribute__((ext_vector_type(2)))' lvalue Var {{.*}} 'f2' 'vector<float, 2>':'float __attribute__((ext_vector_type(2)))'
+  // expected-warning@#i2{{mplicit conversion turns floating-point number into integer: 'vector<float, 2>' (vector of 2 'float' values) to 'vector<int, 2>' (vector of 2 'int' values)}}
+  vector<int,2> i2 = f2; // #i2
+  
+  // CHECK: VarDecl {{.*}} i2_2 'vector<int, 2>':'int __attribute__((ext_vector_type(2)))' cinit
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<int, 2>':'int __attribute__((ext_vector_type(2)))' <FloatingToIntegral>
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'double __attribute__((ext_vector_type(2)))' <VectorTruncation>
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<double, 4>':'double __attribute__((ext_vector_type(4)))' <LValueToRValue>
+  // CHECK-NEXT: DeclRefExpr {{.*}} 'vector<double, 4>':'double __attribute__((ext_vector_type(4)))' lvalue Var {{.*}} 'd4' 'vector<double, 4>':'double __attribute__((ext_vector_type(4)))'
+  // expected-warning@#i2_2{{implicit conversion truncates vector: 'vector<double, 4>' (vector of 4 'double' values) to 'vector<int, 2>' (vector of 2 'int' values)}}
+  // expected-warning@#i2_2{{implicit conversion turns floating-point number into integer: 'vector<double, 4>' (vector of 4 'double' values) to 'vector<int, 2>' (vector of 2 'int' values)}}
+  vector<int,2> i2_2 = d4; // #i2_2
+
+
+  // CHECK: VarDecl {{.*}} used i64_4 'vector<long, 4>':'long __attribute__((ext_vector_type(4)))' cinit
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<long, 4>':'long __attribute__((ext_vector_type(4)))' <FloatingToIntegral>
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<double, 4>':'double __attribute__((ext_vector_type(4)))' <LValueToRValue>
+  // CHECK-NEXT: DeclRefExpr {{.*}} 'vector<double, 4>':'double __attribute__((ext_vector_type(4)))' lvalue Var {{.*}} 'd4' 'vector<double, 4>':'double __attribute__((ext_vector_type(4)))'
+  // expected-warning@#i64_4{{implicit conversion turns floating-point number into integer: 'vector<double, 4>' (vector of 4 'double' values) to 'vector<long, 4>' (vector of 4 'long' values)}}
+  vector<long,4> i64_4 = d4; // #i64_4
+
+  // CHECK: VarDecl {{.*}} used i2_3 'vector<int, 2>':'int __attribute__((ext_vector_type(2)))' cinit
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<int, 2>':'int __attribute__((ext_vector_type(2)))' <IntegralCast>
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'long __attribute__((ext_vector_type(2)))' <VectorTruncation>
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<long, 4>':'long __attribute__((ext_vector_type(4)))' <LValueToRValue>
+  // CHECK-NEXT: DeclRefExpr {{.*}} 'vector<long, 4>':'long __attribute__((ext_vector_type(4)))' lvalue Var {{.*}} 'i64_4' 'vector<long, 4>':'long __attribute__((ext_vector_type(4)))'
+  // expected-warning@#i2_3{{implicit conversion loses integer precision: 'vector<long, 4>' (vector of 4 'long' values) to 'vector<int, 2>' (vector of 2 'int' values)}}
+  // expected-warning@#i2_3{{implicit conversion truncates vector: 'vector<long, 4>' (vector of 4 'long' values) to 'vector<int, 2>' (vector of 2 'int' values)}}
+  vector<int,2> i2_3 = i64_4; // #i2_3
+
+  //CHECK: VarDecl {{.*}} b2 'vector<bool, 2>':'bool __attribute__((ext_vector_type(2)))' cinit
+  //CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<bool, 2>':'bool __attribute__((ext_vector_type(2)))' <IntegralToBoolean>
+  //CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<int, 2>':'int __attribute__((ext_vector_type(2)))' <LValueToRValue>
+  //CHECK-NEXT: DeclRefExpr {{.*}} 'vector<int, 2>':'int __attribute__((ext_vector_type(2)))' lvalue Var {{.*}} 'i2_3' 'vector<int, 2>':'int __attribute__((ext_vector_type(2)))'
+  vector<bool, 2> b2 = i2_3; // No warning for integer to bool conversion.
+
+  // CHECK: VarDecl {{.*}} b2_2 'vector<bool, 2>':'bool __attribute__((ext_vector_type(2)))' cinit
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<bool, 2>':'bool __attribute__((ext_vector_type(2)))' <FloatingToBoolean>
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'double __attribute__((ext_vector_type(2)))' <VectorTruncation>
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<double, 4>':'double __attribute__((ext_vector_type(4)))' <LValueToRValue>
+  // CHECK-NEXT: DeclRefExpr {{.*}} 'vector<double, 4>':'double __attribute__((ext_vector_type(4)))' lvalue Var {{.*}} 'd4' 'vector<double, 4>':'double __attribute__((ext_vector_type(4)))'
+  // expected-warning@#b2_2{{implicit conversion truncates vector: 'vector<double, 4>' (vector of 4 'double' values) to 'vector<bool, 2>' (vector of 2 'bool' values)}}
+  // expected-warning@#b2_2{{implicit conversion turns floating-point number into integer: 'vector<double, 4>' (vector of 4 'double' values) to 'vector<bool, 2>' (vector of 2 'bool' values)}}
+  vector<bool, 2> b2_2 = d4; // #b2_2
+}
+
+#ifndef NO_ERR
+
+void illegal() {
+  // vector extension is illegal
+  vector<float,3> f3 = 1.0;
+  vector<float,4> f4 = f3; // expected-error{{cannot initialize a variable of type 'vector<[...], 4>' with an lvalue of type 'vector<[...], 3>'}}
+}
+
+#endif

>From 06c7a72d8894ccca3f045d30c62e929630d07900 Mon Sep 17 00:00:00 2001
From: Chris Bieneman <chris.bieneman at me.com>
Date: Wed, 29 Nov 2023 11:52:47 -0600
Subject: [PATCH 02/11] Rename VectorTruncation to HLSLVectorTruncation

../clang/test/SemaHLSL/standard_conversion_sequences.hlsl
../clang/test/CodeGenHLSL/builtins/ScalarSwizzles.hlsl
../clang/test/SemaHLSL/Types/BuiltinVector/ScalarSwizzles.hlsl
---
 clang/include/clang/AST/OperationKinds.def             |  2 +-
 clang/lib/AST/Expr.cpp                                 |  2 +-
 clang/lib/AST/ExprConstant.cpp                         |  4 ++--
 clang/lib/CodeGen/CGExpr.cpp                           |  2 +-
 clang/lib/CodeGen/CGExprAgg.cpp                        |  4 ++--
 clang/lib/CodeGen/CGExprComplex.cpp                    |  2 +-
 clang/lib/CodeGen/CGExprConstant.cpp                   |  2 +-
 clang/lib/CodeGen/CGExprScalar.cpp                     |  2 +-
 clang/lib/Edit/RewriteObjCFoundationAPI.cpp            |  2 +-
 clang/lib/Sema/SemaExprCXX.cpp                         |  2 +-
 clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp          |  2 +-
 clang/test/SemaHLSL/standard_conversion_sequences.hlsl | 10 +++++-----
 12 files changed, 18 insertions(+), 18 deletions(-)

diff --git a/clang/include/clang/AST/OperationKinds.def b/clang/include/clang/AST/OperationKinds.def
index e497fe4d1f93ff..e8d934782d4482 100644
--- a/clang/include/clang/AST/OperationKinds.def
+++ b/clang/include/clang/AST/OperationKinds.def
@@ -362,7 +362,7 @@ CAST_OPERATION(AddressSpaceConversion)
 CAST_OPERATION(IntToOCLSampler)
 
 // Truncate a vector type (HLSL only).
-CAST_OPERATION(VectorTruncation)
+CAST_OPERATION(HLSLVectorTruncation)
 
 //===- Binary Operations  -------------------------------------------------===//
 // Operators listed in order of precedence.
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index bfbdb6cf0f929d..135f04fb93c7df 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -1898,7 +1898,7 @@ bool CastExpr::CastConsistency() const {
   case CK_FixedPointToIntegral:
   case CK_IntegralToFixedPoint:
   case CK_MatrixCast:
-  case CK_VectorTruncation:
+  case CK_HLSLVectorTruncation:
     assert(!getType()->isBooleanType() && "unheralded conversion to bool");
     goto CheckNoBasePath;
 
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 24c12cd0fd939d..f737a64656a5b2 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -13875,7 +13875,7 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) {
   case CK_FixedPointCast:
   case CK_IntegralToFixedPoint:
   case CK_MatrixCast:
-  case CK_VectorTruncation:
+  case CK_HLSLVectorTruncation:
     llvm_unreachable("invalid cast kind for integral value");
 
   case CK_BitCast:
@@ -14714,7 +14714,7 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) {
   case CK_FixedPointToIntegral:
   case CK_IntegralToFixedPoint:
   case CK_MatrixCast:
-  case CK_VectorTruncation:
+  case CK_HLSLVectorTruncation:
     llvm_unreachable("invalid cast kind for complex value");
 
   case CK_LValueToRValue:
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 9101f81c718b71..e748af5f89d32b 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -4933,7 +4933,7 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) {
   case CK_FixedPointToIntegral:
   case CK_IntegralToFixedPoint:
   case CK_MatrixCast:
-  case CK_VectorTruncation:
+  case CK_HLSLVectorTruncation:
     return EmitUnsupportedLValue(E, "unexpected cast lvalue");
 
   case CK_Dependent:
diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp
index f9278133e5ddc6..8949ad8a4dce46 100644
--- a/clang/lib/CodeGen/CGExprAgg.cpp
+++ b/clang/lib/CodeGen/CGExprAgg.cpp
@@ -930,7 +930,7 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) {
   case CK_BuiltinFnToFnPtr:
   case CK_ZeroToOCLOpaqueType:
   case CK_MatrixCast:
-  case CK_VectorTruncation:
+  case CK_HLSLVectorTruncation:
 
   case CK_IntToOCLSampler:
   case CK_FloatingToFixedPoint:
@@ -1455,7 +1455,7 @@ static bool castPreservesZero(const CastExpr *CE) {
   case CK_MatrixCast:
   case CK_NonAtomicToAtomic:
   case CK_AtomicToNonAtomic:
-  case CK_VectorTruncation:
+  case CK_HLSLVectorTruncation:
     return true;
 
   case CK_BaseToDerivedMemberPointer:
diff --git a/clang/lib/CodeGen/CGExprComplex.cpp b/clang/lib/CodeGen/CGExprComplex.cpp
index 45862701d59bb4..6ffcb418666512 100644
--- a/clang/lib/CodeGen/CGExprComplex.cpp
+++ b/clang/lib/CodeGen/CGExprComplex.cpp
@@ -556,7 +556,7 @@ ComplexPairTy ComplexExprEmitter::EmitCast(CastKind CK, Expr *Op,
   case CK_FixedPointToIntegral:
   case CK_IntegralToFixedPoint:
   case CK_MatrixCast:
-  case CK_VectorTruncation:
+  case CK_HLSLVectorTruncation:
     llvm_unreachable("invalid cast kind for complex value");
 
   case CK_FloatingRealToComplex:
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index 577eee2ee1ff0f..f5b144a0736467 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -1225,7 +1225,7 @@ class ConstExprEmitter :
     case CK_IntegralToFixedPoint:
     case CK_ZeroToOCLOpaqueType:
     case CK_MatrixCast:
-    case CK_VectorTruncation:
+    case CK_HLSLVectorTruncation:
       return nullptr;
     }
     llvm_unreachable("Invalid CastKind");
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 7b57f33865c03b..121a26a3b5df83 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -2475,7 +2475,7 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
   case CK_IntToOCLSampler:
     return CGF.CGM.createOpenCLIntToSamplerConversion(E, CGF);
 
-  case CK_VectorTruncation: {
+  case CK_HLSLVectorTruncation: {
     assert(DestTy->isVectorType() && "Expected dest type to be vector type");
     Value *Vec = Visit(const_cast<Expr *>(E));
     SmallVector<int, 16> Mask;
diff --git a/clang/lib/Edit/RewriteObjCFoundationAPI.cpp b/clang/lib/Edit/RewriteObjCFoundationAPI.cpp
index d42532cc15d27e..3c5ddcc3bb0faf 100644
--- a/clang/lib/Edit/RewriteObjCFoundationAPI.cpp
+++ b/clang/lib/Edit/RewriteObjCFoundationAPI.cpp
@@ -1087,7 +1087,7 @@ static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
     case CK_BooleanToSignedIntegral:
       llvm_unreachable("OpenCL-specific cast in Objective-C?");
 
-    case CK_VectorTruncation:
+    case CK_HLSLVectorTruncation:
       llvm_unreachable("HLSL-specific cast in Objective-C?");
       break;
 
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 5052af7905ca14..f628f2ff9b02c9 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -4772,7 +4772,7 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
     QualType ElType = FromVec->getElementType();
     QualType TruncTy =
         Context.getExtVectorType(ElType, ToVec->getNumElements());
-    From = ImpCastExprToType(From, TruncTy, CK_VectorTruncation,
+    From = ImpCastExprToType(From, TruncTy, CK_HLSLVectorTruncation,
                              From->getValueKind())
                .get();
     break;
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index c90423784a3425..c3fc56ac30ee9f 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -521,7 +521,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
       case CK_ToUnion:
       case CK_MatrixCast:
       case CK_VectorSplat:
-      case CK_VectorTruncation: {
+      case CK_HLSLVectorTruncation: {
         QualType resultType = CastE->getType();
         if (CastE->isGLValue())
           resultType = getContext().getPointerType(resultType);
diff --git a/clang/test/SemaHLSL/standard_conversion_sequences.hlsl b/clang/test/SemaHLSL/standard_conversion_sequences.hlsl
index ca9f2a372c0261..a0d398105f15d6 100644
--- a/clang/test/SemaHLSL/standard_conversion_sequences.hlsl
+++ b/clang/test/SemaHLSL/standard_conversion_sequences.hlsl
@@ -17,7 +17,7 @@ void test() {
   vector<double,4> d4 = f3.xyzx; // No warnings for promotion or explicit extension.
 
   // CHECK: VarDecl {{.*}} used f2 'vector<float, 2>':'float __attribute__((ext_vector_type(2)))' cinit
-  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'float __attribute__((ext_vector_type(2)))' <VectorTruncation>
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'float __attribute__((ext_vector_type(2)))' <HLSLVectorTruncation>
   // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<float, 3>':'float __attribute__((ext_vector_type(3)))' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr {{.*}} 'vector<float, 3>':'float __attribute__((ext_vector_type(3)))' lvalue Var {{.*}} 'f3' 'vector<float, 3>':'float __attribute__((ext_vector_type(3)))'
   // expected-warning@#f2{{implicit conversion truncates vector: 'vector<float, 3>' (vector of 3 'float' values) to 'float __attribute__((ext_vector_type(2)))' (vector of 2 'float' values)}}
@@ -25,7 +25,7 @@ void test() {
 
   // CHECK: VarDecl {{.*}} f2_2 'vector<float, 2>':'float __attribute__((ext_vector_type(2)))' cinit
   // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<float, 2>':'float __attribute__((ext_vector_type(2)))' <FloatingCast>
-  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'double __attribute__((ext_vector_type(2)))' <VectorTruncation>
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'double __attribute__((ext_vector_type(2)))' <HLSLVectorTruncation>
   // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<double, 4>':'double __attribute__((ext_vector_type(4)))' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr {{.*}} 'vector<double, 4>':'double __attribute__((ext_vector_type(4)))' lvalue Var {{.*}} 'd4' 'vector<double, 4>':'double __attribute__((ext_vector_type(4)))'
   // expected-warning@#f2_2{{implicit conversion truncates vector: 'vector<double, 4>' (vector of 4 'double' values) to 'vector<float, 2>' (vector of 2 'float' values)}}
@@ -41,7 +41,7 @@ void test() {
   
   // CHECK: VarDecl {{.*}} i2_2 'vector<int, 2>':'int __attribute__((ext_vector_type(2)))' cinit
   // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<int, 2>':'int __attribute__((ext_vector_type(2)))' <FloatingToIntegral>
-  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'double __attribute__((ext_vector_type(2)))' <VectorTruncation>
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'double __attribute__((ext_vector_type(2)))' <HLSLVectorTruncation>
   // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<double, 4>':'double __attribute__((ext_vector_type(4)))' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr {{.*}} 'vector<double, 4>':'double __attribute__((ext_vector_type(4)))' lvalue Var {{.*}} 'd4' 'vector<double, 4>':'double __attribute__((ext_vector_type(4)))'
   // expected-warning@#i2_2{{implicit conversion truncates vector: 'vector<double, 4>' (vector of 4 'double' values) to 'vector<int, 2>' (vector of 2 'int' values)}}
@@ -58,7 +58,7 @@ void test() {
 
   // CHECK: VarDecl {{.*}} used i2_3 'vector<int, 2>':'int __attribute__((ext_vector_type(2)))' cinit
   // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<int, 2>':'int __attribute__((ext_vector_type(2)))' <IntegralCast>
-  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'long __attribute__((ext_vector_type(2)))' <VectorTruncation>
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'long __attribute__((ext_vector_type(2)))' <HLSLVectorTruncation>
   // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<long, 4>':'long __attribute__((ext_vector_type(4)))' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr {{.*}} 'vector<long, 4>':'long __attribute__((ext_vector_type(4)))' lvalue Var {{.*}} 'i64_4' 'vector<long, 4>':'long __attribute__((ext_vector_type(4)))'
   // expected-warning@#i2_3{{implicit conversion loses integer precision: 'vector<long, 4>' (vector of 4 'long' values) to 'vector<int, 2>' (vector of 2 'int' values)}}
@@ -73,7 +73,7 @@ void test() {
 
   // CHECK: VarDecl {{.*}} b2_2 'vector<bool, 2>':'bool __attribute__((ext_vector_type(2)))' cinit
   // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<bool, 2>':'bool __attribute__((ext_vector_type(2)))' <FloatingToBoolean>
-  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'double __attribute__((ext_vector_type(2)))' <VectorTruncation>
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'double __attribute__((ext_vector_type(2)))' <HLSLVectorTruncation>
   // CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<double, 4>':'double __attribute__((ext_vector_type(4)))' <LValueToRValue>
   // CHECK-NEXT: DeclRefExpr {{.*}} 'vector<double, 4>':'double __attribute__((ext_vector_type(4)))' lvalue Var {{.*}} 'd4' 'vector<double, 4>':'double __attribute__((ext_vector_type(4)))'
   // expected-warning@#b2_2{{implicit conversion truncates vector: 'vector<double, 4>' (vector of 4 'double' values) to 'vector<bool, 2>' (vector of 2 'bool' values)}}

>From 6cbf74c358895edb8dba02182fe5fffd38bf6bd8 Mon Sep 17 00:00:00 2001
From: Chris Bieneman <chris.bieneman at me.com>
Date: Wed, 29 Nov 2023 13:09:47 -0600
Subject: [PATCH 03/11] Remove clang-format comments
 ../clang/test/CodeGenHLSL/builtins/ScalarSwizzles.hlsl
 ../clang/test/SemaHLSL/Types/BuiltinVector/ScalarSwizzles.hlsl

---
 clang/lib/Sema/SemaOverload.cpp | 2 --
 1 file changed, 2 deletions(-)

diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index e3509546acc0c7..39418bd9c7a3b6 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -120,7 +120,6 @@ CompareDerivedToBaseConversions(Sema &S, SourceLocation Loc,
                                 const StandardConversionSequence& SCS1,
                                 const StandardConversionSequence& SCS2);
 
-// clang-format off - disable clang format for this block.
 /// GetConversionRank - Retrieve the implicit conversion rank
 /// corresponding to the given implicit conversion kind.
 ImplicitConversionRank clang::GetConversionRank(ImplicitConversionKind Kind) {
@@ -165,7 +164,6 @@ ImplicitConversionRank clang::GetConversionRank(ImplicitConversionKind Kind) {
   static_assert(std::size(Rank) == (int)ICK_Num_Conversion_Kinds);
   return Rank[(int)Kind];
 }
-// clang-format on
 
 /// GetImplicitConversionName - Return the name of this kind of
 /// implicit conversion.

>From 899d5298c1dd1847ac4c022f9da6d5be1cab91f8 Mon Sep 17 00:00:00 2001
From: Chris Bieneman <chris.bieneman at me.com>
Date: Wed, 29 Nov 2023 13:10:07 -0600
Subject: [PATCH 04/11] Update scalar swizzle tests

The new "correct" implicit conversion sequences change how some of these
ASTs are formed and the initial code generation.
../clang/test/CodeGenHLSL/builtins/ScalarSwizzles.hlsl
../clang/test/SemaHLSL/Types/BuiltinVector/ScalarSwizzles.hlsl
---
 .../CodeGenHLSL/builtins/ScalarSwizzles.hlsl  | 34 +++++--------------
 .../Types/BuiltinVector/ScalarSwizzles.hlsl   | 25 ++------------
 2 files changed, 11 insertions(+), 48 deletions(-)

diff --git a/clang/test/CodeGenHLSL/builtins/ScalarSwizzles.hlsl b/clang/test/CodeGenHLSL/builtins/ScalarSwizzles.hlsl
index 2e169ef60c70c4..73bab556b9a6db 100644
--- a/clang/test/CodeGenHLSL/builtins/ScalarSwizzles.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/ScalarSwizzles.hlsl
@@ -85,7 +85,9 @@ float4 FillTwoPointFiveFloat(){
 // CHECK: [[vec1Ptr:%.*]] = alloca <1 x float>, align 4
 // CHECK: store <1 x float> <float 5.000000e-01>, ptr [[vec1Ptr]], align 4
 // CHECK: [[vec1:%.*]] = load <1 x float>, ptr [[vec1Ptr]], align 4
-// CHECK: [[vec1Ret:%.*]] = shufflevector <1 x float> [[vec1]], <1 x float> undef, <1 x i32> zeroinitializer
+// CHECK: [[el0:%.*]] = extractelement <1 x float> [[vec1]], i32 0
+// CHECK: [[vec1Splat:%.*]] = insertelement <1 x float> poison, float [[el0]], i64 0
+// CHECK: [[vec1Ret:%.*]] = shufflevector <1 x float> [[vec1Splat]], <1 x float> poison, <1 x i32> zeroinitializer
 // CHECK: ret <1 x float> [[vec1Ret]]
 vector<float, 1> FillOneHalfFloat(){
   return .5f.r;
@@ -113,30 +115,12 @@ float2 HowManyFloats(float V) {
 // up nicely too.
 
 // CHECK-LABEL: AllRighty
-// CHECK: [[XTmp:%.*]] = alloca <1 x double>, align 8
-// CHECK: [[YTmp:%.*]] = alloca <1 x double>, align 8
-// CHECK: [[ZTmp:%.*]] = alloca <1 x double>, align 8
-
-// CHECK: store <1 x double> <double 1.000000e+00>, ptr [[XTmp]], align 8
-// CHECK: [[XVec:%.*]] = load <1 x double>, ptr [[XTmp]], align 8
-// CHECK: [[XVec3:%.*]] = shufflevector <1 x double> [[XVec]], <1 x double> poison, <3 x i32> zeroinitializer
-// CHECK: [[XVal:%.*]] = extractelement <3 x double> [[XVec3]], i32 0
-// CHECK: [[XValF:%.*]] = fptrunc double [[XVal]] to float
-// CHECK: [[Vec3F1:%.*]] = insertelement <3 x float> undef, float [[XValF]], i32 0
-
-// CHECK: store <1 x double> <double 1.000000e+00>, ptr [[YTmp]], align 8
-// CHECK: [[YVec:%.*]] = load <1 x double>, ptr [[YTmp]], align 8
-// CHECK: [[YVec3:%.*]] = shufflevector <1 x double> [[YVec]], <1 x double> poison, <3 x i32> zeroinitializer
-// CHECK: [[YVal:%.*]] = extractelement <3 x double> [[YVec3]], i32 1
-// CHECK: [[YValF:%.*]] = fptrunc double [[YVal]] to float
-// CHECK: [[Vec3F2:%.*]] = insertelement <3 x float> [[Vec3F1]], float [[YValF]], i32 1
-
-// CHECK: store <1 x double> <double 1.000000e+00>, ptr [[ZTmp]], align 8
-// CHECK: [[ZVec:%.*]] = load <1 x double>, ptr [[ZTmp]], align 8
-// CHECK: [[ZVec3:%.*]] = shufflevector <1 x double> [[ZVec]], <1 x double> poison, <3 x i32> zeroinitializer
-// CHECK: [[ZVal:%.*]] = extractelement <3 x double> [[ZVec3]], i32 2
-// CHECK: [[ZValF:%.*]] = fptrunc double [[ZVal]] to float
-// CHECK: [[Vec3F3:%.*]] = insertelement <3 x float> [[Vec3F2]], float [[ZValF]], i32 2
+// CHECK: [[Tmp:%.*]] = alloca <1 x double>, align 8
+// CHECK: store <1 x double> <double 1.000000e+00>, ptr [[Tmp]], align 8
+// CHECK: [[vec1:%.*]] = load <1 x double>, ptr [[Tmp]], align 8
+// CHECK: [[vec3:%.*]] = shufflevector <1 x double> [[vec1]], <1 x double> poison, <3 x i32> zeroinitializer
+// CHECK: [[vec3f:%.*]] = fptrunc <3 x double> [[vec3]] to <3 x float>
+// ret <3 x float> [[vec3f]]
 
 // ret <3 x float> [[Vec3F3]]
 float3 AllRighty() {
diff --git a/clang/test/SemaHLSL/Types/BuiltinVector/ScalarSwizzles.hlsl b/clang/test/SemaHLSL/Types/BuiltinVector/ScalarSwizzles.hlsl
index 4ffbcacf3f30c0..a2e9a5f865ece1 100644
--- a/clang/test/SemaHLSL/Types/BuiltinVector/ScalarSwizzles.hlsl
+++ b/clang/test/SemaHLSL/Types/BuiltinVector/ScalarSwizzles.hlsl
@@ -80,7 +80,7 @@ float4 FillTwoPointFiveFloat(){
 // initialze the returned vector.
 
 // CHECK-LABEL: FillOneHalfFloat
-// CHECK: InitListExpr {{.*}} 'vector<float, 1>':'float __attribute__((ext_vector_type(1)))'
+// CHECK: ImplicitCastExpr {{.*}} 'vector<float, 1>':'float __attribute__((ext_vector_type(1)))' <VectorSplat>
 // CHECK-NEXT: ExtVectorElementExpr {{.*}} 'float' r
 // CHECK-NEXT: ImplicitCastExpr {{.*}} 'float __attribute__((ext_vector_type(1)))' <VectorSplat>
 // CHECK-NEXT: FloatingLiteral {{.*}} 'float' 5.000000e-01
@@ -113,31 +113,10 @@ int64_t4 HooBoy() {
 // list with float truncation casts.
 
 // CHECK-LABEL: AllRighty
-// CHECK: InitListExpr {{.*}} 'float3':'float __attribute__((ext_vector_type(3)))'
-
-// Vector element 0:
-// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' <FloatingCast>
-// CHECK-NEXT: ArraySubscriptExpr {{.*}} 'double'
-// CHECK-NEXT: ExtVectorElementExpr {{.*}} 'double __attribute__((ext_vector_type(3)))' rrr
-// CHECK-NEXT: ImplicitCastExpr {{.*}} 'double __attribute__((ext_vector_type(1)))' <VectorSplat>
-// CHECK-NEXT: FloatingLiteral {{.*}} 'double' 1.000000e+00
-// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 0
-
-// Vector element 1:
-// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' <FloatingCast>
-// CHECK-NEXT: ArraySubscriptExpr {{.*}} 'double'
-// CHECK-NEXT: ExtVectorElementExpr {{.*}} 'double __attribute__((ext_vector_type(3)))' rrr
-// CHECK-NEXT: ImplicitCastExpr {{.*}} 'double __attribute__((ext_vector_type(1)))' <VectorSplat>
-// CHECK-NEXT: FloatingLiteral {{.*}} 'double' 1.000000e+00
-// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 1
-
-// Vector element 2:
-// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' <FloatingCast>
-// CHECK-NEXT: ArraySubscriptExpr {{.*}} 'double'
+// CHECK: ImplicitCastExpr {{.*}} 'float3':'float __attribute__((ext_vector_type(3)))' <FloatingCast>
 // CHECK-NEXT: ExtVectorElementExpr {{.*}} 'double __attribute__((ext_vector_type(3)))' rrr
 // CHECK-NEXT: ImplicitCastExpr {{.*}} 'double __attribute__((ext_vector_type(1)))' <VectorSplat>
 // CHECK-NEXT: FloatingLiteral {{.*}} 'double' 1.000000e+00
-// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 2
 
 float3 AllRighty() {
   return 1..rrr;

>From 86c0dc0ce139dd4370523b54393133ce8f72def0 Mon Sep 17 00:00:00 2001
From: Chris B <cbieneman at microsoft.com>
Date: Wed, 29 Nov 2023 13:34:54 -0600
Subject: [PATCH 05/11] Update clang/lib/Sema/SemaExprCXX.cpp

Co-authored-by: Aaron Ballman <aaron at aaronballman.com>
---
 clang/lib/Sema/SemaExprCXX.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index f628f2ff9b02c9..0e1acf6bd5dddd 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -4790,7 +4790,7 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
   }
 
   if (SCS.Element != ICK_Identity) {
-    // if SCS.Element is not ICK_Identity the To and From types must be HLSL
+    // If SCS.Element is not ICK_Identity the To and From types must be HLSL
     // vectors or matrices. HLSL matrices aren't yet supported so this code only
     // handles vectors for now.
 

>From ac9711e3212b666ac4e6a2fd28c43bc7e66bf0fd Mon Sep 17 00:00:00 2001
From: Chris Bieneman <chris.bieneman at me.com>
Date: Wed, 29 Nov 2023 13:37:02 -0600
Subject: [PATCH 06/11] Remove spurious whitespace change

---
 clang/lib/CodeGen/CGExpr.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index e748af5f89d32b..107c1c86f3c8fb 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -5061,7 +5061,6 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) {
     return MakeAddrLValue(V, E->getType(), LV.getBaseInfo(),
                           CGM.getTBAAInfoForSubobject(LV, E->getType()));
   }
-
   case CK_ZeroToOCLOpaqueType:
     llvm_unreachable("NULL to OpenCL opaque type lvalue cast is not valid");
 

>From b9585121c4424de4540ae332b5ed9a57068708d2 Mon Sep 17 00:00:00 2001
From: Chris Bieneman <chris.bieneman at me.com>
Date: Wed, 13 Dec 2023 19:50:11 -0600
Subject: [PATCH 07/11] Avoid falling into EmitScalarConversion

'beanz/cbieneman/hlsl-standard-conversions-trunc' by 1664 commits.
---
 clang/lib/CodeGen/CGExprScalar.cpp | 59 ++++++++++++++++++++++--------
 1 file changed, 44 insertions(+), 15 deletions(-)

diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 53ab09f8e63f17..769b17ca1bbb3b 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -610,8 +610,6 @@ class ScalarExprEmitter
   llvm::Value *EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV,
                                        bool isInc, bool isPre);
 
-  llvm::Value *EmitVectorElementConversion(QualType SrcType, QualType DstType,
-                                           llvm::Value *Src);
 
   Value *VisitUnaryAddrOf(const UnaryOperator *E) {
     if (isa<MemberPointerType>(E->getType())) // never sugared
@@ -1424,9 +1422,6 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType,
     return Builder.CreateVectorSplat(NumElements, Src, "splat");
   }
 
-  if (SrcType->isExtVectorType() && DstType->isExtVectorType())
-    return EmitVectorElementConversion(SrcType, DstType, Src);
-
   if (SrcType->isMatrixType() && DstType->isMatrixType())
     return EmitScalarCast(Src, SrcType, DstType, SrcTy, DstTy, Opts);
 
@@ -1706,14 +1701,10 @@ Value *ScalarExprEmitter::VisitShuffleVectorExpr(ShuffleVectorExpr *E) {
 }
 
 Value *ScalarExprEmitter::VisitConvertVectorExpr(ConvertVectorExpr *E) {
-  QualType SrcType = E->getSrcExpr()->getType(), DstType = E->getType();
-  Value *Src = CGF.EmitScalarExpr(E->getSrcExpr());
-  return EmitVectorElementConversion(SrcType, DstType, Src);
-}
+  QualType SrcType = E->getSrcExpr()->getType(),
+           DstType = E->getType();
 
-llvm::Value *ScalarExprEmitter::EmitVectorElementConversion(QualType SrcType,
-                                                            QualType DstType,
-                                                            Value *Src) {
+  Value *Src  = CGF.EmitScalarExpr(E->getSrcExpr());
 
   SrcType = CGF.getContext().getCanonicalType(SrcType);
   DstType = CGF.getContext().getCanonicalType(DstType);
@@ -2415,6 +2406,12 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
                                 CE->getExprLoc());
 
   case CK_IntegralCast: {
+    if (E->getType()->isExtVectorType() && DestTy->isExtVectorType()) {
+      QualType SrcElTy = E->getType()->castAs<VectorType>()->getElementType();
+      return Builder.CreateIntCast(Visit(E), ConvertType(DestTy),
+                                   SrcElTy->isSignedIntegerOrEnumerationType(),
+                                   "conv");
+    }
     ScalarConversionOpts Opts;
     if (auto *ICE = dyn_cast<ImplicitCastExpr>(CE)) {
       if (!ICE->isPartOfExplicitCast())
@@ -2423,9 +2420,41 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
     return EmitScalarConversion(Visit(E), E->getType(), DestTy,
                                 CE->getExprLoc(), Opts);
   }
-  case CK_IntegralToFloating:
-  case CK_FloatingToIntegral:
-  case CK_FloatingCast:
+  case CK_IntegralToFloating: {
+    if (E->getType()->isExtVectorType() && DestTy->isExtVectorType()) {
+      QualType SrcElTy = E->getType()->castAs<VectorType>()->getElementType();
+      if (SrcElTy->isSignedIntegerOrEnumerationType())
+        return Builder.CreateSIToFP(Visit(E), ConvertType(DestTy), "conv");
+      return Builder.CreateUIToFP(Visit(E), ConvertType(DestTy), "conv");
+    }
+    CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, CE);
+    return EmitScalarConversion(Visit(E), E->getType(), DestTy,
+                                CE->getExprLoc());
+  }
+  case CK_FloatingToIntegral: {
+    if (E->getType()->isExtVectorType() && DestTy->isExtVectorType()) {
+      QualType DstElTy = DestTy->castAs<VectorType>()->getElementType();
+      if (DstElTy->isSignedIntegerOrEnumerationType())
+        return Builder.CreateFPToSI(Visit(E), ConvertType(DestTy), "conv");
+      return Builder.CreateFPToUI(Visit(E), ConvertType(DestTy), "conv");
+    }
+    CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, CE);
+    return EmitScalarConversion(Visit(E), E->getType(), DestTy,
+                                CE->getExprLoc());
+  }
+  case CK_FloatingCast: {
+    if (E->getType()->isExtVectorType() && DestTy->isExtVectorType()) {
+      QualType SrcElTy = E->getType()->castAs<VectorType>()->getElementType();
+      QualType DstElTy = DestTy->castAs<VectorType>()->getElementType();
+      if (DstElTy->castAs<BuiltinType>()->getKind() <
+          SrcElTy->castAs<BuiltinType>()->getKind())
+        return Builder.CreateFPTrunc(Visit(E), ConvertType(DestTy), "conv");
+      return Builder.CreateFPExt(Visit(E), ConvertType(DestTy), "conv");
+    }
+    CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, CE);
+    return EmitScalarConversion(Visit(E), E->getType(), DestTy,
+                                CE->getExprLoc());
+  }
   case CK_FixedPointToFloating:
   case CK_FloatingToFixedPoint: {
     CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, CE);

>From 2b90d415a5d7d605192e4aa1cdb352433058cb62 Mon Sep 17 00:00:00 2001
From: Chris Bieneman <chris.bieneman at me.com>
Date: Wed, 10 Jan 2024 13:57:34 -0600
Subject: [PATCH 08/11] Updates based on feedback from @rjmccall

---
 clang/lib/CodeGen/CGExprScalar.cpp |  3 ++-
 clang/lib/Sema/SemaChecking.cpp    |  4 ++--
 clang/lib/Sema/SemaExprCXX.cpp     | 12 +++++++-----
 3 files changed, 11 insertions(+), 8 deletions(-)

diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 2c7a246932fdf5..d96948260d2175 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -2508,7 +2508,8 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
     assert(DestTy->isVectorType() && "Expected dest type to be vector type");
     Value *Vec = Visit(const_cast<Expr *>(E));
     SmallVector<int, 16> Mask;
-    Mask.insert(Mask.begin(), DestTy->getAs<VectorType>()->getNumElements(), 0);
+    Mask.insert(Mask.begin(), DestTy->castAs<VectorType>()->getNumElements(),
+                0);
     return Builder.CreateShuffleVector(Vec, Mask, "trunc");
   }
 
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index e076be38760ec2..e051ec1a6b4759 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -15609,8 +15609,8 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
         return;
       return DiagnoseImpCast(S, E, T, CC, diag::warn_impcast_vector_scalar);
     } else if (S.getLangOpts().HLSL &&
-               Target->getAs<VectorType>()->getNumElements() <
-                   Source->getAs<VectorType>()->getNumElements()) {
+               Target->castAs<VectorType>()->getNumElements() <
+                   Source->castAs<VectorType>()->getNumElements()) {
       // Diagnose vector truncation but don't return. We may also want to
       // diagnose an element conversion.
       DiagnoseImpCast(S, E, T, CC, diag::warn_hlsl_impcast_vector_truncation);
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index c1bd6d75986d61..d945f18906810f 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -4764,11 +4764,13 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
                              From->getValueKind()).get();
     break;
   case ICK_HLSL_Vector_Truncation: {
-    // Note: HLSL 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.
-    auto *FromVec = From->getType()->castAs<ExtVectorType>();
-    auto *ToVec = ToType->castAs<ExtVectorType>();
+    // 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<VectorType>();
+    auto *ToVec = ToType->castAs<VectorType>();
     QualType ElType = FromVec->getElementType();
     QualType TruncTy =
         Context.getExtVectorType(ElType, ToVec->getNumElements());

>From c5430b391d400624c6de2486069f5909733896f5 Mon Sep 17 00:00:00 2001
From: Chris Bieneman <chris.bieneman at me.com>
Date: Wed, 7 Feb 2024 16:47:33 -0600
Subject: [PATCH 09/11] Updates based on review feedback

This addresses feedback from @rjmcall. The main updates are:
* Added asserts and todos about handling constrained intrinsics.
* Fixed sufflevector code generation (and updated tests)
* Fixed shadowed promotion casts. Writing a test for this revealed
another issue.
* Added Element conversion to conversion rank checking, and added test
to verify promotion is preferred over conversion.
* Added HLSL integral promotion check.

There are still at least two outstanding bugs that are exposed by this
change which I've added an XFAIL'd test for (#81047 & #81049).

HLSL's promotion rules are not well written down, but specifically in
overload resolution integer and floating point values can implicitly
cast to larger types and the smallest of the larger types is the best
match.
---
 clang/lib/CodeGen/CGExprScalar.cpp            | 21 +++++++++---
 clang/lib/Sema/SemaOverload.cpp               | 26 ++++++++++-----
 .../standard_conversion_sequences.hlsl        | 10 +++---
 clang/test/CodeGenHLSL/builtins/sqrt.hlsl     |  5 ---
 .../test/SemaHLSL/OverloadResolutionBugs.hlsl | 33 +++++++++++++++++++
 .../SemaHLSL/VectorOverloadResolution.hlsl    | 30 +++++++++++++++++
 6 files changed, 101 insertions(+), 24 deletions(-)
 create mode 100644 clang/test/SemaHLSL/OverloadResolutionBugs.hlsl
 create mode 100644 clang/test/SemaHLSL/VectorOverloadResolution.hlsl

diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index d96948260d2175..98ea75c8185b42 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -2421,7 +2421,10 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
                                 CE->getExprLoc(), Opts);
   }
   case CK_IntegralToFloating: {
-    if (E->getType()->isExtVectorType() && DestTy->isExtVectorType()) {
+    if (E->getType()->isVectorType() && DestTy->isVectorType()) {
+      // TODO: Support constrained FP intrinsics.
+      assert(!Builder.getIsFPConstrained() &&
+             "FP Constrained vector casts not supported yet.");
       QualType SrcElTy = E->getType()->castAs<VectorType>()->getElementType();
       if (SrcElTy->isSignedIntegerOrEnumerationType())
         return Builder.CreateSIToFP(Visit(E), ConvertType(DestTy), "conv");
@@ -2432,7 +2435,10 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
                                 CE->getExprLoc());
   }
   case CK_FloatingToIntegral: {
-    if (E->getType()->isExtVectorType() && DestTy->isExtVectorType()) {
+    if (E->getType()->isVectorType() && DestTy->isVectorType()) {
+      // TODO: Support constrained FP intrinsics.
+      assert(!Builder.getIsFPConstrained() &&
+             "FP Constrained vector casts not supported yet.");
       QualType DstElTy = DestTy->castAs<VectorType>()->getElementType();
       if (DstElTy->isSignedIntegerOrEnumerationType())
         return Builder.CreateFPToSI(Visit(E), ConvertType(DestTy), "conv");
@@ -2443,7 +2449,10 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
                                 CE->getExprLoc());
   }
   case CK_FloatingCast: {
-    if (E->getType()->isExtVectorType() && DestTy->isExtVectorType()) {
+    if (E->getType()->isVectorType() && DestTy->isVectorType()) {
+      // TODO: Support constrained FP intrinsics.
+      assert(!Builder.getIsFPConstrained() &&
+             "FP Constrained vector casts not supported yet.");
       QualType SrcElTy = E->getType()->castAs<VectorType>()->getElementType();
       QualType DstElTy = DestTy->castAs<VectorType>()->getElementType();
       if (DstElTy->castAs<BuiltinType>()->getKind() <
@@ -2508,8 +2517,10 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
     assert(DestTy->isVectorType() && "Expected dest type to be vector type");
     Value *Vec = Visit(const_cast<Expr *>(E));
     SmallVector<int, 16> Mask;
-    Mask.insert(Mask.begin(), DestTy->castAs<VectorType>()->getNumElements(),
-                0);
+    unsigned NumElts = DestTy->castAs<VectorType>()->getNumElements();
+    for (unsigned I = 0; I != NumElts; ++I)
+      Mask.push_back(I);
+    
     return Builder.CreateShuffleVector(Vec, Mask, "trunc");
   }
 
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 30ed788f40157c..93b978fdd1b3d4 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -232,6 +232,8 @@ ImplicitConversionRank StandardConversionSequence::getRank() const {
     Rank = GetConversionRank(First);
   if  (GetConversionRank(Second) > Rank)
     Rank = GetConversionRank(Second);
+  if  (GetConversionRank(Element) > Rank)
+    Rank = GetConversionRank(Element);
   if  (GetConversionRank(Third) > Rank)
     Rank = GetConversionRank(Third);
   return Rank;
@@ -1879,13 +1881,13 @@ static bool IsVectorElementConversion(Sema &S, QualType FromType,
   if (S.Context.hasSameUnqualifiedType(FromType, ToType))
     return true;
 
-  if (IsFloatingPointConversion(S, FromType, ToType)) {
-    ICK = ICK_Floating_Conversion;
+  if (S.IsFloatingPointPromotion(FromType, ToType)) {
+    ICK = ICK_Floating_Promotion;
     return true;
   }
 
-  if (S.IsFloatingPointPromotion(FromType, ToType)) {
-    ICK = ICK_Floating_Promotion;
+  if (IsFloatingPointConversion(S, FromType, ToType)) {
+    ICK = ICK_Floating_Conversion;
     return true;
   }
 
@@ -1894,14 +1896,14 @@ static bool IsVectorElementConversion(Sema &S, QualType FromType,
     return true;
   }
 
-  if (FromType->isIntegralOrUnscopedEnumerationType() &&
-      ToType->isIntegralType(S.Context)) {
-    ICK = ICK_Integral_Conversion;
+  if (S.IsIntegralPromotion(From, FromType, ToType)) {
+    ICK = ICK_Integral_Promotion;
     return true;
   }
 
-  if (S.IsIntegralPromotion(From, FromType, ToType)) {
-    ICK = ICK_Integral_Promotion;
+  if (FromType->isIntegralOrUnscopedEnumerationType() &&
+      ToType->isIntegralType(S.Context)) {
+    ICK = ICK_Integral_Conversion;
     return true;
   }
 
@@ -2531,6 +2533,12 @@ bool Sema::IsIntegralPromotion(Expr *From, QualType FromType, QualType ToType) {
     return true;
   }
 
+  // In HLSL an rvalue of integral type can be promoted to an rvalue of a larger
+  // integral type.
+  if (Context.getLangOpts().HLSL)
+    return Context.getTypeSize(FromType) < Context.getTypeSize(ToType);
+
+
   return false;
 }
 
diff --git a/clang/test/CodeGenHLSL/BasicFeatures/standard_conversion_sequences.hlsl b/clang/test/CodeGenHLSL/BasicFeatures/standard_conversion_sequences.hlsl
index 85b8eed4dafbe5..06e3cc5af87e1a 100644
--- a/clang/test/CodeGenHLSL/BasicFeatures/standard_conversion_sequences.hlsl
+++ b/clang/test/CodeGenHLSL/BasicFeatures/standard_conversion_sequences.hlsl
@@ -18,7 +18,7 @@ void f3_to_d4() {
 // CHECK: [[f2:%.*]] = alloca <2 x float>
 // CHECK: store <3 x float> <float 2.000000e+00, float 2.000000e+00, float 2.000000e+00>, ptr [[f3]]
 // CHECK: [[vecf3:%.*]] = load <3 x float>, ptr [[f3]]
-// CHECK: [[vecf2:%.*]] = shufflevector <3 x float> [[vecf3]], <3 x float> poison, <2 x i32> zeroinitializer
+// CHECK: [[vecf2:%.*]] = shufflevector <3 x float> [[vecf3]], <3 x float> poison, <2 x i32> <i32 0, i32 1>
 // CHECK: store <2 x float> [[vecf2]], ptr [[f2]]
 void f3_to_f2() {
   vector<float,3> f3 = 2.0;
@@ -30,7 +30,7 @@ void f3_to_f2() {
 // CHECK: [[f2:%.*]] = alloca <2 x float>
 // CHECK: store <4 x double> <double 3.000000e+00, double 3.000000e+00, double 3.000000e+00, double 3.000000e+00>, ptr [[d4]]
 // CHECK: [[vecd4:%.*]] = load <4 x double>, ptr [[d4]]
-// CHECK: [[vecd2:%.*]] = shufflevector <4 x double> [[vecd4]], <4 x double> poison, <2 x i32> zeroinitializer
+// CHECK: [[vecd2:%.*]] = shufflevector <4 x double> [[vecd4]], <4 x double> poison, <2 x i32> <i32 0, i32 1>
 // CHECK: [[vecf2:%.*]] = fptrunc <2 x double> [[vecd2]] to <2 x float>
 // CHECK: store <2 x float> [[vecf2]], ptr [[f2]]
 void d4_to_f2() {
@@ -55,7 +55,7 @@ void f2_to_i2() {
 // CHECK: [[i2:%.*]] = alloca <2 x i32>
 // CHECK: store <4 x double> <double 5.000000e+00, double 5.000000e+00, double 5.000000e+00, double 5.000000e+00>, ptr [[d4]]
 // CHECK: [[vecd4:%.*]] = load <4 x double>, ptr [[d4]]
-// CHECK: [[vecd2:%.*]] = shufflevector <4 x double> [[vecd4]], <4 x double> poison, <2 x i32> zeroinitializer
+// CHECK: [[vecd2:%.*]] = shufflevector <4 x double> [[vecd4]], <4 x double> poison, <2 x i32> <i32 0, i32 1>
 // CHECK: [[veci2]] = fptosi <2 x double> [[vecd2]] to <2 x i32>
 // CHECK: store <2 x i32> [[veci2]], ptr [[i2]]
 void d4_to_i2() {
@@ -81,7 +81,7 @@ void d4_to_l4() {
 // CHECK: [[i2:%.*]] = alloca <2 x i32>
 // CHECK: store <4 x i64> <i64 7, i64 7, i64 7, i64 7>, ptr [[l4]]
 // CHECK: [[vecl4:%.*]] = load <4 x i64>, ptr [[l4]]
-// CHECK: [[vecl2:%.*]] = shufflevector <4 x i64> [[vecl4]], <4 x i64> poison, <2 x i32> zeroinitializer
+// CHECK: [[vecl2:%.*]] = shufflevector <4 x i64> [[vecl4]], <4 x i64> poison, <2 x i32> <i32 0, i32 1>
 // CHECK: [[veci2:%.*]] = trunc <2 x i64> [[vecl2]] to <2 x i32>
 // CHECK: store <2 x i32> [[veci2]], ptr [[i2]]
 void l4_to_i2() {
@@ -108,7 +108,7 @@ void i2_to_b2() {
 // CHECK: [[b2:%.*]] = alloca i8
 // CHECK: store <4 x double> <double 9.000000e+00, double 9.000000e+00, double 9.000000e+00, double 9.000000e+00>, ptr [[d4]]
 // CHECK: [[vecd4:%.*]] = load <4 x double>, ptr [[d4]]
-// CHECK: [[vecd2:%.*]] = shufflevector <4 x double> [[vecd4]], <4 x double> poison, <2 x i32> zeroinitializer
+// CHECK: [[vecd2:%.*]] = shufflevector <4 x double> [[vecd4]], <4 x double> poison, <2 x i32> <i32 0, i32 1>
 // CHECK: [[vecb2:%.*]] = fcmp une <2 x double> [[vecd2]], zeroinitializer
 // CHECK: [[vecb8:%.*]] = shufflevector <2 x i1> [[vecb2]], <2 x i1> poison, <8 x i32> <i32 0, i32 1, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
 // CHECK: [[i8:%.*]] = bitcast <8 x i1> [[vecb8]] to i8
diff --git a/clang/test/CodeGenHLSL/builtins/sqrt.hlsl b/clang/test/CodeGenHLSL/builtins/sqrt.hlsl
index 776269395a662b..2c2a09617cf86a 100644
--- a/clang/test/CodeGenHLSL/builtins/sqrt.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/sqrt.hlsl
@@ -1,9 +1,6 @@
 // RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -x hlsl -triple \
 // RUN:   dxil-pc-shadermodel6.2-library %s -fnative-half-type \
 // RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s
-// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -x hlsl -triple \
-// RUN:   dxil-pc-shadermodel6.2-library %s -emit-llvm -disable-llvm-passes \
-// RUN:   -o - | FileCheck %s --check-prefix=NO_HALF
 
 using hlsl::sqrt;
 
@@ -30,5 +27,3 @@ half sqrt_h(half x)
 
 // CHECK: define noundef half @"?sqrt_h@@YA$f16@$f16@@Z"(
 // CHECK: call half @llvm.sqrt.f16(half %0)
-// NO_HALF: define noundef float @"?sqrt_h@@YA$halff@$halff@@Z"(
-// NO_HALF: call float @llvm.sqrt.f32(float %0)
diff --git a/clang/test/SemaHLSL/OverloadResolutionBugs.hlsl b/clang/test/SemaHLSL/OverloadResolutionBugs.hlsl
new file mode 100644
index 00000000000000..197ca1a3ebcb14
--- /dev/null
+++ b/clang/test/SemaHLSL/OverloadResolutionBugs.hlsl
@@ -0,0 +1,33 @@
+// 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 Fn3(double2 D);
+void Fn3(float2 F);
+
+void Call3(half2 H) {
+  Fn3(H);
+}
+
+void Fn4(int64_t2 L);
+void Fn4(int2 I);
+
+void Call4(int16_t H) {
+  Fn4(H);
+}
+
+// https://github.com/llvm/llvm-project/issues/81049
+
+// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -x hlsl -triple \
+// RUN:   dxil-pc-shadermodel6.2-library %s -emit-llvm -disable-llvm-passes \
+// RUN:   -o - | FileCheck %s --check-prefix=NO_HALF
+
+half sqrt_h(half x)
+{
+  return sqrt(x);
+}
+
+// NO_HALF: define noundef float @"?sqrt_h@@YA$halff@$halff@@Z"(
+// NO_HALF: call float @llvm.sqrt.f32(float %0)
diff --git a/clang/test/SemaHLSL/VectorOverloadResolution.hlsl b/clang/test/SemaHLSL/VectorOverloadResolution.hlsl
new file mode 100644
index 00000000000000..e07391f803f899
--- /dev/null
+++ b/clang/test/SemaHLSL/VectorOverloadResolution.hlsl
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -triple dxil-unknown-shadermodel6.6-library -S -fnative-half-type -finclude-default-header -o - -ast-dump %s | FileCheck %s
+void Fn(double2 D);
+void Fn(half2 H);
+
+// CHECK: FunctionDecl {{.*}} Call 'void (float2)'
+// CHECK: CallExpr {{.*}}'void'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(double2)' <FunctionToPointerDecay>
+// CHECK-NEXT: DeclRefExpr {{.*}}'void (double2)' lvalue Function {{.*}} 'Fn' 'void (double2)'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'double2':'double __attribute__((ext_vector_type(2)))' <FloatingCast>
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float2':'float __attribute__((ext_vector_type(2)))' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'float2':'float __attribute__((ext_vector_type(2)))' lvalue ParmVar {{.*}} 'F' 'float2':'float __attribute__((ext_vector_type(2)))'
+
+void Call(float2 F) {
+  Fn(F);
+}
+
+void Fn2(int64_t2 L);
+void Fn2(int16_t2 S);
+
+// CHECK: FunctionDecl {{.*}} Call2 'void (int2)'
+// CHECK: CallExpr {{.*}} 'void'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(int64_t2)' <FunctionToPointerDecay>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'void (int64_t2)' lvalue Function {{.*}} 'Fn2' 'void (int64_t2)'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int64_t2':'long __attribute__((ext_vector_type(2)))' <IntegralCast>
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int2':'int __attribute__((ext_vector_type(2)))' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'int2':'int __attribute__((ext_vector_type(2)))' lvalue ParmVar {{.*}} 'I' 'int2':'int __attribute__((ext_vector_type(2)))'
+
+void Call2(int2 I) {
+  Fn2(I);
+}

>From 003bd4bc64758239881c73fc96fa12a51d37d783 Mon Sep 17 00:00:00 2001
From: Chris Bieneman <chris.bieneman at me.com>
Date: Tue, 13 Feb 2024 09:47:01 -0600
Subject: [PATCH 10/11] Update comment to clearly specify the behavior
 'beanz/cbieneman/hlsl-standard-conversions-trunc'.

---
 clang/include/clang/AST/OperationKinds.def | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/include/clang/AST/OperationKinds.def b/clang/include/clang/AST/OperationKinds.def
index e8d934782d4482..ef05072800f11a 100644
--- a/clang/include/clang/AST/OperationKinds.def
+++ b/clang/include/clang/AST/OperationKinds.def
@@ -361,7 +361,7 @@ CAST_OPERATION(AddressSpaceConversion)
 // Convert an integer initializer to an OpenCL sampler.
 CAST_OPERATION(IntToOCLSampler)
 
-// Truncate a vector type (HLSL only).
+// Truncate a vector type by dropping elements from the end (HLSL only).
 CAST_OPERATION(HLSLVectorTruncation)
 
 //===- Binary Operations  -------------------------------------------------===//

>From f1c13db5bb15884d5323b86f3b29e5f990e82bee Mon Sep 17 00:00:00 2001
From: Chris Bieneman <chris.bieneman at me.com>
Date: Tue, 13 Feb 2024 10:04:02 -0600
Subject: [PATCH 11/11] Fixing clang-format
 'beanz/cbieneman/hlsl-standard-conversions-trunc'.

---
 clang/lib/CodeGen/CGExprScalar.cpp | 2 +-
 clang/lib/Sema/SemaOverload.cpp    | 8 ++++----
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 33524fdada5c0d..e8a94b37c2d57f 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -2523,7 +2523,7 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
     unsigned NumElts = DestTy->castAs<VectorType>()->getNumElements();
     for (unsigned I = 0; I != NumElts; ++I)
       Mask.push_back(I);
-    
+
     return Builder.CreateShuffleVector(Vec, Mask, "trunc");
   }
 
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 7eacf138f63dbd..c39e64c896a714 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -228,13 +228,13 @@ void StandardConversionSequence::setAsIdentityConversion() {
 /// implicit conversions.
 ImplicitConversionRank StandardConversionSequence::getRank() const {
   ImplicitConversionRank Rank = ICR_Exact_Match;
-  if  (GetConversionRank(First) > Rank)
+  if (GetConversionRank(First) > Rank)
     Rank = GetConversionRank(First);
-  if  (GetConversionRank(Second) > Rank)
+  if (GetConversionRank(Second) > Rank)
     Rank = GetConversionRank(Second);
-  if  (GetConversionRank(Element) > Rank)
+  if (GetConversionRank(Element) > Rank)
     Rank = GetConversionRank(Element);
-  if  (GetConversionRank(Third) > Rank)
+  if (GetConversionRank(Third) > Rank)
     Rank = GetConversionRank(Third);
   return Rank;
 }



More information about the cfe-commits mailing list