Skip to content

Commit d91ff3f

Browse files
authored
[HLSL] Rework implicit conversion sequences (#96011)
This PR reworks HLSL's implicit conversion sequences. Initially I was seeking to match DXC's behavior more closely, but that was leading to a pile of special case rules to tie-break ambiguous cases that should really be left as ambiguous. We've decided that we're going to break compatibility with DXC here, and we may port this new behavior over to DXC instead. This change is a bit closer to C++'s overload resolution rules, but it does have a bit of nuance around how dimension adjustment conversions are ranked. Conversion sequence ranks for HLSL are: * Exact match * Scalar Widening (i.e. splat) * Promotion * Scalar Widening with Promotion * Conversion * Scalar Widening with Conversion * Dimension Reduction (i.e. truncation) * Dimension Reduction with Promotion * Dimension Reduction with Conversion In this implementation I've folded the disambiguation into the conversion sequence ranks which does add some complexity as compared to C++, however this avoids needing to add special casing in `CompareStandardConversionSequences`. I believe the added conversion rank values provide a simpler approach, but feedback is appreciated. The HLSL language spec updates are in the PR here: microsoft/hlsl-specs#261
1 parent 2d2893d commit d91ff3f

16 files changed

+446
-339
lines changed

clang/docs/HLSL/ExpectedDifferences.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,16 @@ behavior between Clang and DXC. Some examples include:
6767
void takesDoubles(double, double, double);
6868

6969
cbuffer CB {
70+
bool B;
7071
uint U;
7172
int I;
7273
float X, Y, Z;
7374
double3 A, B;
7475
}
7576

77+
void twoParams(int, int);
78+
void twoParams(float, float);
79+
7680
export void call() {
7781
halfOrInt16(U); // DXC: Fails with call ambiguous between int16_t and uint16_t overloads
7882
// Clang: Resolves to halfOrInt16(uint16_t).
@@ -98,6 +102,13 @@ behavior between Clang and DXC. Some examples include:
98102
// FXC: Expands to compute double dot product with fmul/fadd
99103
// Clang: Resolves to dot(float3, float3), emits conversion warnings.
100104

105+
#ifndef IGNORE_ERRORS
106+
tan(B); // DXC: resolves to tan(float).
107+
// Clang: Fails to resolve, ambiguous between integer types.
108+
109+
twoParams(I, X); // DXC: resolves twoParams(int, int).
110+
// Clang: Fails to resolve ambiguous conversions.
111+
#endif
101112
}
102113

103114
.. note::

clang/include/clang/Sema/Overload.h

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,9 @@ class Sema;
201201
/// HLSL non-decaying array rvalue cast.
202202
ICK_HLSL_Array_RValue,
203203

204+
// HLSL vector splat from scalar or boolean type.
205+
ICK_HLSL_Vector_Splat,
206+
204207
/// The number of conversion kinds
205208
ICK_Num_Conversion_Kinds,
206209
};
@@ -213,15 +216,27 @@ class Sema;
213216
/// Exact Match
214217
ICR_Exact_Match = 0,
215218

219+
/// HLSL Scalar Widening
220+
ICR_HLSL_Scalar_Widening,
221+
216222
/// Promotion
217223
ICR_Promotion,
218224

225+
/// HLSL Scalar Widening with promotion
226+
ICR_HLSL_Scalar_Widening_Promotion,
227+
228+
/// HLSL Matching Dimension Reduction
229+
ICR_HLSL_Dimension_Reduction,
230+
219231
/// Conversion
220232
ICR_Conversion,
221233

222234
/// OpenCL Scalar Widening
223235
ICR_OCL_Scalar_Widening,
224236

237+
/// HLSL Scalar Widening with conversion
238+
ICR_HLSL_Scalar_Widening_Conversion,
239+
225240
/// Complex <-> Real conversion
226241
ICR_Complex_Real_Conversion,
227242

@@ -233,11 +248,21 @@ class Sema;
233248

234249
/// Conversion not allowed by the C standard, but that we accept as an
235250
/// extension anyway.
236-
ICR_C_Conversion_Extension
251+
ICR_C_Conversion_Extension,
252+
253+
/// HLSL Dimension reduction with promotion
254+
ICR_HLSL_Dimension_Reduction_Promotion,
255+
256+
/// HLSL Dimension reduction with conversion
257+
ICR_HLSL_Dimension_Reduction_Conversion,
237258
};
238259

239260
ImplicitConversionRank GetConversionRank(ImplicitConversionKind Kind);
240261

262+
ImplicitConversionRank
263+
GetDimensionConversionRank(ImplicitConversionRank Base,
264+
ImplicitConversionKind Dimension);
265+
241266
/// NarrowingKind - The kind of narrowing conversion being performed by a
242267
/// standard conversion sequence according to C++11 [dcl.init.list]p7.
243268
enum NarrowingKind {
@@ -277,11 +302,10 @@ class Sema;
277302
/// pointer-to-member conversion, or boolean conversion.
278303
ImplicitConversionKind Second : 8;
279304

280-
/// Element - Between the second and third conversion a vector or matrix
281-
/// element conversion may occur. If this is not ICK_Identity this
282-
/// conversion is applied element-wise to each element in the vector or
283-
/// matrix.
284-
ImplicitConversionKind Element : 8;
305+
/// Dimension - Between the second and third conversion a vector or matrix
306+
/// dimension conversion may occur. If this is not ICK_Identity this
307+
/// conversion truncates the vector or matrix, or extends a scalar.
308+
ImplicitConversionKind Dimension : 8;
285309

286310
/// Third - The third conversion can be a qualification conversion
287311
/// or a function conversion.
@@ -379,7 +403,7 @@ class Sema;
379403
void setAsIdentityConversion();
380404

381405
bool isIdentityConversion() const {
382-
return Second == ICK_Identity && Element == ICK_Identity &&
406+
return Second == ICK_Identity && Dimension == ICK_Identity &&
383407
Third == ICK_Identity;
384408
}
385409

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 76 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -4294,6 +4294,20 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
42944294
return From;
42954295
}
42964296

4297+
// adjustVectorType - Compute the intermediate cast type casting elements of the
4298+
// from type to the elements of the to type without resizing the vector.
4299+
static QualType adjustVectorType(ASTContext &Context, QualType FromTy,
4300+
QualType ToType, QualType *ElTy = nullptr) {
4301+
auto *ToVec = ToType->castAs<VectorType>();
4302+
QualType ElType = ToVec->getElementType();
4303+
if (ElTy)
4304+
*ElTy = ElType;
4305+
if (!FromTy->isVectorType())
4306+
return ElType;
4307+
auto *FromVec = FromTy->castAs<VectorType>();
4308+
return Context.getExtVectorType(ElType, FromVec->getNumElements());
4309+
}
4310+
42974311
ExprResult
42984312
Sema::PerformImplicitConversion(Expr *From, QualType ToType,
42994313
const StandardConversionSequence& SCS,
@@ -4443,27 +4457,36 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
44434457
break;
44444458

44454459
case ICK_Integral_Promotion:
4446-
case ICK_Integral_Conversion:
4447-
if (ToType->isBooleanType()) {
4460+
case ICK_Integral_Conversion: {
4461+
QualType ElTy = ToType;
4462+
QualType StepTy = ToType;
4463+
if (ToType->isVectorType())
4464+
StepTy = adjustVectorType(Context, FromType, ToType, &ElTy);
4465+
if (ElTy->isBooleanType()) {
44484466
assert(FromType->castAs<EnumType>()->getDecl()->isFixed() &&
44494467
SCS.Second == ICK_Integral_Promotion &&
44504468
"only enums with fixed underlying type can promote to bool");
4451-
From = ImpCastExprToType(From, ToType, CK_IntegralToBoolean, VK_PRValue,
4469+
From = ImpCastExprToType(From, StepTy, CK_IntegralToBoolean, VK_PRValue,
44524470
/*BasePath=*/nullptr, CCK)
44534471
.get();
44544472
} else {
4455-
From = ImpCastExprToType(From, ToType, CK_IntegralCast, VK_PRValue,
4473+
From = ImpCastExprToType(From, StepTy, CK_IntegralCast, VK_PRValue,
44564474
/*BasePath=*/nullptr, CCK)
44574475
.get();
44584476
}
44594477
break;
4478+
}
44604479

44614480
case ICK_Floating_Promotion:
4462-
case ICK_Floating_Conversion:
4463-
From = ImpCastExprToType(From, ToType, CK_FloatingCast, VK_PRValue,
4481+
case ICK_Floating_Conversion: {
4482+
QualType StepTy = ToType;
4483+
if (ToType->isVectorType())
4484+
StepTy = adjustVectorType(Context, FromType, ToType);
4485+
From = ImpCastExprToType(From, StepTy, CK_FloatingCast, VK_PRValue,
44644486
/*BasePath=*/nullptr, CCK)
44654487
.get();
44664488
break;
4489+
}
44674490

44684491
case ICK_Complex_Promotion:
44694492
case ICK_Complex_Conversion: {
@@ -4486,16 +4509,21 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
44864509
break;
44874510
}
44884511

4489-
case ICK_Floating_Integral:
4490-
if (ToType->isRealFloatingType())
4491-
From = ImpCastExprToType(From, ToType, CK_IntegralToFloating, VK_PRValue,
4512+
case ICK_Floating_Integral: {
4513+
QualType ElTy = ToType;
4514+
QualType StepTy = ToType;
4515+
if (ToType->isVectorType())
4516+
StepTy = adjustVectorType(Context, FromType, ToType, &ElTy);
4517+
if (ElTy->isRealFloatingType())
4518+
From = ImpCastExprToType(From, StepTy, CK_IntegralToFloating, VK_PRValue,
44924519
/*BasePath=*/nullptr, CCK)
44934520
.get();
44944521
else
4495-
From = ImpCastExprToType(From, ToType, CK_FloatingToIntegral, VK_PRValue,
4522+
From = ImpCastExprToType(From, StepTy, CK_FloatingToIntegral, VK_PRValue,
44964523
/*BasePath=*/nullptr, CCK)
44974524
.get();
44984525
break;
4526+
}
44994527

45004528
case ICK_Fixed_Point_Conversion:
45014529
assert((FromType->isFixedPointType() || ToType->isFixedPointType()) &&
@@ -4617,18 +4645,26 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
46174645
break;
46184646
}
46194647

4620-
case ICK_Boolean_Conversion:
4648+
case ICK_Boolean_Conversion: {
46214649
// Perform half-to-boolean conversion via float.
46224650
if (From->getType()->isHalfType()) {
46234651
From = ImpCastExprToType(From, Context.FloatTy, CK_FloatingCast).get();
46244652
FromType = Context.FloatTy;
46254653
}
4654+
QualType ElTy = FromType;
4655+
QualType StepTy = ToType;
4656+
if (FromType->isVectorType()) {
4657+
if (getLangOpts().HLSL)
4658+
StepTy = adjustVectorType(Context, FromType, ToType);
4659+
ElTy = FromType->castAs<VectorType>()->getElementType();
4660+
}
46264661

4627-
From = ImpCastExprToType(From, Context.BoolTy,
4628-
ScalarTypeToBooleanCastKind(FromType), VK_PRValue,
4662+
From = ImpCastExprToType(From, StepTy, ScalarTypeToBooleanCastKind(ElTy),
4663+
VK_PRValue,
46294664
/*BasePath=*/nullptr, CCK)
46304665
.get();
46314666
break;
4667+
}
46324668

46334669
case ICK_Derived_To_Base: {
46344670
CXXCastPath BasePath;
@@ -4754,22 +4790,6 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
47544790
CK_ZeroToOCLOpaqueType,
47554791
From->getValueKind()).get();
47564792
break;
4757-
case ICK_HLSL_Vector_Truncation: {
4758-
// Note: HLSL built-in vectors are ExtVectors. Since this truncates a vector
4759-
// to a smaller vector, this can only operate on arguments where the source
4760-
// and destination types are ExtVectors.
4761-
assert(From->getType()->isExtVectorType() && ToType->isExtVectorType() &&
4762-
"HLSL vector truncation should only apply to ExtVectors");
4763-
auto *FromVec = From->getType()->castAs<VectorType>();
4764-
auto *ToVec = ToType->castAs<VectorType>();
4765-
QualType ElType = FromVec->getElementType();
4766-
QualType TruncTy =
4767-
Context.getExtVectorType(ElType, ToVec->getNumElements());
4768-
From = ImpCastExprToType(From, TruncTy, CK_HLSLVectorTruncation,
4769-
From->getValueKind())
4770-
.get();
4771-
break;
4772-
}
47734793

47744794
case ICK_Lvalue_To_Rvalue:
47754795
case ICK_Array_To_Pointer:
@@ -4780,73 +4800,45 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
47804800
case ICK_C_Only_Conversion:
47814801
case ICK_Incompatible_Pointer_Conversion:
47824802
case ICK_HLSL_Array_RValue:
4803+
case ICK_HLSL_Vector_Truncation:
4804+
case ICK_HLSL_Vector_Splat:
47834805
llvm_unreachable("Improper second standard conversion");
47844806
}
47854807

4786-
if (SCS.Element != ICK_Identity) {
4808+
if (SCS.Dimension != ICK_Identity) {
47874809
// If SCS.Element is not ICK_Identity the To and From types must be HLSL
47884810
// vectors or matrices.
47894811

47904812
// TODO: Support HLSL matrices.
47914813
assert((!From->getType()->isMatrixType() && !ToType->isMatrixType()) &&
4792-
"Element conversion for matrix types is not implemented yet.");
4793-
assert(From->getType()->isVectorType() && ToType->isVectorType() &&
4794-
"Element conversion is only supported for vector types.");
4795-
assert(From->getType()->getAs<VectorType>()->getNumElements() ==
4796-
ToType->getAs<VectorType>()->getNumElements() &&
4797-
"Element conversion is only supported for vectors with the same "
4798-
"element counts.");
4799-
QualType FromElTy = From->getType()->getAs<VectorType>()->getElementType();
4800-
unsigned NumElts = ToType->getAs<VectorType>()->getNumElements();
4801-
switch (SCS.Element) {
4802-
case ICK_Boolean_Conversion:
4803-
// Perform half-to-boolean conversion via float.
4804-
if (FromElTy->isHalfType()) {
4805-
QualType FPExtType = Context.getExtVectorType(FromElTy, NumElts);
4806-
From = ImpCastExprToType(From, FPExtType, CK_FloatingCast).get();
4807-
FromType = FPExtType;
4808-
}
4809-
4810-
From =
4811-
ImpCastExprToType(From, ToType, ScalarTypeToBooleanCastKind(FromElTy),
4812-
VK_PRValue,
4813-
/*BasePath=*/nullptr, CCK)
4814-
.get();
4815-
break;
4816-
case ICK_Integral_Promotion:
4817-
case ICK_Integral_Conversion:
4818-
if (ToType->isBooleanType()) {
4819-
assert(FromType->castAs<EnumType>()->getDecl()->isFixed() &&
4820-
SCS.Second == ICK_Integral_Promotion &&
4821-
"only enums with fixed underlying type can promote to bool");
4822-
From = ImpCastExprToType(From, ToType, CK_IntegralToBoolean, VK_PRValue,
4823-
/*BasePath=*/nullptr, CCK)
4824-
.get();
4825-
} else {
4826-
From = ImpCastExprToType(From, ToType, CK_IntegralCast, VK_PRValue,
4827-
/*BasePath=*/nullptr, CCK)
4828-
.get();
4829-
}
4830-
break;
4831-
4832-
case ICK_Floating_Promotion:
4833-
case ICK_Floating_Conversion:
4834-
From = ImpCastExprToType(From, ToType, CK_FloatingCast, VK_PRValue,
4814+
"Dimension conversion for matrix types is not implemented yet.");
4815+
assert(ToType->isVectorType() &&
4816+
"Dimension conversion is only supported for vector types.");
4817+
switch (SCS.Dimension) {
4818+
case ICK_HLSL_Vector_Splat: {
4819+
// Vector splat from any arithmetic type to a vector.
4820+
Expr *Elem = prepareVectorSplat(ToType, From).get();
4821+
From = ImpCastExprToType(Elem, ToType, CK_VectorSplat, VK_PRValue,
48354822
/*BasePath=*/nullptr, CCK)
48364823
.get();
48374824
break;
4838-
case ICK_Floating_Integral:
4839-
if (ToType->hasFloatingRepresentation())
4840-
From =
4841-
ImpCastExprToType(From, ToType, CK_IntegralToFloating, VK_PRValue,
4842-
/*BasePath=*/nullptr, CCK)
4843-
.get();
4844-
else
4845-
From =
4846-
ImpCastExprToType(From, ToType, CK_FloatingToIntegral, VK_PRValue,
4847-
/*BasePath=*/nullptr, CCK)
4848-
.get();
4825+
}
4826+
case ICK_HLSL_Vector_Truncation: {
4827+
// Note: HLSL built-in vectors are ExtVectors. Since this truncates a
4828+
// vector to a smaller vector, this can only operate on arguments where
4829+
// the source and destination types are ExtVectors.
4830+
assert(From->getType()->isExtVectorType() && ToType->isExtVectorType() &&
4831+
"HLSL vector truncation should only apply to ExtVectors");
4832+
auto *FromVec = From->getType()->castAs<VectorType>();
4833+
auto *ToVec = ToType->castAs<VectorType>();
4834+
QualType ElType = FromVec->getElementType();
4835+
QualType TruncTy =
4836+
Context.getExtVectorType(ElType, ToVec->getNumElements());
4837+
From = ImpCastExprToType(From, TruncTy, CK_HLSLVectorTruncation,
4838+
From->getValueKind())
4839+
.get();
48494840
break;
4841+
}
48504842
case ICK_Identity:
48514843
default:
48524844
llvm_unreachable("Improper element standard conversion");

0 commit comments

Comments
 (0)