Jlm
Loading...
Searching...
No Matches
LocalAliasAnalysisTests.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
20#include <jlm/llvm/ir/types.hpp>
25#include <jlm/rvsdg/control.hpp>
26#include <jlm/rvsdg/gamma.hpp>
27#include <jlm/rvsdg/graph.hpp>
28#include <jlm/rvsdg/theta.hpp>
29#include <jlm/rvsdg/view.hpp>
30
34static void
37 const jlm::rvsdg::Output & p1,
38 size_t s1,
39 const jlm::rvsdg::Output & p2,
40 size_t s2,
42{
43 const auto actual = aa.Query(p1, s1, p2, s2);
44 EXPECT_EQ(actual, expected);
45
46 // An alias analysis query should always be symmetrical, so check the opposite as well
47 const auto mirror = aa.Query(p2, s2, p1, s1);
48 EXPECT_EQ(mirror, expected);
49}
50
86{
107
108public:
109 const Outputs &
110 GetOutputs() const noexcept
111 {
112 return Outputs_;
113 }
114
115private:
116 std::unique_ptr<jlm::llvm::LlvmRvsdgModule>
117 SetupRvsdg() override
118 {
119 using namespace jlm;
120 using namespace jlm::llvm;
121
122 auto rvsdgModule = LlvmRvsdgModule::Create(jlm::util::FilePath(""), "", "");
123 auto & rvsdg = rvsdgModule->Rvsdg();
124
125 const auto pointerType = PointerType::Create();
126 const auto intType = rvsdg::BitType::Create(32);
127 const auto shortType = rvsdg::BitType::Create(16);
128 const auto byteType = rvsdg::BitType::Create(8);
129 const auto intArrayType = ArrayType::Create(intType, 10);
130 const auto ioStateType = IOStateType::Create();
131 const auto memoryStateType = MemoryStateType::Create();
132
133 const auto funcType = rvsdg::FunctionType::Create(
134 { pointerType, ioStateType, memoryStateType },
135 { ioStateType, memoryStateType });
136
137 const auto getPtrFuncType = rvsdg::FunctionType::Create(
138 { ioStateType, memoryStateType },
139 { pointerType, ioStateType, memoryStateType });
140
141 Outputs_.GetPtr = &LlvmGraphImport::createFunctionImport(
142 rvsdg,
143 getPtrFuncType,
144 "getPtr",
145 Linkage::externalLinkage,
146 CallingConvention::Default);
147
148 Outputs_.Global = &LlvmGraphImport::createGlobalImport(
149 rvsdg,
150 intType,
151 pointerType,
152 "global",
153 Linkage::externalLinkage,
154 false,
155 4);
156 Outputs_.GlobalShort = &LlvmGraphImport::createGlobalImport(
157 rvsdg,
158 shortType,
159 pointerType,
160 "globalShort",
161 Linkage::externalLinkage,
162 false,
163 4);
164 Outputs_.Array = &LlvmGraphImport::createGlobalImport(
165 rvsdg,
166 intArrayType,
167 pointerType,
168 "array",
169 Linkage::externalLinkage,
170 false,
171 4);
172
173 // Setup the function "func"
174 {
175 auto & lambdaNode = *rvsdg::LambdaNode::Create(
176 rvsdg.GetRootRegion(),
177 LlvmLambdaOperation::Create(funcType, "func", Linkage::internalLinkage));
178
179 Outputs_.P = lambdaNode.GetFunctionArguments()[0];
180 auto ioState = lambdaNode.GetFunctionArguments()[1];
181 auto memoryState = lambdaNode.GetFunctionArguments()[2];
182
183 const auto getPtrCtxVar = lambdaNode.AddContextVar(*Outputs_.GetPtr).inner;
184 const auto arrayCtxVar = lambdaNode.AddContextVar(*Outputs_.Array).inner;
185 const auto globalCtxVar = lambdaNode.AddContextVar(*Outputs_.Global).inner;
186
187 const auto constantOne =
188 &rvsdg::BitConstantOperation::create(*lambdaNode.subregion(), { 32, 1 });
189 const auto constantTwo =
190 &rvsdg::BitConstantOperation::create(*lambdaNode.subregion(), { 32, 2 });
191 const auto constantThree =
192 &rvsdg::BitConstantOperation::create(*lambdaNode.subregion(), { 32, 3 });
193 const auto constantFour =
194 &rvsdg::BitConstantOperation::create(*lambdaNode.subregion(), { 32, 4 });
195 const auto constantMinusTwo =
196 &rvsdg::BitConstantOperation::create(*lambdaNode.subregion(), { 32, -2 });
197
198 const auto alloca1Outputs = AllocaOperation::create(intType, constantOne, 4);
199 const auto alloca2Outputs = AllocaOperation::create(intType, constantOne, 4);
200
201 Outputs_.Alloca1 = alloca1Outputs[0];
202 Outputs_.Alloca2 = alloca2Outputs[0];
203
204 memoryState =
205 MemoryStateMergeOperation::Create({ memoryState, alloca1Outputs[1], alloca2Outputs[1] });
206
207 // Load from the pointer p
208 const auto loadP =
209 LoadNonVolatileOperation::Create(Outputs_.P, { memoryState }, pointerType, 8);
210 memoryState = loadP[1];
211
212 Outputs_.Q = GetElementPtrOperation::create(loadP[0], { constantTwo }, intType);
213 Outputs_.QPlus2 = GetElementPtrOperation::create(loadP[0], { constantFour }, intType);
215 GetElementPtrOperation::create(Outputs_.QPlus2, { constantMinusTwo }, intType);
216
217 // Create offsets into array
218 Outputs_.Arr1 = GetElementPtrOperation::create(arrayCtxVar, { constantOne }, intType);
219 Outputs_.Arr2 = GetElementPtrOperation::create(arrayCtxVar, { constantTwo }, intType);
220 Outputs_.Arr3 = GetElementPtrOperation::create(arrayCtxVar, { constantThree }, intType);
221
222 // Create a load of the global integer variable "global"
223 const auto loadGlobal =
224 LoadNonVolatileOperation::Create(globalCtxVar, { memoryState }, intType, 4);
225 memoryState = loadGlobal[1];
227 GetElementPtrOperation::create(arrayCtxVar, { loadGlobal[0] }, byteType);
228
229 // Make alloca2 escape
230 const auto storeOutputs =
231 StoreNonVolatileOperation::Create(Outputs_.P, Outputs_.Alloca2, { memoryState }, 4);
232 memoryState = storeOutputs[0];
233
234 // Get bytePtr by calling getPtr()
235 const auto callOutputs =
236 CallOperation::Create(getPtrCtxVar, getPtrFuncType, { ioState, memoryState });
237 Outputs_.BytePtr = callOutputs[0];
238 ioState = callOutputs[1];
239 memoryState = callOutputs[2];
240
242 GetElementPtrOperation::create(Outputs_.BytePtr, { constantTwo }, byteType);
243
244 lambdaNode.finalize({ ioState, memoryState });
245 Outputs_.Func = lambdaNode.output();
246 }
247
248 return rvsdgModule;
249 }
250
252};
253
254TEST(LocalAliasAnalysisTests, TestLocalAliasAnalysis)
255{
256 using namespace jlm::llvm::aa;
257
258 // Arrange
260 rvsdg.InitializeTest();
261 const auto & outputs = rvsdg.GetOutputs();
262
263 jlm::rvsdg::view(&rvsdg.graph().GetRootRegion(), stdout);
264
266
267 // Assert
268
269 // Distinct global variables do not alias
270 Expect(aa, *outputs.Global, 4, *outputs.GlobalShort, 2, AliasAnalysis::NoAlias);
271 Expect(aa, *outputs.Global, 4, *outputs.Arr1, 4, AliasAnalysis::NoAlias);
272
273 // An alloca never aliases any other memory allocating operation
274 Expect(aa, *outputs.Alloca2, 4, *outputs.Alloca1, 4, AliasAnalysis::NoAlias);
275 Expect(aa, *outputs.Alloca2, 4, *outputs.Global, 4, AliasAnalysis::NoAlias);
276 Expect(aa, *outputs.Alloca2, 4, *outputs.Array, 4, AliasAnalysis::NoAlias);
277 Expect(aa, *outputs.Alloca2, 4, *outputs.Arr1, 4, AliasAnalysis::NoAlias);
278
279 // An alloca that has not "escaped" can not alias external pointers
280 Expect(aa, *outputs.Alloca1, 4, *outputs.BytePtr, 4, AliasAnalysis::NoAlias);
281
282 // An alloca that has "escaped" may alias external pointers
283 Expect(aa, *outputs.Alloca2, 4, *outputs.BytePtr, 4, AliasAnalysis::MayAlias);
284
285 // Distinct offsets can not alias, unless the access regions overlap
286 Expect(aa, *outputs.Q, 8, *outputs.QPlus2, 8, AliasAnalysis::NoAlias);
287 Expect(aa, *outputs.Q, 9, *outputs.QPlus2, 8, AliasAnalysis::MayAlias);
288 Expect(aa, *outputs.Q, 8, *outputs.QPlus2, 16, AliasAnalysis::NoAlias);
289
290 // Identical offsets are MustAlias
291 Expect(aa, *outputs.Q, 4, *outputs.QAgain, 4, AliasAnalysis::MustAlias);
292
293 // q is at least 8 bytes into the storage instance of *p
294 // so it can not alias with the first 8 bytes of array
295 Expect(aa, *outputs.Array, 8, *outputs.Q, 4, AliasAnalysis::NoAlias);
296 Expect(aa, *outputs.Array, 9, *outputs.Q, 4, AliasAnalysis::MayAlias);
297 // We know that arr1, arr2 and arr3 are 4, 8 and 12 bytes into array
298 Expect(aa, *outputs.Arr1, 4, *outputs.Q, 4, AliasAnalysis::NoAlias);
299 Expect(aa, *outputs.Arr1, 5, *outputs.Q, 4, AliasAnalysis::MayAlias);
300 Expect(aa, *outputs.Arr2, 4, *outputs.Q, 4, AliasAnalysis::MayAlias);
301 Expect(aa, *outputs.Arr3, 4, *outputs.Q, 4, AliasAnalysis::MayAlias);
302
303 // An unknown offset into array can only alias with array, at all offsets
304 Expect(aa, *outputs.ArrUnknown, 4, *outputs.Array, 4, AliasAnalysis::MayAlias);
305 Expect(aa, *outputs.ArrUnknown, 4, *outputs.Arr1, 4, AliasAnalysis::MayAlias);
306 Expect(aa, *outputs.ArrUnknown, 4, *outputs.Arr2, 4, AliasAnalysis::MayAlias);
307 Expect(aa, *outputs.ArrUnknown, 4, *outputs.Arr3, 4, AliasAnalysis::MayAlias);
308 Expect(aa, *outputs.ArrUnknown, 4, *outputs.Global, 4, AliasAnalysis::NoAlias);
309 // Q may be a pointer into array, so it is also "MayAlias"
310 Expect(aa, *outputs.ArrUnknown, 4, *outputs.Q, 4, AliasAnalysis::MayAlias);
311
312 // We know that q is at least 16 bytes into its storage instance,
313 // so it may not alias with storage instances that are 16 bytes or less
314 Expect(aa, *outputs.Q, 4, *outputs.Global, 4, AliasAnalysis::NoAlias);
315
316 // A five byte operation can never target the 4 byte global variable
317 Expect(aa, *outputs.BytePtr, 5, *outputs.Global, 4, AliasAnalysis::NoAlias);
318 // A four byte operation can, however
319 Expect(aa, *outputs.BytePtr, 4, *outputs.Global, 4, AliasAnalysis::MayAlias);
320 // Even a 40 byte operation can target the 40 byte global array
321 Expect(aa, *outputs.BytePtr, 40, *outputs.Array, 4, AliasAnalysis::MayAlias);
322 // The 40 byte operation may overlap with 4 bytes at any offset within the Array
323 Expect(aa, *outputs.BytePtr, 40, *outputs.Arr3, 4, AliasAnalysis::MayAlias);
324
325 // BytePtrPlus2 has an offset of at least 2, so can not alias with the first 2 bytes of anything
326 Expect(aa, *outputs.BytePtrPlus2, 2, *outputs.Alloca2, 2, AliasAnalysis::NoAlias);
327 Expect(aa, *outputs.BytePtrPlus2, 2, *outputs.Alloca2, 3, AliasAnalysis::MayAlias);
328 Expect(aa, *outputs.BytePtrPlus2, 2, *outputs.Array, 2, AliasAnalysis::NoAlias);
329 Expect(aa, *outputs.BytePtrPlus2, 2, *outputs.Array, 3, AliasAnalysis::MayAlias);
330 // Arr1 is already 4 bytes into array, so BytePtrPlus2 can alias with it
331 Expect(aa, *outputs.BytePtrPlus2, 2, *outputs.Arr1, 2, AliasAnalysis::MayAlias);
332}
333
365{
380
381public:
382 const Outputs &
383 GetOutputs() const noexcept
384 {
385 return Outputs_;
386 }
387
388private:
389 std::unique_ptr<jlm::llvm::LlvmRvsdgModule>
390 SetupRvsdg() override
391 {
392 using namespace jlm;
393 using namespace jlm::llvm;
394
395 auto rvsdgModule = LlvmRvsdgModule::Create(jlm::util::FilePath(""), "", "");
396 auto & rvsdg = rvsdgModule->Rvsdg();
397
398 const auto pointerType = PointerType::Create();
399 const auto int1Type = rvsdg::BitType::Create(1);
400 const auto int32Type = rvsdg::BitType::Create(32);
401 const auto int64Type = rvsdg::BitType::Create(64);
402 const auto intArrayType = ArrayType::Create(int32Type, 2);
403 const auto ioStateType = IOStateType::Create();
404 const auto memoryStateType = MemoryStateType::Create();
405
406 const auto funcType = rvsdg::FunctionType::Create(
407 { int1Type, pointerType, ioStateType, memoryStateType },
408 { ioStateType, memoryStateType });
409
410 // Setup the function "func"
411 {
412 auto & lambdaNode = *rvsdg::LambdaNode::Create(
413 rvsdg.GetRootRegion(),
414 LlvmLambdaOperation::Create(funcType, "func", Linkage::internalLinkage));
415
416 Outputs_.X = lambdaNode.GetFunctionArguments()[0];
417 Outputs_.Ptr = lambdaNode.GetFunctionArguments()[1];
418 auto ioState = lambdaNode.GetFunctionArguments()[2];
419 auto memoryState = lambdaNode.GetFunctionArguments()[3];
420
421 const auto constantZero =
422 &rvsdg::BitConstantOperation::create(*lambdaNode.subregion(), { 32, 0 });
423 const auto constantOne =
424 &rvsdg::BitConstantOperation::create(*lambdaNode.subregion(), { 32, 1 });
425
426 const auto alloca1Outputs = AllocaOperation::create(int32Type, constantOne, 4);
427 const auto alloca2Outputs = AllocaOperation::create(int64Type, constantOne, 4);
428 const auto alloca3Outputs = AllocaOperation::create(intArrayType, constantOne, 4);
429
430 Outputs_.Alloca1 = alloca1Outputs[0];
431 Outputs_.Alloca2 = alloca2Outputs[0];
432 Outputs_.Alloca3 = alloca3Outputs[0];
433
434 memoryState = MemoryStateMergeOperation::Create(
435 { memoryState, alloca1Outputs[1], alloca2Outputs[1], alloca3Outputs[1] });
436
437 const auto matchResult = rvsdg::MatchOperation::Create(*Outputs_.X, { { 1, 1 } }, 0, 2);
438 const auto gamma = rvsdg::GammaNode::create(matchResult, 2);
439 const auto entryVarA1 = gamma->AddEntryVar(Outputs_.Alloca1);
440 const auto entryVarA2 = gamma->AddEntryVar(Outputs_.Alloca2);
441 const auto exitVar =
442 gamma->AddExitVar({ entryVarA1.branchArgument[0], entryVarA2.branchArgument[1] });
443 Outputs_.AllocaUnknown = exitVar.output;
444
446 GetElementPtrOperation::create(Outputs_.AllocaUnknown, { constantOne }, int32Type);
447
448 Outputs_.Alloca3Plus1 = GetElementPtrOperation::create(
450 { constantZero, constantOne },
451 intArrayType);
452
453 Outputs_.Alloca3UnknownOffset = rvsdg::CreateOpNode<SelectOperation>(
455 pointerType)
456 .output(0);
457
459 rvsdg::CreateOpNode<SelectOperation>(
461 pointerType)
462 .output(0);
463
464 lambdaNode.finalize({ ioState, memoryState });
465 Outputs_.Func = lambdaNode.output();
466 }
467
468 return rvsdgModule;
469 }
470
471 Outputs Outputs_ = {};
472};
473
474TEST(LocalAliasAnalysisTests, TestLocalAliasAnalysisMultipleOrigins)
475{
476 using namespace jlm::llvm::aa;
477
478 // Arrange
480 rvsdg.InitializeTest();
481 const auto & outputs = rvsdg.GetOutputs();
482
483 jlm::rvsdg::view(&rvsdg.graph().GetRootRegion(), stdout);
484
486
487 // Assert
488
489 // First check that none of the allocas have been mixed up with unknown pointers
490 Expect(aa, *outputs.Alloca1, 4, *outputs.Ptr, 4, AliasAnalysis::NoAlias);
491 Expect(aa, *outputs.Alloca2, 4, *outputs.Ptr, 4, AliasAnalysis::NoAlias);
492 Expect(aa, *outputs.Alloca3, 4, *outputs.Ptr, 4, AliasAnalysis::NoAlias);
493 Expect(aa, *outputs.Alloca3Plus1, 4, *outputs.Ptr, 4, AliasAnalysis::NoAlias);
494
495 // Check that allocaUnknown may alias only alloca1 or alloca2
496 Expect(aa, *outputs.AllocaUnknown, 4, *outputs.Alloca1, 4, AliasAnalysis::MayAlias);
497 Expect(aa, *outputs.AllocaUnknown, 4, *outputs.Alloca2, 4, AliasAnalysis::MayAlias);
498 Expect(aa, *outputs.AllocaUnknown, 4, *outputs.Alloca3, 4, AliasAnalysis::NoAlias);
499 Expect(aa, *outputs.AllocaUnknown, 4, *outputs.Alloca3Plus1, 4, AliasAnalysis::NoAlias);
500 Expect(aa, *outputs.AllocaUnknown, 4, *outputs.Ptr, 4, AliasAnalysis::NoAlias);
501
502 // If performing an 8 byte operation, it may only alias alloca2, becoming a must alias
503 Expect(aa, *outputs.AllocaUnknown, 8, *outputs.Alloca1, 4, AliasAnalysis::NoAlias);
504 Expect(aa, *outputs.AllocaUnknown, 8, *outputs.Alloca2, 4, AliasAnalysis::MustAlias);
505 // Performing a 9 byte operation is neither legal for alloca1 nor alloca2
506 Expect(aa, *outputs.AllocaUnknown, 9, *outputs.Alloca2, 4, AliasAnalysis::NoAlias);
507
508 // Adding a 4 byte offset forces all operations to be on alloca2
509 Expect(aa, *outputs.AllocaUnknownPlus1, 1, *outputs.Alloca1, 1, AliasAnalysis::NoAlias);
510 // We also know that we are 4 bytes into alloca2
511 Expect(aa, *outputs.AllocaUnknownPlus1, 4, *outputs.Alloca2, 4, AliasAnalysis::NoAlias);
512 Expect(aa, *outputs.AllocaUnknownPlus1, 4, *outputs.Alloca2, 5, AliasAnalysis::MayAlias);
513 // Performing a 5 byte operation is neither legal for alloca1 nor alloca2
514 Expect(aa, *outputs.AllocaUnknownPlus1, 5, *outputs.Alloca2, 8, AliasAnalysis::NoAlias);
515
516 // Check that the offset of allocaUnknown is correctly calculated (4 bytes)
517 Expect(aa, *outputs.AllocaUnknown, 4, *outputs.AllocaUnknownPlus1, 4, AliasAnalysis::NoAlias);
518 Expect(aa, *outputs.AllocaUnknown, 5, *outputs.AllocaUnknownPlus1, 4, AliasAnalysis::MayAlias);
519
520 // Check that the pointer with an unknown offset into alloca3 does not alias anything else
521 Expect(aa, *outputs.Alloca3UnknownOffset, 4, *outputs.Alloca1, 4, AliasAnalysis::NoAlias);
522 Expect(aa, *outputs.Alloca3UnknownOffset, 4, *outputs.Alloca2, 4, AliasAnalysis::NoAlias);
523 Expect(aa, *outputs.Alloca3UnknownOffset, 4, *outputs.AllocaUnknown, 4, AliasAnalysis::NoAlias);
524 Expect(aa, *outputs.Alloca3UnknownOffset, 4, *outputs.Ptr, 4, AliasAnalysis::NoAlias);
525
526 // It may alias alloca3 and alloca3 + 1
527 Expect(aa, *outputs.Alloca3UnknownOffset, 4, *outputs.Alloca3, 4, AliasAnalysis::MayAlias);
528 Expect(aa, *outputs.Alloca3UnknownOffset, 4, *outputs.Alloca3Plus1, 4, AliasAnalysis::MayAlias);
529
530 // If performing an 8 byte operation, we know that we are at the start of alloca3
531 Expect(aa, *outputs.Alloca3UnknownOffset, 8, *outputs.Alloca3, 4, AliasAnalysis::MustAlias);
532 // We still overlap with the second half of alloca3
533 Expect(aa, *outputs.Alloca3UnknownOffset, 8, *outputs.Alloca3Plus1, 4, AliasAnalysis::MayAlias);
534
535 // The select with duplicate operands should be a single origin: alloca3
536 Expect(aa, *outputs.Alloca3KnownOffset, 4, *outputs.Alloca3, 4, AliasAnalysis::NoAlias);
537 Expect(aa, *outputs.Alloca3KnownOffset, 4, *outputs.Alloca3Plus1, 4, AliasAnalysis::MustAlias);
538}
539
564{
576
577public:
578 const Outputs &
579 GetOutputs() const noexcept
580 {
581 return Outputs_;
582 }
583
584private:
585 std::unique_ptr<jlm::llvm::LlvmRvsdgModule>
586 SetupRvsdg() override
587 {
588 using namespace jlm;
589 using namespace jlm::llvm;
590
591 auto rvsdgModule = LlvmRvsdgModule::Create(jlm::util::FilePath(""), "", "");
592 auto & rvsdg = rvsdgModule->Rvsdg();
593
594 const auto pointerType = PointerType::Create();
595 const auto intType = rvsdg::BitType::Create(32);
596 const auto intArrayType = ArrayType::Create(intType, 101);
597 const auto memoryStateType = MemoryStateType::Create();
598
599 const auto funcType =
600 rvsdg::FunctionType::Create({ pointerType, memoryStateType }, { intType, memoryStateType });
601
602 Outputs_.GlobalArray = &LlvmGraphImport::createGlobalImport(
603 rvsdg,
604 intArrayType,
605 pointerType,
606 "globalArray",
607 Linkage::externalLinkage,
608 false,
609 4);
610
611 {
612 auto & lambdaNode = *rvsdg::LambdaNode::Create(
613 rvsdg.GetRootRegion(),
614 LlvmLambdaOperation::Create(funcType, "func", Linkage::internalLinkage));
615
616 Outputs_.Unknown = lambdaNode.GetFunctionArguments()[0];
617 auto memoryState = lambdaNode.GetFunctionArguments()[1];
618 const auto globalArrayCtxVar = lambdaNode.AddContextVar(*Outputs_.GlobalArray).inner;
619
620 const auto constantZero =
621 &rvsdg::BitConstantOperation::create(*lambdaNode.subregion(), { 32, 0 });
622 const auto constantOne =
623 &rvsdg::BitConstantOperation::create(*lambdaNode.subregion(), { 32, 1 });
624
625 const auto allocaArrayOutputs = AllocaOperation::create(intArrayType, constantOne, 4);
626 Outputs_.Array = allocaArrayOutputs[0];
627 memoryState = MemoryStateMergeOperation::Create(
628 std::vector<jlm::rvsdg::Output *>{ memoryState, allocaArrayOutputs[1] });
629
630 const auto initialP = GetElementPtrOperation::create(
632 { constantZero, constantZero },
633 intArrayType);
634 const auto initialQ = GetElementPtrOperation::create(
635 globalArrayCtxVar,
636 { constantZero, constantZero },
637 intArrayType);
638
639 auto thetaNode = rvsdg::ThetaNode::create(lambdaNode.subregion());
640 auto i = thetaNode->AddLoopVar(constantZero);
641 auto p = thetaNode->AddLoopVar(initialP);
642 auto q = thetaNode->AddLoopVar(initialQ);
643 auto m = thetaNode->AddLoopVar(memoryState);
644
645 const auto constantOneTheta =
646 &rvsdg::BitConstantOperation::create(*thetaNode->subregion(), { 32, 1 });
647 const auto constantHundredTheta =
648 &rvsdg::BitConstantOperation::create(*thetaNode->subregion(), { 32, 100 });
649
650 Outputs_.PInLoop = p.pre;
651 Outputs_.QInLoop = q.pre;
652
653 const auto storeP = StoreNonVolatileOperation::Create(Outputs_.PInLoop, i.pre, { m.pre }, 4);
654 const auto storeQ =
655 StoreNonVolatileOperation::Create(Outputs_.QInLoop, i.pre, { storeP[0] }, 4);
656 const auto nextP = GetElementPtrOperation::create(p.pre, { constantOneTheta }, intType);
657 const auto nextQ = GetElementPtrOperation::create(q.pre, { constantOneTheta }, intType);
658 const auto increment = jlm::rvsdg::bitadd_op::create(32, i.pre, constantOneTheta);
659 const auto continueLoop = jlm::rvsdg::bitult_op::create(32, increment, constantHundredTheta);
660 auto & predicateNode = rvsdg::MatchOperation::CreateNode(*continueLoop, { { 1, 1 } }, 0, 2);
661
662 i.post->divert_to(increment);
663 p.post->divert_to(nextP);
664 q.post->divert_to(nextQ);
665 m.post->divert_to(storeQ[0]);
666 thetaNode->set_predicate(predicateNode.output(0));
667
668 Outputs_.PAfterLoop = p.output;
669 Outputs_.QAfterLoop = q.output;
670
671 const auto loadP =
672 LoadNonVolatileOperation::Create(Outputs_.PAfterLoop, { m.output }, intType, 4);
673 const auto loadQ =
674 LoadNonVolatileOperation::Create(Outputs_.QAfterLoop, { loadP[1] }, intType, 4);
675 const auto sum = jlm::rvsdg::bitadd_op::create(32, loadP[0], loadQ[0]);
676
677 lambdaNode.finalize({ sum, loadQ[1] });
678 Outputs_.Func = lambdaNode.output();
679 }
680
681 return rvsdgModule;
682 }
683
685};
686
687TEST(LocalAliasAnalysisTests, testLoopVariantPointers)
688{
689 using namespace jlm;
690 using namespace jlm::llvm;
691 using namespace jlm::llvm::aa;
692
704 // Arrange
706 rvsdg.InitializeTest();
707 const auto & outputs = rvsdg.GetOutputs();
708
709 jlm::rvsdg::view(&rvsdg.graph().GetRootRegion(), stdout);
710
712
713 // Assert for loop-internal pointers
714 Expect(aa, *outputs.PInLoop, 4, *outputs.QInLoop, 4, AliasAnalysis::NoAlias);
715 Expect(aa, *outputs.PInLoop, 4, *outputs.GlobalArray, 4, AliasAnalysis::NoAlias);
716 Expect(aa, *outputs.QInLoop, 4, *outputs.Array, 4, AliasAnalysis::NoAlias);
717 Expect(aa, *outputs.PInLoop, 4, *outputs.Array, 4, AliasAnalysis::MayAlias);
718 Expect(aa, *outputs.QInLoop, 4, *outputs.GlobalArray, 4, AliasAnalysis::MayAlias);
719 // Against the unknown pointer
720 Expect(aa, *outputs.PInLoop, 4, *outputs.Unknown, 4, AliasAnalysis::NoAlias);
721 Expect(aa, *outputs.QInLoop, 4, *outputs.Unknown, 4, AliasAnalysis::MayAlias);
722
723 // Assert for loop-external pointers traced through the theta node
724 Expect(aa, *outputs.PAfterLoop, 4, *outputs.QAfterLoop, 4, AliasAnalysis::NoAlias);
725 Expect(aa, *outputs.PAfterLoop, 4, *outputs.GlobalArray, 4, AliasAnalysis::NoAlias);
726 Expect(aa, *outputs.QAfterLoop, 4, *outputs.Array, 4, AliasAnalysis::NoAlias);
727 Expect(aa, *outputs.PAfterLoop, 4, *outputs.Array, 4, AliasAnalysis::MayAlias);
728 Expect(aa, *outputs.QAfterLoop, 4, *outputs.GlobalArray, 4, AliasAnalysis::MayAlias);
729 // Against the unknown pointer
730 Expect(aa, *outputs.PAfterLoop, 4, *outputs.Unknown, 4, AliasAnalysis::NoAlias);
731 Expect(aa, *outputs.QAfterLoop, 4, *outputs.Unknown, 4, AliasAnalysis::MayAlias);
732}
733
753{
760
761public:
762 const Outputs &
763 GetOutputs() const noexcept
764 {
765 return Outputs_;
766 }
767
768private:
769 std::unique_ptr<jlm::llvm::LlvmRvsdgModule>
770 SetupRvsdg() override
771 {
772 using namespace jlm;
773 using namespace jlm::llvm;
774
775 auto rvsdgModule = LlvmRvsdgModule::Create(jlm::util::FilePath(""), "", "");
776 auto & rvsdg = rvsdgModule->Rvsdg();
777
778 const auto pointerType = PointerType::Create();
779 const auto intType = rvsdg::BitType::Create(32);
780 const auto ioStateType = IOStateType::Create();
781 const auto memoryStateType = MemoryStateType::Create();
782
783 const auto funcType = rvsdg::FunctionType::Create(
784 { pointerType, ioStateType, memoryStateType },
785 { intType, ioStateType, memoryStateType });
786
787 auto & opaqueImport = LlvmGraphImport::createFunctionImport(
788 rvsdg,
789 funcType,
790 "opaque",
791 Linkage::externalLinkage,
792 CallingConvention::Default);
793
794 {
795 auto & lambdaNode = *rvsdg::LambdaNode::Create(
796 rvsdg.GetRootRegion(),
797 LlvmLambdaOperation::Create(funcType, "func", Linkage::internalLinkage));
798
799 Outputs_.Arg = lambdaNode.GetFunctionArguments()[0];
800 auto ioState = lambdaNode.GetFunctionArguments()[1];
801 auto memoryState = lambdaNode.GetFunctionArguments()[2];
802 const auto opaqueCtxVar = lambdaNode.AddContextVar(opaqueImport);
803
804 const auto constantOne =
805 IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 1).output(0);
806 const auto allocaOutputs = AllocaOperation::create(intType, constantOne, 4);
807
808 Outputs_.LocalAlloca = allocaOutputs[0];
809 memoryState = MemoryStateMergeOperation::Create(
810 std::vector<jlm::rvsdg::Output *>{ memoryState, allocaOutputs[1] });
811
812 // call opaque(&local);
813 auto & callOpaque = CallOperation::CreateNode(
814 opaqueCtxVar.inner,
815 funcType,
816 { Outputs_.LocalAlloca, ioState, memoryState });
817 ioState = &CallOperation::GetIOStateOutput(callOpaque);
818 memoryState = &CallOperation::GetMemoryStateOutput(callOpaque);
819
820 // local = 20
821 const auto constantTwenty =
822 IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 20).output(0);
823 const auto storeLocalOutputs = StoreNonVolatileOperation::Create(
825 constantTwenty,
826 { memoryState },
827 4);
828 memoryState = storeLocalOutputs[0];
829
830 // return *arg
831 const auto loadArgOutputs =
832 LoadNonVolatileOperation::Create(Outputs_.Arg, { memoryState }, intType, 4);
833 auto & loadArg = *loadArgOutputs[0];
834 memoryState = loadArgOutputs[1];
835
836 lambdaNode.finalize({ &loadArg, ioState, memoryState });
837 Outputs_.Func = lambdaNode.output();
838 }
839
840 return rvsdgModule;
841 }
842
844};
845
846TEST(LocalAliasAnalysisTests, testAllocasArguments)
847{
848 using namespace jlm;
849 using namespace jlm::llvm;
850 using namespace jlm::llvm::aa;
851
859 // Arrange
861 rvsdg.InitializeTest();
862 const auto & outputs = rvsdg.GetOutputs();
863
864 jlm::rvsdg::view(&rvsdg.graph().GetRootRegion(), stdout);
865
867
868 // Assert
869 Expect(aa, *outputs.Arg, 4, *outputs.LocalAlloca, 4, AliasAnalysis::NoAlias);
870}
std::int64_t expected
TEST(LocalAliasAnalysisTests, TestLocalAliasAnalysis)
static void Expect(jlm::llvm::aa::AliasAnalysis &aa, const jlm::rvsdg::Output &p1, size_t s1, const jlm::rvsdg::Output &p2, size_t s2, jlm::llvm::aa::AliasAnalysis::AliasQueryResponse expected)
const Outputs & GetOutputs() const noexcept
std::unique_ptr< jlm::llvm::LlvmRvsdgModule > SetupRvsdg() override
Create RVSDG for this test.
std::unique_ptr< jlm::llvm::LlvmRvsdgModule > SetupRvsdg() override
Create RVSDG for this test.
std::unique_ptr< jlm::llvm::LlvmRvsdgModule > SetupRvsdg() override
Create RVSDG for this test.
const Outputs & GetOutputs() const noexcept
std::unique_ptr< jlm::llvm::LlvmRvsdgModule > SetupRvsdg() override
Create RVSDG for this test.
const Outputs & GetOutputs() const noexcept
RvsdgTest class.
const rvsdg::Graph & graph()
virtual AliasQueryResponse Query(const rvsdg::Output &p1, size_t s1, const rvsdg::Output &p2, size_t s2)=0
Region & GetRootRegion() const noexcept
Definition graph.hpp:99
std::unique_ptr< BitBinaryOperation > create(size_t nbits) const override
std::unique_ptr< BitCompareOperation > create(size_t nbits) const override
Global memory state passed between functions.
std::string view(const rvsdg::Region *region)
Definition view.cpp:142