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, testTracingIOBarrier)
26{
27 using namespace jlm;
28 using namespace jlm::llvm;
29
30 // Creates a graph that looks like
31 // GraphImport("x") GraphImport("io")
32 // | /---------/
33 // v v |
34 // IOBarrier |
35 // | /---------/
36 // v v
37 // IOBarrier
38 //
39 // And checks that both the IOBarrier outputs are traced back up to the "x" graph import
40
41 // Arrange
42 rvsdg::Graph graph;
43
44 const auto int32Type = rvsdg::BitType::Create(32);
45 const auto ioStateType = IOStateType::Create();
46
47 const auto myInt = &rvsdg::GraphImport::Create(graph, int32Type, "x");
48 const auto myIo = &rvsdg::GraphImport::Create(graph, ioStateType, "io");
49
50 const auto ioBarrier1 = &rvsdg::CreateOpNode<IOBarrierOperation>({ myInt, myIo }, int32Type);
51 const auto ioBarrier1Output = ioBarrier1->output(0);
52
53 const auto ioBarrier2 =
54 &rvsdg::CreateOpNode<IOBarrierOperation>({ ioBarrier1Output, myIo }, int32Type);
55 const auto ioBarrier2Output = ioBarrier2->output(0);
56
57 // Assert
58 EXPECT_EQ(&jlm::llvm::traceOutput(*ioBarrier1Output), myInt);
59 EXPECT_EQ(&jlm::llvm::traceOutput(*ioBarrier2Output), myInt);
60}
61
62TEST(TraceTests, testGetConstantSignedInteger)
63{
64 using namespace jlm;
65 using namespace jlm::llvm;
66
67 // Creates a graph that looks like
68 //
69 // BITS64(-37)
70 // |
71 // v
72 // +-------------------------------------------+
73 // | LAMBDA f() |
74 // +-------------------------------------------+
75 // | | |
76 // | | |
77 // | | |
78 // | | IntegerConstantOperation(20) |
79 // | v | |
80 // | MATCH | |
81 // | v v |
82 // | +-----------------------+ |
83 // | | gamma | |
84 // | | +-------+ +-------+ | |
85 // | | | | | | | | | |
86 // | | | v | | v | | |
87 // | | +-------+ +-------+ | |
88 // | +-----------------------+ |
89 // | | |
90 // | v |
91 // +-------------------------------------------+
92 // And checks that outputs with constant integer values lead to the correct value.
93 //
94
95 // Arrange
96 rvsdg::Graph graph;
97
98 const auto int64Type = rvsdg::BitType::Create(64);
99 const auto int32Type = rvsdg::BitType::Create(32);
100
101 const auto bits64Output = &rvsdg::BitConstantOperation::create(
102 graph.GetRootRegion(),
104
105 const auto functionType = rvsdg::FunctionType::Create({}, { int32Type });
106 const auto lambdaNode = jlm::rvsdg::LambdaNode::Create(
107 graph.GetRootRegion(),
109 functionType,
110 "f",
112
113 const auto bits64CtxVar = lambdaNode->AddContextVar(*bits64Output).inner;
114 const auto matchOutput = rvsdg::MatchOperation::Create(*bits64CtxVar, { { 0, 0 } }, 1, 2);
115 const auto & integerConstantNode =
116 IntegerConstantOperation::Create(*lambdaNode->subregion(), 32, 20);
117
118 auto & gammaNode = *rvsdg::GammaNode::create(matchOutput, 2);
119 const auto entryVar = gammaNode.AddEntryVar(integerConstantNode.output(0));
120
121 const auto exitVarOutput =
122 gammaNode.AddExitVar({ entryVar.branchArgument[0], entryVar.branchArgument[1] }).output;
123 const auto lambdaOutput = lambdaNode->finalize({ exitVarOutput });
124
125 // Assert
126
127 // The -37 can be found both inside and outside the lambda
128 EXPECT_EQ(tryGetConstantSignedInteger(*bits64Output), -37);
129 EXPECT_EQ(tryGetConstantSignedInteger(*bits64CtxVar), -37);
130
131 // The 20 can be found both before, inside and after the gamma
132 EXPECT_EQ(tryGetConstantSignedInteger(*integerConstantNode.output(0)), 20u);
133 EXPECT_EQ(tryGetConstantSignedInteger(*entryVar.branchArgument[0]), 20u);
134 EXPECT_EQ(tryGetConstantSignedInteger(*entryVar.branchArgument[1]), 20u);
135 EXPECT_EQ(tryGetConstantSignedInteger(*exitVarOutput), 20u);
136
137 // A match output is not a constant integer, neither is the lambda output
138 EXPECT_EQ(tryGetConstantSignedInteger(*matchOutput), std::nullopt);
139 EXPECT_EQ(tryGetConstantSignedInteger(*lambdaOutput), std::nullopt);
140}
141
142TEST(TraceTests, testGetConstantSignedIntegerExtAndTrunc)
143{
144 using namespace jlm;
145 using namespace jlm::llvm;
146
166 // Arrange
167 rvsdg::Graph graph;
168
169 const auto int64Type = rvsdg::BitType::Create(64);
170 const auto int32Type = rvsdg::BitType::Create(32);
171 const auto int8Type = rvsdg::BitType::Create(8);
172
173 auto & bits32Output5 = rvsdg::BitConstantOperation::create(
174 graph.GetRootRegion(),
176 auto & sextOutput = SExtOperation::create(64, bits32Output5);
177 auto & zextOutput = ZExtOperation::create(64, bits32Output5);
178
179 auto & bits8OutputMinus20 = rvsdg::BitConstantOperation::create(
180 graph.GetRootRegion(),
182 auto & sext2Output = SExtOperation::create(32, bits8OutputMinus20);
183 auto & zext2Output = ZExtOperation::create(32, bits8OutputMinus20);
184
185 auto & bits32Output1023 = rvsdg::BitConstantOperation::create(
186 graph.GetRootRegion(),
188 auto & truncOutput = TruncOperation::create(8, bits32Output1023);
189 auto & sext3Output = SExtOperation::create(32, truncOutput);
190 auto & zext3Output = ZExtOperation::create(32, truncOutput);
191
192 // Assert
193 // c = BITS32(5), sext = SExt(32 -> 64), zext = ZExt(32 -> 64)
194 EXPECT_EQ(tryGetConstantSignedInteger(bits32Output5), 5u);
195 EXPECT_EQ(tryGetConstantSignedInteger(sextOutput), 5u);
196 EXPECT_EQ(tryGetConstantSignedInteger(zextOutput), 5u);
197
198 // c2 = BITS8(-20), sext2 = SExt(8 -> 32), zext2 = ZExt(8 -> 32)
199 EXPECT_EQ(tryGetConstantSignedInteger(bits8OutputMinus20), -20);
200 EXPECT_EQ(tryGetConstantSignedInteger(sext2Output), -20);
201 EXPECT_EQ(tryGetConstantSignedInteger(zext2Output), 236u);
202
203 // c3 = BITS32(1023), trunc3 = Trunc(32 -> 8), sext3 = SExt(8 -> 32), zext3 = ZExt(8 -> 32)
204 EXPECT_EQ(tryGetConstantSignedInteger(bits32Output1023), 1023u);
205 EXPECT_EQ(tryGetConstantSignedInteger(truncOutput), -1);
206 EXPECT_EQ(tryGetConstantSignedInteger(sext3Output), -1);
207 EXPECT_EQ(tryGetConstantSignedInteger(zext3Output), 255u);
208}
209
210TEST(TraceTests, testGetConstantSignedIntegerExtThroughGamma)
211{
212 using namespace jlm;
213 using namespace jlm::llvm;
214
236 // Arrange
237 rvsdg::Graph graph;
238
239 auto & bits32Output = rvsdg::BitConstantOperation::create(
240 graph.GetRootRegion(),
242 auto & controlOutput = rvsdg::ControlConstantOperation::create(graph.GetRootRegion(), 2, 1);
243
244 auto & gammaNode = *rvsdg::GammaNode::create(&controlOutput, 2);
245 auto entryVar = gammaNode.AddEntryVar(&bits32Output);
246 auto exitVarOutput = gammaNode.AddExitVar(entryVar.branchArgument).output;
247
248 auto & sextOutput = SExtOperation::create(32, *exitVarOutput);
249 auto & truncOutput = TruncOperation::create(16, sextOutput);
250 auto & zextOutput = ZExtOperation::create(32, truncOutput);
251
252 // Assert
253 // The -20 can be found through the gamma (invariant across both branches)
254 EXPECT_EQ(tryGetConstantSignedInteger(bits32Output), -20);
255 EXPECT_EQ(tryGetConstantSignedInteger(*exitVarOutput), -20);
256
257 // After extensions and truncation
258 EXPECT_EQ(tryGetConstantSignedInteger(sextOutput), -20);
259 EXPECT_EQ(tryGetConstantSignedInteger(truncOutput), -20);
260 EXPECT_EQ(tryGetConstantSignedInteger(zextOutput), 65516u);
261}
262
263TEST(TraceTests, testTraceAllPointerOriginsTheta)
264{
265 using namespace jlm;
266 using namespace jlm::llvm;
267
292 // Arrange
293 rvsdg::Graph graph;
294
295 const auto int32Type = rvsdg::BitType::Create(32);
296 const auto pointerType = PointerType::Create();
297 const auto arrayType = ArrayType::Create(int32Type, 100);
298
299 auto & zero = IntegerConstantOperation::Create(graph.GetRootRegion(), 32, 0);
300 auto & one = IntegerConstantOperation::Create(graph.GetRootRegion(), 32, 1);
301
302 auto & allocaNode = AllocaOperation::createNode(arrayType, *one.output(0), 4);
303 auto * arrayPointer = allocaNode.output(0);
304 auto * initialPointer =
305 GetElementPtrOperation::create(arrayPointer, { zero.output(0), zero.output(0) }, arrayType);
306
307 auto * theta = rvsdg::ThetaNode::create(&graph.GetRootRegion());
308 auto i = theta->AddLoopVar(zero.output(0));
309 auto p = theta->AddLoopVar(initialPointer);
310
311 auto & oneInLoop = IntegerConstantOperation::Create(*theta->subregion(), 32, 1);
312 auto & hundredInLoop = IntegerConstantOperation::Create(*theta->subregion(), 32, 100);
313
314 auto * incrementedPointer =
315 GetElementPtrOperation::create(p.pre, { oneInLoop.output(0) }, int32Type);
316 auto * incrementedI =
317 rvsdg::CreateOpNode<rvsdg::bitadd_op>({ i.pre, oneInLoop.output(0) }, 32).output(0);
318 auto * isLessThanHundred =
319 rvsdg::CreateOpNode<rvsdg::bitult_op>({ incrementedI, hundredInLoop.output(0) }, 32)
320 .output(0);
321 auto & matchNode = rvsdg::MatchOperation::CreateNode(*isLessThanHundred, { { 1, 1 } }, 0, 2);
322
323 i.post->divert_to(incrementedI);
324 p.post->divert_to(incrementedPointer);
325 theta->set_predicate(matchNode.output(0));
326
327 auto * pAfterLoop = theta->output(1);
328
329 // Act
330 const auto pInLoopTraced = TracePointerOriginPrecise(*p.pre);
331 TraceCollection pInLoopTraceCollection;
332 ASSERT_TRUE(TraceAllPointerOrigins(pInLoopTraced, pInLoopTraceCollection, 16));
333
334 const auto pAfterLoopTraced = TracePointerOriginPrecise(*pAfterLoop);
335 TraceCollection pAfterLoopTraceCollection;
336 ASSERT_TRUE(TraceAllPointerOrigins(pAfterLoopTraced, pAfterLoopTraceCollection, 16));
337
338 // Assert
339 EXPECT_EQ(pInLoopTraceCollection.TopOrigins.size(), 1u);
340 EXPECT_EQ(pInLoopTraceCollection.TopOrigins.count(arrayPointer), 1u);
341 EXPECT_EQ(pInLoopTraceCollection.TopOrigins.at(arrayPointer), std::nullopt);
342
343 EXPECT_EQ(pAfterLoopTraceCollection.TopOrigins.size(), 1u);
344 EXPECT_EQ(pAfterLoopTraceCollection.TopOrigins.count(arrayPointer), 1u);
345 EXPECT_EQ(pAfterLoopTraceCollection.TopOrigins.at(arrayPointer), std::nullopt);
346}
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
Region & GetRootRegion() const noexcept
Definition graph.hpp:99
static LambdaNode * Create(rvsdg::Region &parent, std::unique_ptr< LambdaOperation > operation)
Definition lambda.cpp:141
TEST(TraceTests, testTracingIOBarrier)
Global memory state passed between functions.
rvsdg::Output & traceOutput(rvsdg::Output &output, const rvsdg::Region *withinRegion)
Definition Trace.cpp:62
std::unordered_map< const rvsdg::Output *, std::optional< int64_t > > TopOrigins
Definition Trace.hpp:135