[clang] [clang][bytecode][HLSL] Complete the HLSL aggregate splat and elementwise cast implementations, and enable the new constant interpreter on all HLSL tests with static asserts (PR #189126)
Deric C. via cfe-commits
cfe-commits at lists.llvm.org
Fri Apr 17 15:50:38 PDT 2026
https://github.com/Icohedron updated https://github.com/llvm/llvm-project/pull/189126
>From 93dd66fe4bd12704e3237fa81164cfddf1756a3a Mon Sep 17 00:00:00 2001
From: Deric Cheung <cheung.deric at gmail.com>
Date: Fri, 27 Mar 2026 13:41:15 -0700
Subject: [PATCH 1/6] Enable experimental new constexpr evalutor on HLSL tests
Completes the implementations of CK_HLSLAggregateSplatCast and
CK_HLSLElementwiseCast in Clang's new bytecode-based constant expression
evaluation engine / interpreter.
Adds -fexperimental-new-constant-interpreter to all HLSL tests that
have _Static_asserts.
Assisted-by: GitHub Copilot
---
clang/lib/AST/ByteCode/Compiler.cpp | 558 ++++++++++++++----
clang/lib/AST/ByteCode/Compiler.h | 23 +
.../SemaHLSL/BuiltIns/asfloat-constexpr.hlsl | 1 +
.../SemaHLSL/BuiltIns/asuint-constexpr.hlsl | 1 +
.../Language/InitIncompleteArrays.hlsl | 1 +
.../Types/AggregateSplatConstantExpr.hlsl | 1 +
.../SemaHLSL/Types/Arithmetic/half_size.hlsl | 4 +
.../Types/Arithmetic/literal_suffixes.hlsl | 1 +
.../Arithmetic/literal_suffixes_202x.hlsl | 4 +
.../Arithmetic/literal_suffixes_no_16bit.hlsl | 1 +
.../BooleanVectorConstantExpr.hlsl | 1 +
.../BuiltinVector/TruncationConstantExpr.hlsl | 1 +
.../Types/ElementwiseCastConstantExpr.hlsl | 1 +
.../SemaHLSL/Types/InitListConstantExpr.hlsl | 1 +
.../Types/Traits/IsIntangibleType.hlsl | 2 +
.../Types/Traits/IsIntangibleTypeErrors.hlsl | 1 +
.../IsTypedResourceElementCompatible.hlsl | 1 +
.../Traits/ScalarizedLayoutCompatible.hlsl | 2 +
.../ScalarizedLayoutCompatibleErrors.hlsl | 1 +
clang/test/SemaHLSL/Types/typedefs.hlsl | 2 +
clang/test/SemaHLSL/group_shared.hlsl | 1 +
21 files changed, 504 insertions(+), 105 deletions(-)
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 4d129a7ccd497..95dd64fdbdeda 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -855,26 +855,11 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *E) {
}
case CK_HLSLAggregateSplatCast: {
- // Aggregate splat cast: convert a scalar value to one of an aggregate type,
- // inserting casts when necessary to convert the scalar to the aggregate's
- // element type(s).
- // TODO: Aggregate splat to struct and array types
+ // Aggregate splat cast: convert a scalar value to one of an aggregate type
+ // by replicating and casting the scalar to every element of the destination
+ // aggregate (vector, matrix, array, or struct).
assert(canClassify(SubExpr->getType()));
- unsigned NumElems;
- PrimType DestElemT;
- QualType DestElemType;
- if (const auto *VT = E->getType()->getAs<VectorType>()) {
- NumElems = VT->getNumElements();
- DestElemType = VT->getElementType();
- } else if (const auto *MT = E->getType()->getAs<ConstantMatrixType>()) {
- NumElems = MT->getNumElementsFlattened();
- DestElemType = MT->getElementType();
- } else {
- return false;
- }
- DestElemT = classifyPrim(DestElemType);
-
if (!Initializing) {
UnsignedOrNone LocalIndex = allocateLocal(E);
if (!LocalIndex)
@@ -883,99 +868,54 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *E) {
return false;
}
+ // The scalar to be splatted is stored in a local to be repeatedly loaded
+ // once for every scalar element of the destination.
PrimType SrcElemT = classifyPrim(SubExpr->getType());
unsigned SrcOffset =
- allocateLocalPrimitive(SubExpr, DestElemT, /*IsConst=*/true);
+ allocateLocalPrimitive(SubExpr, SrcElemT, /*IsConst=*/true);
if (!this->visit(SubExpr))
return false;
- if (SrcElemT != DestElemT) {
- if (!this->emitPrimCast(SrcElemT, DestElemT, DestElemType, E))
- return false;
- }
- if (!this->emitSetLocal(DestElemT, SrcOffset, E))
+ if (!this->emitSetLocal(SrcElemT, SrcOffset, E))
return false;
- for (unsigned I = 0; I != NumElems; ++I) {
- if (!this->emitGetLocal(DestElemT, SrcOffset, E))
- return false;
- if (!this->emitInitElem(DestElemT, I, E))
- return false;
- }
- return true;
+ // Recursively splat the scalar into every element of the destination.
+ return emitHLSLAggregateSplat(SrcElemT, SrcOffset, E->getType(), E);
}
case CK_HLSLElementwiseCast: {
- // Elementwise cast: flatten source elements of one aggregate type and store
- // to a destination scalar or aggregate type of the same or fewer number of
- // elements, while inserting casts as necessary.
- // TODO: Elementwise cast to structs, nested arrays, and arrays of composite
- // types
+ // Elementwise cast: flatten the elements of one aggregate source type and
+ // store to a destination scalar or aggregate type of the same or fewer
+ // number of elements. Casts are inserted element-wise to convert each
+ // source scalar element to its corresponding destination scalar element.
QualType SrcType = SubExpr->getType();
QualType DestType = E->getType();
- // Allowed SrcTypes
- const auto *SrcVT = SrcType->getAs<VectorType>();
- const auto *SrcMT = SrcType->getAs<ConstantMatrixType>();
- const auto *SrcAT = SrcType->getAsArrayTypeUnsafe();
- const auto *SrcCAT = SrcAT ? dyn_cast<ConstantArrayType>(SrcAT) : nullptr;
-
- // Allowed DestTypes
- const auto *DestVT = DestType->getAs<VectorType>();
- const auto *DestMT = DestType->getAs<ConstantMatrixType>();
- const auto *DestAT = DestType->getAsArrayTypeUnsafe();
- const auto *DestCAT =
- DestAT ? dyn_cast<ConstantArrayType>(DestAT) : nullptr;
- const OptPrimType DestPT = classify(DestType);
-
- if (!SrcVT && !SrcMT && !SrcCAT)
- return false;
- if (!DestVT && !DestMT && !DestCAT && !DestPT)
- return false;
-
- unsigned SrcNumElems;
- PrimType SrcElemT;
- if (SrcVT) {
- SrcNumElems = SrcVT->getNumElements();
- SrcElemT = classifyPrim(SrcVT->getElementType());
- } else if (SrcMT) {
- SrcNumElems = SrcMT->getNumElementsFlattened();
- SrcElemT = classifyPrim(SrcMT->getElementType());
- } else if (SrcCAT) {
- SrcNumElems = SrcCAT->getZExtSize();
- SrcElemT = classifyPrim(SrcCAT->getElementType());
- }
-
- if (DestPT) {
- // Scalar destination: extract element 0 and cast.
+ const OptPrimType DestT = classify(DestType);
+ if (DestT) {
+ // When the destination is a scalar, we only need the first scalar
+ // element of the source.
+ unsigned SrcPtrOffset =
+ allocateLocalPrimitive(SubExpr, PT_Ptr, /*IsConst=*/true);
if (!this->visit(SubExpr))
return false;
- if (!this->emitArrayElemPop(SrcElemT, 0, E))
+ if (!this->emitSetLocal(PT_Ptr, SrcPtrOffset, E))
+ return false;
+
+ SmallVector<HLSLFlatElement, 1> Elements;
+ if (!emitHLSLFlattenAggregate(SrcType, SrcPtrOffset, Elements, 1, E))
+ return false;
+ if (Elements.empty())
return false;
- if (SrcElemT != *DestPT) {
- if (!this->emitPrimCast(SrcElemT, *DestPT, DestType, E))
- return false;
- }
- return true;
- }
- unsigned DestNumElems;
- PrimType DestElemT;
- QualType DestElemType;
- if (DestVT) {
- DestNumElems = DestVT->getNumElements();
- DestElemType = DestVT->getElementType();
- } else if (DestMT) {
- DestNumElems = DestMT->getNumElementsFlattened();
- DestElemType = DestMT->getElementType();
- } else if (DestCAT) {
- DestNumElems = DestCAT->getZExtSize();
- DestElemType = DestCAT->getElementType();
+ const HLSLFlatElement &Src = Elements[0];
+ if (!this->emitGetLocal(Src.Type, Src.LocalOffset, E))
+ return false;
+ return this->emitPrimCast(Src.Type, *DestT, DestType, E);
}
- DestElemT = classifyPrim(DestElemType);
if (!Initializing) {
- UnsignedOrNone LocalIndex = allocateTemporary(E);
+ UnsignedOrNone LocalIndex = allocateLocal(E);
if (!LocalIndex)
return false;
if (!this->emitGetPtrLocal(*LocalIndex, E))
@@ -989,20 +929,14 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *E) {
if (!this->emitSetLocal(PT_Ptr, SrcOffset, E))
return false;
- unsigned NumElems = std::min(SrcNumElems, DestNumElems);
- for (unsigned I = 0; I != NumElems; ++I) {
- if (!this->emitGetLocal(PT_Ptr, SrcOffset, E))
- return false;
- if (!this->emitArrayElemPop(SrcElemT, I, E))
- return false;
- if (SrcElemT != DestElemT) {
- if (!this->emitPrimCast(SrcElemT, DestElemT, DestElemType, E))
- return false;
- }
- if (!this->emitInitElem(DestElemT, I, E))
- return false;
- }
- return true;
+ // Only flatten as many source elements as the destination requires.
+ unsigned MaxElems = countHLSLFlatElements(DestType);
+
+ SmallVector<HLSLFlatElement, 16> Elements;
+ if (!emitHLSLFlattenAggregate(SrcType, SrcOffset, Elements, MaxElems, E))
+ return false;
+
+ return emitHLSLConstructAggregate(DestType, Elements, E);
}
default:
@@ -7992,6 +7926,420 @@ bool Compiler<Emitter>::emitBuiltinBitCast(const CastExpr *E) {
return true;
}
+/// Replicate a scalar value into every scalar element of an aggregate.
+/// The scalar is stored in a local at \p SrcOffset and a pointer to the
+/// destination must be on top of the interpreter stack. Each element receives
+/// the scalar, cast to its own type.
+template <class Emitter>
+bool Compiler<Emitter>::emitHLSLAggregateSplat(PrimType SrcT,
+ unsigned SrcOffset,
+ QualType DestType,
+ const Expr *E) {
+ // Vectors and matrices are treated as flat sequences of elements.
+ unsigned NumElems = 0;
+ QualType ElemType;
+ if (const auto *VT = DestType->getAs<VectorType>()) {
+ NumElems = VT->getNumElements();
+ ElemType = VT->getElementType();
+ } else if (const auto *MT = DestType->getAs<ConstantMatrixType>()) {
+ NumElems = MT->getNumElementsFlattened();
+ ElemType = MT->getElementType();
+ }
+ if (NumElems > 0) {
+ PrimType ElemT = classifyPrim(ElemType);
+ for (unsigned I = 0; I != NumElems; ++I) {
+ if (!this->emitGetLocal(SrcT, SrcOffset, E))
+ return false;
+ if (!this->emitPrimCast(SrcT, ElemT, ElemType, E))
+ return false;
+ if (!this->emitInitElem(ElemT, I, E))
+ return false;
+ }
+ return true;
+ }
+
+ // Arrays: primitive elements are filled directly; composite elements
+ // require recursion into each sub-aggregate.
+ if (const auto *AT = DestType->getAsArrayTypeUnsafe()) {
+ const auto *CAT = cast<ConstantArrayType>(AT);
+ QualType ArrElemType = CAT->getElementType();
+ unsigned ArrSize = CAT->getZExtSize();
+
+ if (OptPrimType ElemT = classify(ArrElemType)) {
+ for (unsigned I = 0; I != ArrSize; ++I) {
+ if (!this->emitGetLocal(SrcT, SrcOffset, E))
+ return false;
+ if (!this->emitPrimCast(SrcT, *ElemT, ArrElemType, E))
+ return false;
+ if (!this->emitInitElem(*ElemT, I, E))
+ return false;
+ }
+ } else {
+ for (unsigned I = 0; I != ArrSize; ++I) {
+ if (!this->emitConstUint32(I, E))
+ return false;
+ if (!this->emitArrayElemPtrUint32(E))
+ return false;
+ if (!emitHLSLAggregateSplat(SrcT, SrcOffset, ArrElemType, E))
+ return false;
+ if (!this->emitFinishInitPop(E))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Records: fill base classes first, then named fields in declaration
+ // order.
+ if (DestType->isRecordType()) {
+ const Record *R = getRecord(DestType);
+ if (!R)
+ return false;
+
+ if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(R->getDecl())) {
+ for (const CXXBaseSpecifier &BS : CXXRD->bases()) {
+ const Record::Base *B = R->getBase(BS.getType());
+ assert(B);
+ if (!this->emitGetPtrBase(B->Offset, E))
+ return false;
+ if (!emitHLSLAggregateSplat(SrcT, SrcOffset, BS.getType(), E))
+ return false;
+ if (!this->emitFinishInitPop(E))
+ return false;
+ }
+ }
+
+ for (const Record::Field &F : R->fields()) {
+ if (F.isUnnamedBitField())
+ continue;
+
+ QualType FieldType = F.Decl->getType();
+ if (OptPrimType FieldT = classify(FieldType)) {
+ if (!this->emitGetLocal(SrcT, SrcOffset, E))
+ return false;
+ if (!this->emitPrimCast(SrcT, *FieldT, FieldType, E))
+ return false;
+ if (F.isBitField()) {
+ if (!this->emitInitBitField(*FieldT, F.Offset, F.bitWidth(), E))
+ return false;
+ } else {
+ if (!this->emitInitField(*FieldT, F.Offset, E))
+ return false;
+ }
+ } else {
+ if (!this->emitGetPtrField(F.Offset, E))
+ return false;
+ if (!emitHLSLAggregateSplat(SrcT, SrcOffset, FieldType, E))
+ return false;
+ if (!this->emitPopPtr(E))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ return false;
+}
+
+/// Return the total number of scalar elements in a type. This is used
+/// to cap how many source elements are extracted during an elementwise cast,
+/// so we never flatten more than the destination can hold.
+template <class Emitter>
+unsigned Compiler<Emitter>::countHLSLFlatElements(QualType Ty) {
+ // Vector and matrix types are treated as flat sequences of elements.
+ if (const auto *VT = Ty->getAs<VectorType>())
+ return VT->getNumElements();
+ if (const auto *MT = Ty->getAs<ConstantMatrixType>())
+ return MT->getNumElementsFlattened();
+ // Arrays: total count is array size * scalar elements per element.
+ if (const auto *AT = Ty->getAsArrayTypeUnsafe()) {
+ const auto *CAT = cast<ConstantArrayType>(AT);
+ return CAT->getZExtSize() * countHLSLFlatElements(CAT->getElementType());
+ }
+ // Records: sum scalar element counts of base classes and named fields.
+ if (Ty->isRecordType()) {
+ const Record *R = getRecord(Ty);
+ if (!R)
+ return 0;
+ unsigned Count = 0;
+ if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(R->getDecl())) {
+ for (const CXXBaseSpecifier &BS : CXXRD->bases())
+ Count += countHLSLFlatElements(BS.getType());
+ }
+ for (const Record::Field &F : R->fields()) {
+ if (F.isUnnamedBitField())
+ continue;
+ Count += countHLSLFlatElements(F.Decl->getType());
+ }
+ return Count;
+ }
+ // Scalar primitive types contribute one element.
+ if (classify(Ty))
+ return 1;
+ return 0;
+}
+
+/// Walk a source aggregate and extract every scalar element into its own local
+/// variable. The results are appended to \p Elements in declaration order,
+/// stopping once \p MaxElements have been collected. A pointer to the
+/// source aggregate must be stored in the local at \p SrcOffset.
+template <class Emitter>
+bool Compiler<Emitter>::emitHLSLFlattenAggregate(
+ QualType SrcType, unsigned SrcOffset,
+ SmallVectorImpl<HLSLFlatElement> &Elements, unsigned MaxElements,
+ const Expr *E) {
+
+ // Save a scalar value from the stack into a new local and record it.
+ auto saveToLocal = [&](PrimType T) -> bool {
+ unsigned Offset = allocateLocalPrimitive(E, T, /*IsConst=*/true);
+ if (!this->emitSetLocal(T, Offset, E))
+ return false;
+ Elements.push_back({Offset, T});
+ return true;
+ };
+
+ // Save a pointer from the stack into a new local for later use.
+ auto savePtrToLocal = [&]() -> UnsignedOrNone {
+ unsigned Offset = allocateLocalPrimitive(E, PT_Ptr, /*IsConst=*/true);
+ if (!this->emitSetLocal(PT_Ptr, Offset, E))
+ return std::nullopt;
+ return Offset;
+ };
+
+ // Vectors and matrices are flat sequences of elements.
+ unsigned NumElems = 0;
+ QualType ElemType;
+ if (const auto *VT = SrcType->getAs<VectorType>()) {
+ NumElems = VT->getNumElements();
+ ElemType = VT->getElementType();
+ } else if (const auto *MT = SrcType->getAs<ConstantMatrixType>()) {
+ NumElems = MT->getNumElementsFlattened();
+ ElemType = MT->getElementType();
+ }
+ if (NumElems > 0) {
+ PrimType ElemT = classifyPrim(ElemType);
+ for (unsigned I = 0; I != NumElems && Elements.size() < MaxElements; ++I) {
+ if (!this->emitGetLocal(PT_Ptr, SrcOffset, E))
+ return false;
+ if (!this->emitArrayElemPop(ElemT, I, E))
+ return false;
+ if (!saveToLocal(ElemT))
+ return false;
+ }
+ return true;
+ }
+
+ // Arrays: primitive elements are extracted directly; composite elements
+ // require recursion into each sub-aggregate.
+ if (const auto *AT = SrcType->getAsArrayTypeUnsafe()) {
+ const auto *CAT = cast<ConstantArrayType>(AT);
+ QualType ArrElemType = CAT->getElementType();
+ unsigned ArrSize = CAT->getZExtSize();
+
+ if (OptPrimType ElemT = classify(ArrElemType)) {
+ for (unsigned I = 0; I != ArrSize && Elements.size() < MaxElements; ++I) {
+ if (!this->emitGetLocal(PT_Ptr, SrcOffset, E))
+ return false;
+ if (!this->emitArrayElemPop(*ElemT, I, E))
+ return false;
+ if (!saveToLocal(*ElemT))
+ return false;
+ }
+ } else {
+ for (unsigned I = 0; I != ArrSize && Elements.size() < MaxElements; ++I) {
+ if (!this->emitGetLocal(PT_Ptr, SrcOffset, E))
+ return false;
+ if (!this->emitConstUint32(I, E))
+ return false;
+ if (!this->emitArrayElemPtrPopUint32(E))
+ return false;
+ UnsignedOrNone ElemPtrOffset = savePtrToLocal();
+ if (!ElemPtrOffset)
+ return false;
+ if (!emitHLSLFlattenAggregate(ArrElemType, *ElemPtrOffset, Elements,
+ MaxElements, E))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Records: base classes come first, then named fields in declaration
+ // order.
+ if (SrcType->isRecordType()) {
+ const Record *R = getRecord(SrcType);
+ if (!R)
+ return false;
+
+ if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(R->getDecl())) {
+ for (const CXXBaseSpecifier &BS : CXXRD->bases()) {
+ if (Elements.size() >= MaxElements)
+ break;
+ const Record::Base *B = R->getBase(BS.getType());
+ assert(B);
+ if (!this->emitGetLocal(PT_Ptr, SrcOffset, E))
+ return false;
+ if (!this->emitGetPtrBasePop(B->Offset, /*NullOK=*/false, E))
+ return false;
+ UnsignedOrNone BasePtrOffset = savePtrToLocal();
+ if (!BasePtrOffset)
+ return false;
+ if (!emitHLSLFlattenAggregate(BS.getType(), *BasePtrOffset, Elements,
+ MaxElements, E))
+ return false;
+ }
+ }
+
+ for (const Record::Field &F : R->fields()) {
+ if (Elements.size() >= MaxElements)
+ break;
+ if (F.isUnnamedBitField())
+ continue;
+
+ QualType FieldType = F.Decl->getType();
+ if (!this->emitGetLocal(PT_Ptr, SrcOffset, E))
+ return false;
+ if (!this->emitGetPtrFieldPop(F.Offset, E))
+ return false;
+
+ if (OptPrimType FieldT = classify(FieldType)) {
+ if (!this->emitLoadPop(*FieldT, E))
+ return false;
+ if (!saveToLocal(*FieldT))
+ return false;
+ } else {
+ UnsignedOrNone FieldPtrOffset = savePtrToLocal();
+ if (!FieldPtrOffset)
+ return false;
+ if (!emitHLSLFlattenAggregate(FieldType, *FieldPtrOffset, Elements,
+ MaxElements, E))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ return false;
+}
+
+/// Populate an HLSL aggregate from a flat list of previously extracted source
+/// elements, casting each to the corresponding destination element type.
+/// \p ElemIdx tracks the current position in \p Elements and is advanced as
+/// elements are consumed. A pointer to the destination must be on top of the
+/// interpreter stack.
+template <class Emitter>
+bool Compiler<Emitter>::emitHLSLConstructAggregate(
+ QualType DestType, ArrayRef<HLSLFlatElement> Elements, unsigned &ElemIdx,
+ const Expr *E) {
+
+ // Consume the next source element, cast it, and leave it on the stack.
+ auto loadAndCast = [&](PrimType DestT, QualType DestQT) -> bool {
+ const auto &Src = Elements[ElemIdx++];
+ if (!this->emitGetLocal(Src.Type, Src.LocalOffset, E))
+ return false;
+ return this->emitPrimCast(Src.Type, DestT, DestQT, E);
+ };
+
+ // Vectors and matrices are flat sequences of elements.
+ unsigned NumElems = 0;
+ QualType ElemType;
+ if (const auto *VT = DestType->getAs<VectorType>()) {
+ NumElems = VT->getNumElements();
+ ElemType = VT->getElementType();
+ } else if (const auto *MT = DestType->getAs<ConstantMatrixType>()) {
+ NumElems = MT->getNumElementsFlattened();
+ ElemType = MT->getElementType();
+ }
+ if (NumElems > 0) {
+ PrimType DestElemT = classifyPrim(ElemType);
+ for (unsigned I = 0; I != NumElems; ++I) {
+ if (!loadAndCast(DestElemT, ElemType))
+ return false;
+ if (!this->emitInitElem(DestElemT, I, E))
+ return false;
+ }
+ return true;
+ }
+
+ // Arrays: primitive elements are filled directly; composite elements
+ // require recursion into each sub-aggregate.
+ if (const auto *AT = DestType->getAsArrayTypeUnsafe()) {
+ const auto *CAT = cast<ConstantArrayType>(AT);
+ QualType ArrElemType = CAT->getElementType();
+ unsigned ArrSize = CAT->getZExtSize();
+
+ if (OptPrimType ElemT = classify(ArrElemType)) {
+ for (unsigned I = 0; I != ArrSize; ++I) {
+ if (!loadAndCast(*ElemT, ArrElemType))
+ return false;
+ if (!this->emitInitElem(*ElemT, I, E))
+ return false;
+ }
+ } else {
+ for (unsigned I = 0; I != ArrSize; ++I) {
+ if (!this->emitConstUint32(I, E))
+ return false;
+ if (!this->emitArrayElemPtrUint32(E))
+ return false;
+ if (!emitHLSLConstructAggregate(ArrElemType, Elements, ElemIdx, E))
+ return false;
+ if (!this->emitFinishInitPop(E))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Records: base classes come first, then named fields in declaration
+ // order.
+ if (DestType->isRecordType()) {
+ const Record *R = getRecord(DestType);
+ if (!R)
+ return false;
+
+ if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(R->getDecl())) {
+ for (const CXXBaseSpecifier &BS : CXXRD->bases()) {
+ const Record::Base *B = R->getBase(BS.getType());
+ assert(B);
+ if (!this->emitGetPtrBase(B->Offset, E))
+ return false;
+ if (!emitHLSLConstructAggregate(BS.getType(), Elements, ElemIdx, E))
+ return false;
+ if (!this->emitFinishInitPop(E))
+ return false;
+ }
+ }
+
+ for (const Record::Field &F : R->fields()) {
+ if (F.isUnnamedBitField())
+ continue;
+
+ QualType FieldType = F.Decl->getType();
+ if (OptPrimType FieldT = classify(FieldType)) {
+ if (!loadAndCast(*FieldT, FieldType))
+ return false;
+ if (F.isBitField()) {
+ if (!this->emitInitBitField(*FieldT, F.Offset, F.bitWidth(), E))
+ return false;
+ } else {
+ if (!this->emitInitField(*FieldT, F.Offset, E))
+ return false;
+ }
+ } else {
+ if (!this->emitGetPtrField(F.Offset, E))
+ return false;
+ if (!emitHLSLConstructAggregate(FieldType, Elements, ElemIdx, E))
+ return false;
+ if (!this->emitPopPtr(E))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ return false;
+}
+
namespace clang {
namespace interp {
diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h
index f867fcc9fcbaa..1ca6f787adc63 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -423,6 +423,29 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
const QualType DerivedType);
bool emitLambdaStaticInvokerBody(const CXXMethodDecl *MD);
bool emitBuiltinBitCast(const CastExpr *E);
+
+ bool emitHLSLAggregateSplat(PrimType SrcT, unsigned SrcOffset,
+ QualType DestType, const Expr *E);
+
+ /// A scalar element extracted during HLSL aggregate flattening.
+ struct HLSLFlatElement {
+ unsigned LocalOffset;
+ PrimType Type;
+ };
+ unsigned countHLSLFlatElements(QualType Ty);
+ bool emitHLSLFlattenAggregate(QualType SrcType, unsigned SrcPtrOffset,
+ SmallVectorImpl<HLSLFlatElement> &Elements,
+ unsigned MaxElements, const Expr *E);
+ bool emitHLSLConstructAggregate(QualType DestType,
+ ArrayRef<HLSLFlatElement> Elements,
+ unsigned &ElemIdx, const Expr *E);
+ bool emitHLSLConstructAggregate(QualType DestType,
+ ArrayRef<HLSLFlatElement> Elements,
+ const Expr *E) {
+ unsigned ElemIdx = 0;
+ return emitHLSLConstructAggregate(DestType, Elements, ElemIdx, E);
+ }
+
bool compileConstructor(const CXXConstructorDecl *Ctor);
bool compileDestructor(const CXXDestructorDecl *Dtor);
bool compileUnionAssignmentOperator(const CXXMethodDecl *MD);
diff --git a/clang/test/SemaHLSL/BuiltIns/asfloat-constexpr.hlsl b/clang/test/SemaHLSL/BuiltIns/asfloat-constexpr.hlsl
index fb90d927a9a43..e342e15855c6b 100644
--- a/clang/test/SemaHLSL/BuiltIns/asfloat-constexpr.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/asfloat-constexpr.hlsl
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -verify
+// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library -fexperimental-new-constant-interpreter %s -verify
// expected-no-diagnostics
diff --git a/clang/test/SemaHLSL/BuiltIns/asuint-constexpr.hlsl b/clang/test/SemaHLSL/BuiltIns/asuint-constexpr.hlsl
index be6afda3be663..a480e41d81794 100644
--- a/clang/test/SemaHLSL/BuiltIns/asuint-constexpr.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/asuint-constexpr.hlsl
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -verify
+// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library -fexperimental-new-constant-interpreter %s -verify
// expected-no-diagnostics
diff --git a/clang/test/SemaHLSL/Language/InitIncompleteArrays.hlsl b/clang/test/SemaHLSL/Language/InitIncompleteArrays.hlsl
index 98eba8e6d0699..5558575afb28d 100644
--- a/clang/test/SemaHLSL/Language/InitIncompleteArrays.hlsl
+++ b/clang/test/SemaHLSL/Language/InitIncompleteArrays.hlsl
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -finclude-default-header -verify -Wdouble-promotion %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -finclude-default-header -fexperimental-new-constant-interpreter -verify -Wdouble-promotion %s
// Some helpers!
template <typename T, typename U>
diff --git a/clang/test/SemaHLSL/Types/AggregateSplatConstantExpr.hlsl b/clang/test/SemaHLSL/Types/AggregateSplatConstantExpr.hlsl
index 630acd8297642..b8eab5c00e581 100644
--- a/clang/test/SemaHLSL/Types/AggregateSplatConstantExpr.hlsl
+++ b/clang/test/SemaHLSL/Types/AggregateSplatConstantExpr.hlsl
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -fnative-half-type -fnative-int16-type -std=hlsl202x -verify %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -fnative-half-type -fnative-int16-type -std=hlsl202x -fexperimental-new-constant-interpreter -verify %s
// expected-no-diagnostics
diff --git a/clang/test/SemaHLSL/Types/Arithmetic/half_size.hlsl b/clang/test/SemaHLSL/Types/Arithmetic/half_size.hlsl
index 22e18769a2fe4..2bdb7cd03de26 100644
--- a/clang/test/SemaHLSL/Types/Arithmetic/half_size.hlsl
+++ b/clang/test/SemaHLSL/Types/Arithmetic/half_size.hlsl
@@ -2,6 +2,10 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.4-library -verify -fnative-half-type -fnative-int16-type %s
// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -verify %s
// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -verify -fnative-half-type -fnative-int16-type %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.4-library -fexperimental-new-constant-interpreter -verify %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.4-library -fexperimental-new-constant-interpreter -verify -fnative-half-type -fnative-int16-type %s
+// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -fexperimental-new-constant-interpreter -verify %s
+// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -fexperimental-new-constant-interpreter -verify -fnative-half-type -fnative-int16-type %s
// expected-no-diagnostics
#ifdef __HLSL_ENABLE_16_BIT
diff --git a/clang/test/SemaHLSL/Types/Arithmetic/literal_suffixes.hlsl b/clang/test/SemaHLSL/Types/Arithmetic/literal_suffixes.hlsl
index 00a1856fd9c17..e76eb97b2570b 100644
--- a/clang/test/SemaHLSL/Types/Arithmetic/literal_suffixes.hlsl
+++ b/clang/test/SemaHLSL/Types/Arithmetic/literal_suffixes.hlsl
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-library -fnative-half-type -verify %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-library -fnative-half-type -fexperimental-new-constant-interpreter -verify %s
void literal_assignments() {
half h;
diff --git a/clang/test/SemaHLSL/Types/Arithmetic/literal_suffixes_202x.hlsl b/clang/test/SemaHLSL/Types/Arithmetic/literal_suffixes_202x.hlsl
index 95f08463f55ef..9dd523494b60e 100644
--- a/clang/test/SemaHLSL/Types/Arithmetic/literal_suffixes_202x.hlsl
+++ b/clang/test/SemaHLSL/Types/Arithmetic/literal_suffixes_202x.hlsl
@@ -2,6 +2,10 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.4-library -finclude-default-header -verify -fnative-half-type %s
// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -finclude-default-header -verify %s
// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -finclude-default-header -verify -fnative-half-type %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.4-library -finclude-default-header -fexperimental-new-constant-interpreter -verify %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.4-library -finclude-default-header -fexperimental-new-constant-interpreter -verify -fnative-half-type %s
+// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -finclude-default-header -fexperimental-new-constant-interpreter -verify %s
+// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -finclude-default-header -fexperimental-new-constant-interpreter -verify -fnative-half-type %s
// This test is adapted from the test in DXC:
// tools/clang/test/SemaHLSL/v202x/conforming-literals/valid-literals.hlsl
diff --git a/clang/test/SemaHLSL/Types/Arithmetic/literal_suffixes_no_16bit.hlsl b/clang/test/SemaHLSL/Types/Arithmetic/literal_suffixes_no_16bit.hlsl
index c378817cc0b3b..0063bb091fd33 100644
--- a/clang/test/SemaHLSL/Types/Arithmetic/literal_suffixes_no_16bit.hlsl
+++ b/clang/test/SemaHLSL/Types/Arithmetic/literal_suffixes_no_16bit.hlsl
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-library -verify %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-library -fexperimental-new-constant-interpreter -verify %s
void literal_assignments() {
half h;
diff --git a/clang/test/SemaHLSL/Types/BuiltinVector/BooleanVectorConstantExpr.hlsl b/clang/test/SemaHLSL/Types/BuiltinVector/BooleanVectorConstantExpr.hlsl
index 1d368befc839a..e071c6115f64b 100644
--- a/clang/test/SemaHLSL/Types/BuiltinVector/BooleanVectorConstantExpr.hlsl
+++ b/clang/test/SemaHLSL/Types/BuiltinVector/BooleanVectorConstantExpr.hlsl
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -std=hlsl202x -verify %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -std=hlsl202x -fexperimental-new-constant-interpreter -verify %s
// expected-no-diagnostics
diff --git a/clang/test/SemaHLSL/Types/BuiltinVector/TruncationConstantExpr.hlsl b/clang/test/SemaHLSL/Types/BuiltinVector/TruncationConstantExpr.hlsl
index 918daa03d8032..272c0a26ccf0c 100644
--- a/clang/test/SemaHLSL/Types/BuiltinVector/TruncationConstantExpr.hlsl
+++ b/clang/test/SemaHLSL/Types/BuiltinVector/TruncationConstantExpr.hlsl
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -std=hlsl202x -verify %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -std=hlsl202x -fexperimental-new-constant-interpreter -verify %s
// expected-no-diagnostics
diff --git a/clang/test/SemaHLSL/Types/ElementwiseCastConstantExpr.hlsl b/clang/test/SemaHLSL/Types/ElementwiseCastConstantExpr.hlsl
index c9963c36ce23a..2b9c6e69e9c24 100644
--- a/clang/test/SemaHLSL/Types/ElementwiseCastConstantExpr.hlsl
+++ b/clang/test/SemaHLSL/Types/ElementwiseCastConstantExpr.hlsl
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -fnative-half-type -fnative-int16-type -std=hlsl202x -verify %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -fnative-half-type -fnative-int16-type -std=hlsl202x -fexperimental-new-constant-interpreter -verify %s
// expected-no-diagnostics
diff --git a/clang/test/SemaHLSL/Types/InitListConstantExpr.hlsl b/clang/test/SemaHLSL/Types/InitListConstantExpr.hlsl
index c2797f5c8d94e..27e82fe41d2f8 100644
--- a/clang/test/SemaHLSL/Types/InitListConstantExpr.hlsl
+++ b/clang/test/SemaHLSL/Types/InitListConstantExpr.hlsl
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -fnative-half-type -fnative-int16-type -std=hlsl202x -verify %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -fnative-half-type -fnative-int16-type -std=hlsl202x -fexperimental-new-constant-interpreter -verify %s
// expected-no-diagnostics
// XFAIL because of this issue: https://github.com/llvm/llvm-project/issues/188577
diff --git a/clang/test/SemaHLSL/Types/Traits/IsIntangibleType.hlsl b/clang/test/SemaHLSL/Types/Traits/IsIntangibleType.hlsl
index 1223a131af35c..cb1a7cbd42c24 100644
--- a/clang/test/SemaHLSL/Types/Traits/IsIntangibleType.hlsl
+++ b/clang/test/SemaHLSL/Types/Traits/IsIntangibleType.hlsl
@@ -1,5 +1,7 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -verify %s
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -fnative-half-type -verify %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -fexperimental-new-constant-interpreter -verify %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -fnative-half-type -fexperimental-new-constant-interpreter -verify %s
// expected-no-diagnostics
_Static_assert(__builtin_hlsl_is_intangible(__hlsl_resource_t), "");
diff --git a/clang/test/SemaHLSL/Types/Traits/IsIntangibleTypeErrors.hlsl b/clang/test/SemaHLSL/Types/Traits/IsIntangibleTypeErrors.hlsl
index 33614e87640da..2b8006b4f786d 100644
--- a/clang/test/SemaHLSL/Types/Traits/IsIntangibleTypeErrors.hlsl
+++ b/clang/test/SemaHLSL/Types/Traits/IsIntangibleTypeErrors.hlsl
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -verify %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -fexperimental-new-constant-interpreter -verify %s
struct Undefined; // expected-note {{forward declaration of 'Undefined'}}
_Static_assert(!__builtin_hlsl_is_intangible(Undefined), ""); // expected-error{{incomplete type 'Undefined' used in type trait expression}}
diff --git a/clang/test/SemaHLSL/Types/Traits/IsTypedResourceElementCompatible.hlsl b/clang/test/SemaHLSL/Types/Traits/IsTypedResourceElementCompatible.hlsl
index 61aae9d178aa7..dcc6ca2163a79 100644
--- a/clang/test/SemaHLSL/Types/Traits/IsTypedResourceElementCompatible.hlsl
+++ b/clang/test/SemaHLSL/Types/Traits/IsTypedResourceElementCompatible.hlsl
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -fnative-half-type -verify %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -fnative-half-type -fexperimental-new-constant-interpreter -verify %s
// expected-no-diagnostics
diff --git a/clang/test/SemaHLSL/Types/Traits/ScalarizedLayoutCompatible.hlsl b/clang/test/SemaHLSL/Types/Traits/ScalarizedLayoutCompatible.hlsl
index db46a8e141495..932d2a9ad0dd7 100644
--- a/clang/test/SemaHLSL/Types/Traits/ScalarizedLayoutCompatible.hlsl
+++ b/clang/test/SemaHLSL/Types/Traits/ScalarizedLayoutCompatible.hlsl
@@ -1,5 +1,7 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -verify %s
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -fnative-half-type -verify %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -fexperimental-new-constant-interpreter -verify %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -fnative-half-type -fexperimental-new-constant-interpreter -verify %s
// expected-no-diagnostics
// Case 1: How many ways can I come up with to represent three float values?
diff --git a/clang/test/SemaHLSL/Types/Traits/ScalarizedLayoutCompatibleErrors.hlsl b/clang/test/SemaHLSL/Types/Traits/ScalarizedLayoutCompatibleErrors.hlsl
index 85c6580d052de..602903d6f3963 100644
--- a/clang/test/SemaHLSL/Types/Traits/ScalarizedLayoutCompatibleErrors.hlsl
+++ b/clang/test/SemaHLSL/Types/Traits/ScalarizedLayoutCompatibleErrors.hlsl
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -verify %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -fexperimental-new-constant-interpreter -verify %s
// Some things that don't work!
diff --git a/clang/test/SemaHLSL/Types/typedefs.hlsl b/clang/test/SemaHLSL/Types/typedefs.hlsl
index c9c8ff2fc02de..36d65324f0d8e 100644
--- a/clang/test/SemaHLSL/Types/typedefs.hlsl
+++ b/clang/test/SemaHLSL/Types/typedefs.hlsl
@@ -1,5 +1,7 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.4-library -finclude-default-header -verify -fnative-half-type -fnative-int16-type %s
// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -finclude-default-header -verify -fnative-half-type -fnative-int16-type %s
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.4-library -finclude-default-header -fexperimental-new-constant-interpreter -verify -fnative-half-type -fnative-int16-type %s
+// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -finclude-default-header -fexperimental-new-constant-interpreter -verify -fnative-half-type -fnative-int16-type %s
// expected-no-diagnostics
#define SizeCheck(Ty, SizeInBits) \
diff --git a/clang/test/SemaHLSL/group_shared.hlsl b/clang/test/SemaHLSL/group_shared.hlsl
index 4df6cf73c4fc0..65df0a922042f 100644
--- a/clang/test/SemaHLSL/group_shared.hlsl
+++ b/clang/test/SemaHLSL/group_shared.hlsl
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -fexperimental-new-constant-interpreter -verify
groupshared float a[10];
>From 9ffe53face1fe6bdf5e9d16bbd32f0e2f2e43b0e Mon Sep 17 00:00:00 2001
From: "Deric C." <cheung.deric at gmail.com>
Date: Mon, 13 Apr 2026 15:02:12 -0700
Subject: [PATCH 2/6] Define DestT within if-condition itself
Co-authored-by: Timm Baeder <tbaeder at redhat.com>
---
clang/lib/AST/ByteCode/Compiler.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 95dd64fdbdeda..2bfeadcd4bc42 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -892,7 +892,7 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *E) {
QualType DestType = E->getType();
const OptPrimType DestT = classify(DestType);
- if (DestT) {
+ if (OptPrimType DestT = classify(DestType)) {
// When the destination is a scalar, we only need the first scalar
// element of the source.
unsigned SrcPtrOffset =
>From fe939ee3d343fa2e69ad20ceb1349999ab60977e Mon Sep 17 00:00:00 2001
From: "Deric C." <cheung.deric at gmail.com>
Date: Mon, 13 Apr 2026 15:02:42 -0700
Subject: [PATCH 3/6] Use canClassify instead of classify within if-condition
Co-authored-by: Timm Baeder <tbaeder at redhat.com>
---
clang/lib/AST/ByteCode/Compiler.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 2bfeadcd4bc42..6ae03b1eb768f 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -8074,7 +8074,7 @@ unsigned Compiler<Emitter>::countHLSLFlatElements(QualType Ty) {
return Count;
}
// Scalar primitive types contribute one element.
- if (classify(Ty))
+ if (canClassify(Ty))
return 1;
return 0;
}
>From c57a12cf798a2c49377356f3815d4e97f8f77949 Mon Sep 17 00:00:00 2001
From: Deric Cheung <cheung.deric at gmail.com>
Date: Fri, 17 Apr 2026 12:45:44 -0700
Subject: [PATCH 4/6] Remove unused DestT variable
---
clang/lib/AST/ByteCode/Compiler.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 779143863814c..2affbb93ce57d 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -893,7 +893,6 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *E) {
QualType SrcType = SubExpr->getType();
QualType DestType = E->getType();
- const OptPrimType DestT = classify(DestType);
if (OptPrimType DestT = classify(DestType)) {
// When the destination is a scalar, we only need the first scalar
// element of the source.
>From 53b3d9efcbf1edbcfbf4b33b19f19cc710463c55 Mon Sep 17 00:00:00 2001
From: Deric Cheung <cheung.deric at gmail.com>
Date: Fri, 17 Apr 2026 12:45:58 -0700
Subject: [PATCH 5/6] Reserve MaxElems elements before flattening
---
clang/lib/AST/ByteCode/Compiler.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 2affbb93ce57d..4e854e7e3da7c 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -934,6 +934,7 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *E) {
unsigned MaxElems = countHLSLFlatElements(DestType);
SmallVector<HLSLFlatElement, 16> Elements;
+ Elements.reserve(MaxElems);
if (!emitHLSLFlattenAggregate(SrcType, SrcOffset, Elements, MaxElems, E))
return false;
>From a37b968508ed2bdf11a74f8b5d8915d4976a7d32 Mon Sep 17 00:00:00 2001
From: Deric Cheung <cheung.deric at gmail.com>
Date: Fri, 17 Apr 2026 15:37:33 -0700
Subject: [PATCH 6/6] Add assertion on element count. Rename MaxElems to
ElemCount
---
clang/lib/AST/ByteCode/Compiler.cpp | 43 ++++++++++++++++-------------
1 file changed, 24 insertions(+), 19 deletions(-)
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index af74c549af76e..2c0a557d82323 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -931,13 +931,18 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *E) {
return false;
// Only flatten as many source elements as the destination requires.
- unsigned MaxElems = countHLSLFlatElements(DestType);
+ unsigned ElemCount = countHLSLFlatElements(DestType);
SmallVector<HLSLFlatElement, 16> Elements;
- Elements.reserve(MaxElems);
- if (!emitHLSLFlattenAggregate(SrcType, SrcOffset, Elements, MaxElems, E))
+ Elements.reserve(ElemCount);
+ if (!emitHLSLFlattenAggregate(SrcType, SrcOffset, Elements, ElemCount, E))
return false;
+ // Sema is expected to reject an elementwise cast whose source has fewer
+ // scalar elements than the destination.
+ assert(Elements.size() == ElemCount &&
+ "Source type has fewer scalar elements than the destination type");
+
return emitHLSLConstructAggregate(DestType, Elements, E);
}
@@ -6105,28 +6110,28 @@ bool Compiler<Emitter>::visitWhileStmt(const WhileStmt *S) {
return false;
}
- if (!this->visitBool(Cond))
- return false;
+ if (!this->visitBool(Cond))
+ return false;
- if (!this->maybeEmitDeferredVarInit(S->getConditionVariable()))
- return false;
+ if (!this->maybeEmitDeferredVarInit(S->getConditionVariable()))
+ return false;
- if (!this->jumpFalse(EndLabel, S))
- return false;
+ if (!this->jumpFalse(EndLabel, S))
+ return false;
- if (!this->visitStmt(Body))
- return false;
+ if (!this->visitStmt(Body))
+ return false;
- if (!CondScope.destroyLocals())
- return false;
- // } End of loop body.
+ if (!CondScope.destroyLocals())
+ return false;
+ // } End of loop body.
- if (!this->jump(CondLabel, S))
- return false;
- this->fallthrough(EndLabel);
- this->emitLabel(EndLabel);
+ if (!this->jump(CondLabel, S))
+ return false;
+ this->fallthrough(EndLabel);
+ this->emitLabel(EndLabel);
- return CondScope.destroyLocals() && WholeLoopScope.destroyLocals();
+ return CondScope.destroyLocals() && WholeLoopScope.destroyLocals();
}
template <class Emitter> bool Compiler<Emitter>::visitDoStmt(const DoStmt *S) {
More information about the cfe-commits
mailing list