Skip to content

Commit 5c57fd7

Browse files
[HLSL] Vector standard conversions (#71098)
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) --------- Co-authored-by: Aaron Ballman <[email protected]>
1 parent edfc859 commit 5c57fd7

24 files changed

+599
-99
lines changed

clang/include/clang/AST/OperationKinds.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,9 @@ CAST_OPERATION(AddressSpaceConversion)
361361
// Convert an integer initializer to an OpenCL sampler.
362362
CAST_OPERATION(IntToOCLSampler)
363363

364+
// Truncate a vector type by dropping elements from the end (HLSL only).
365+
CAST_OPERATION(HLSLVectorTruncation)
366+
364367
//===- Binary Operations -------------------------------------------------===//
365368
// Operators listed in order of precedence.
366369
// Note that additions to this should also update the StmtVisitor class,

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12112,6 +12112,9 @@ def err_hlsl_operator_unsupported : Error<
1211212112
def err_hlsl_param_qualifier_mismatch :
1211312113
Error<"conflicting parameter qualifier %0 on parameter %1">;
1211412114

12115+
def warn_hlsl_impcast_vector_truncation : Warning<
12116+
"implicit conversion truncates vector: %0 to %1">, InGroup<Conversion>;
12117+
1211512118
// Layout randomization diagnostics.
1211612119
def err_non_designated_init_used : Error<
1211712120
"a randomized struct can only be initialized with a designated initializer">;

clang/include/clang/Sema/Overload.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,9 @@ class Sema;
195195
/// Fixed point type conversions according to N1169.
196196
ICK_Fixed_Point_Conversion,
197197

198+
/// HLSL vector truncation.
199+
ICK_HLSL_Vector_Truncation,
200+
198201
/// The number of conversion kinds
199202
ICK_Num_Conversion_Kinds,
200203
};
@@ -271,6 +274,12 @@ class Sema;
271274
/// pointer-to-member conversion, or boolean conversion.
272275
ImplicitConversionKind Second : 8;
273276

277+
/// Element - Between the second and third conversion a vector or matrix
278+
/// element conversion may occur. If this is not ICK_Identity this
279+
/// conversion is applied element-wise to each element in the vector or
280+
/// matrix.
281+
ImplicitConversionKind Element : 8;
282+
274283
/// Third - The third conversion can be a qualification conversion
275284
/// or a function conversion.
276285
ImplicitConversionKind Third : 8;
@@ -367,7 +376,8 @@ class Sema;
367376
void setAsIdentityConversion();
368377

369378
bool isIdentityConversion() const {
370-
return Second == ICK_Identity && Third == ICK_Identity;
379+
return Second == ICK_Identity && Element == ICK_Identity &&
380+
Third == ICK_Identity;
371381
}
372382

373383
ImplicitConversionRank getRank() const;

clang/lib/AST/Expr.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1897,6 +1897,7 @@ bool CastExpr::CastConsistency() const {
18971897
case CK_FixedPointToIntegral:
18981898
case CK_IntegralToFixedPoint:
18991899
case CK_MatrixCast:
1900+
case CK_HLSLVectorTruncation:
19001901
assert(!getType()->isBooleanType() && "unheralded conversion to bool");
19011902
goto CheckNoBasePath;
19021903

clang/lib/AST/ExprConstant.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13980,6 +13980,7 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) {
1398013980
case CK_FixedPointCast:
1398113981
case CK_IntegralToFixedPoint:
1398213982
case CK_MatrixCast:
13983+
case CK_HLSLVectorTruncation:
1398313984
llvm_unreachable("invalid cast kind for integral value");
1398413985

1398513986
case CK_BitCast:
@@ -14818,6 +14819,7 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) {
1481814819
case CK_FixedPointToIntegral:
1481914820
case CK_IntegralToFixedPoint:
1482014821
case CK_MatrixCast:
14822+
case CK_HLSLVectorTruncation:
1482114823
llvm_unreachable("invalid cast kind for complex value");
1482214824

1482314825
case CK_LValueToRValue:

clang/lib/CodeGen/CGExpr.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5180,6 +5180,7 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) {
51805180
case CK_FixedPointToIntegral:
51815181
case CK_IntegralToFixedPoint:
51825182
case CK_MatrixCast:
5183+
case CK_HLSLVectorTruncation:
51835184
return EmitUnsupportedLValue(E, "unexpected cast lvalue");
51845185

51855186
case CK_Dependent:

clang/lib/CodeGen/CGExprAgg.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,7 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) {
933933
case CK_BuiltinFnToFnPtr:
934934
case CK_ZeroToOCLOpaqueType:
935935
case CK_MatrixCast:
936+
case CK_HLSLVectorTruncation:
936937

937938
case CK_IntToOCLSampler:
938939
case CK_FloatingToFixedPoint:
@@ -1457,6 +1458,7 @@ static bool castPreservesZero(const CastExpr *CE) {
14571458
case CK_MatrixCast:
14581459
case CK_NonAtomicToAtomic:
14591460
case CK_AtomicToNonAtomic:
1461+
case CK_HLSLVectorTruncation:
14601462
return true;
14611463

14621464
case CK_BaseToDerivedMemberPointer:

clang/lib/CodeGen/CGExprComplex.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,7 @@ ComplexPairTy ComplexExprEmitter::EmitCast(CastKind CK, Expr *Op,
564564
case CK_FixedPointToIntegral:
565565
case CK_IntegralToFixedPoint:
566566
case CK_MatrixCast:
567+
case CK_HLSLVectorTruncation:
567568
llvm_unreachable("invalid cast kind for complex value");
568569

569570
case CK_FloatingRealToComplex:

clang/lib/CodeGen/CGExprConstant.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1225,6 +1225,7 @@ class ConstExprEmitter :
12251225
case CK_IntegralToFixedPoint:
12261226
case CK_ZeroToOCLOpaqueType:
12271227
case CK_MatrixCast:
1228+
case CK_HLSLVectorTruncation:
12281229
return nullptr;
12291230
}
12301231
llvm_unreachable("Invalid CastKind");

clang/lib/CodeGen/CGExprScalar.cpp

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2408,6 +2408,12 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
24082408
CE->getExprLoc());
24092409

24102410
case CK_IntegralCast: {
2411+
if (E->getType()->isExtVectorType() && DestTy->isExtVectorType()) {
2412+
QualType SrcElTy = E->getType()->castAs<VectorType>()->getElementType();
2413+
return Builder.CreateIntCast(Visit(E), ConvertType(DestTy),
2414+
SrcElTy->isSignedIntegerOrEnumerationType(),
2415+
"conv");
2416+
}
24112417
ScalarConversionOpts Opts;
24122418
if (auto *ICE = dyn_cast<ImplicitCastExpr>(CE)) {
24132419
if (!ICE->isPartOfExplicitCast())
@@ -2416,9 +2422,50 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
24162422
return EmitScalarConversion(Visit(E), E->getType(), DestTy,
24172423
CE->getExprLoc(), Opts);
24182424
}
2419-
case CK_IntegralToFloating:
2420-
case CK_FloatingToIntegral:
2421-
case CK_FloatingCast:
2425+
case CK_IntegralToFloating: {
2426+
if (E->getType()->isVectorType() && DestTy->isVectorType()) {
2427+
// TODO: Support constrained FP intrinsics.
2428+
assert(!Builder.getIsFPConstrained() &&
2429+
"FP Constrained vector casts not supported yet.");
2430+
QualType SrcElTy = E->getType()->castAs<VectorType>()->getElementType();
2431+
if (SrcElTy->isSignedIntegerOrEnumerationType())
2432+
return Builder.CreateSIToFP(Visit(E), ConvertType(DestTy), "conv");
2433+
return Builder.CreateUIToFP(Visit(E), ConvertType(DestTy), "conv");
2434+
}
2435+
CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, CE);
2436+
return EmitScalarConversion(Visit(E), E->getType(), DestTy,
2437+
CE->getExprLoc());
2438+
}
2439+
case CK_FloatingToIntegral: {
2440+
if (E->getType()->isVectorType() && DestTy->isVectorType()) {
2441+
// TODO: Support constrained FP intrinsics.
2442+
assert(!Builder.getIsFPConstrained() &&
2443+
"FP Constrained vector casts not supported yet.");
2444+
QualType DstElTy = DestTy->castAs<VectorType>()->getElementType();
2445+
if (DstElTy->isSignedIntegerOrEnumerationType())
2446+
return Builder.CreateFPToSI(Visit(E), ConvertType(DestTy), "conv");
2447+
return Builder.CreateFPToUI(Visit(E), ConvertType(DestTy), "conv");
2448+
}
2449+
CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, CE);
2450+
return EmitScalarConversion(Visit(E), E->getType(), DestTy,
2451+
CE->getExprLoc());
2452+
}
2453+
case CK_FloatingCast: {
2454+
if (E->getType()->isVectorType() && DestTy->isVectorType()) {
2455+
// TODO: Support constrained FP intrinsics.
2456+
assert(!Builder.getIsFPConstrained() &&
2457+
"FP Constrained vector casts not supported yet.");
2458+
QualType SrcElTy = E->getType()->castAs<VectorType>()->getElementType();
2459+
QualType DstElTy = DestTy->castAs<VectorType>()->getElementType();
2460+
if (DstElTy->castAs<BuiltinType>()->getKind() <
2461+
SrcElTy->castAs<BuiltinType>()->getKind())
2462+
return Builder.CreateFPTrunc(Visit(E), ConvertType(DestTy), "conv");
2463+
return Builder.CreateFPExt(Visit(E), ConvertType(DestTy), "conv");
2464+
}
2465+
CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, CE);
2466+
return EmitScalarConversion(Visit(E), E->getType(), DestTy,
2467+
CE->getExprLoc());
2468+
}
24222469
case CK_FixedPointToFloating:
24232470
case CK_FloatingToFixedPoint: {
24242471
CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, CE);
@@ -2468,6 +2515,17 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
24682515
case CK_IntToOCLSampler:
24692516
return CGF.CGM.createOpenCLIntToSamplerConversion(E, CGF);
24702517

2518+
case CK_HLSLVectorTruncation: {
2519+
assert(DestTy->isVectorType() && "Expected dest type to be vector type");
2520+
Value *Vec = Visit(const_cast<Expr *>(E));
2521+
SmallVector<int, 16> Mask;
2522+
unsigned NumElts = DestTy->castAs<VectorType>()->getNumElements();
2523+
for (unsigned I = 0; I != NumElts; ++I)
2524+
Mask.push_back(I);
2525+
2526+
return Builder.CreateShuffleVector(Vec, Mask, "trunc");
2527+
}
2528+
24712529
} // end of switch
24722530

24732531
llvm_unreachable("unknown scalar cast");

clang/lib/Edit/RewriteObjCFoundationAPI.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,6 +1083,10 @@ static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
10831083
case CK_BooleanToSignedIntegral:
10841084
llvm_unreachable("OpenCL-specific cast in Objective-C?");
10851085

1086+
case CK_HLSLVectorTruncation:
1087+
llvm_unreachable("HLSL-specific cast in Objective-C?");
1088+
break;
1089+
10861090
case CK_FloatingToFixedPoint:
10871091
case CK_FixedPointToFloating:
10881092
case CK_FixedPointCast:

clang/lib/Sema/SemaChecking.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15676,11 +15676,18 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
1567615676
if (S.SourceMgr.isInSystemMacro(CC))
1567715677
return;
1567815678
return DiagnoseImpCast(S, E, T, CC, diag::warn_impcast_vector_scalar);
15679+
} else if (S.getLangOpts().HLSL &&
15680+
Target->castAs<VectorType>()->getNumElements() <
15681+
Source->castAs<VectorType>()->getNumElements()) {
15682+
// Diagnose vector truncation but don't return. We may also want to
15683+
// diagnose an element conversion.
15684+
DiagnoseImpCast(S, E, T, CC, diag::warn_hlsl_impcast_vector_truncation);
1567915685
}
1568015686

1568115687
// If the vector cast is cast between two vectors of the same size, it is
15682-
// a bitcast, not a conversion.
15683-
if (S.Context.getTypeSize(Source) == S.Context.getTypeSize(Target))
15688+
// a bitcast, not a conversion, except under HLSL where it is a conversion.
15689+
if (!S.getLangOpts().HLSL &&
15690+
S.Context.getTypeSize(Source) == S.Context.getTypeSize(Target))
1568415691
return;
1568515692

1568615693
Source = cast<VectorType>(Source)->getElementType().getTypePtr();

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4762,6 +4762,22 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
47624762
CK_ZeroToOCLOpaqueType,
47634763
From->getValueKind()).get();
47644764
break;
4765+
case ICK_HLSL_Vector_Truncation: {
4766+
// Note: HLSL built-in vectors are ExtVectors. Since this truncates a vector
4767+
// to a smaller vector, this can only operate on arguments where the source
4768+
// and destination types are ExtVectors.
4769+
assert(From->getType()->isExtVectorType() && ToType->isExtVectorType() &&
4770+
"HLSL vector truncation should only apply to ExtVectors");
4771+
auto *FromVec = From->getType()->castAs<VectorType>();
4772+
auto *ToVec = ToType->castAs<VectorType>();
4773+
QualType ElType = FromVec->getElementType();
4774+
QualType TruncTy =
4775+
Context.getExtVectorType(ElType, ToVec->getNumElements());
4776+
From = ImpCastExprToType(From, TruncTy, CK_HLSLVectorTruncation,
4777+
From->getValueKind())
4778+
.get();
4779+
break;
4780+
}
47654781

47664782
case ICK_Lvalue_To_Rvalue:
47674783
case ICK_Array_To_Pointer:
@@ -4774,6 +4790,76 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
47744790
llvm_unreachable("Improper second standard conversion");
47754791
}
47764792

4793+
if (SCS.Element != ICK_Identity) {
4794+
// If SCS.Element is not ICK_Identity the To and From types must be HLSL
4795+
// vectors or matrices.
4796+
4797+
// TODO: Support HLSL matrices.
4798+
assert((!From->getType()->isMatrixType() && !ToType->isMatrixType()) &&
4799+
"Element conversion for matrix types is not implemented yet.");
4800+
assert(From->getType()->isVectorType() && ToType->isVectorType() &&
4801+
"Element conversion is only supported for vector types.");
4802+
assert(From->getType()->getAs<VectorType>()->getNumElements() ==
4803+
ToType->getAs<VectorType>()->getNumElements() &&
4804+
"Element conversion is only supported for vectors with the same "
4805+
"element counts.");
4806+
QualType FromElTy = From->getType()->getAs<VectorType>()->getElementType();
4807+
unsigned NumElts = ToType->getAs<VectorType>()->getNumElements();
4808+
switch (SCS.Element) {
4809+
case ICK_Boolean_Conversion:
4810+
// Perform half-to-boolean conversion via float.
4811+
if (FromElTy->isHalfType()) {
4812+
QualType FPExtType = Context.getExtVectorType(FromElTy, NumElts);
4813+
From = ImpCastExprToType(From, FPExtType, CK_FloatingCast).get();
4814+
FromType = FPExtType;
4815+
}
4816+
4817+
From =
4818+
ImpCastExprToType(From, ToType, ScalarTypeToBooleanCastKind(FromElTy),
4819+
VK_PRValue,
4820+
/*BasePath=*/nullptr, CCK)
4821+
.get();
4822+
break;
4823+
case ICK_Integral_Promotion:
4824+
case ICK_Integral_Conversion:
4825+
if (ToType->isBooleanType()) {
4826+
assert(FromType->castAs<EnumType>()->getDecl()->isFixed() &&
4827+
SCS.Second == ICK_Integral_Promotion &&
4828+
"only enums with fixed underlying type can promote to bool");
4829+
From = ImpCastExprToType(From, ToType, CK_IntegralToBoolean, VK_PRValue,
4830+
/*BasePath=*/nullptr, CCK)
4831+
.get();
4832+
} else {
4833+
From = ImpCastExprToType(From, ToType, CK_IntegralCast, VK_PRValue,
4834+
/*BasePath=*/nullptr, CCK)
4835+
.get();
4836+
}
4837+
break;
4838+
4839+
case ICK_Floating_Promotion:
4840+
case ICK_Floating_Conversion:
4841+
From = ImpCastExprToType(From, ToType, CK_FloatingCast, VK_PRValue,
4842+
/*BasePath=*/nullptr, CCK)
4843+
.get();
4844+
break;
4845+
case ICK_Floating_Integral:
4846+
if (ToType->isRealFloatingType())
4847+
From =
4848+
ImpCastExprToType(From, ToType, CK_IntegralToFloating, VK_PRValue,
4849+
/*BasePath=*/nullptr, CCK)
4850+
.get();
4851+
else
4852+
From =
4853+
ImpCastExprToType(From, ToType, CK_FloatingToIntegral, VK_PRValue,
4854+
/*BasePath=*/nullptr, CCK)
4855+
.get();
4856+
break;
4857+
case ICK_Identity:
4858+
default:
4859+
llvm_unreachable("Improper element standard conversion");
4860+
}
4861+
}
4862+
47774863
switch (SCS.Third) {
47784864
case ICK_Identity:
47794865
// Nothing to do.

clang/lib/Sema/SemaInit.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6432,7 +6432,7 @@ void InitializationSequence::InitializeFrom(Sema &S,
64326432
// For HLSL ext vector types we allow list initialization behavior for C++
64336433
// constructor syntax. This is accomplished by converting initialization
64346434
// arguments an InitListExpr late.
6435-
if (S.getLangOpts().HLSL && DestType->isExtVectorType() &&
6435+
if (S.getLangOpts().HLSL && Args.size() > 1 && DestType->isExtVectorType() &&
64366436
(SourceType.isNull() ||
64376437
!Context.hasSameUnqualifiedType(SourceType, DestType))) {
64386438

0 commit comments

Comments
 (0)