@@ -51,42 +51,114 @@ namespace clad {
5151 // / other (intermediate) statements, they are output to the current block.
5252 class StmtDiff {
5353 private:
54- std::array<clang::Stmt*, 2 > data ;
54+ std::array<clang::Stmt*, 2 > m_Data{} ;
5555 clang::Stmt* m_ValueForRevSweep;
5656
57+ // Lazy representations. When a source is set (via LazyClone() in a
58+ // constructor argument), the matching slot is produced by cloning the
59+ // source on first read and then cached, so a representation no consumer
60+ // reads is never cloned (no orphaned "dead clone"), while two
61+ // representations off the same source still materialize distinct nodes (no
62+ // sharing). An unset source means the slot is eager -- the stored pointer
63+ // (possibly null) is the value -- the default for every non-lazy argument.
64+ const clang::Stmt* m_StmtSrc = nullptr ;
65+ const clang::Stmt* m_StmtDxSrc = nullptr ;
66+ const clang::Stmt* m_RevSweepSrc = nullptr ;
67+ utils::StmtClone* m_Cloner = nullptr ;
68+
69+ // Clone Src into Slot on first read; a no-op when Src is null (eager slot).
70+ clang::Stmt* materialize (clang::Stmt*& Slot, const clang::Stmt*& Src);
71+
5772 public:
58- StmtDiff (clang::Stmt* orig = nullptr , clang::Stmt* diff = nullptr ,
59- clang::Stmt* valueForRevSweep = nullptr )
60- : m_ValueForRevSweep(valueForRevSweep) {
61- data[1 ] = orig;
62- data[0 ] = diff;
73+ // / A deferred clone marker: a representation cloned from Src on first read,
74+ // / created by VisitorBase::LazyClone(). No member initializers, so In
75+ // / (which embeds it) can be a `= {}` default argument while StmtDiff is
76+ // / still being defined.
77+ struct Lazy {
78+ utils::StmtClone* Cloner;
79+ const clang::Stmt* Src;
80+ };
81+
82+ // / A constructor input for one representation: either an already-built node
83+ // / (eager -- the node itself is the value) or a Lazy deferred clone. Node
84+ // / and Deferred are set in the constructors rather than by default member
85+ // / initializers, so In can be used as a `= {}` default argument here.
86+ struct In {
87+ clang::Stmt* Node;
88+ Lazy Deferred;
89+ In (clang::Stmt* N = nullptr ) : Node(N), Deferred{nullptr , nullptr } {}
90+ In (Lazy L) : Node(nullptr ), Deferred(L) {}
91+ };
92+
93+ // / Implicit single-representation constructor. Keeps the Expr*/Stmt* ->
94+ // / StmtDiff conversion pervasive code relies on (return expr; sd = expr;),
95+ // / which needs one user-defined conversion -- reaching the general
96+ // / constructor below through In(Stmt*) would need two.
97+ StmtDiff (clang::Stmt* orig) : m_ValueForRevSweep(nullptr ) {
98+ m_Data[1 ] = orig;
99+ m_Data[0 ] = nullptr ;
100+ }
101+
102+ // / General constructor: each representation is eager (a node, the default
103+ // / for every existing multi-argument call site) or lazy (LazyClone(src)).
104+ // / Reads e.g. StmtDiff(fwd, LazyClone(dx)) or StmtDiff(LazyClone(fwd), dx).
105+ StmtDiff (In orig = {}, In diff = {}, In valueForRevSweep = {})
106+ : m_ValueForRevSweep(valueForRevSweep.Node),
107+ m_StmtSrc (orig.Deferred.Src), m_StmtDxSrc(diff.Deferred.Src),
108+ m_RevSweepSrc(valueForRevSweep.Deferred.Src),
109+ // Every lazy slot shares the one cloner (VisitorBase::m_NodeCloner);
110+ // take the first non-null.
111+ m_Cloner([&] {
112+ if (orig.Deferred .Cloner )
113+ return orig.Deferred .Cloner ;
114+ if (diff.Deferred .Cloner )
115+ return diff.Deferred .Cloner ;
116+ return valueForRevSweep.Deferred .Cloner ;
117+ }()) {
118+ m_Data[1 ] = orig.Node ;
119+ m_Data[0 ] = diff.Node ;
63120 }
64121
65- clang::Stmt* getStmt () { return data [1 ]; }
66- clang::Stmt* getStmt_dx () { return data [0 ]; }
122+ clang::Stmt* getStmt () { return materialize (m_Data [1 ], m_StmtSrc) ; }
123+ clang::Stmt* getStmt_dx () { return materialize (m_Data [0 ], m_StmtDxSrc) ; }
67124 clang::Expr* getExpr () {
68125 return llvm::cast_or_null<clang::Expr>(getStmt ());
69126 }
70127 clang::Expr* getExpr_dx () {
71128 return llvm::cast_or_null<clang::Expr>(getStmt_dx ());
72129 }
73130
74- void updateStmt (clang::Stmt* S) { data[1 ] = S; }
75- void updateStmtDx (clang::Stmt* S) { data[0 ] = S; }
76- void updateRevSweep (clang::Stmt* S) { m_ValueForRevSweep = S; }
131+ void updateStmt (clang::Stmt* S) {
132+ m_Data[1 ] = S;
133+ m_StmtSrc = nullptr ;
134+ }
135+ void updateStmtDx (clang::Stmt* S) {
136+ m_Data[0 ] = S;
137+ m_StmtDxSrc = nullptr ;
138+ }
139+ void updateRevSweep (clang::Stmt* S) {
140+ m_ValueForRevSweep = S;
141+ m_RevSweepSrc = nullptr ;
142+ }
77143 // Stmt_dx goes first!
78- std::array<clang::Stmt*, 2 >& getBothStmts () { return data; }
144+ std::array<clang::Stmt*, 2 >& getBothStmts () {
145+ // A caller taking the array by reference parents both directions, so
146+ // materialize both.
147+ getStmt ();
148+ getStmt_dx ();
149+ return m_Data;
150+ }
79151
80152 clang::Expr* getRevSweepAsExpr () {
81153 return llvm::cast_or_null<clang::Expr>(getRevSweepStmt ());
82154 }
83155
84156 clang::Stmt* getRevSweepStmt () {
85- // / If there is no specific value for
86- // / the reverse sweep, use Stmt_dx.
87- if (!m_ValueForRevSweep)
88- return data[ 1 ];
89- return m_ValueForRevSweep ;
157+ if (clang::Stmt* R = materialize (m_ValueForRevSweep, m_RevSweepSrc))
158+ return R;
159+ // If there is no specific value for the reverse sweep, use the forward
160+ // statement.
161+ return getStmt () ;
90162 }
91163 };
92164
@@ -703,6 +775,13 @@ namespace clad {
703775 clang::Expr* CloneNode (const clang::Expr* E);
704776 // / Statement overload of the structural copy above.
705777 clang::Stmt* CloneNode (const clang::Stmt* S);
778+ // / A deferred CloneNode(\p N): the clone is produced only if the StmtDiff
779+ // / representation it is stored in is actually read, so a representation no
780+ // / consumer needs allocates no orphaned clone. Drop-in for CloneNode(N) in
781+ // / a StmtDiff argument position.
782+ StmtDiff::Lazy LazyClone (const clang::Stmt* N) {
783+ return {m_Builder.m_NodeCloner .get (), N};
784+ }
706785 // / Cloning types is necessary since VariableArrayType
707786 // / store a pointer to their size expression.
708787 clang::QualType CloneType (clang::QualType T);
0 commit comments