[clang] [NFC][clang][bytecode][HLSL] Refactor HLSL helper functions to use a common visitor class (PR #194393)
Deric C. via cfe-commits
cfe-commits at lists.llvm.org
Mon Apr 27 07:56:40 PDT 2026
https://github.com/Icohedron created https://github.com/llvm/llvm-project/pull/194393
Addressing the code repetition concerns pointed out by @shafik in https://github.com/llvm/llvm-project/pull/189126#discussion_r3141480667
This PR refactors `emitHLSLAggregateSplat`, `countHLSLFlatElements`, `emitHLSLFlattenAggregate`, and `emitHLSLConstructAggregate` to reduce code repetition by creating a common `HLSLElementStoreVisitor` class for visiting HLSL aggregate types.
Assisted-by: Claude Opus 4.6
>From c58afed2ee405c497f9b540fd174f2fd254d81a4 Mon Sep 17 00:00:00 2001
From: Deric Cheung <cheung.deric at gmail.com>
Date: Sun, 26 Apr 2026 23:26:04 +0000
Subject: [PATCH 1/8] Add HLSLAggregateVisitor CRTP base class
Introduce a reusable CRTP base class HLSLAggregateVisitor that walks the
scalar elements of HLSL aggregate types (vectors, matrices, arrays,
records). Derived classes override visit* hooks to customize behavior at
each node.
This is preparatory infrastructure for refactoring the HLSL cast
helpers to use this visitor pattern, eliminating duplicated traversal
logic.
Assisted-by: Claude Opus 4.6
---
clang/lib/AST/ByteCode/Compiler.h | 105 ++++++++++++++++++++++++++++++
1 file changed, 105 insertions(+)
diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h
index a80f19e07e70a..a890c71f6069c 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -41,6 +41,9 @@ template <class Emitter> class LabelScope;
template <class Emitter> class SwitchScope;
template <class Emitter> class StmtExprScope;
template <class Emitter> class LocOverrideScope;
+template <class Emitter> class HLSLElementStoreVisitor;
+template <class Emitter> class HLSLFlatElementCounter;
+template <class Emitter> class HLSLElementFlattenVisitor;
template <class Emitter> class Compiler;
struct InitLink {
@@ -353,6 +356,9 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
friend class SwitchScope<Emitter>;
friend class StmtExprScope<Emitter>;
friend class LocOverrideScope<Emitter>;
+ friend class HLSLElementStoreVisitor<Emitter>;
+ friend class HLSLFlatElementCounter<Emitter>;
+ friend class HLSLElementFlattenVisitor<Emitter>;
/// Emits a zero initializer.
bool visitZeroInitializer(PrimType T, QualType QT, const Expr *E);
@@ -422,6 +428,105 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
bool emitLambdaStaticInvokerBody(const CXXMethodDecl *MD);
bool emitBuiltinBitCast(const CastExpr *E);
+ /// Base class for visiting the scalar elements of an HLSL aggregate type
+ /// (vectors, matrices, arrays, records). Derived classes override the
+ /// \c visit* methods to customise behaviour at each node. The walker
+ /// calls:
+ ///
+ /// - \c visitScalarElem for each scalar element of a vector, matrix,
+ /// or primitive array.
+ /// - \c visitArrayComposite for each composite element of an array.
+ /// - \c visitBase for each base class of a record.
+ /// - \c visitField for each scalar field of a record.
+ /// - \c visitFieldComposite for each composite field of a record.
+ ///
+ /// Composite visitors receive the sub-type's \c QualType and must
+ /// call \c visit(SubType) themselves to recurse.
+ template <typename Derived> class HLSLAggregateVisitor {
+ public:
+ explicit HLSLAggregateVisitor(Compiler<Emitter> &C) : C(C) {}
+
+ bool visit(QualType Ty) {
+ // Vectors and matrices are flat sequences of scalar elements.
+ unsigned NumElems = 0;
+ QualType ElemType;
+ if (const auto *VT = Ty->template getAs<VectorType>()) {
+ NumElems = VT->getNumElements();
+ ElemType = VT->getElementType();
+ } else if (const auto *MT = Ty->template getAs<ConstantMatrixType>()) {
+ NumElems = MT->getNumElementsFlattened();
+ ElemType = MT->getElementType();
+ }
+ if (NumElems > 0) {
+ PrimType ElemT = C.classifyPrim(ElemType);
+ for (unsigned I = 0; I != NumElems; ++I) {
+ if (!derived().visitScalarElem(ElemType, ElemT, I))
+ return false;
+ }
+ return true;
+ }
+
+ // Arrays.
+ if (const auto *AT = Ty->getAsArrayTypeUnsafe()) {
+ const auto *CAT = cast<ConstantArrayType>(AT);
+ QualType ArrElemType = CAT->getElementType();
+ unsigned ArrSize = CAT->getZExtSize();
+
+ if (OptPrimType ElemT = C.classify(ArrElemType)) {
+ for (unsigned I = 0; I != ArrSize; ++I) {
+ if (!derived().visitScalarElem(ArrElemType, *ElemT, I))
+ return false;
+ }
+ } else {
+ for (unsigned I = 0; I != ArrSize; ++I) {
+ if (!derived().visitArrayComposite(ArrElemType, I))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Records.
+ if (Ty->isRecordType()) {
+ const Record *R = C.getRecord(Ty);
+ 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 (!derived().visitBase(BS.getType(), B))
+ return false;
+ }
+ }
+
+ for (const Record::Field &F : R->fields()) {
+ if (F.isUnnamedBitField())
+ continue;
+ QualType FieldType = F.Decl->getType();
+ if (OptPrimType FieldT = C.classify(FieldType)) {
+ if (!derived().visitField(FieldType, *FieldT, &F))
+ return false;
+ } else {
+ if (!derived().visitFieldComposite(FieldType, &F))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // All other types: abort visit.
+ return false;
+ }
+
+ protected:
+ Compiler<Emitter> &C;
+
+ private:
+ Derived &derived() { return static_cast<Derived &>(*this); }
+ };
+
bool emitHLSLAggregateSplat(PrimType SrcT, unsigned SrcOffset,
QualType DestType, const Expr *E);
>From b74adf44e142a1bf26f8429d844400d95628eb78 Mon Sep 17 00:00:00 2001
From: Deric Cheung <cheung.deric at gmail.com>
Date: Sun, 26 Apr 2026 23:26:04 +0000
Subject: [PATCH 2/8] Add HLSLElementStoreVisitor
Add a concrete HLSLAggregateVisitor subclass that stores values into an
HLSL destination type. It accepts a ProduceValue callback that places
a value on the interpreter stack, then stores it with the appropriate
InitElem / InitField / InitBitField opcode.
This visitor will be used in subsequent commits to replace the
hand-rolled traversals in emitHLSLAggregateSplat and
emitHLSLConstructAggregate.
Assisted-by: Claude Opus 4.6
---
clang/lib/AST/ByteCode/Compiler.cpp | 71 +++++++++++++++++++++++++++++
1 file changed, 71 insertions(+)
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index d1878cbedae58..5f5b04e074e2e 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -8018,6 +8018,77 @@ bool Compiler<Emitter>::emitBuiltinBitCast(const CastExpr *E) {
return true;
}
+namespace clang {
+namespace interp {
+
+/// Visitor that stores values into an HLSL destination type.
+/// ProduceValue must leave exactly one value of the requested type on the
+/// interpreter stack; the visitor then stores it with the appropriate
+/// InitElem / InitField / etc. opcode.
+template <class Emitter>
+class HLSLElementStoreVisitor
+ : public Compiler<Emitter>::template HLSLAggregateVisitor<
+ HLSLElementStoreVisitor<Emitter>> {
+ using VisitorBase = typename Compiler<Emitter>::template HLSLAggregateVisitor<
+ HLSLElementStoreVisitor<Emitter>>;
+
+public:
+ HLSLElementStoreVisitor(
+ Compiler<Emitter> &C,
+ llvm::function_ref<bool(PrimType, QualType, const Expr *)> ProduceValue,
+ const Expr *E)
+ : VisitorBase(C), ProduceValue(ProduceValue), E(E) {}
+
+ bool visitScalarElem(QualType ElemType, PrimType ElemT, unsigned I) {
+ if (!ProduceValue(ElemT, ElemType, E))
+ return false;
+ return this->C.emitInitElem(ElemT, I, E);
+ }
+
+ bool visitArrayComposite(QualType ElemType, unsigned I) {
+ if (!this->C.emitConstUint32(I, E))
+ return false;
+ if (!this->C.emitArrayElemPtrUint32(E))
+ return false;
+ if (!this->visit(ElemType))
+ return false;
+ return this->C.emitFinishInitPop(E);
+ }
+
+ bool visitBase(QualType BaseType, const Record::Base *B) {
+ if (!this->C.emitGetPtrBase(B->Offset, E))
+ return false;
+ if (!this->visit(BaseType))
+ return false;
+ return this->C.emitFinishInitPop(E);
+ }
+
+ bool visitField(QualType FieldType, PrimType FieldT, const Record::Field *F) {
+ if (!ProduceValue(FieldT, FieldType, E))
+ return false;
+ if (F->isBitField())
+ return this->C.emitInitBitField(FieldT, F->Offset, F->bitWidth(), E);
+ return this->C.emitInitField(FieldT, F->Offset, E);
+ }
+
+ bool visitFieldComposite(QualType FieldType, const Record::Field *F) {
+ if (!this->C.emitGetPtrField(F->Offset, E))
+ return false;
+ if (!this->visit(FieldType))
+ return false;
+ return this->C.emitPopPtr(E);
+ }
+
+private:
+ /// Non-owning — the visitor must not outlive the callable passed at
+ /// construction.
+ llvm::function_ref<bool(PrimType, QualType, const Expr *)> ProduceValue;
+ const Expr *E;
+};
+
+} // namespace interp
+} // namespace clang
+
/// 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
>From 306a77bec04ff34bac4d21075e03e1fd1b26d575 Mon Sep 17 00:00:00 2001
From: Deric Cheung <cheung.deric at gmail.com>
Date: Sun, 26 Apr 2026 23:26:04 +0000
Subject: [PATCH 3/8] Use HLSLElementStoreVisitor in emitHLSLAggregateSplat
Replace the hand-rolled recursive type traversal in
emitHLSLAggregateSplat with a short delegation to
HLSLElementStoreVisitor.
Assisted-by: Claude Opus 4.6
---
clang/lib/AST/ByteCode/Compiler.cpp | 110 ++--------------------------
1 file changed, 7 insertions(+), 103 deletions(-)
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 5f5b04e074e2e..dd37a691b425e 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -8098,110 +8098,14 @@ 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)
+ auto ProduceValue = [&](PrimType DestT, QualType DestQT,
+ const Expr *E) -> bool {
+ if (!this->emitGetLocal(SrcT, SrcOffset, E))
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 this->emitPrimCast(SrcT, DestT, DestQT, E);
+ };
+ HLSLElementStoreVisitor<Emitter> W(*this, ProduceValue, E);
+ return W.visit(DestType);
}
/// Return the total number of scalar elements in a type. This is used
>From 3a2abeea689723a6c400aba3becab7c32caa6abe Mon Sep 17 00:00:00 2001
From: Deric Cheung <cheung.deric at gmail.com>
Date: Sun, 26 Apr 2026 23:26:04 +0000
Subject: [PATCH 4/8] Add HLSLFlatElementCounter
Add a concrete HLSLAggregateVisitor subclass that counts the total
number of scalar elements in an HLSL type by incrementing a counter
at each scalar leaf.
This visitor will be used in the next commit to replace the
hand-rolled recursive counting in countHLSLFlatElements.
Assisted-by: Claude Opus 4.6
---
clang/lib/AST/ByteCode/Compiler.cpp | 35 +++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index dd37a691b425e..62f77436403dc 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -8086,6 +8086,41 @@ class HLSLElementStoreVisitor
const Expr *E;
};
+/// Visitor that counts the total number of scalar elements in an HLSL type.
+template <class Emitter>
+class HLSLFlatElementCounter
+ : public Compiler<Emitter>::template HLSLAggregateVisitor<
+ HLSLFlatElementCounter<Emitter>> {
+ using VisitorBase = typename Compiler<Emitter>::template HLSLAggregateVisitor<
+ HLSLFlatElementCounter<Emitter>>;
+
+public:
+ explicit HLSLFlatElementCounter(Compiler<Emitter> &C) : VisitorBase(C) {}
+
+ unsigned getCount() const { return Count; }
+
+ bool visitScalarElem(QualType, PrimType, unsigned) {
+ ++Count;
+ return true;
+ }
+ bool visitArrayComposite(QualType ElemType, unsigned) {
+ return this->visit(ElemType);
+ }
+ bool visitBase(QualType BaseType, const Record::Base *) {
+ return this->visit(BaseType);
+ }
+ bool visitField(QualType, PrimType, const Record::Field *) {
+ ++Count;
+ return true;
+ }
+ bool visitFieldComposite(QualType FieldType, const Record::Field *) {
+ return this->visit(FieldType);
+ }
+
+private:
+ unsigned Count = 0;
+};
+
} // namespace interp
} // namespace clang
>From ff5d19df14ff302415ba19bfc3ba974edff83672 Mon Sep 17 00:00:00 2001
From: Deric Cheung <cheung.deric at gmail.com>
Date: Sun, 26 Apr 2026 23:26:04 +0000
Subject: [PATCH 5/8] Use HLSLFlatElementCounter in countHLSLFlatElements
Replace the hand-rolled recursive element counting in
countHLSLFlatElements with a short delegation to
HLSLFlatElementCounter.
Assisted-by: Claude Opus 4.6
---
clang/lib/AST/ByteCode/Compiler.cpp | 33 ++++-------------------------
1 file changed, 4 insertions(+), 29 deletions(-)
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 62f77436403dc..c9e851aede905 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -8148,37 +8148,12 @@ bool Compiler<Emitter>::emitHLSLAggregateSplat(PrimType SrcT,
/// 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 (canClassify(Ty))
return 1;
- return 0;
+ HLSLFlatElementCounter<Emitter> Counter(*this);
+ if (!Counter.visit(Ty))
+ return 0;
+ return Counter.getCount();
}
/// Walk a source aggregate and extract every scalar element into its own local
>From 7406c6cec0237cd30dd1a0240d3b39550fa67d49 Mon Sep 17 00:00:00 2001
From: Deric Cheung <cheung.deric at gmail.com>
Date: Sun, 26 Apr 2026 23:26:04 +0000
Subject: [PATCH 6/8] Add HLSLElementFlattenVisitor
Add a concrete HLSLAggregateVisitor subclass that extracts every
scalar element of a source value into its own local variable. It
tracks a MaxElements cap and uses SaveAndRestore to manage the
current source pointer offset across recursive sub-aggregate calls.
This visitor will be used in the next commit to replace the
hand-rolled recursive extraction in emitHLSLFlattenAggregate.
Assisted-by: Claude Opus 4.6
---
clang/lib/AST/ByteCode/Compiler.cpp | 106 ++++++++++++++++++++++++++++
1 file changed, 106 insertions(+)
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index c9e851aede905..e9d6744bcfdab 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -8121,6 +8121,112 @@ class HLSLFlatElementCounter
unsigned Count = 0;
};
+/// Visitor that extracts every scalar element of a source value into
+/// its own local variable.
+template <class Emitter>
+class HLSLElementFlattenVisitor
+ : public Compiler<Emitter>::template HLSLAggregateVisitor<
+ HLSLElementFlattenVisitor<Emitter>> {
+ using VisitorBase = typename Compiler<Emitter>::template HLSLAggregateVisitor<
+ HLSLElementFlattenVisitor<Emitter>>;
+ using HLSLFlatElement = typename Compiler<Emitter>::HLSLFlatElement;
+
+public:
+ HLSLElementFlattenVisitor(Compiler<Emitter> &C, unsigned SrcOffset,
+ SmallVectorImpl<HLSLFlatElement> &Elements,
+ unsigned MaxElements, const Expr *E)
+ : VisitorBase(C), CurrentSrcOffset(SrcOffset), Elements(Elements),
+ MaxElements(MaxElements), E(E) {}
+
+ bool isDone() const { return Done; }
+
+ bool visitScalarElem(QualType, PrimType ElemT, unsigned I) {
+ if (checkDone())
+ return false;
+ if (!this->C.emitGetLocal(PT_Ptr, CurrentSrcOffset, E))
+ return false;
+ if (!this->C.emitArrayElemPop(ElemT, I, E))
+ return false;
+ return saveToLocal(ElemT);
+ }
+
+ bool visitArrayComposite(QualType ElemType, unsigned I) {
+ if (checkDone())
+ return false;
+ if (!this->C.emitGetLocal(PT_Ptr, CurrentSrcOffset, E))
+ return false;
+ if (!this->C.emitConstUint32(I, E))
+ return false;
+ if (!this->C.emitArrayElemPtrPopUint32(E))
+ return false;
+ return enterSubAggregate(ElemType);
+ }
+
+ bool visitBase(QualType BaseType, const Record::Base *B) {
+ if (checkDone())
+ return false;
+ if (!this->C.emitGetLocal(PT_Ptr, CurrentSrcOffset, E))
+ return false;
+ if (!this->C.emitGetPtrBasePop(B->Offset, /*NullOK=*/false, E))
+ return false;
+ return enterSubAggregate(BaseType);
+ }
+
+ bool visitField(QualType, PrimType FieldT, const Record::Field *F) {
+ if (checkDone())
+ return false;
+ if (!this->C.emitGetLocal(PT_Ptr, CurrentSrcOffset, E))
+ return false;
+ if (!this->C.emitGetPtrFieldPop(F->Offset, E))
+ return false;
+ if (!this->C.emitLoadPop(FieldT, E))
+ return false;
+ return saveToLocal(FieldT);
+ }
+
+ bool visitFieldComposite(QualType FieldType, const Record::Field *F) {
+ if (checkDone())
+ return false;
+ if (!this->C.emitGetLocal(PT_Ptr, CurrentSrcOffset, E))
+ return false;
+ if (!this->C.emitGetPtrFieldPop(F->Offset, E))
+ return false;
+ return enterSubAggregate(FieldType);
+ }
+
+private:
+ bool checkDone() {
+ if (Done || Elements.size() >= MaxElements) {
+ Done = true;
+ return true;
+ }
+ return false;
+ }
+
+ bool saveToLocal(PrimType T) {
+ unsigned Off = this->C.allocateLocalPrimitive(E, T, /*IsConst=*/true);
+ if (!this->C.emitSetLocal(T, Off, E))
+ return false;
+ Elements.push_back({Off, T});
+ return true;
+ }
+
+ bool enterSubAggregate(QualType SubType) {
+ unsigned Offset =
+ this->C.allocateLocalPrimitive(E, PT_Ptr, /*IsConst=*/true);
+ if (!this->C.emitSetLocal(PT_Ptr, Offset, E))
+ return false;
+ llvm::SaveAndRestore SrcOffsetScope(CurrentSrcOffset, Offset);
+ return this->visit(SubType);
+ }
+
+ unsigned CurrentSrcOffset;
+ SmallVectorImpl<HLSLFlatElement> &Elements;
+ unsigned MaxElements;
+ const Expr *E;
+ bool Done = false;
+};
+
} // namespace interp
} // namespace clang
>From 11bacb40ca6297c6042f47b008df8e7ed4e1ebed Mon Sep 17 00:00:00 2001
From: Deric Cheung <cheung.deric at gmail.com>
Date: Sun, 26 Apr 2026 23:26:04 +0000
Subject: [PATCH 7/8] Use HLSLElementFlattenVisitor in emitHLSLFlattenAggregate
Replace the hand-rolled recursive extraction in
emitHLSLFlattenAggregate with a short delegation to
HLSLElementFlattenVisitor.
Assisted-by: Claude Opus 4.6
---
clang/lib/AST/ByteCode/Compiler.cpp | 136 +---------------------------
1 file changed, 4 insertions(+), 132 deletions(-)
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index e9d6744bcfdab..452ba21340e61 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -8271,138 +8271,10 @@ 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;
+ HLSLElementFlattenVisitor<Emitter> W(*this, SrcOffset, Elements, MaxElements,
+ E);
+ bool Result = W.visit(SrcType);
+ return W.isDone() || Result;
}
/// Populate an HLSL aggregate from a flat list of previously extracted source
>From 2ad73afaa2dcdc60a59374a49c6b82ba104bd3ae Mon Sep 17 00:00:00 2001
From: Deric Cheung <cheung.deric at gmail.com>
Date: Sun, 26 Apr 2026 23:26:04 +0000
Subject: [PATCH 8/8] Use HLSLElementStoreVisitor in emitHLSLConstructAggregate
Replace the hand-rolled recursive type traversal in
emitHLSLConstructAggregate with the existing HLSLElementStoreVisitor.
The loadAndCast lambda becomes a ProduceValue callback that indexes
into the flat element list. This also simplifies the API by removing
the two-overload pattern (with/without ElemIdx); the index is now
a local variable inside the single remaining overload.
Assisted-by: Claude Opus 4.6
---
clang/lib/AST/ByteCode/Compiler.cpp | 115 +++-------------------------
clang/lib/AST/ByteCode/Compiler.h | 8 +-
2 files changed, 10 insertions(+), 113 deletions(-)
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 452ba21340e61..a1f46f0631fbf 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -8277,122 +8277,25 @@ bool Compiler<Emitter>::emitHLSLFlattenAggregate(
return W.isDone() || Result;
}
-/// Populate an HLSL aggregate from a flat list of previously extracted source
-/// elements, casting each to the corresponding destination element type.
+/// 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 {
+ QualType DestType, ArrayRef<HLSLFlatElement> Elements, const Expr *E) {
+ unsigned ElemIdx = 0;
+ auto ProduceValue = [&](PrimType DestT, QualType DestQT,
+ const Expr *E) -> bool {
+ assert(ElemIdx < Elements.size() && "Element index out of bounds");
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;
+ HLSLElementStoreVisitor<Emitter> W(*this, ProduceValue, E);
+ return W.visit(DestType);
}
namespace clang {
diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h
index a890c71f6069c..576c59ad8ac88 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -541,13 +541,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
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);
- }
+ const Expr *E);
bool compileConstructor(const CXXConstructorDecl *Ctor);
bool compileDestructor(const CXXDestructorDecl *Dtor);
More information about the cfe-commits
mailing list