[clang] [HLSL] Vector Usual Arithmetic Conversions (PR #108659)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Sep 13 15:57:59 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-hlsl
Author: Chris B (llvm-beanz)
<details>
<summary>Changes</summary>
HLSL has a different set of usual arithmetic conversions for vector types to resolve a common type for binary operator expressions.
This PR implements the current spec proposal from: https://github.com/microsoft/hlsl-specs/pull/311
There is one case that may need additional handling for implicitly truncating `vector<T,1>` to `T` early to allow other transformations.
Fixes #<!-- -->106253
---
Patch is 37.87 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/108659.diff
7 Files Affected:
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+3)
- (modified) clang/include/clang/Driver/Options.td (+1-1)
- (modified) clang/include/clang/Sema/Sema.h (+2-1)
- (modified) clang/include/clang/Sema/SemaHLSL.h (+5)
- (modified) clang/lib/Sema/SemaExpr.cpp (+13-2)
- (modified) clang/lib/Sema/SemaHLSL.cpp (+188)
- (added) clang/test/SemaHLSL/Language/UsualArithmeticConversions.hlsl (+379)
``````````diff
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index bf97d939f02ce9..ff5a742ee9bf20 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12383,6 +12383,9 @@ def err_hlsl_operator_unsupported : Error<
def err_hlsl_param_qualifier_mismatch :
Error<"conflicting parameter qualifier %0 on parameter %1">;
+def err_hlsl_vector_compound_assignment_truncation : Error<
+ "left hand operand of type %0 to compound assignment cannot be truncated "
+ "when used with right hand operand of type %1">;
def warn_hlsl_impcast_vector_truncation : Warning<
"implicit conversion truncates vector: %0 to %1">, InGroup<Conversion>;
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index dc8bfc69e9889b..f5a7e3bbc21742 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2957,7 +2957,7 @@ def flax_vector_conversions_EQ : Joined<["-"], "flax-vector-conversions=">, Grou
"LangOptions::LaxVectorConversionKind::Integer",
"LangOptions::LaxVectorConversionKind::All"]>,
MarshallingInfoEnum<LangOpts<"LaxVectorConversions">,
- open_cl.KeyPath #
+ !strconcat("(", open_cl.KeyPath, " || ", hlsl.KeyPath, ")") #
" ? LangOptions::LaxVectorConversionKind::None" #
" : LangOptions::LaxVectorConversionKind::All">;
def flax_vector_conversions : Flag<["-"], "flax-vector-conversions">, Group<f_Group>,
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 99eef472223a00..1762a9ab77d752 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -7423,7 +7423,8 @@ class Sema final : public SemaBase {
SourceLocation Loc,
BinaryOperatorKind Opc);
QualType CheckVectorLogicalOperands(ExprResult &LHS, ExprResult &RHS,
- SourceLocation Loc);
+ SourceLocation Loc,
+ BinaryOperatorKind Opc);
/// Context in which we're performing a usual arithmetic conversion.
enum ArithConvKind {
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 64b39ca7712eeb..5a18522738df2b 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -62,6 +62,11 @@ class SemaHLSL : public SemaBase {
std::initializer_list<llvm::Triple::EnvironmentType> AllowedStages);
void DiagnoseAvailabilityViolations(TranslationUnitDecl *TU);
+ QualType handleVectorBinOpConversion(ExprResult &LHS, ExprResult &RHS,
+ QualType LHSType, QualType RHSType,
+ bool IsCompAssign);
+ void emitLogicalOperatorFixIt(Expr *LHS, Expr *RHS, BinaryOperatorKind Opc);
+
void handleNumThreadsAttr(Decl *D, const ParsedAttr &AL);
void handleWaveSizeAttr(Decl *D, const ParsedAttr &AL);
void handleSV_DispatchThreadIDAttr(Decl *D, const ParsedAttr &AL);
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 8f3e15cc9a9bb7..a43248b546e841 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -10131,6 +10131,10 @@ QualType Sema::CheckVectorOperands(ExprResult &LHS, ExprResult &RHS,
const VectorType *RHSVecType = RHSType->getAs<VectorType>();
assert(LHSVecType || RHSVecType);
+ if (getLangOpts().HLSL)
+ return HLSL().handleVectorBinOpConversion(LHS, RHS, LHSType, RHSType,
+ IsCompAssign);
+
// AltiVec-style "vector bool op vector bool" combinations are allowed
// for some operators but not others.
if (!AllowBothBool && LHSVecType &&
@@ -12861,7 +12865,8 @@ static void diagnoseXorMisusedAsPow(Sema &S, const ExprResult &XorLHS,
}
QualType Sema::CheckVectorLogicalOperands(ExprResult &LHS, ExprResult &RHS,
- SourceLocation Loc) {
+ SourceLocation Loc,
+ BinaryOperatorKind Opc) {
// Ensure that either both operands are of the same vector type, or
// one operand is of a vector type and the other is of its element type.
QualType vType = CheckVectorOperands(LHS, RHS, Loc, false,
@@ -12881,6 +12886,12 @@ QualType Sema::CheckVectorLogicalOperands(ExprResult &LHS, ExprResult &RHS,
if (!getLangOpts().CPlusPlus &&
!(isa<ExtVectorType>(vType->getAs<VectorType>())))
return InvalidLogicalVectorOperands(Loc, LHS, RHS);
+ if (getLangOpts().HLSL &&
+ getLangOpts().getHLSLVersion() >= LangOptionsBase::HLSL_2021) {
+ (void)InvalidOperands(Loc, LHS, RHS);
+ HLSL().emitLogicalOperatorFixIt(LHS.get(), RHS.get(), Opc);
+ return QualType();
+ }
return GetSignedVectorType(LHS.get()->getType());
}
@@ -13052,7 +13063,7 @@ inline QualType Sema::CheckLogicalOperands(ExprResult &LHS, ExprResult &RHS,
// Check vector operands differently.
if (LHS.get()->getType()->isVectorType() ||
RHS.get()->getType()->isVectorType())
- return CheckVectorLogicalOperands(LHS, RHS, Loc);
+ return CheckVectorLogicalOperands(LHS, RHS, Loc, Opc);
bool EnumConstantInBoolContext = false;
for (const ExprResult &HS : {LHS, RHS}) {
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 527718c8e7e324..890cfb09ab5142 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -356,6 +356,194 @@ void SemaHLSL::DiagnoseAttrStageMismatch(
<< (AllowedStages.size() != 1) << join(StageStrings, ", ");
}
+template <CastKind Kind>
+static void castVector(Sema &S, ExprResult &E, QualType &Ty, unsigned Sz) {
+ if (const auto *VTy = Ty->getAs<VectorType>())
+ Ty = VTy->getElementType();
+ Ty = S.getASTContext().getExtVectorType(Ty, Sz);
+ E = S.ImpCastExprToType(E.get(), Ty, Kind);
+}
+
+template <CastKind Kind>
+static QualType castElement(Sema &S, ExprResult &E, QualType Ty) {
+ E = S.ImpCastExprToType(E.get(), Ty, Kind);
+ return Ty;
+}
+
+static QualType handleFloatVectorBinOpConversion(
+ Sema &SemaRef, ExprResult &LHS, ExprResult &RHS, QualType LHSType,
+ QualType RHSType, QualType LElTy, QualType RElTy, bool IsCompAssign) {
+ bool LHSFloat = LElTy->isRealFloatingType();
+ bool RHSFloat = RElTy->isRealFloatingType();
+
+ if (LHSFloat && RHSFloat) {
+ if (IsCompAssign ||
+ SemaRef.getASTContext().getFloatingTypeOrder(LElTy, RElTy) > 0)
+ return castElement<CK_FloatingCast>(SemaRef, RHS, LHSType);
+
+ return castElement<CK_FloatingCast>(SemaRef, LHS, RHSType);
+ }
+
+ if (LHSFloat)
+ return castElement<CK_IntegralToFloating>(SemaRef, RHS, LHSType);
+
+ assert(RHSFloat);
+ if (IsCompAssign)
+ return castElement<clang::CK_FloatingToIntegral>(SemaRef, RHS, LHSType);
+
+ return castElement<CK_IntegralToFloating>(SemaRef, LHS, RHSType);
+}
+
+static QualType handleIntegerVectorBinOpConversion(
+ Sema &SemaRef, ExprResult &LHS, ExprResult &RHS, QualType LHSType,
+ QualType RHSType, QualType LElTy, QualType RElTy, bool IsCompAssign) {
+
+ int IntOrder = SemaRef.Context.getIntegerTypeOrder(LElTy, RElTy);
+ bool LHSSigned = LElTy->hasSignedIntegerRepresentation();
+ bool RHSSigned = RElTy->hasSignedIntegerRepresentation();
+ auto &Ctx = SemaRef.getASTContext();
+
+ // If both types have the same signedness, use the higher ranked type.
+ if (LHSSigned == RHSSigned) {
+ if (IsCompAssign || IntOrder >= 0)
+ return castElement<CK_IntegralCast>(SemaRef, RHS, LHSType);
+
+ return castElement<CK_IntegralCast>(SemaRef, LHS, RHSType);
+ }
+
+ // If the unsigned type has greater than or equal rank of the signed type, use
+ // the unsigned type.
+ if (IntOrder != (LHSSigned ? 1 : -1)) {
+ if (IsCompAssign || RHSSigned)
+ return castElement<CK_IntegralCast>(SemaRef, RHS, LHSType);
+ return castElement<CK_IntegralCast>(SemaRef, LHS, RHSType);
+ }
+
+ // At this point the signed type has higher rank than the unsigned type, which
+ // means it will be the same size or bigger. If the signed type is bigger, it
+ // can represent all the values of the unsigned type, so select it.
+ if (Ctx.getIntWidth(LElTy) != Ctx.getIntWidth(RElTy)) {
+ if (IsCompAssign || LHSSigned)
+ return castElement<CK_IntegralCast>(SemaRef, RHS, LHSType);
+ return castElement<CK_IntegralCast>(SemaRef, LHS, RHSType);
+ }
+
+ // This is a bit of an odd duck case in HLSL. It shouldn't happen, but can due
+ // to C/C++ leaking through. The place this happens today is long vs long
+ // long. When arguments are vector<unsigned long, N> and vector<long long, N>,
+ // the long long has higher rank than long even though they are the same size.
+
+ // If this is a compound assignment cast the right hand side to the left hand
+ // side's type.
+ if (IsCompAssign)
+ return castElement<CK_IntegralCast>(SemaRef, RHS, LHSType);
+
+ // If this isn't a compound assignment we convert to unsigned long long.
+ QualType ElTy = Ctx.getCorrespondingUnsignedType(LHSSigned ? LElTy : RElTy);
+ QualType NewTy = Ctx.getExtVectorType(
+ ElTy, RHSType->castAs<VectorType>()->getNumElements());
+ (void)castElement<CK_IntegralCast>(SemaRef, RHS, NewTy);
+
+ return castElement<CK_IntegralCast>(SemaRef, LHS, NewTy);
+}
+
+static CastKind getScalarCastKind(ASTContext &Ctx, QualType DestTy,
+ QualType SrcTy) {
+ if (DestTy->isRealFloatingType() && SrcTy->isRealFloatingType())
+ return CK_FloatingCast;
+ if (DestTy->isIntegralType(Ctx) && SrcTy->isIntegralType(Ctx))
+ return CK_IntegralCast;
+ if (DestTy->isRealFloatingType())
+ return CK_IntegralToFloating;
+ assert(SrcTy->isRealFloatingType() && DestTy->isIntegralType(Ctx));
+ return CK_FloatingToIntegral;
+}
+
+QualType SemaHLSL::handleVectorBinOpConversion(ExprResult &LHS, ExprResult &RHS,
+ QualType LHSType,
+ QualType RHSType,
+ bool IsCompAssign) {
+ const auto *LVecTy = LHSType->getAs<VectorType>();
+ const auto *RVecTy = RHSType->getAs<VectorType>();
+ auto &Ctx = getASTContext();
+
+ // If the LHS is not a vector and this is a compound assignment, we truncate
+ // the argument to a scalar then convert it to the LHS's type.
+ if (!LVecTy && IsCompAssign) {
+ QualType RElTy = RHSType->castAs<VectorType>()->getElementType();
+ RHS = SemaRef.ImpCastExprToType(RHS.get(), RElTy, CK_HLSLVectorTruncation);
+ RHSType = RHS.get()->getType();
+ if (Ctx.hasSameUnqualifiedType(LHSType, RHSType))
+ return LHSType;
+ RHS = SemaRef.ImpCastExprToType(RHS.get(), LHSType,
+ getScalarCastKind(Ctx, LHSType, RHSType));
+ return LHSType;
+ }
+
+ unsigned EndSz = std::numeric_limits<unsigned>::max();
+ unsigned LSz = 0;
+ if (LVecTy)
+ LSz = EndSz = LVecTy->getNumElements();
+ if (RVecTy)
+ EndSz = std::min(RVecTy->getNumElements(), EndSz);
+ assert(EndSz != std::numeric_limits<unsigned>::max() &&
+ "one of the above should have had a value");
+
+ // In a compound assignment, the left operand does not change type, the right
+ // operand is converted to the type of the left operand.
+ if (IsCompAssign && LSz != EndSz) {
+ Diag(LHS.get()->getBeginLoc(),
+ diag::err_hlsl_vector_compound_assignment_truncation)
+ << LHSType << RHSType;
+ return QualType();
+ }
+
+ if (RVecTy && RVecTy->getNumElements() > EndSz)
+ castVector<CK_HLSLVectorTruncation>(SemaRef, RHS, RHSType, EndSz);
+ if (!IsCompAssign && LVecTy && LVecTy->getNumElements() > EndSz)
+ castVector<CK_HLSLVectorTruncation>(SemaRef, LHS, LHSType, EndSz);
+
+ if (!RVecTy)
+ castVector<CK_VectorSplat>(SemaRef, RHS, RHSType, EndSz);
+ if (!IsCompAssign && !LVecTy)
+ castVector<CK_VectorSplat>(SemaRef, LHS, LHSType, EndSz);
+
+ // If we're at the same type after resizing we can stop here.
+ if (Ctx.hasSameUnqualifiedType(LHSType, RHSType))
+ return Ctx.getCommonSugaredType(LHSType, RHSType);
+
+ QualType LElTy = LHSType->castAs<VectorType>()->getElementType();
+ QualType RElTy = RHSType->castAs<VectorType>()->getElementType();
+
+ // Handle conversion for floating point vectors.
+ if (LElTy->isRealFloatingType() || RElTy->isRealFloatingType())
+ return handleFloatVectorBinOpConversion(SemaRef, LHS, RHS, LHSType, RHSType,
+ LElTy, RElTy, IsCompAssign);
+
+ assert(LElTy->isIntegralType(Ctx) && RElTy->isIntegralType(Ctx) &&
+ "HLSL Vectors can only contain integer or floating point types");
+ return handleIntegerVectorBinOpConversion(SemaRef, LHS, RHS, LHSType, RHSType,
+ LElTy, RElTy, IsCompAssign);
+}
+
+void SemaHLSL::emitLogicalOperatorFixIt(Expr *LHS, Expr *RHS,
+ BinaryOperatorKind Opc) {
+ assert((Opc == BO_LOr || Opc == BO_LAnd) &&
+ "Called with non-logical operator");
+ llvm::SmallVector<char, 256> Buff;
+ llvm::raw_svector_ostream OS(Buff);
+ PrintingPolicy PP(SemaRef.getLangOpts());
+ StringRef NewFnName = Opc == BO_LOr ? "or" : "and";
+ OS << NewFnName << "(";
+ LHS->printPretty(OS, nullptr, PP);
+ OS << ", ";
+ RHS->printPretty(OS, nullptr, PP);
+ OS << ")";
+ SourceRange FullRange = SourceRange(LHS->getBeginLoc(), RHS->getEndLoc());
+ SemaRef.Diag(LHS->getBeginLoc(), diag::note_function_suggestion)
+ << NewFnName << FixItHint::CreateReplacement(FullRange, OS.str());
+}
+
void SemaHLSL::handleNumThreadsAttr(Decl *D, const ParsedAttr &AL) {
llvm::VersionTuple SMVersion =
getASTContext().getTargetInfo().getTriple().getOSVersion();
diff --git a/clang/test/SemaHLSL/Language/UsualArithmeticConversions.hlsl b/clang/test/SemaHLSL/Language/UsualArithmeticConversions.hlsl
new file mode 100644
index 00000000000000..6138169e299fd7
--- /dev/null
+++ b/clang/test/SemaHLSL/Language/UsualArithmeticConversions.hlsl
@@ -0,0 +1,379 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -finclude-default-header -fnative-half-type %s -DERRORS -Wconversion -Wdouble-promotion -verify
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -std=hlsl2018 -finclude-default-header -fnative-half-type %s -DERRORS -Wconversion -Wdouble-promotion -verify
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -finclude-default-header -fnative-half-type %s -ast-dump | FileCheck %s
+
+//----------------------------------------------------------------------------//
+// Case 1: float4 * int4 and inverse.
+//
+// In both cases here the int is converted to a float and the computation
+// produces a float value.
+//----------------------------------------------------------------------------//
+
+// CHECK-LABEL: FunctionDecl {{.*}} used f4f4i4 'float4 (float4, int4)'
+// CHECK: BinaryOperator {{.*}} 'float4':'vector<float, 4>' '*'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float4':'vector<float, 4>' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'float4':'vector<float, 4>' lvalue ParmVar {{.*}} 'A' 'float4':'vector<float, 4>'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float4':'vector<float, 4>' <IntegralToFloating>
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int4':'vector<int, 4>' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'int4':'vector<int, 4>' lvalue ParmVar {{.*}} 'B' 'int4':'vector<int, 4>'
+export float4 f4f4i4(float4 A, int4 B) {
+ return A * B; // expected-warning{{implicit conversion from 'int4' (aka 'vector<int, 4>') to 'float4' (aka 'vector<float, 4>') may lose precision}}
+}
+
+// CHECK-LABEL: FunctionDecl {{.*}} used f4i4f4 'float4 (float4, int4)'
+// CHECK: BinaryOperator {{.*}} 'float4':'vector<float, 4>' '*'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float4':'vector<float, 4>' <IntegralToFloating>
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int4':'vector<int, 4>' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'int4':'vector<int, 4>' lvalue ParmVar {{.*}} 'B' 'int4':'vector<int, 4>'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float4':'vector<float, 4>' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'float4':'vector<float, 4>' lvalue ParmVar {{.*}} 'A' 'float4':'vector<float, 4>'
+export float4 f4i4f4(float4 A, int4 B) {
+ return B * A; // expected-warning{{implicit conversion from 'int4' (aka 'vector<int, 4>') to 'float4' (aka 'vector<float, 4>') may lose precision}}
+}
+
+//----------------------------------------------------------------------------//
+// Case 2: float4 * int2 and inverse.
+//
+// In both cases the float vector is trunctated to a float2 and the integer
+// vector is converted to a float2.
+//----------------------------------------------------------------------------//
+
+// CHECK-LABEL: FunctionDecl {{.*}} used f2f4i2 'float2 (float4, int2)'
+// CHECK: BinaryOperator {{.*}} 'vector<float, 2>' '*'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<float, 2>' <HLSLVectorTruncation>
+// CHECK-NEXT: ImplicitCastExpr {{.*}}'float4':'vector<float, 4>' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'float4':'vector<float, 4>' lvalue ParmVar {{.*}} 'A' 'float4':'vector<float, 4>'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<float, 2>' <IntegralToFloating>
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int2':'vector<int, 2>' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'int2':'vector<int, 2>' lvalue ParmVar {{.*}} 'B' 'int2':'vector<int, 2>'
+export float2 f2f4i2(float4 A, int2 B) {
+ // expected-warning@#f2f4i2 {{implicit conversion from 'int2' (aka 'vector<int, 2>') to 'vector<float, 2>' (vector of 2 'float' values) may lose precision}}
+ // expected-warning@#f2f4i2 {{implicit conversion truncates vector: 'float4' (aka 'vector<float, 4>') to 'vector<float, 2>' (vector of 2 'float' values)}}
+ return A * B; // #f2f4i2
+}
+
+// CHECK-LABEL: FunctionDecl {{.*}} used f2i2f4 'float2 (float4, int2)'
+// CHECK: BinaryOperator {{.*}} 'vector<float, 2>' '*'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<float, 2>' <IntegralToFloating>
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int2':'vector<int, 2>' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'int2':'vector<int, 2>' lvalue ParmVar {{.*}} 'B' 'int2':'vector<int, 2>'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<float, 2>' <HLSLVectorTruncation>
+// CHECK-NEXT: ImplicitCastExpr {{.*}}'float4':'vector<float, 4>' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'float4':'vector<float, 4>' lvalue ParmVar {{.*}} 'A' 'float4':'vector<float, 4>'
+export float2 f2i2f4(float4 A, int2 B) {
+ // expected-warning@#f2i2f4 {{implicit conversion from 'int2' (aka 'vector<int, 2>') to 'vector<float, 2>' (vector of 2 'float' values) may lose precision}}
+ // expected-warning@#f2i2f4 {{implicit conversion truncates vector: 'float4' (aka 'vector<float, 4>') to 'vector<float, 2>' (vector of 2 'float' values)}}
+ return B * A; // #f2i2f4
+}
+
+//----------------------------------------------------------------------------//
+// Case 3: Integers of mismatched sign, equivalent size, but the unsigned type
+// has lower conversion rank.
+//
+// This is the odd-ball case for HLSL that isn't really in spec, but we should
+// handle gracefully. The lower-ranked unsigned type is converted to the
+// equivalent unsigned type of higher rank, and the signed type is also
+// converted to that unsigned type (meaning `unsigned long` becomes `unsinged
+// long long`, and `long long` becomes `unsigned long long`).
+//----------------------------------------------------------------------------//
+
+// CHECK-LABEL: FunctionDecl {{.*}} used wierdo 'int4 (vector<unsigned long, 4>, vector<long long, 4>)'
+// CHECK: BinaryOperator {{.*}} 'vector<unsigned long long, 4>' '*'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<unsigned long long, 4>' <IntegralCast>
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<unsigned long, 4>' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr{{.*}} 'vector<unsigned long, 4>' lvalue ParmVar {{.*}} 'A' 'vector<unsigned long, 4>'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector<unsigned long long, 4>' <IntegralCast>
+// CHECK-NEXT: ImplicitCastExpr{{.*}}> ...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/108659
More information about the cfe-commits
mailing list