Jlm
Loading...
Searching...
No Matches
TraceTests.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 <gtest/gtest.h>
7
14#include <jlm/llvm/ir/Trace.hpp>
15#include <jlm/llvm/ir/types.hpp>
18#include <jlm/rvsdg/control.hpp>
19#include <jlm/rvsdg/gamma.hpp>
20#include <jlm/rvsdg/lambda.hpp>
21#include <jlm/rvsdg/theta.hpp>
22
23#include <cassert>
24
25TEST(TraceTests, testTracingMemoryHoistBarrier)
26{
27 using namespace jlm;
28 using namespace jlm::llvm;
29
30 // Arrange
31 rvsdg::Graph graph;
32
33 const auto ptrType = PointerType::Create();
34 const auto ioStateType = IOStateType::Create();
35
36 const auto myPtr = &rvsdg::GraphImport::Create(graph, ptrType, "x");
37 const auto myIo = &rvsdg::GraphImport::Create(graph, ioStateType, "io");
38
39 const auto & hoistBarrier1 = MemoryHoistBarrierOperation::createNode(*myPtr, *myIo, 0);
40 const auto hoistBarrier1Output = hoistBarrier1.output(0);
41
42 const auto & hoistBarrier2 =
43 MemoryHoistBarrierOperation::createNode(*hoistBarrier1Output, *myIo, 0);
44 const auto hoistBarrier2Output = hoistBarrier2.output(0);
45
46 // Act & Assert
47 OutputTracer tracer;
48 EXPECT_EQ(&tracer.trace(*hoistBarrier1Output), myPtr);
49 EXPECT_EQ(&tracer.trace(*hoistBarrier2Output), myPtr);
50
52 EXPECT_EQ(&tracer.trace(*hoistBarrier1Output), hoistBarrier1Output);
53 EXPECT_EQ(&tracer.trace(*hoistBarrier2Output), hoistBarrier2Output);
54}
55
56TEST(TraceTests, testGetConstantSignedInteger)
57{
58 using namespace jlm;
59 using namespace jlm::llvm;
60
61 // Creates a graph that looks like
62 //
63 // BITS64(-37)
64 // |
65 // v
66 // +-------------------------------------------+
67 // | LAMBDA f() |
68 // +-------------------------------------------+
69 // | | |
70 // | | |
71 // | | |
72 // | | IntegerConstantOperation(20) |
73 // | v | |
74 // | MATCH | |
75 // | v v |
76 // | +-----------------------+ |
77 // | | gamma | |
78 // | | +-------+ +-------+ | |
79 // | | | | | | | | | |
80 // | | | v | | v | | |
81 // | | +-------+ +-------+ | |
82 // | +-----------------------+ |
83 // | | |
84 // | v |
85 // +-------------------------------------------+
86 // And checks that outputs with constant integer values lead to the correct value.
87 //
88
89 // Arrange
90 rvsdg::Graph graph;
91
92 const auto int64Type = rvsdg::BitType::Create(64);
93 const auto int32Type = rvsdg::BitType::Create(32);
94
95 const auto bits64Output = &rvsdg::BitConstantOperation::create(
96 graph.GetRootRegion(),
98
99 const auto functionType = rvsdg::FunctionType::Create({}, { int32Type });
100 const auto lambdaNode = jlm::rvsdg::LambdaNode::Create(
101 graph.GetRootRegion(),
103 functionType,
104 "f",
106
107 const auto bits64CtxVar = lambdaNode->AddContextVar(*bits64Output).inner;
108 const auto matchOutput = rvsdg::MatchOperation::Create(*bits64CtxVar, { { 0, 0 } }, 1, 2);
109 const auto & integerConstantNode =
110 IntegerConstantOperation::Create(*lambdaNode->subregion(), 32, 20);
111
112 auto & gammaNode = *rvsdg::GammaNode::create(matchOutput, 2);
113 const auto entryVar = gammaNode.AddEntryVar(integerConstantNode.output(0));
114
115 const auto exitVarOutput =
116 gammaNode.AddExitVar({ entryVar.branchArgument[0], entryVar.branchArgument[1] }).output;
117 const auto lambdaOutput = lambdaNode->finalize({ exitVarOutput });
118
119 // Assert
120
121 // The -37 can be found both inside and outside the lambda
122 EXPECT_EQ(tryGetConstantSignedInteger(*bits64Output), -37);
123 EXPECT_EQ(tryGetConstantSignedInteger(*bits64CtxVar), -37);
124
125 // The 20 can be found both before, inside and after the gamma
126 EXPECT_EQ(tryGetConstantSignedInteger(*integerConstantNode.output(0)), 20u);
127 EXPECT_EQ(tryGetConstantSignedInteger(*entryVar.branchArgument[0]), 20u);
128 EXPECT_EQ(tryGetConstantSignedInteger(*entryVar.branchArgument[1]), 20u);
129 EXPECT_EQ(tryGetConstantSignedInteger(*exitVarOutput), 20u);
130
131 // A match output is not a constant integer, neither is the lambda output
132 EXPECT_EQ(tryGetConstantSignedInteger(*matchOutput), std::nullopt);
133 EXPECT_EQ(tryGetConstantSignedInteger(*lambdaOutput), std::nullopt);
134}
135
136TEST(TraceTests, testGetConstantSignedIntegerExtAndTrunc)
137{
138 using namespace jlm;
139 using namespace jlm::llvm;
140
160 // Arrange
161 rvsdg::Graph graph;
162
163 const auto int64Type = rvsdg::BitType::Create(64);
164 const auto int32Type = rvsdg::BitType::Create(32);
165 const auto int8Type = rvsdg::BitType::Create(8);
166
167 auto & bits32Output5 = rvsdg::BitConstantOperation::create(
168 graph.GetRootRegion(),
170 auto & sextOutput = SExtOperation::create(64, bits32Output5);
171 auto & zextOutput = ZExtOperation::create(64, bits32Output5);
172
173 auto & bits8OutputMinus20 = rvsdg::BitConstantOperation::create(
174 graph.GetRootRegion(),
176 auto & sext2Output = SExtOperation::create(32, bits8OutputMinus20);
177 auto & zext2Output = ZExtOperation::create(32, bits8OutputMinus20);
178
179 auto & bits32Output1023 = rvsdg::BitConstantOperation::create(
180 graph.GetRootRegion(),
182 auto & truncOutput = TruncOperation::create(8, bits32Output1023);
183 auto & sext3Output = SExtOperation::create(32, truncOutput);
184 auto & zext3Output = ZExtOperation::create(32, truncOutput);
185
186 // Assert
187 // c = BITS32(5), sext = SExt(32 -> 64), zext = ZExt(32 -> 64)
188 EXPECT_EQ(tryGetConstantSignedInteger(bits32Output5), 5u);
189 EXPECT_EQ(tryGetConstantSignedInteger(sextOutput), 5u);
190 EXPECT_EQ(tryGetConstantSignedInteger(zextOutput), 5u);
191
192 // c2 = BITS8(-20), sext2 = SExt(8 -> 32), zext2 = ZExt(8 -> 32)
193 EXPECT_EQ(tryGetConstantSignedInteger(bits8OutputMinus20), -20);
194 EXPECT_EQ(tryGetConstantSignedInteger(sext2Output), -20);
195 EXPECT_EQ(tryGetConstantSignedInteger(zext2Output), 236u);
196
197 // c3 = BITS32(1023), trunc3 = Trunc(32 -> 8), sext3 = SExt(8 -> 32), zext3 = ZExt(8 -> 32)
198 EXPECT_EQ(tryGetConstantSignedInteger(bits32Output1023), 1023u);
199 EXPECT_EQ(tryGetConstantSignedInteger(truncOutput), -1);
200 EXPECT_EQ(tryGetConstantSignedInteger(sext3Output), -1);
201 EXPECT_EQ(tryGetConstantSignedInteger(zext3Output), 255u);
202}
203
204TEST(TraceTests, testGetConstantSignedIntegerExtThroughGamma)
205{
206 using namespace jlm;
207 using namespace jlm::llvm;
208
230 // Arrange
231 rvsdg::Graph graph;
232
233 auto & bits32Output = rvsdg::BitConstantOperation::create(
234 graph.GetRootRegion(),
236 auto & controlOutput = rvsdg::ControlConstantOperation::create(graph.GetRootRegion(), 2, 1);
237
238 auto & gammaNode = *rvsdg::GammaNode::create(&controlOutput, 2);
239 auto entryVar = gammaNode.AddEntryVar(&bits32Output);
240 auto exitVarOutput = gammaNode.AddExitVar(entryVar.branchArgument).output;
241
242 auto & sextOutput = SExtOperation::create(32, *exitVarOutput);
243 auto & truncOutput = TruncOperation::create(16, sextOutput);
244 auto & zextOutput = ZExtOperation::create(32, truncOutput);
245
246 // Assert
247 // The -20 can be found through the gamma (invariant across both branches)
248 EXPECT_EQ(tryGetConstantSignedInteger(bits32Output), -20);
249 EXPECT_EQ(tryGetConstantSignedInteger(*exitVarOutput), -20);
250
251 // After extensions and truncation
252 EXPECT_EQ(tryGetConstantSignedInteger(sextOutput), -20);
253 EXPECT_EQ(tryGetConstantSignedInteger(truncOutput), -20);
254 EXPECT_EQ(tryGetConstantSignedInteger(zextOutput), 65516u);
255}
256
257TEST(TraceTests, testGetConstantSignedIntegerOnThetaOutput)
258{
259 using namespace jlm;
260 using namespace jlm::llvm;
261
278 // Arrange
279 rvsdg::Graph graph;
280
281 auto & zero = IntegerConstantOperation::Create(graph.GetRootRegion(), 32, 0);
282
283 auto * theta = rvsdg::ThetaNode::create(&graph.GetRootRegion());
284 auto loopVar = theta->AddLoopVar(zero.output(0));
285
286 auto & c = IntegerConstantOperation::Create(*theta->subregion(), 32, 42);
287 loopVar.post->divert_to(c.output(0));
288 theta->set_predicate(&rvsdg::ControlConstantOperation::create(*theta->subregion(), 2, 1));
289
290 // Assert
291 EXPECT_EQ(tryGetConstantSignedInteger(*loopVar.output), 42);
292}
293
294TEST(TraceTests, testTraceAllPointerOriginsTheta)
295{
296 using namespace jlm;
297 using namespace jlm::llvm;
298
323 // Arrange
324 rvsdg::Graph graph;
325
326 const auto int32Type = rvsdg::BitType::Create(32);
327 const auto pointerType = PointerType::Create();
328 const auto arrayType = ArrayType::Create(int32Type, 100);
329
330 auto & zero = IntegerConstantOperation::Create(graph.GetRootRegion(), 32, 0);
331 auto & one = IntegerConstantOperation::Create(graph.GetRootRegion(), 32, 1);
332
333 auto & allocaNode = AllocaOperation::createNode(arrayType, *one.output(0), 4);
334 auto * arrayPointer = allocaNode.output(0);
335 auto * initialPointer =
336 GetElementPtrOperation::create(arrayPointer, { zero.output(0), zero.output(0) }, arrayType);
337
338 auto * theta = rvsdg::ThetaNode::create(&graph.GetRootRegion());
339 auto i = theta->AddLoopVar(zero.output(0));
340 auto p = theta->AddLoopVar(initialPointer);
341
342 auto & oneInLoop = IntegerConstantOperation::Create(*theta->subregion(), 32, 1);
343 auto & hundredInLoop = IntegerConstantOperation::Create(*theta->subregion(), 32, 100);
344
345 auto * incrementedPointer =
346 GetElementPtrOperation::create(p.pre, { oneInLoop.output(0) }, int32Type);
347 auto * incrementedI =
348 rvsdg::CreateOpNode<rvsdg::bitadd_op>({ i.pre, oneInLoop.output(0) }, 32).output(0);
349 auto * isLessThanHundred =
350 rvsdg::CreateOpNode<rvsdg::bitult_op>({ incrementedI, hundredInLoop.output(0) }, 32)
351 .output(0);
352 auto & matchNode = rvsdg::MatchOperation::CreateNode(*isLessThanHundred, { { 1, 1 } }, 0, 2);
353
354 i.post->divert_to(incrementedI);
355 p.post->divert_to(incrementedPointer);
356 theta->set_predicate(matchNode.output(0));
357
358 auto * pAfterLoop = theta->output(1);
359
360 // Act
361 const auto pInLoopTraced = TracePointerOriginPrecise(*p.pre);
362 TraceCollection pInLoopTraceCollection;
363 ASSERT_TRUE(TraceAllPointerOrigins(pInLoopTraced, pInLoopTraceCollection, 16));
364
365 const auto pAfterLoopTraced = TracePointerOriginPrecise(*pAfterLoop);
366 TraceCollection pAfterLoopTraceCollection;
367 ASSERT_TRUE(TraceAllPointerOrigins(pAfterLoopTraced, pAfterLoopTraceCollection, 16));
368
369 // Assert
370 EXPECT_EQ(pInLoopTraceCollection.TopOrigins.size(), 1u);
371 EXPECT_EQ(pInLoopTraceCollection.TopOrigins.count(arrayPointer), 1u);
372 EXPECT_EQ(pInLoopTraceCollection.TopOrigins.at(arrayPointer), std::nullopt);
373
374 EXPECT_EQ(pAfterLoopTraceCollection.TopOrigins.size(), 1u);
375 EXPECT_EQ(pAfterLoopTraceCollection.TopOrigins.count(arrayPointer), 1u);
376 EXPECT_EQ(pAfterLoopTraceCollection.TopOrigins.at(arrayPointer), std::nullopt);
377}
static std::unique_ptr< LlvmLambdaOperation > Create(std::shared_ptr< const jlm::rvsdg::FunctionType > type, std::string name, const jlm::llvm::Linkage &linkage, jlm::llvm::CallingConvention callingConvention, jlm::llvm::AttributeSet attributes)
Definition lambda.hpp:84
void setTracingThroughHoistBarriers(const bool traceThroughHoistBarriers)
Definition Trace.hpp:60
Region & GetRootRegion() const noexcept
Definition graph.hpp:99
static LambdaNode * Create(rvsdg::Region &parent, std::unique_ptr< LambdaOperation > operation)
Definition lambda.cpp:141
Output & trace(Output &output)
Definition Trace.cpp:21
TEST(TraceTests, testTracingMemoryHoistBarrier)
Global memory state passed between functions.
std::unordered_map< const rvsdg::Output *, std::optional< int64_t > > TopOrigins
Definition Trace.hpp:165