Jlm
Loading...
Searching...
No Matches
IOBarrierEliminationTests.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2026 Nico Reißmann <nico.reissmann@gmail.com>
3 * See COPYING for terms of redistribution.
4 */
5
6#include <gtest/gtest.h>
7
15#include <jlm/rvsdg/control.hpp>
16#include <jlm/rvsdg/gamma.hpp>
17#include <jlm/rvsdg/lambda.hpp>
21
22namespace jlm::llvm
23{
24
25static void
27{
28
30 IOBarrierElimination ioBarrierElimination;
31 ioBarrierElimination.Run(rvsdgModule, statisticsCollector);
32}
33
34TEST(IOBarrierEliminationTests, testLambdaArgument)
35{
36 using namespace jlm::rvsdg;
37
38 // Arrange
39 auto i32Type = BitType::Create(32);
40 auto pointerType = PointerType::Create();
41 auto ioStateType = IOStateType::Create();
42 auto functionType = FunctionType::Create({ pointerType, ioStateType }, { i32Type, ioStateType });
43
44 auto rvsdgModule = LlvmRvsdgModule::Create(util::FilePath(""), "", "");
45 auto & rvsdg = rvsdgModule->Rvsdg();
46
47 auto lambdaNode = LambdaNode::Create(
48 rvsdg.GetRootRegion(),
50 auto ptrArgument = lambdaNode->GetFunctionArguments()[0];
51 auto ioStateArgument = lambdaNode->GetFunctionArguments()[1];
52
53 auto & hoistBarrierNode =
54 MemoryHoistBarrierOperation::createNode(*ptrArgument, *ioStateArgument, 0);
55
56 auto & loadNode =
57 LoadNonVolatileOperation::CreateNode(*hoistBarrierNode.output(0), {}, i32Type, 4);
58
59 auto lambdaOutput = lambdaNode->finalize({ loadNode.output(0), ioStateArgument });
60 GraphExport::Create(*lambdaOutput, "test");
61
62 // Act
63 runIOBarrierElimination(*rvsdgModule);
64
65 // Assert
66 // We expect the MemoryHoistBarrierOperation node connected to a lambda argument to NOT be
67 // eliminated
68 EXPECT_TRUE(Region::containsOperation<MemoryHoistBarrierOperation>(rvsdg.GetRootRegion(), true));
69}
70
71TEST(IOBarrierEliminationTests, testSizeIsRespected)
72{
73 using namespace jlm::rvsdg;
74
75 // Arrange
76 auto i8Type = BitType::Create(8);
77 auto i32Type = BitType::Create(32);
78 auto pointerType = PointerType::Create();
79 auto ioStateType = IOStateType::Create();
80 auto functionType =
81 FunctionType::Create({ pointerType, ioStateType }, { i8Type, i32Type, ioStateType });
82
83 auto rvsdgModule = LlvmRvsdgModule::Create(util::FilePath(""), "", "");
84 auto & rvsdg = rvsdgModule->Rvsdg();
85
86 auto lambdaNode = LambdaNode::Create(
87 rvsdg.GetRootRegion(),
89 auto ptrArgument = lambdaNode->GetFunctionArguments()[0];
90 auto ioStateArgument = lambdaNode->GetFunctionArguments()[1];
91
92 auto & loadNode1 = LoadNonVolatileOperation::CreateNode(*ptrArgument, {}, i8Type, 4);
93
94 auto testNode =
95 TestOperation::createNode(lambdaNode->subregion(), { ioStateArgument }, { ioStateType });
96
97 auto & hoistBarrierNode =
98 MemoryHoistBarrierOperation::createNode(*ptrArgument, *testNode->output(0), 0);
99
100 auto & loadNode2 =
101 LoadNonVolatileOperation::CreateNode(*hoistBarrierNode.output(0), {}, i32Type, 4);
102
103 auto lambdaOutput =
104 lambdaNode->finalize({ loadNode1.output(0), loadNode2.output(0), testNode->output(0) });
105 GraphExport::Create(*lambdaOutput, "test");
106
107 // Act
108 runIOBarrierElimination(*rvsdgModule);
109
110 // Assert
111 // We expect the MemoryHoistBarrierOperation node to NOT be eliminated as loadNode1 marks the
112 // pointer argument only dereferenceable with size i8, but loadNode2 requires size i32.
113 EXPECT_TRUE(Region::containsOperation<MemoryHoistBarrierOperation>(rvsdg.GetRootRegion(), true));
114}
115
116TEST(IOBarrierEliminationTests, testSuccess)
117{
118 using namespace jlm::rvsdg;
119
120 // Arrange
121 auto i32Type = BitType::Create(32);
122 auto i64Type = BitType::Create(64);
123 auto pointerType = PointerType::Create();
124 auto ioStateType = IOStateType::Create();
125 auto functionType =
126 FunctionType::Create({ pointerType, ioStateType }, { i64Type, i32Type, ioStateType });
127
128 auto rvsdgModule = LlvmRvsdgModule::Create(util::FilePath(""), "", "");
129 auto & rvsdg = rvsdgModule->Rvsdg();
130
131 auto lambdaNode = LambdaNode::Create(
132 rvsdg.GetRootRegion(),
134 auto ptrArgument = lambdaNode->GetFunctionArguments()[0];
135 auto ioStateArgument = lambdaNode->GetFunctionArguments()[1];
136
137 auto & loadNode1 = LoadNonVolatileOperation::CreateNode(*ptrArgument, {}, i64Type, 4);
138
139 auto testNode =
140 TestOperation::createNode(lambdaNode->subregion(), { ioStateArgument }, { ioStateType });
141
142 auto & hoistBarrierNode =
143 MemoryHoistBarrierOperation::createNode(*ptrArgument, *testNode->output(0), 0);
144
145 auto & loadNode2 =
146 LoadNonVolatileOperation::CreateNode(*hoistBarrierNode.output(0), {}, i32Type, 4);
147
148 auto lambdaOutput =
149 lambdaNode->finalize({ loadNode1.output(0), loadNode2.output(0), testNode->output(0) });
150 GraphExport::Create(*lambdaOutput, "test");
151
152 // Act
153 runIOBarrierElimination(*rvsdgModule);
154
155 // Assert
156 // We expect the MemoryHoistBarrierOperation node to be eliminated as loadNode1 marks the pointer
157 // argument dereferenceable with size i64, but loadNode2 only requires size i32.
158 EXPECT_FALSE(Region::containsOperation<MemoryHoistBarrierOperation>(rvsdg.GetRootRegion(), true));
159}
160
161TEST(IOBarrierEliminationTests, testInvidiualIOBarrierUserRerouting)
162{
163 using namespace jlm::rvsdg;
164
165 // Arrange
166 auto i8Type = BitType::Create(8);
167 auto i32Type = BitType::Create(32);
168 auto i64Type = BitType::Create(64);
169 auto pointerType = PointerType::Create();
170 auto ioStateType = IOStateType::Create();
171 auto functionType =
172 FunctionType::Create({ pointerType, ioStateType }, { i32Type, i8Type, i64Type, ioStateType });
173
174 auto rvsdgModule = LlvmRvsdgModule::Create(util::FilePath(""), "", "");
175 auto & rvsdg = rvsdgModule->Rvsdg();
176
177 auto lambdaNode = LambdaNode::Create(
178 rvsdg.GetRootRegion(),
180 auto ptrArgument = lambdaNode->GetFunctionArguments()[0];
181 auto ioStateArgument = lambdaNode->GetFunctionArguments()[1];
182
183 auto & load32Node = LoadNonVolatileOperation::CreateNode(*ptrArgument, {}, i32Type, 4);
184
185 auto testNode =
186 TestOperation::createNode(lambdaNode->subregion(), { ioStateArgument }, { ioStateType });
187
188 auto & hoistBarrierNode =
189 MemoryHoistBarrierOperation::createNode(*ptrArgument, *testNode->output(0), 0);
190
191 auto & load8Node =
192 LoadNonVolatileOperation::CreateNode(*hoistBarrierNode.output(0), {}, i8Type, 4);
193
194 auto & load64Node =
195 LoadNonVolatileOperation::CreateNode(*hoistBarrierNode.output(0), {}, i64Type, 4);
196
197 auto lambdaOutput = lambdaNode->finalize(
198 { load32Node.output(0), load8Node.output(0), load64Node.output(0), testNode->output(0) });
199 GraphExport::Create(*lambdaOutput, "test");
200
201 // Act
202 runIOBarrierElimination(*rvsdgModule);
203
204 // Assert
205 EXPECT_TRUE(Region::containsOperation<MemoryHoistBarrierOperation>(rvsdg.GetRootRegion(), true));
206
207 // We expect that the load8Node is not any longer barred behind the MemoryHoistBarrierOperation
208 // node as ptrArgument is dereferenceable for 32 bits.
209 EXPECT_EQ(LoadOperation::AddressInput(load8Node).origin(), ptrArgument);
210
211 // We expect that the load64Node is still barred behind the MemoryHoistBarrierOperation node as
212 // ptrArgument is only dereferenceable for 32 bits.
213 {
215 *LoadOperation::AddressInput(load64Node).origin());
216 EXPECT_NE(mhbOp, nullptr);
217 EXPECT_EQ(mhbOp->getDereferenceableSize(), 4);
218 }
219}
220
221TEST(IOBarrierEliminationTests, testGamma)
222{
223 using namespace jlm::rvsdg;
224
225 // Arrange
226 auto i32Type = BitType::Create(32);
227 auto i64Type = BitType::Create(64);
228 auto controlType = ControlType::Create(2);
229 auto pointerType = PointerType::Create();
230 auto ioStateType = IOStateType::Create();
231 auto functionType = FunctionType::Create(
232 { pointerType, controlType, ioStateType },
233 { i32Type, i32Type, ioStateType });
234
235 auto rvsdgModule = LlvmRvsdgModule::Create(util::FilePath(""), "", "");
236 auto & rvsdg = rvsdgModule->Rvsdg();
237
238 auto lambdaNode = LambdaNode::Create(
239 rvsdg.GetRootRegion(),
241 auto ptrArgument = lambdaNode->GetFunctionArguments()[0];
242 auto controlArgument = lambdaNode->GetFunctionArguments()[1];
243 auto ioStateArgument = lambdaNode->GetFunctionArguments()[2];
244
245 auto & loadNode = LoadNonVolatileOperation::CreateNode(*ptrArgument, {}, i32Type, 4);
246
247 auto gammaNode = GammaNode::create(controlArgument, 2);
248 auto ptrEntryVar = gammaNode->AddEntryVar(ptrArgument);
249 auto ioStateEntryVar = gammaNode->AddEntryVar(ioStateArgument);
250
251 // subregion 0
252 auto & hoistBarrierNode0 = MemoryHoistBarrierOperation::createNode(
253 *ptrEntryVar.branchArgument[0],
254 *ioStateEntryVar.branchArgument[0],
255 0);
256 auto & load32Node =
257 LoadNonVolatileOperation::CreateNode(*hoistBarrierNode0.output(0), {}, i32Type, 4);
258
259 // subregion 1
260 auto & hoistBarrierNode1 = MemoryHoistBarrierOperation::createNode(
261 *ptrEntryVar.branchArgument[1],
262 *ioStateEntryVar.branchArgument[1],
263 0);
264 auto & load64Node =
265 LoadNonVolatileOperation::CreateNode(*hoistBarrierNode1.output(0), {}, i64Type, 4);
266 auto testNode =
267 TestOperation::createNode(gammaNode->subregion(1), { load64Node.output(0) }, { i32Type });
268
269 // gamma exit
270 auto i32ExitVar = gammaNode->AddExitVar({ load32Node.output(0), testNode->output(0) });
271 auto ioStateExitVar = gammaNode->AddExitVar(
272 { ioStateEntryVar.branchArgument[0], ioStateEntryVar.branchArgument[1] });
273
274 auto lambdaOutput =
275 lambdaNode->finalize({ loadNode.output(0), i32ExitVar.output, ioStateExitVar.output });
276 GraphExport::Create(*lambdaOutput, "test");
277
278 // Act
279 runIOBarrierElimination(*rvsdgModule);
280
281 // Assert
282 // We expect the MemoryHoistBarrierOperation node in gamma subregion 0 to be eliminated
283 EXPECT_FALSE(
284 Region::containsOperation<MemoryHoistBarrierOperation>(*gammaNode->subregion(0), true));
285
286 // We expect the MemoryHoistBarrierOperation nodes in gamma subregion 1 NOT to be eliminated
287 EXPECT_TRUE(
288 Region::containsOperation<MemoryHoistBarrierOperation>(*gammaNode->subregion(1), true));
289}
290
291TEST(IOBarrierEliminationTests, testOnlyLoadsInGamma)
292{
293 using namespace jlm::rvsdg;
294
295 // Arrange
296 auto i32Type = BitType::Create(32);
297 auto controlType = ControlType::Create(2);
298 auto pointerType = PointerType::Create();
299 auto ioStateType = IOStateType::Create();
300 auto functionType =
301 FunctionType::Create({ pointerType, controlType, ioStateType }, { i32Type, ioStateType });
302
303 auto rvsdgModule = LlvmRvsdgModule::Create(util::FilePath(""), "", "");
304 auto & rvsdg = rvsdgModule->Rvsdg();
305
306 auto lambdaNode = LambdaNode::Create(
307 rvsdg.GetRootRegion(),
309 auto ptrArgument = lambdaNode->GetFunctionArguments()[0];
310 auto controlArgument = lambdaNode->GetFunctionArguments()[1];
311 auto ioStateArgument = lambdaNode->GetFunctionArguments()[2];
312
313 auto outerGammaNode = GammaNode::create(controlArgument, 2);
314 auto outerPtrEntryVar = outerGammaNode->AddEntryVar(ptrArgument);
315 auto outerCtlEntryVar = outerGammaNode->AddEntryVar(controlArgument);
316 auto outerIOStateEntryVar = outerGammaNode->AddEntryVar(ioStateArgument);
317
318 // outerGammaNode - subregion 0
320 *outerPtrEntryVar.branchArgument[0],
321 *outerIOStateEntryVar.branchArgument[0],
322 0);
323 auto & load32Node1 = LoadNonVolatileOperation::CreateNode(*mhbNode1.output(0), {}, i32Type, 4);
324
325 // outerGammaNode - subregion 1
326 auto innerGammaNode = GammaNode::create(outerCtlEntryVar.branchArgument[1], 2);
327 auto innerPtrEntryVar = innerGammaNode->AddEntryVar(outerPtrEntryVar.branchArgument[1]);
328 auto innerIOStateEntryVar = innerGammaNode->AddEntryVar(outerIOStateEntryVar.branchArgument[1]);
329
330 // innerGammaNode - subregion 0
332 *innerPtrEntryVar.branchArgument[0],
333 *innerIOStateEntryVar.branchArgument[0],
334 0);
335 auto & load32Node2 = LoadNonVolatileOperation::CreateNode(*mhbNode2.output(0), {}, i32Type, 4);
336
337 // innerGammaNode - subregion 1
339 *innerPtrEntryVar.branchArgument[1],
340 *innerIOStateEntryVar.branchArgument[1],
341 0);
342 auto & load32Node3 = LoadNonVolatileOperation::CreateNode(*mhbNode3.output(0), {}, i32Type, 4);
343
344 // innerGammaNode - finalize
345 auto innerI32ExitVar =
346 innerGammaNode->AddExitVar({ load32Node2.output(0), load32Node3.output(0) });
347 auto innerIOStateExitVar = innerGammaNode->AddExitVar(
348 { innerIOStateEntryVar.branchArgument[0], innerIOStateEntryVar.branchArgument[1] });
349
350 // outerGammaNode - finalize
351 auto outerI32ExitVar =
352 outerGammaNode->AddExitVar({ load32Node1.output(0), innerI32ExitVar.output });
353 auto outerIOStateExitVar = outerGammaNode->AddExitVar(
354 { outerIOStateEntryVar.branchArgument[0], innerIOStateExitVar.output });
355
356 auto lambdaOutput = lambdaNode->finalize({ outerI32ExitVar.output, outerIOStateExitVar.output });
357 GraphExport::Create(*lambdaOutput, "test");
358
359 // Act
360 runIOBarrierElimination(*rvsdgModule);
361
362 // Assert
363
364 {
366 *outerPtrEntryVar.input->origin());
367 EXPECT_NE(mhbOp, nullptr);
368 EXPECT_EQ(mhbOp->getDereferenceableSize(), 0u);
369 }
370
371 // FIXME: Preferably, I would have liked for this MemoryHoistBarrierOperation nodes to be
372 // eliminated, but we need to improve the sweep phase first.
373 {
375 *innerPtrEntryVar.input->origin());
376 EXPECT_NE(mhbOp, nullptr);
377 EXPECT_EQ(mhbOp->getDereferenceableSize(), 4u);
378 }
379}
380
381TEST(IOBarrierEliminationTests, testOnlyLoadsInGammaFailure)
382{
383 using namespace jlm::rvsdg;
384
385 // Arrange
386 auto i32Type = BitType::Create(32);
387 auto controlType = ControlType::Create(2);
388 auto pointerType = PointerType::Create();
389 auto ioStateType = IOStateType::Create();
390 auto functionType =
391 FunctionType::Create({ pointerType, controlType, ioStateType }, { i32Type, ioStateType });
392
393 auto rvsdgModule = LlvmRvsdgModule::Create(util::FilePath(""), "", "");
394 auto & rvsdg = rvsdgModule->Rvsdg();
395
396 auto lambdaNode = LambdaNode::Create(
397 rvsdg.GetRootRegion(),
399 auto ptrArgument = lambdaNode->GetFunctionArguments()[0];
400 auto controlArgument = lambdaNode->GetFunctionArguments()[1];
401 auto ioStateArgument = lambdaNode->GetFunctionArguments()[2];
402
403 auto gammaNode = GammaNode::create(controlArgument, 2);
404 auto ptrEntryVar = gammaNode->AddEntryVar(ptrArgument);
405 auto ioStateEntryVar = gammaNode->AddEntryVar(ioStateArgument);
406
407 // outerGammaNode - subregion 0
409 *ptrEntryVar.branchArgument[0],
410 *ioStateEntryVar.branchArgument[0],
411 0);
412 auto & load32Node1 = LoadNonVolatileOperation::CreateNode(*mhbNode1.output(0), {}, i32Type, 4);
413
414 // outerGammaNode - subregion 1
415 auto testNode = TestOperation::createNode(
416 gammaNode->subregion(1),
417 { ioStateEntryVar.branchArgument[1] },
418 { ioStateType });
420 *ptrEntryVar.branchArgument[1],
421 *testNode->output(0),
422 0);
423 auto & load32Node2 = LoadNonVolatileOperation::CreateNode(*mhbNode2.output(0), {}, i32Type, 4);
424
425 // outerGammaNode - finalize
426 auto outerI32ExitVar = gammaNode->AddExitVar({ load32Node1.output(0), load32Node2.output(0) });
427 auto outerIOStateExitVar =
428 gammaNode->AddExitVar({ ioStateEntryVar.branchArgument[0], testNode->output(0) });
429
430 auto lambdaOutput = lambdaNode->finalize({ outerI32ExitVar.output, outerIOStateExitVar.output });
431 GraphExport::Create(*lambdaOutput, "test");
432
433 // Act
434 runIOBarrierElimination(*rvsdgModule);
435
436 // Assert
437 // We would expect no MemoryHoistBarrierOperation node in the lambda subregion.
438 EXPECT_FALSE(
439 Region::containsOperation<MemoryHoistBarrierOperation>(*lambdaNode->subregion(), false));
440}
441
442TEST(IOBarrierEliminationTests, testLoadsInGammaNoReturn)
443{
444 using namespace jlm::rvsdg;
445
446 // Arrange
447 auto i32Type = BitType::Create(32);
448 auto controlType = ControlType::Create(2);
449 auto pointerType = PointerType::Create();
450 auto ioStateType = IOStateType::Create();
451 auto functionType =
452 FunctionType::Create({ pointerType, controlType, ioStateType }, { i32Type, ioStateType });
453
454 auto rvsdgModule = LlvmRvsdgModule::Create(util::FilePath(""), "", "");
455 auto & rvsdg = rvsdgModule->Rvsdg();
456
457 auto lambdaNode = LambdaNode::Create(
458 rvsdg.GetRootRegion(),
460 auto ptrArgument = lambdaNode->GetFunctionArguments()[0];
461 auto controlArgument = lambdaNode->GetFunctionArguments()[1];
462 auto ioStateArgument = lambdaNode->GetFunctionArguments()[2];
463
464 auto upperGammaNode = GammaNode::create(controlArgument, 2);
465 auto upperPtrEntryVar = upperGammaNode->AddEntryVar(ptrArgument);
466 auto upperIOStateEntryVar = upperGammaNode->AddEntryVar(ioStateArgument);
467
468 // upperGammaNode - subregion 0
470 *upperPtrEntryVar.branchArgument[0],
471 *upperIOStateEntryVar.branchArgument[0],
472 0);
473 auto & load32Node1 = LoadNonVolatileOperation::CreateNode(*mhbNode1.output(0), {}, i32Type, 4);
474
475 // upperGammaNode - subregion 1
476 auto testNode = TestOperation::createNode(
477 upperGammaNode->subregion(1),
478 { upperIOStateEntryVar.branchArgument[1] },
479 { ioStateType });
481 *upperPtrEntryVar.branchArgument[1],
482 *testNode->output(0),
483 0);
484 auto & load32Node2 = LoadNonVolatileOperation::CreateNode(*mhbNode2.output(0), {}, i32Type, 4);
485
486 // upperGammaNode - finalize
487 auto upperI32ExitVar =
488 upperGammaNode->AddExitVar({ load32Node1.output(0), load32Node2.output(0) });
489 auto upperIOStateExitVar =
490 upperGammaNode->AddExitVar({ upperIOStateEntryVar.branchArgument[0], testNode->output(0) });
491
492 auto lowerGammaNode = GammaNode::create(controlArgument, 2);
493 auto lowerPtrEntryVar = lowerGammaNode->AddEntryVar(ptrArgument);
494 auto lowerIOStateEntryVar = lowerGammaNode->AddEntryVar(upperIOStateExitVar.output);
495
496 // lowerGammaNode - subregion 0
498 *lowerPtrEntryVar.branchArgument[0],
499 *lowerIOStateEntryVar.branchArgument[0],
500 0);
501 auto & load32Node3 = LoadNonVolatileOperation::CreateNode(*mhbNode3.output(0), {}, i32Type, 4);
502
503 // lowerGammaNode - subregion 1
505 *lowerPtrEntryVar.branchArgument[1],
506 *lowerIOStateEntryVar.branchArgument[1],
507 0);
508 auto & load32Node4 = LoadNonVolatileOperation::CreateNode(*mhbNode4.output(0), {}, i32Type, 4);
509
510 // lowerGammaNode - finalize
511 auto lowerI32ExitVar =
512 lowerGammaNode->AddExitVar({ load32Node3.output(0), load32Node4.output(0) });
513 auto lowerIOStateExitVar = lowerGammaNode->AddExitVar(
514 { lowerIOStateEntryVar.branchArgument[0], lowerIOStateEntryVar.branchArgument[1] });
515
516 auto testNode2 = TestOperation::createNode(
517 lambdaNode->subregion(),
518 { upperI32ExitVar.output, lowerI32ExitVar.output },
519 { i32Type });
520
521 auto lambdaOutput = lambdaNode->finalize({ testNode2->output(0), lowerIOStateExitVar.output });
522 GraphExport::Create(*lambdaOutput, "test");
523
524 // Act
525 runIOBarrierElimination(*rvsdgModule);
526
527 // Assert
528 // We would expect no MemoryHoistBarrierOperation node in the lowerGamma subregions.
529 EXPECT_FALSE(
530 Region::containsOperation<MemoryHoistBarrierOperation>(*lowerGammaNode->subregion(0), false));
531 EXPECT_FALSE(
532 Region::containsOperation<MemoryHoistBarrierOperation>(*lowerGammaNode->subregion(1), false));
533
534 // We would expect a MemoryHoistBarrierOperation node with size=0 before the ptr entry variable of
535 // lowerGammaNode.
536 {
537 auto [mhbNode, mhbOpt] = TryGetSimpleNodeAndOptionalOp<MemoryHoistBarrierOperation>(
538 *lowerPtrEntryVar.input->origin());
539 EXPECT_NE(mhbOpt, nullptr);
540 EXPECT_EQ(mhbOpt->getDereferenceableSize(), 0u);
541 }
542
543 // We would expect a MemoryHoistBarrierOperation node in each of the upperGamma subregions.
544 EXPECT_TRUE(
545 Region::containsOperation<MemoryHoistBarrierOperation>(*upperGammaNode->subregion(0), false));
546 EXPECT_TRUE(
547 Region::containsOperation<MemoryHoistBarrierOperation>(*upperGammaNode->subregion(1), false));
548}
549
550TEST(IOBarrierEliminationTest, testNormalizeation)
551{
552 using namespace jlm::rvsdg;
553
554 // Arrange
555 auto pointerType = PointerType::Create();
556 auto ioStateType = IOStateType::Create();
557 auto functionType = FunctionType::Create(
558 { pointerType, ioStateType },
559 { pointerType, pointerType, pointerType, pointerType, pointerType, ioStateType });
560
561 Graph rvsdg;
562
563 auto lambdaNode = LambdaNode::Create(
564 rvsdg.GetRootRegion(),
566 auto ptrArgument = lambdaNode->GetFunctionArguments()[0];
567 auto ioStateArgument = lambdaNode->GetFunctionArguments()[1];
568
569 auto & hoistBarrierNode0 =
570 MemoryHoistBarrierOperation::createNode(*ptrArgument, *ioStateArgument, 0);
571
572 auto structuralNode = TestStructuralNode::create(lambdaNode->subregion(), 2);
573 auto ptrInputVar = structuralNode->addInputWithArguments(*ptrArgument);
574 auto ioStateInputVar = structuralNode->addInputWithArguments(*ioStateArgument);
575
576 // subregion 0
577 auto & hoistBarrierNode1 = MemoryHoistBarrierOperation::createNode(
578 *ptrInputVar.argument[0],
579 *ioStateInputVar.argument[0],
580 0);
581
582 // subregion 1
583 // Nothing needs to be done
584
585 // finalize
586 auto ptrOutputVar1 = structuralNode->addOutputWithResults(
587 { hoistBarrierNode1.output(0), ptrInputVar.argument[1] });
588 auto ptrOutputVar2 =
589 structuralNode->addOutputWithResults({ ptrInputVar.argument[0], ptrInputVar.argument[1] });
590 auto ioStateOutputVar = structuralNode->addOutputWithResults(
591 { ioStateInputVar.argument[0], ioStateInputVar.argument[1] });
592
593 auto & hoistBarrierNode2 =
594 MemoryHoistBarrierOperation::createNode(*ptrOutputVar1.output, *ioStateOutputVar.output, 0);
595
596 auto & hoistBarrierNode3 =
597 MemoryHoistBarrierOperation::createNode(*ptrArgument, *ioStateOutputVar.output, 0);
598
599 auto lambdaOutput = lambdaNode->finalize({ hoistBarrierNode0.output(0),
600 hoistBarrierNode2.output(0),
601 ptrOutputVar1.output,
602 ptrOutputVar2.output,
603 hoistBarrierNode3.output(0),
604 ioStateOutputVar.output });
605 GraphExport::Create(*lambdaOutput, "test");
606
607 // Act
609
610 // Assert
611 EXPECT_EQ(ptrArgument->nusers(), 1);
612 EXPECT_EQ(ptrInputVar.argument[0]->nusers(), 1);
613 EXPECT_EQ(ptrOutputVar1.output->nusers(), 1);
614
615 EXPECT_EQ(ptrInputVar.input->origin(), hoistBarrierNode0.output(0));
616 EXPECT_EQ(ptrOutputVar2.result[0]->origin(), hoistBarrierNode1.output(0));
617 EXPECT_EQ(lambdaNode->GetFunctionResults()[2]->origin(), hoistBarrierNode2.output(0));
618 EXPECT_EQ(
619 MemoryHoistBarrierOperation::getAddressInput(hoistBarrierNode3).origin(),
620 hoistBarrierNode0.output(0));
621}
622
623TEST(IOBarrierElimination, testStoreMarking)
624{
625 using namespace jlm::rvsdg;
626
627 // Arrange
628 auto i32Type = BitType::Create(32);
629 auto controlType = ControlType::Create(2);
630 auto pointerType = PointerType::Create();
631 auto ioStateType = IOStateType::Create();
632 auto memoryState = MemoryStateType::Create();
633 auto functionType = FunctionType::Create(
634 { pointerType, i32Type, controlType, ioStateType, memoryState },
635 { i32Type, ioStateType, memoryState });
636
637 auto rvsdgModule = LlvmRvsdgModule::Create(util::FilePath(""), "", "");
638 auto & rvsdg = rvsdgModule->Rvsdg();
639
640 auto lambdaNode = LambdaNode::Create(
641 rvsdg.GetRootRegion(),
643 auto ptrArgument = lambdaNode->GetFunctionArguments()[0];
644 auto i32Argument = lambdaNode->GetFunctionArguments()[1];
645 auto controlArgument = lambdaNode->GetFunctionArguments()[2];
646 auto ioStateArgument = lambdaNode->GetFunctionArguments()[3];
647 auto memoryStateArgument = lambdaNode->GetFunctionArguments()[4];
648
649 auto & storeNode =
650 StoreNonVolatileOperation::CreateNode(*ptrArgument, *i32Argument, { memoryStateArgument }, 4);
651
652 auto gammaNode = GammaNode::create(controlArgument, 2);
653 auto ptrEntryVar = gammaNode->AddEntryVar(ptrArgument);
654 auto ioStateEntryVar = gammaNode->AddEntryVar(ioStateArgument);
655 auto i32EntryVar = gammaNode->AddEntryVar(i32Argument);
656 auto memoryStateEntryVar = gammaNode->AddEntryVar(storeNode.output(0));
657
658 // subregion 0
659 auto & hoistBarrierNode = MemoryHoistBarrierOperation::createNode(
660 *ptrEntryVar.branchArgument[0],
661 *ioStateEntryVar.branchArgument[0],
662 0);
663 auto & load32Node = LoadNonVolatileOperation::CreateNode(
664 *hoistBarrierNode.output(0),
665 { memoryStateEntryVar.branchArgument[0] },
666 i32Type,
667 4);
668
669 // subregion 1
670 // Nothing needs to be done
671
672 auto i32ExitVar = gammaNode->AddExitVar(
673 { &LoadOperation::LoadedValueOutput(load32Node), i32EntryVar.branchArgument[1] });
674 auto ioStateExitVar = gammaNode->AddExitVar(
675 { ioStateEntryVar.branchArgument[0], ioStateEntryVar.branchArgument[1] });
676 auto memoryStateExitVar =
677 gammaNode->AddExitVar({ load32Node.output(1), memoryStateEntryVar.branchArgument[1] });
678
679 auto lambdaOutput =
680 lambdaNode->finalize({ i32ExitVar.output, ioStateExitVar.output, memoryStateExitVar.output });
681 GraphExport::Create(*lambdaOutput, "test");
682
683 // Act
684 runIOBarrierElimination(*rvsdgModule);
685
686 // Assert
687 // We expect both MemoryHoistBarrierOperation nodes to be eliminated
688 EXPECT_FALSE(
689 Region::containsOperation<MemoryHoistBarrierOperation>(*lambdaNode->subregion(), true));
690}
691
692TEST(IOBarrierEliminationTests, testNormalizationFromLoadedAddress)
693{
694 using namespace jlm::rvsdg;
695
696 // Arrange
697 auto i32Type = BitType::Create(32);
698 auto pointerType = PointerType::Create();
699 auto ioStateType = IOStateType::Create();
700 auto controlType = ControlType::Create(2);
701 auto functionType = FunctionType::Create({ pointerType, ioStateType }, { i32Type, ioStateType });
702
703 auto rvsdgModule = LlvmRvsdgModule::Create(util::FilePath(""), "", "");
704 auto & rvsdg = rvsdgModule->Rvsdg();
705
706 auto lambdaNode = LambdaNode::Create(
707 rvsdg.GetRootRegion(),
709 auto ptrArgument = lambdaNode->GetFunctionArguments()[0];
710 auto ioStateArgument = lambdaNode->GetFunctionArguments()[1];
711
712 auto & loadNode1 = LoadNonVolatileOperation::CreateNode(*ptrArgument, {}, pointerType, 4);
713
714 auto & hoistBarrierNode1 = MemoryHoistBarrierOperation::createNode(
716 *ioStateArgument,
717 0);
718 auto & loadNode2 =
719 LoadNonVolatileOperation::CreateNode(*hoistBarrierNode1.output(0), {}, i32Type, 4);
720
721 auto testNode = TestOperation::createNode(lambdaNode->subregion(), {}, { controlType });
722 auto gammaNode = GammaNode::create(testNode->output(0), 2);
723 auto ptrEntryVar = gammaNode->AddEntryVar(&LoadOperation::LoadedValueOutput(loadNode1));
724 auto i32EntryVar = gammaNode->AddEntryVar(&LoadOperation::LoadedValueOutput(loadNode2));
725 auto ioStateEntryVar = gammaNode->AddEntryVar(ioStateArgument);
726
727 // subregion 0
728 auto & hoistBarrierNode2 = MemoryHoistBarrierOperation::createNode(
729 *ptrEntryVar.branchArgument[0],
730 *ioStateEntryVar.branchArgument[0],
731 0);
732 auto & loadNode3 =
733 LoadNonVolatileOperation::CreateNode(*hoistBarrierNode2.output(0), {}, i32Type, 4);
734
735 // subregion 1
736 // Nothing needs to be done
737
738 auto i32ExitVar = gammaNode->AddExitVar(
739 { &LoadOperation::LoadedValueOutput(loadNode3), i32EntryVar.branchArgument[1] });
740 auto ioStateExitVar = gammaNode->AddExitVar(
741 { ioStateEntryVar.branchArgument[0], ioStateEntryVar.branchArgument[1] });
742
743 auto lambdaOutput = lambdaNode->finalize({ i32ExitVar.output, ioStateExitVar.output });
744 GraphExport::Create(*lambdaOutput, "test");
745
746 // Act
748
749 // Assert
750 // We expect the origin of the ptrEntryVar to be the outermost MemoryHoistBarrierOperation node
751 EXPECT_EQ(ptrEntryVar.input->origin(), hoistBarrierNode1.output(0));
752}
753
754}
void Run(rvsdg::RvsdgModule &module, util::StatisticsCollector &statisticsCollector) override
Perform RVSDG transformation.
static void normalizeMemoryHoistBarriers(rvsdg::Region &region)
static std::shared_ptr< const IOStateType > Create()
Definition types.cpp:343
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
static std::unique_ptr< LlvmRvsdgModule > Create(const util::FilePath &sourceFileName, const std::string &targetTriple, const std::string &dataLayout)
static rvsdg::SimpleNode & CreateNode(rvsdg::Region &region, std::unique_ptr< LoadNonVolatileOperation > loadOperation, const std::vector< rvsdg::Output * > &operands)
Definition Load.hpp:470
static rvsdg::Output & LoadedValueOutput(const rvsdg::Node &node)
Definition Load.hpp:84
static rvsdg::Input & AddressInput(const rvsdg::Node &node) noexcept
Definition Load.hpp:75
static rvsdg::SimpleNode & createNode(rvsdg::Output &address, rvsdg::Output &ioState, const std::size_t dereferenceableSize)
static rvsdg::Input & getAddressInput(const rvsdg::Node &node) noexcept
static std::shared_ptr< const MemoryStateType > Create()
Definition types.cpp:379
static std::shared_ptr< const PointerType > Create()
Definition types.cpp:45
static rvsdg::SimpleNode & CreateNode(rvsdg::Output &address, rvsdg::Output &value, const std::vector< rvsdg::Output * > &memoryStates, size_t alignment)
Definition Store.hpp:360
Global memory state passed between functions.
static util::StatisticsCollector statisticsCollector
static void runIOBarrierElimination(LlvmRvsdgModule &rvsdgModule)
TEST(ControlOperationsTests, foldConstants)
NodeType * TryGetOwnerNode(const rvsdg::Input &input) noexcept
Checks if this is an input to a node of specified type.
Definition node.hpp:872