Jlm
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 "delta.hpp"
7 #include "gamma.hpp"
8 #include "lambda.hpp"
9 #include "Phi.hpp"
10 #include "theta.hpp"
11 #include <jlm/rvsdg/Trace.hpp>
12 
13 namespace jlm::rvsdg
14 {
15 OutputTracer::~OutputTracer() = default;
16 
17 OutputTracer::OutputTracer() = default;
18 
19 Output &
21 {
22  return trace(output, true);
23 }
24 
25 Output &
26 OutputTracer::trace(Output & output, bool mayLeaveRegion)
27 {
28  Output * head = &output;
29 
30  // Keep tracing until the output stops changing
31  while (true)
32  {
33  Output * prevHead = head;
34  head = &traceStep(*head, mayLeaveRegion);
35  if (head == prevHead)
36  {
37  return *head;
38  }
39  }
40 }
41 
48 static Output &
50 {
51  const auto roleVar = gammaNode.MapBranchArgument(output);
52  if (const auto entryVar = std::get_if<GammaNode::EntryVar>(&roleVar))
53  {
54  return *entryVar->input->origin();
55  }
56  if (const auto matchVar = std::get_if<GammaNode::MatchVar>(&roleVar))
57  {
58  return *matchVar->input->origin();
59  }
60  throw std::logic_error("Unhandled role variable type.");
61 }
62 
63 Output *
65 {
66  const auto exitVar = gammaNode.MapOutputExitVar(output);
67 
68  // The shared output that is the origin of the entry variable(s) going into the gamma node
69  Output * commonOrigin = nullptr;
70 
71  for (auto branchResult : exitVar.branchResult)
72  {
73  auto tracedInner = branchResult->origin();
74 
75  // If deep tracing is enabled, make a greater effort to trace up to a region argument
77  {
78  tracedInner = &trace(*tracedInner, false);
79  }
80 
81  // The traced output must reach a region argument in the gamma subregion
82  if (TryGetRegionParentNode<GammaNode>(*tracedInner) != &gammaNode)
83  return nullptr;
84 
85  // Get the origin of the region argument outside the gamma
86  Output & outerOrigin = mapGammaArgumentToOrigin(gammaNode, *tracedInner);
87 
88  // Check that the origin matches with all other origins
89  if (commonOrigin == nullptr)
90  {
91  commonOrigin = &outerOrigin;
92  }
93  else if (commonOrigin != &outerOrigin)
94  {
95  return nullptr;
96  }
97  }
98 
99  JLM_ASSERT(commonOrigin != nullptr);
100  return commonOrigin;
101 }
102 
103 Output *
105 {
106  const auto loopVar = thetaNode.MapOutputLoopVar(output);
107 
108  auto tracedInner = loopVar.post->origin();
109 
110  // If deep tracing is enabled, make a greater effort in tracing up to a region argument
112  {
113  tracedInner = &trace(*tracedInner, false);
114  }
115 
116  if (tracedInner == loopVar.pre)
117  {
118  return loopVar.input->origin();
119  }
120  return nullptr;
121 }
122 
123 Output &
124 OutputTracer::traceStep(Output & output, bool mayLeaveRegion)
125 {
126  if (!mayLeaveRegion && TryGetRegionParentNode<Node>(output))
127  {
128  // We are not allowed to leave the region, so return early on region arguments
129  return output;
130  }
131 
132  // Handle gamma node outputs
133  if (const auto gammaNode = TryGetOwnerNode<GammaNode>(output))
134  {
135  if (const auto traced = tryTraceThroughGamma(*gammaNode, output))
136  return *traced;
137 
138  return output;
139  }
140 
141  // Handle gamma node arguments
142  if (const auto gammaNode = TryGetRegionParentNode<GammaNode>(output))
143  {
144  return mapGammaArgumentToOrigin(*gammaNode, output);
145  }
146 
147  // Handle theta node outputs
148  if (const auto thetaNode = TryGetOwnerNode<ThetaNode>(output))
149  {
150  if (const auto traced = tryTraceThroughTheta(*thetaNode, output))
151  {
152  return *traced;
153  }
154 
155  return output;
156  }
157 
158  // Handle theta node arguments
159  if (const auto thetaNode = TryGetRegionParentNode<ThetaNode>(output))
160  {
161  // Tracing from inside a theta to outside it is only valid if the loop variable is invariant
162  const auto loopVar = thetaNode->MapPreLoopVar(output);
163  if (const auto traced = tryTraceThroughTheta(*thetaNode, *loopVar.output))
164  {
165  return *traced;
166  }
167 
168  return output;
169  }
170 
171  // If we are not doing interprocedural tracing, stop tracing now
172  if (!isInterprocedural_)
173  return output;
174 
175  // Handle lambda context variables
176  if (const auto lambda = rvsdg::TryGetRegionParentNode<LambdaNode>(output))
177  {
178  // If the argument is a contex variable, continue tracing
179  if (const auto ctxVar = lambda->MapBinderContextVar(output))
180  return *ctxVar->input->origin();
181 
182  return output;
183  }
184 
185  // Handle delta context variables
186  if (const auto delta = rvsdg::TryGetRegionParentNode<DeltaNode>(output))
187  {
188  // If the argument is a contex variable, continue tracing
189  const auto ctxVar = delta->MapBinderContextVar(output);
190  return *ctxVar.input->origin();
191  }
192 
193  // Handle phi outputs
194  if (const auto phiNode = rvsdg::TryGetOwnerNode<PhiNode>(output))
195  {
196  if (enterPhiNodes_)
197  {
198  const auto fixVar = phiNode->MapOutputFixVar(output);
199  return *fixVar.result->origin();
200  }
201  return output;
202  }
203 
204  // Handle phi region arguments
205  if (const auto phiNode = rvsdg::TryGetRegionParentNode<PhiNode>(output))
206  {
207  // Wo only trace through contex variables.
208  // Going through recursion variables would hide the fact that recursion is happening,
209  // and risks producing an output that is a successor of the output we started with in the DAG.
210  const auto argument = phiNode->MapArgument(output);
211  if (const auto ctxVar = std::get_if<PhiNode::ContextVar>(&argument))
212  {
213  // Follow the context variable to outside the phi
214  return *ctxVar->input->origin();
215  }
216  return output;
217  }
218 
219  return output;
220 }
221 
222 Output &
224 {
225  OutputTracer tracer;
226  tracer.setInterprocedural(false);
227  return tracer.trace(output);
228 }
229 
230 Output &
232 {
233  OutputTracer tracer;
234  return tracer.trace(output);
235 }
236 
237 }
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:377
std::variant< MatchVar, EntryVar > MapBranchArgument(const rvsdg::Output &output) const
Maps branch subregion entry argument to its role (pattern match or entry variable).
Definition: gamma.cpp:328
Output * origin() const noexcept
Definition: node.hpp:58
Output * tryTraceThroughGamma(GammaNode &gammaNode, Output &output)
Definition: Trace.cpp:64
void setInterprocedural(bool value) noexcept
Definition: Trace.hpp:99
Output & trace(Output &output)
Definition: Trace.cpp:20
virtual Output & traceStep(Output &output, bool mayLeaveRegion)
Definition: Trace.cpp:124
bool traceThroughStrucutalNodes_
Definition: Trace.hpp:159
Output * tryTraceThroughTheta(ThetaNode &thetaNode, Output &output)
Definition: Trace.cpp:104
LoopVar MapOutputLoopVar(const rvsdg::Output &output) const
Maps variable at exit to full varibale description.
Definition: theta.cpp:166
#define JLM_ASSERT(x)
Definition: common.hpp:16
static Output & mapGammaArgumentToOrigin(GammaNode &gammaNode, Output &output)
Definition: Trace.cpp:49
Output & traceOutput(Output &output)
Definition: Trace.cpp:231
Output & traceOutputIntraProcedurally(Output &output)
Definition: Trace.cpp:223
rvsdg::Input * post
Variable after iteration (output result from subregion).
Definition: theta.hpp:62