Jlm
Loading...
Searching...
No Matches
CallTests.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2021 Nico Reißmann <nico.reissmann@gmail.com>
3 * See COPYING for terms of redistribution.
4 */
5
6#include <gtest/gtest.h>
7
18#include <jlm/rvsdg/gamma.hpp>
21#include <jlm/rvsdg/theta.hpp>
22#include <jlm/rvsdg/view.hpp>
24
25TEST(CallOperationTests, TestCopy)
26{
27 using namespace jlm::llvm;
28
29 // Arrange
31 auto iOStateType = IOStateType::Create();
32 auto memoryStateType = MemoryStateType::Create();
33 auto functionType = jlm::rvsdg::FunctionType::Create(
34 { valueType, IOStateType::Create(), MemoryStateType::Create() },
35 { valueType, IOStateType::Create(), MemoryStateType::Create() });
36
38 auto function1 = &jlm::rvsdg::GraphImport::Create(rvsdg, functionType, "function1");
39 auto value1 = &jlm::rvsdg::GraphImport::Create(rvsdg, valueType, "value1");
40 auto iOState1 = &jlm::rvsdg::GraphImport::Create(rvsdg, iOStateType, "iOState1");
41 auto memoryState1 = &jlm::rvsdg::GraphImport::Create(rvsdg, memoryStateType, "memoryState1");
42
43 auto function2 = &jlm::rvsdg::GraphImport::Create(rvsdg, functionType, "function2");
44 auto value2 = &jlm::rvsdg::GraphImport::Create(rvsdg, valueType, "value2");
45 auto iOState2 = &jlm::rvsdg::GraphImport::Create(rvsdg, iOStateType, "iOState2");
46 auto memoryState2 = &jlm::rvsdg::GraphImport::Create(rvsdg, memoryStateType, "memoryState2");
47
48 auto callResults =
49 CallOperation::Create(function1, functionType, { value1, iOState1, memoryState1 });
50
51 // Act
52 auto node = jlm::rvsdg::TryGetOwnerNode<jlm::rvsdg::SimpleNode>(*callResults[0]);
53 auto copiedNode =
54 node->copy(&rvsdg.GetRootRegion(), { function2, value2, iOState2, memoryState2 });
55
56 // Assert
57 EXPECT_EQ(
58 node->GetOperation(),
59 jlm::util::assertedCast<jlm::rvsdg::SimpleNode>(copiedNode)->GetOperation());
60}
61
62TEST(CallOperationTests, TestCallNodeAccessors)
63{
64 using namespace jlm::llvm;
65 using namespace jlm::rvsdg;
66
67 // Arrange
68 auto valueType = TestType::createValueType();
69 auto iOStateType = IOStateType::Create();
70 auto memoryStateType = MemoryStateType::Create();
71 auto functionType = jlm::rvsdg::FunctionType::Create(
72 { valueType, IOStateType::Create(), MemoryStateType::Create() },
73 { valueType, IOStateType::Create(), MemoryStateType::Create() });
74
76 auto f = &jlm::rvsdg::GraphImport::Create(rvsdg, functionType, "function");
77 auto v = &jlm::rvsdg::GraphImport::Create(rvsdg, valueType, "value");
78 auto i = &jlm::rvsdg::GraphImport::Create(rvsdg, iOStateType, "IOState");
79 auto m = &jlm::rvsdg::GraphImport::Create(rvsdg, memoryStateType, "memoryState");
80
81 // Act
82 auto results = CallOperation::Create(f, functionType, { v, i, m });
83 auto & callNode =
84 *jlm::util::assertedCast<SimpleNode>(jlm::rvsdg::TryGetOwnerNode<Node>(*results[0]));
85
86 // Assert
87 EXPECT_EQ(CallOperation::NumArguments(callNode), 3u);
88 EXPECT_EQ(CallOperation::NumArguments(callNode), callNode.ninputs() - 1);
89 EXPECT_EQ(CallOperation::Argument(callNode, 0u)->origin(), v);
90 EXPECT_EQ(CallOperation::Argument(callNode, 1u)->origin(), i);
91 EXPECT_EQ(CallOperation::Argument(callNode, 2u)->origin(), m);
92
93 EXPECT_EQ(callNode.noutputs(), 3u);
94 EXPECT_EQ(*callNode.output(0)->Type(), *valueType);
95 EXPECT_EQ(*callNode.output(1)->Type(), *iOStateType);
96 EXPECT_EQ(*callNode.output(2)->Type(), *memoryStateType);
97
98 EXPECT_EQ(CallOperation::GetFunctionInput(callNode).origin(), f);
99 EXPECT_EQ(CallOperation::GetIOStateInput(callNode).origin(), i);
100 EXPECT_EQ(CallOperation::GetMemoryStateInput(callNode).origin(), m);
101
102 EXPECT_EQ(*CallOperation::GetIOStateOutput(callNode).Type(), *iOStateType);
103 EXPECT_EQ(*CallOperation::GetMemoryStateOutput(callNode).Type(), *memoryStateType);
104}
105
106TEST(CallOperationTests, TestCallTypeClassifierIndirectCall)
107{
108 using namespace jlm::llvm;
109
110 // Arrange
112 auto iOStateType = IOStateType::Create();
113 auto memoryStateType = MemoryStateType::Create();
114 auto fcttype1 = jlm::rvsdg::FunctionType::Create(
115 { IOStateType::Create(), MemoryStateType::Create() },
116 { vt, IOStateType::Create(), MemoryStateType::Create() });
117 auto fcttype2 = jlm::rvsdg::FunctionType::Create(
118 { PointerType::Create(), IOStateType::Create(), MemoryStateType::Create() },
119 { vt, IOStateType::Create(), MemoryStateType::Create() });
120
121 auto module = LlvmRvsdgModule::Create(jlm::util::FilePath(""), "", "");
122 auto graph = &module->Rvsdg();
123
124 auto SetupFunction = [&]()
125 {
126 auto lambda = jlm::rvsdg::LambdaNode::Create(
127 graph->GetRootRegion(),
128 LlvmLambdaOperation::Create(fcttype2, "fct", Linkage::externalLinkage));
129 auto iOStateArgument = lambda->GetFunctionArguments()[1];
130 auto memoryStateArgument = lambda->GetFunctionArguments()[2];
131
132 auto one = &jlm::rvsdg::BitConstantOperation::create(*lambda->subregion(), { 32, 1 });
133
134 auto alloca = AllocaOperation::create(PointerType::Create(), one, 8);
135
136 auto store = StoreNonVolatileOperation::Create(
137 alloca[0],
138 lambda->GetFunctionArguments()[0],
139 { alloca[1] },
140 8);
141
142 auto load = LoadNonVolatileOperation::Create(alloca[0], store, PointerType::Create(), 8);
143 auto fn = jlm::rvsdg::CreateOpNode<PointerToFunctionOperation>({ load[0] }, fcttype1).output(0);
144
145 auto callResults =
146 CallOperation::Create(fn, fcttype1, { iOStateArgument, memoryStateArgument });
147
148 lambda->finalize(callResults);
149
150 jlm::rvsdg::GraphExport::Create(*lambda->output(), "f");
151
152 return std::make_tuple(
153 jlm::util::assertedCast<jlm::rvsdg::SimpleNode>(
155 fn);
156 };
157
158 auto [callNode, loadOutput] = SetupFunction();
159
160 // Act
161 auto callTypeClassifier = CallOperation::ClassifyCall(*callNode);
162
163 // Assert
164 EXPECT_TRUE(callTypeClassifier->IsIndirectCall());
165 EXPECT_EQ(loadOutput, &callTypeClassifier->GetFunctionOrigin());
166}
167
168TEST(CallOperationTests, TestCallTypeClassifierNonRecursiveDirectCall)
169{
170 // Arrange
171 using namespace jlm::llvm;
172 using namespace jlm::rvsdg;
173
174 auto module = jlm::llvm::LlvmRvsdgModule::Create(jlm::util::FilePath(""), "", "");
175 auto graph = &module->Rvsdg();
176
178 auto iOStateType = IOStateType::Create();
179 auto memoryStateType = MemoryStateType::Create();
180
181 auto functionTypeG = jlm::rvsdg::FunctionType::Create(
182 { IOStateType::Create(), MemoryStateType::Create() },
183 { vt, IOStateType::Create(), MemoryStateType::Create() });
184
185 auto SetupFunctionG = [&]()
186 {
187 auto lambda = jlm::rvsdg::LambdaNode::Create(
188 graph->GetRootRegion(),
189 LlvmLambdaOperation::Create(functionTypeG, "g", Linkage::externalLinkage));
190 auto iOStateArgument = lambda->GetFunctionArguments()[0];
191 auto memoryStateArgument = lambda->GetFunctionArguments()[1];
192
193 auto constant = TestOperation::createNode(lambda->subregion(), {}, { vt });
194
195 auto lambdaOutput =
196 lambda->finalize({ constant->output(0), iOStateArgument, memoryStateArgument });
197
198 return lambdaOutput;
199 };
200
201 auto SetupFunctionF = [&](jlm::rvsdg::Output * g)
202 {
203 auto SetupOuterTheta = [](jlm::rvsdg::Region * region, jlm::rvsdg::Output * functionG)
204 {
205 auto outerTheta = jlm::rvsdg::ThetaNode::create(region);
206 auto otf = outerTheta->AddLoopVar(functionG);
207
208 auto innerTheta = jlm::rvsdg::ThetaNode::create(outerTheta->subregion());
209 auto itf = innerTheta->AddLoopVar(otf.pre);
210
211 auto predicate = &jlm::rvsdg::ControlConstantOperation::createFalse(*innerTheta->subregion());
212 auto gamma = jlm::rvsdg::GammaNode::create(predicate, 2);
213 auto ev = gamma->AddEntryVar(itf.pre);
214 auto xv = gamma->AddExitVar(ev.branchArgument);
215
216 itf.post->divert_to(xv.output);
217 otf.post->divert_to(itf.output);
218
219 return otf;
220 };
221
223 auto iOStateType = IOStateType::Create();
224 auto memoryStateType = MemoryStateType::Create();
225
226 auto functionType = jlm::rvsdg::FunctionType::Create(
227 { IOStateType::Create(), MemoryStateType::Create() },
228 { vt, IOStateType::Create(), MemoryStateType::Create() });
229
230 auto lambda = jlm::rvsdg::LambdaNode::Create(
231 graph->GetRootRegion(),
232 LlvmLambdaOperation::Create(functionType, "f", Linkage::externalLinkage));
233 auto functionGArgument = lambda->AddContextVar(*g).inner;
234 auto iOStateArgument = lambda->GetFunctionArguments()[0];
235 auto memoryStateArgument = lambda->GetFunctionArguments()[1];
236
237 auto functionG = SetupOuterTheta(lambda->subregion(), functionGArgument);
238
239 auto callResults = CallOperation::Create(
240 functionG.output,
241 functionTypeG,
242 { iOStateArgument, memoryStateArgument });
243
244 lambda->finalize(callResults);
245
246 return std::make_tuple(
247 lambda,
248 jlm::util::assertedCast<jlm::rvsdg::SimpleNode>(
250 };
251
252 auto g = SetupFunctionG();
253 auto [f, callNode] = SetupFunctionF(g);
254
255 jlm::rvsdg::GraphExport::Create(*f->output(), "f");
256
257 // jlm::rvsdg::view(&graph->GetRootRegion(), stdout);
258
259 // Act
260 auto callTypeClassifier = CallOperation::ClassifyCall(*callNode);
261
262 // Assert
263 EXPECT_TRUE(callTypeClassifier->IsNonRecursiveDirectCall());
264 EXPECT_EQ(&callTypeClassifier->GetLambdaOutput(), g);
265}
266
267TEST(CallOperationTests, TestCallTypeClassifierNonRecursiveDirectCallTheta)
268{
269 using namespace jlm::llvm;
270 using namespace jlm::rvsdg;
271
272 // Arrange
273 auto module = jlm::llvm::LlvmRvsdgModule::Create(jlm::util::FilePath(""), "", "");
274 auto graph = &module->Rvsdg();
275
277 auto iOStateType = IOStateType::Create();
278 auto memoryStateType = MemoryStateType::Create();
279
280 auto functionTypeG = jlm::rvsdg::FunctionType::Create(
281 { IOStateType::Create(), MemoryStateType::Create() },
282 { vt, IOStateType::Create(), MemoryStateType::Create() });
283
284 auto SetupFunctionG = [&]()
285 {
286 auto lambda = jlm::rvsdg::LambdaNode::Create(
287 graph->GetRootRegion(),
288 LlvmLambdaOperation::Create(functionTypeG, "g", Linkage::externalLinkage));
289 auto iOStateArgument = lambda->GetFunctionArguments()[0];
290 auto memoryStateArgument = lambda->GetFunctionArguments()[1];
291
292 auto c1 = TestOperation::createNode(lambda->subregion(), {}, { vt });
293
294 return lambda->finalize({ c1->output(0), iOStateArgument, memoryStateArgument });
295 };
296
297 auto SetupFunctionF = [&](jlm::rvsdg::Output * g)
298 {
299 auto SetupOuterTheta = [&](jlm::rvsdg::Region * region,
301 jlm::rvsdg::Output * value,
302 jlm::rvsdg::Output * iOState,
303 jlm::rvsdg::Output * memoryState)
304 {
305 auto SetupInnerTheta = [&](jlm::rvsdg::Region * region, jlm::rvsdg::Output * g)
306 {
307 auto innerTheta = jlm::rvsdg::ThetaNode::create(region);
308 auto thetaOutputG = innerTheta->AddLoopVar(g);
309
310 return thetaOutputG;
311 };
312
313 auto outerTheta = jlm::rvsdg::ThetaNode::create(region);
314 auto thetaOutputG = outerTheta->AddLoopVar(g);
315 auto thetaOutputValue = outerTheta->AddLoopVar(value);
316 auto thetaOutputIoState = outerTheta->AddLoopVar(iOState);
317 auto thetaOutputMemoryState = outerTheta->AddLoopVar(memoryState);
318
319 auto functionG = SetupInnerTheta(outerTheta->subregion(), thetaOutputG.pre);
320
321 auto callResults = CallOperation::Create(
322 functionG.output,
323 functionTypeG,
324 { thetaOutputIoState.pre, thetaOutputMemoryState.pre });
325
326 thetaOutputG.post->divert_to(functionG.output);
327 thetaOutputValue.post->divert_to(callResults[0]);
328 thetaOutputIoState.post->divert_to(callResults[1]);
329 thetaOutputMemoryState.post->divert_to(callResults[2]);
330
331 return std::make_tuple(
332 thetaOutputValue,
333 thetaOutputIoState,
334 thetaOutputMemoryState,
335 jlm::util::assertedCast<jlm::rvsdg::SimpleNode>(
337 };
338
340 auto iOStateType = IOStateType::Create();
341 auto memoryStateType = MemoryStateType::Create();
342
343 auto functionType = jlm::rvsdg::FunctionType::Create(
344 { IOStateType::Create(), MemoryStateType::Create() },
345 { vt, IOStateType::Create(), MemoryStateType::Create() });
346
347 auto lambda = jlm::rvsdg::LambdaNode::Create(
348 graph->GetRootRegion(),
349 LlvmLambdaOperation::Create(functionType, "f", Linkage::externalLinkage));
350 auto functionG = lambda->AddContextVar(*g).inner;
351 auto iOStateArgument = lambda->GetFunctionArguments()[0];
352 auto memoryStateArgument = lambda->GetFunctionArguments()[1];
353
354 auto value = TestOperation::createNode(lambda->subregion(), {}, { vt })->output(0);
355
356 auto [loopValue, iOState, memoryState, callNode] = SetupOuterTheta(
357 lambda->subregion(),
358 functionG,
359 value,
360 iOStateArgument,
361 memoryStateArgument);
362
363 auto lambdaOutput = lambda->finalize({ loopValue.output, iOState.output, memoryState.output });
364
365 return std::make_tuple(lambdaOutput, callNode);
366 };
367
368 auto g = SetupFunctionG();
369 auto [f, callNode] = SetupFunctionF(g);
371
372 jlm::rvsdg::view(&graph->GetRootRegion(), stdout);
373
374 // Act
375 auto callTypeClassifier = CallOperation::ClassifyCall(*callNode);
376
377 // Assert
378 EXPECT_TRUE(callTypeClassifier->IsNonRecursiveDirectCall());
379 EXPECT_EQ(&callTypeClassifier->GetLambdaOutput(), g);
380}
381
382TEST(CallOperationTests, TestCallTypeClassifierRecursiveDirectCall)
383{
384 // Arrange
385 using namespace jlm::llvm;
386
387 auto module = LlvmRvsdgModule::Create(jlm::util::FilePath(""), "", "");
388 auto graph = &module->Rvsdg();
389
390 auto SetupFib = [&]()
391 {
392 auto iOStateType = IOStateType::Create();
393 auto memoryStateType = MemoryStateType::Create();
394 auto functionType = jlm::rvsdg::FunctionType::Create(
396 PointerType::Create(),
397 IOStateType::Create(),
398 MemoryStateType::Create() },
399 { IOStateType::Create(), MemoryStateType::Create() });
400 auto pt = PointerType::Create();
401
403 pb.begin(&graph->GetRootRegion());
404 auto fibrv = pb.AddFixVar(functionType);
405
406 auto lambda = jlm::rvsdg::LambdaNode::Create(
407 *pb.subregion(),
408 LlvmLambdaOperation::Create(functionType, "fib", Linkage::externalLinkage));
409 auto valueArgument = lambda->GetFunctionArguments()[0];
410 auto pointerArgument = lambda->GetFunctionArguments()[1];
411 auto iOStateArgument = lambda->GetFunctionArguments()[2];
412 auto memoryStateArgument = lambda->GetFunctionArguments()[3];
413 auto ctxVarFib = lambda->AddContextVar(*fibrv.recref).inner;
414
415 auto two = &jlm::rvsdg::BitConstantOperation::create(*lambda->subregion(), { 64, 2 });
416 auto bitult = jlm::rvsdg::bitult_op::create(64, valueArgument, two);
417 auto & predicateNode = jlm::rvsdg::MatchOperation::CreateNode(*bitult, { { 0, 1 } }, 0, 2);
418
419 auto gammaNode = jlm::rvsdg::GammaNode::create(predicateNode.output(0), 2);
420 auto nev = gammaNode->AddEntryVar(valueArgument);
421 auto resultev = gammaNode->AddEntryVar(pointerArgument);
422 auto fibev = gammaNode->AddEntryVar(ctxVarFib);
423 auto gIIoState = gammaNode->AddEntryVar(iOStateArgument);
424 auto gIMemoryState = gammaNode->AddEntryVar(memoryStateArgument);
425
426 /* gamma subregion 0 */
427 auto one = &jlm::rvsdg::BitConstantOperation::create(*gammaNode->subregion(0), { 64, 1 });
428 auto nm1 = jlm::rvsdg::bitsub_op::create(64, nev.branchArgument[0], one);
429 auto callfibm1Results = CallOperation::Create(
430 fibev.branchArgument[0],
431 functionType,
432 { nm1,
433 resultev.branchArgument[0],
434 gIIoState.branchArgument[0],
435 gIMemoryState.branchArgument[0] });
436
437 two = &jlm::rvsdg::BitConstantOperation::create(*gammaNode->subregion(0), { 64, 2 });
438 auto nm2 = jlm::rvsdg::bitsub_op::create(64, nev.branchArgument[0], two);
439 auto callfibm2Results = CallOperation::Create(
440 fibev.branchArgument[0],
441 functionType,
442 { nm2, resultev.branchArgument[0], callfibm1Results[0], callfibm1Results[1] });
443
444 auto gepnm1 = GetElementPtrOperation::create(
445 resultev.branchArgument[0],
446 { nm1 },
448 auto ldnm1 = LoadNonVolatileOperation::Create(
449 gepnm1,
450 { callfibm2Results[1] },
452 8);
453
454 auto gepnm2 = GetElementPtrOperation::create(
455 resultev.branchArgument[0],
456 { nm2 },
458 auto ldnm2 =
459 LoadNonVolatileOperation::Create(gepnm2, { ldnm1[1] }, jlm::rvsdg::BitType::Create(64), 8);
460
461 auto sum = jlm::rvsdg::bitadd_op::create(64, ldnm1[0], ldnm2[0]);
462
463 /* gamma subregion 1 */
464 /* Nothing needs to be done */
465
466 auto sumex = gammaNode->AddExitVar({ sum, nev.branchArgument[1] });
467 auto gOIoState = gammaNode->AddExitVar({ callfibm2Results[0], gIIoState.branchArgument[1] });
468 auto gOMemoryState = gammaNode->AddExitVar({ ldnm2[1], gIMemoryState.branchArgument[1] });
469
470 auto gepn = GetElementPtrOperation::create(
471 pointerArgument,
472 { valueArgument },
474 auto store = StoreNonVolatileOperation::Create(gepn, sumex.output, { gOMemoryState.output }, 8);
475
476 auto lambdaOutput = lambda->finalize({ gOIoState.output, store[0] });
477
478 fibrv.result->divert_to(lambdaOutput);
479 pb.end();
480
481 jlm::rvsdg::GraphExport::Create(*fibrv.output, "fib");
482
483 return std::make_tuple(
484 lambdaOutput,
485 jlm::util::assertedCast<jlm::rvsdg::SimpleNode>(
487 jlm::util::assertedCast<jlm::rvsdg::SimpleNode>(
488 jlm::rvsdg::TryGetOwnerNode<jlm::rvsdg::Node>(*callfibm2Results[0])));
489 };
490
491 auto [fibfct, callFib1, callFib2] = SetupFib();
492
493 // Act
494 auto callTypeClassifier1 = CallOperation::ClassifyCall(*callFib1);
495 auto callTypeClassifier2 = CallOperation::ClassifyCall(*callFib2);
496
497 // Assert
498 EXPECT_TRUE(callTypeClassifier1->IsRecursiveDirectCall());
499 EXPECT_EQ(&callTypeClassifier1->GetLambdaOutput(), fibfct);
500
501 EXPECT_TRUE(callTypeClassifier2->IsRecursiveDirectCall());
502 EXPECT_EQ(&callTypeClassifier2->GetLambdaOutput(), fibfct);
503}
TEST(CallOperationTests, TestCopy)
Definition CallTests.cpp:25
std::int64_t c1
static const auto vt
Definition PullTests.cpp:16
static Output & create(Region &region, BitValueRepresentation value)
Definition constant.hpp:44
static std::shared_ptr< const BitType > Create(std::size_t nbits)
Creates bit type of specified width.
Definition type.cpp:45
static Output & createFalse(Region &region)
Definition control.hpp:134
static std::shared_ptr< const FunctionType > Create(std::vector< std::shared_ptr< const jlm::rvsdg::Type > > argumentTypes, std::vector< std::shared_ptr< const jlm::rvsdg::Type > > resultTypes)
static GammaNode * create(jlm::rvsdg::Output *predicate, size_t nalternatives)
Definition gamma.hpp:161
static GraphExport & Create(Output &origin, std::string name)
Definition graph.cpp:62
static GraphImport & Create(Graph &graph, std::shared_ptr< const rvsdg::Type > type, std::string name)
Definition graph.cpp:36
static LambdaNode * Create(rvsdg::Region &parent, std::unique_ptr< LambdaOperation > operation)
Definition lambda.cpp:141
std::unique_ptr< BitBinaryOperation > create(size_t nbits) const override
std::unique_ptr< BitCompareOperation > create(size_t nbits) const override
static Node & CreateNode(Output &predicate, const std::unordered_map< uint64_t, uint64_t > &mapping, const uint64_t defaultAlternative, const size_t numAlternatives)
Definition control.hpp:220
PhiNode * end()
Definition Phi.cpp:270
PhiNode::FixVar AddFixVar(std::shared_ptr< const jlm::rvsdg::Type > type)
Definition Phi.cpp:257
void begin(rvsdg::Region *parent)
Definition Phi.hpp:355
rvsdg::Region * subregion() const noexcept
Definition Phi.hpp:349
Represent acyclic RVSDG subgraphs.
Definition region.hpp:213
static std::shared_ptr< const TestType > createValueType()
Definition TestType.cpp:67
static ThetaNode * create(rvsdg::Region *parent)
Definition theta.hpp:73
Global memory state passed between functions.
std::string view(const rvsdg::Region *region)
Definition view.cpp:142
NodeType * TryGetOwnerNode(const rvsdg::Input &input) noexcept
Checks if this is an input to a node of specified type.
Definition node.hpp:872