Jlm
Loading...
Searching...
No Matches
Trace.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2025 HÃ¥vard Krogstie <krogstie.havard@gmail.com>
3 * See COPYING for terms of redistribution.
4 */
5
6#include <jlm/rvsdg/delta.hpp>
7#include <jlm/rvsdg/gamma.hpp>
9#include <jlm/rvsdg/Phi.hpp>
10#include <jlm/rvsdg/theta.hpp>
11#include <jlm/rvsdg/Trace.hpp>
12
13namespace jlm::rvsdg
14{
16
18 : enableCaching_(enableCaching)
19{}
20
21Output &
23{
24 return trace(output, nullptr);
25}
26
27Output &
29{
30 Output * head = &output;
31
32 // Keep tracing until the output stops changing
33 while (true)
34 {
37 if (head == prevHead)
38 {
39 return *head;
40 }
41 }
42}
43
50static Output &
52{
53 return *gammaNode.mapBranchArgumentToInput(output).origin();
54}
55
56Output *
58{
59 if (const auto traceResultOpt = lookupInCache(output); traceResultOpt.has_value())
60 {
61 return traceResultOpt.value();
62 }
63
64 const auto exitVar = gammaNode.MapOutputExitVar(output);
65
66 // The shared output that is the origin of the entry variable(s) going into the gamma node
67 Output * commonOrigin = nullptr;
68 Input * gammaInput = nullptr;
69
70 for (auto branchResult : exitVar.branchResult)
71 {
72 auto tracedInner = branchResult->origin();
73
74 // If deep tracing is enabled, make a greater effort to trace up to a region argument
76 {
77 // Trace the branch result origin, but only within the gamma subregion
79 }
80
81 // The traced output must reach a region argument in the gamma subregion
83 return insertInCache(output, nullptr);
84
85 // Get the origin of the region argument outside the gamma
87 Output & outerOrigin = *gammaInput->origin();
88
89 // Check that the origin matches with all other origins
90 if (commonOrigin == nullptr)
91 {
93 }
94 else if (commonOrigin != &outerOrigin)
95 {
96 return insertInCache(output, nullptr);
97 }
98 }
99
100 JLM_ASSERT(commonOrigin != nullptr);
101 JLM_ASSERT(gammaInput != nullptr);
102 return insertInCache(output, gammaInput);
103}
104
105Output *
107{
108 if (const auto traceResultOpt = lookupInCache(output); traceResultOpt.has_value())
109 {
110 return traceResultOpt.value();
111 }
112
113 const auto loopVar = thetaNode.MapOutputLoopVar(output);
114
115 auto tracedInner = loopVar.post->origin();
116
117 // If deep tracing is enabled, make a greater effort in tracing up to a region argument
119 {
120 // trace the origin within the thetaNode, but only within the theta's subregion
121 tracedInner = &trace(*tracedInner, thetaNode.subregion());
122 }
123
124 // If tracing reached the pre argument of the same loop variable, it is invariant
125 if (tracedInner == loopVar.pre)
126 {
127 return insertInCache(output, loopVar.input);
128 }
129
130 // If tracing from the post result lead to the pre argument of a different loop variable,
131 // check if that loop variable is trivially invariant, and if it is, return its input origin.
133 {
134 auto originLoopVar = thetaNode.MapPreLoopVar(*tracedInner);
136 {
137 return insertInCache(output, originLoopVar.input);
138 }
139 }
140
141 return insertInCache(output, nullptr);
142}
143
144Output &
146{
148 {
149 // We are not allowed to leave this region, and tracing has reached one of its arguments
150 return output;
151 }
152
153 // Handle gamma node outputs
154 if (const auto gammaNode = TryGetOwnerNode<GammaNode>(output))
155 {
156 if (const auto traced = tryTraceThroughGamma(*gammaNode, output))
157 return *traced;
158
159 return output;
160 }
161
162 // Handle gamma node arguments
163 if (const auto gammaNode = TryGetRegionParentNode<GammaNode>(output))
164 {
165 return mapGammaArgumentToOrigin(*gammaNode, output);
166 }
167
168 // Handle theta node outputs
169 if (const auto thetaNode = TryGetOwnerNode<ThetaNode>(output))
170 {
171 if (const auto traced = tryTraceThroughTheta(*thetaNode, output))
172 {
173 return *traced;
174 }
175
176 return output;
177 }
178
179 // Handle theta node arguments
180 if (const auto thetaNode = TryGetRegionParentNode<ThetaNode>(output))
181 {
182 // Tracing from inside a theta to outside it is only valid if the loop variable is invariant.
183 // This is determined by tracing from the loop variable's post result,
184 // and seeing if it leads to the same input origin as the loop variable's own input.
185
186 // The loop variable whose pre argument is being traced from
187 const auto loopVar = thetaNode->MapPreLoopVar(output);
188
189 // The origin of the loop variable's input.
190 const auto inputOrigin = loopVar.input->origin();
191
192 // The origin reached when tracing from the loop variable's post result,
193 // if it reaches an invariant loop variable and "escapes" the theta.
194 // The invariant loop variable found does not have to be the same as the above loopVar.
195 // See TraceTests' TestIndirectLoopInvariance.
196 const auto postOrigin = tryTraceThroughTheta(*thetaNode, *loopVar.output);
197
198 if (postOrigin == inputOrigin)
199 {
200 return *inputOrigin;
201 }
202
203 return output;
204 }
205
206 // If we are not doing interprocedural tracing, stop tracing now
208 return output;
209
210 // Handle lambda context variables
211 if (const auto lambda = TryGetRegionParentNode<LambdaNode>(output))
212 {
213 // If the argument is a contex variable, continue tracing
214 if (const auto ctxVar = lambda->MapBinderContextVar(output))
215 return *ctxVar->input->origin();
216
217 return output;
218 }
219
220 // Handle delta context variables
221 if (const auto delta = TryGetRegionParentNode<DeltaNode>(output))
222 {
223 // If the argument is a contex variable, continue tracing
224 const auto ctxVar = delta->MapBinderContextVar(output);
225 return *ctxVar.input->origin();
226 }
227
228 // Handle phi outputs
229 if (const auto phiNode = TryGetOwnerNode<PhiNode>(output))
230 {
231 if (enterPhiNodes_)
232 {
233 const auto fixVar = phiNode->MapOutputFixVar(output);
234 return *fixVar.result->origin();
235 }
236 return output;
237 }
238
239 // Handle phi region arguments
240 if (const auto phiNode = TryGetRegionParentNode<PhiNode>(output))
241 {
242 // Wo only trace through contex variables.
243 // Going through recursion variables would hide the fact that recursion is happening,
244 // and risks producing an output that is a successor of the output we started with in the DAG.
245 const auto argument = phiNode->MapArgument(output);
246 if (const auto ctxVar = std::get_if<PhiNode::ContextVar>(&argument))
247 {
248 // Follow the context variable to outside the phi
249 return *ctxVar->input->origin();
250 }
251 return output;
252 }
253
254 return output;
255}
256
257Output *
259{
260 if (!enableCaching_)
261 return traceResult != nullptr ? traceResult->origin() : nullptr;
262
263 JLM_ASSERT(traceCache_.find(&output) == traceCache_.end());
264 traceCache_[&output] = traceResult;
265 return traceResult != nullptr ? traceResult->origin() : nullptr;
266}
267
268std::optional<Output *>
270{
271 if (!enableCaching_)
272 return std::nullopt;
273
274 if (const auto it = traceCache_.find(&output); it != traceCache_.end())
275 {
276 return it->second != nullptr ? it->second->origin() : nullptr;
277 }
278
279 return std::nullopt;
280}
281
282Output &
284{
285 constexpr bool enableCaching = false;
287 tracer.setInterprocedural(false);
288 return tracer.trace(output);
289}
290
291Output &
293{
294 constexpr bool enableCaching = false;
296 return tracer.trace(output, withinRegion);
297}
298
299}
Conditional operator / pattern matching.
Definition gamma.hpp:99
ExitVar MapOutputExitVar(const rvsdg::Output &output) const
Maps gamma output to exit variable description.
Definition gamma.cpp:397
const rvsdg::Input & mapBranchArgumentToInput(const rvsdg::Output &output) const
Maps branch subregion entry argument to its corresponding gamma input.
Definition gamma.cpp:344
Output * origin() const noexcept
Definition node.hpp:58
Output * tryTraceThroughGamma(GammaNode &gammaNode, Output &output)
Definition Trace.cpp:57
void setInterprocedural(bool value) noexcept
Definition Trace.hpp:106
Output * insertInCache(const Output &output, Input *traceResult)
Definition Trace.cpp:258
Output & trace(Output &output)
Definition Trace.cpp:22
virtual Output & traceStep(Output &output, const rvsdg::Region *withinRegion)
Definition Trace.cpp:145
std::unordered_map< const Output *, Input * > traceCache_
Definition Trace.hpp:211
OutputTracer(bool enableCaching) noexcept
Definition Trace.cpp:17
Output * tryTraceThroughTheta(ThetaNode &thetaNode, Output &output)
Definition Trace.cpp:106
std::optional< Output * > lookupInCache(const Output &output)
Definition Trace.cpp:269
Represent acyclic RVSDG subgraphs.
Definition region.hpp:213
LoopVar MapOutputLoopVar(const rvsdg::Output &output) const
Maps variable at exit to full varibale description.
Definition theta.cpp:166
LoopVar MapPreLoopVar(const rvsdg::Output &argument) const
Maps variable at start of loop iteration to full varibale description.
Definition theta.cpp:140
rvsdg::Region * subregion() const noexcept
Definition theta.hpp:79
#define JLM_ASSERT(x)
Definition common.hpp:16
static bool ThetaLoopVarIsInvariant(const ThetaNode::LoopVar &loopVar) noexcept
Definition theta.hpp:227
Output & traceOutput(Output &output, const rvsdg::Region *withinRegion)
Definition Trace.cpp:292
Region * TryGetOwnerRegion(const rvsdg::Input &input) noexcept
Definition node.hpp:1021
NodeType * TryGetOwnerNode(const rvsdg::Input &input) noexcept
Checks if this is an input to a node of specified type.
Definition node.hpp:872
Output & traceOutputIntraProcedurally(Output &output)
Definition Trace.cpp:283
static Output & mapGammaArgumentToOrigin(GammaNode &gammaNode, Output &output)
Definition Trace.cpp:51
rvsdg::Input * post
Variable after iteration (output result from subregion).
Definition theta.hpp:62