Jlm
Loading...
Searching...
No Matches
PointerObjectSetTests.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2023, 2024 HÃ¥vard Krogstie <krogstie.havard@gmail.com>
3 * See COPYING for terms of redistribution.
4 */
5
6#include <gtest/gtest.h>
7
11
12#include <cassert>
13
14static bool
15StringContains(std::string_view haystack, std::string_view needle)
16{
17 return haystack.find(needle) != std::string::npos;
18}
19
20// Test the flag functions on the PointerObject class
21TEST(PointerObjectSetTests, TestFlagFunctions)
22{
23 using namespace jlm::llvm::aa;
24
26 rvsdg.InitializeTest();
27
29 auto registerPO = set.CreateRegisterPointerObject(rvsdg.GetAllocaOutput());
30
31 EXPECT_TRUE(set.CanPoint(registerPO));
32 EXPECT_TRUE(set.IsPointerObjectRegister(registerPO));
33
34 // PointeesEscaping flag
35 EXPECT_FALSE(set.HasPointeesEscaping(registerPO));
36 EXPECT_TRUE(set.MarkAsPointeesEscaping(registerPO));
37 EXPECT_TRUE(set.HasPointeesEscaping(registerPO));
38 // Trying to set the flag again returns false
39 EXPECT_FALSE(set.MarkAsPointeesEscaping(registerPO));
40 EXPECT_TRUE(set.HasPointeesEscaping(registerPO));
41
42 // PointsToExternal flag. For registers, the two flags are completely independent.
43 EXPECT_FALSE(set.IsPointingToExternal(registerPO));
44 EXPECT_TRUE(set.MarkAsPointingToExternal(registerPO));
45 EXPECT_TRUE(set.IsPointingToExternal(registerPO));
46 // Trying to set the flag again returns false
47 EXPECT_FALSE(set.MarkAsPointingToExternal(registerPO));
48 EXPECT_TRUE(set.IsPointingToExternal(registerPO));
49
50 // Create a new PointerObject to start with empty flags
51 auto allocaPO = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(), true);
52 EXPECT_FALSE(set.IsPointerObjectRegister(allocaPO));
53
54 // Escaping means another module can write a pointer to you.
55 // This implies another module might override it with pointers to external.
56 // It also implies any pointees should also escape
57 EXPECT_FALSE(set.IsPointingToExternal(allocaPO));
58 EXPECT_FALSE(set.HasPointeesEscaping(allocaPO));
59 EXPECT_TRUE(set.MarkAsEscaped(allocaPO));
60 EXPECT_TRUE(set.IsPointingToExternal(allocaPO));
61 EXPECT_TRUE(set.HasPointeesEscaping(allocaPO));
62 // Already marked with these flags, trying to set them again makes no difference
63 EXPECT_FALSE(set.MarkAsPointingToExternal(allocaPO));
64 EXPECT_FALSE(set.MarkAsPointeesEscaping(allocaPO));
65}
66
67// Test creating pointer objects for each type of memory node
68TEST(PointerObjectSetTests, TestCreatePointerObjects)
69{
70 using namespace jlm::llvm::aa;
71
73 rvsdg.InitializeTest();
74
76 // Register PointerObjects have some extra ways of being created: Dummy and mapping
77 const auto register0 = set.CreateRegisterPointerObject(rvsdg.GetAllocaOutput());
79 const auto dummy0 = set.CreateDummyRegisterPointerObject();
80
81 // For PointerObjects representing MemoryObjects, there is only one Create function
82 const auto alloca0 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(), false);
83 const auto malloc0 = set.CreateMallocMemoryObject(rvsdg.GetMallocNode(), true);
84 const auto delta0 = set.CreateGlobalMemoryObject(rvsdg.GetDeltaNode(), true);
85 const auto lambda0 = set.CreateFunctionMemoryObject(rvsdg.GetLambdaNode());
86 const auto import0 = set.CreateImportMemoryObject(rvsdg.GetImportOutput(), false);
87
88 EXPECT_EQ(set.NumPointerObjects(), 7u);
90
91 EXPECT_EQ(set.GetPointerObjectKind(register0), PointerObjectKind::Register);
92 EXPECT_EQ(set.GetPointerObjectKind(dummy0), PointerObjectKind::Register);
93 EXPECT_EQ(set.GetPointerObjectKind(alloca0), PointerObjectKind::AllocaMemoryObject);
94 EXPECT_EQ(set.GetPointerObjectKind(malloc0), PointerObjectKind::MallocMemoryObject);
95 EXPECT_EQ(set.GetPointerObjectKind(delta0), PointerObjectKind::GlobalMemoryObject);
96 EXPECT_EQ(set.GetPointerObjectKind(lambda0), PointerObjectKind::FunctionMemoryObject);
97 EXPECT_EQ(set.GetPointerObjectKind(import0), PointerObjectKind::ImportMemoryObject);
98
99 // Most pointer objects don't start out as escaped
100 EXPECT_FALSE(set.HasEscaped(dummy0));
101 EXPECT_FALSE(set.HasEscaped(alloca0));
102 EXPECT_FALSE(set.HasEscaped(malloc0));
103 EXPECT_FALSE(set.HasEscaped(delta0));
104 EXPECT_FALSE(set.HasEscaped(lambda0));
105 // ...but imported objects are always escaped
106 EXPECT_TRUE(set.HasEscaped(import0));
107 // ...which also means it points to external, and has its pointees escaping
108 EXPECT_TRUE(set.IsPointingToExternal(import0) && set.HasPointeesEscaping(import0));
109
110 // Some kinds of PointerObjects have CanPoint() configurable is the constructor
111 EXPECT_FALSE(set.CanPoint(alloca0));
112 EXPECT_TRUE(set.CanPoint(malloc0));
113 EXPECT_TRUE(set.CanPoint(delta0));
114 // ...while others have implied values of CanPoint()
115 EXPECT_TRUE(set.CanPoint(register0));
116 EXPECT_FALSE(set.CanPoint(lambda0));
117 EXPECT_FALSE(set.CanPoint(import0));
118
119 // CanPoint() == false implies pointing to external and having all pointees escaping
120 EXPECT_TRUE(set.IsPointingToExternal(alloca0) && set.HasPointeesEscaping(alloca0));
121
122 // Registers have helper function for looking up existing PointerObjects
123 EXPECT_EQ(set.GetRegisterPointerObject(rvsdg.GetAllocaOutput()), register0);
124 EXPECT_EQ(set.GetRegisterPointerObject(rvsdg.GetDeltaOutput()), register0);
125 EXPECT_EQ(set.TryGetRegisterPointerObject(rvsdg.GetDeltaOutput()).value(), register0);
126
127 // Functions have the same, but also in the other direction
128 EXPECT_EQ(set.GetFunctionMemoryObject(rvsdg.GetLambdaNode()), lambda0);
129 EXPECT_EQ(&set.GetLambdaNodeFromFunctionMemoryObject(lambda0), &rvsdg.GetLambdaNode());
130
131 // The maps can also be accessed directly
132 EXPECT_EQ(set.GetRegisterMap().at(&rvsdg.GetAllocaOutput()), register0);
133 EXPECT_EQ(set.GetRegisterMap().at(&rvsdg.GetDeltaOutput()), register0);
134 EXPECT_EQ(set.GetAllocaMap().at(&rvsdg.GetAllocaNode()), alloca0);
135 EXPECT_EQ(set.GetMallocMap().at(&rvsdg.GetMallocNode()), malloc0);
136 EXPECT_EQ(set.GetGlobalMap().at(&rvsdg.GetDeltaNode()), delta0);
137 EXPECT_EQ(set.GetFunctionMap().LookupKey(&rvsdg.GetLambdaNode()), lambda0);
138 EXPECT_EQ(set.GetImportMap().at(&rvsdg.GetImportOutput()), import0);
139}
140
141TEST(PointerObjectSetTests, TestPointerObjectUnification)
142{
143 using namespace jlm::llvm::aa;
144
146 auto dummy0 = set.CreateDummyRegisterPointerObject();
147 auto dummy1 = set.CreateDummyRegisterPointerObject();
148 EXPECT_TRUE(set.IsUnificationRoot(dummy0));
149
150 auto root = set.UnifyPointerObjects(dummy0, dummy1);
151 EXPECT_EQ(set.GetUnificationRoot(dummy0), root);
152 EXPECT_EQ(set.GetUnificationRoot(dummy1), root);
153
154 // Exactly one of the PointerObjects is the GetRootRegion
155 EXPECT_NE((root == dummy0), (root == dummy1));
156 EXPECT_TRUE(set.IsUnificationRoot(root));
157
158 // Trying to unify again gives the same GetRootRegion
159 EXPECT_EQ(set.UnifyPointerObjects(dummy0, dummy1), root);
160
161 auto notRoot = dummy0 + dummy1 - root;
162 EXPECT_FALSE(set.IsUnificationRoot(notRoot));
163
164 auto dummy2 = set.CreateDummyRegisterPointerObject();
165 auto dummy3 = set.CreateDummyRegisterPointerObject();
166 set.UnifyPointerObjects(dummy0, dummy2);
167 auto newRoot = set.UnifyPointerObjects(dummy1, dummy3);
168 EXPECT_EQ(set.GetUnificationRoot(dummy0), newRoot);
169 EXPECT_EQ(set.GetUnificationRoot(dummy1), newRoot);
170 EXPECT_EQ(set.GetUnificationRoot(dummy2), newRoot);
171 EXPECT_EQ(set.GetUnificationRoot(dummy3), newRoot);
172}
173
174TEST(PointerObjectSetTests, TestPointerObjectUnificationPointees)
175{
176 using namespace jlm::llvm::aa;
177
179 rvsdg.InitializeTest();
180
182 auto lambda0 = set.CreateFunctionMemoryObject(rvsdg.GetLambdaNode());
183 auto alloca0 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(), true);
184 auto delta0 = set.CreateGlobalMemoryObject(rvsdg.GetDeltaNode(), true);
185
186 set.AddToPointsToSet(alloca0, lambda0);
187 EXPECT_EQ(set.GetPointsToSet(alloca0).Size(), 1u);
188 EXPECT_EQ(set.GetPointsToSet(delta0).Size(), 0u);
189
190 set.UnifyPointerObjects(delta0, alloca0);
191
192 // Now both should share points-to sets
193 EXPECT_TRUE(set.GetPointsToSet(alloca0).Contains(lambda0));
194 EXPECT_TRUE(set.GetPointsToSet(delta0).Contains(lambda0));
195
196 // Marking one as pointing to external marks all as pointing to external
197 EXPECT_TRUE(set.MarkAsPointingToExternal(alloca0));
198 EXPECT_TRUE(set.IsPointingToExternal(alloca0));
199 EXPECT_TRUE(set.IsPointingToExternal(delta0));
200
201 // Marking one as pointees escaping marks all as pointees escaping
202 EXPECT_TRUE(set.MarkAsPointeesEscaping(delta0));
203 EXPECT_TRUE(set.HasPointeesEscaping(alloca0));
204 EXPECT_TRUE(set.HasPointeesEscaping(delta0));
205
206 // Adding a new pointee adds it to all members
207 auto import0 = set.CreateImportMemoryObject(rvsdg.GetImportOutput(), false);
208 EXPECT_TRUE(set.AddToPointsToSet(delta0, import0));
209 EXPECT_TRUE(set.GetPointsToSet(alloca0).Contains(import0));
210
211 // Escaping is not shared within the unification
212 EXPECT_TRUE(set.MarkAsEscaped(delta0));
213 EXPECT_TRUE(set.HasEscaped(delta0));
214 EXPECT_FALSE(set.HasEscaped(alloca0));
215}
216
217// Test the PointerObjectSet method for adding pointer objects to another pointer object's
218// points-to-set
219TEST(PointerObjectSetTests, TestAddToPointsToSet)
220{
221 using namespace jlm::llvm::aa;
222
224 rvsdg.InitializeTest();
225
227 const auto alloca0 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(0), false);
228 const auto reg0 = set.CreateRegisterPointerObject(rvsdg.GetAllocaOutput(0));
229
230 EXPECT_EQ(set.GetPointsToSet(reg0).Size(), 0u);
231
232 EXPECT_TRUE(set.AddToPointsToSet(reg0, alloca0));
233 EXPECT_EQ(set.GetPointsToSet(reg0).Size(), 1u);
234 EXPECT_TRUE(set.GetPointsToSet(reg0).Contains(alloca0));
235
236 // Trying to add it again returns false
237 EXPECT_FALSE(set.AddToPointsToSet(reg0, alloca0));
238}
239
240// Test the PointerObjectSet method for making one points-to-set a superset of another
241TEST(PointerObjectSetTests, TestMakePointsToSetSuperset)
242{
243 using namespace jlm::llvm::aa;
244
246 rvsdg.InitializeTest();
247
249 const auto alloca0 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(0), false);
250 const auto reg0 = set.CreateRegisterPointerObject(rvsdg.GetAllocaOutput(0));
251 const auto alloca1 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(1), false);
252 const auto reg1 = set.CreateRegisterPointerObject(rvsdg.GetAllocaOutput(1));
253 const auto alloca2 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(2), false);
254
255 set.AddToPointsToSet(reg0, alloca0);
256 set.AddToPointsToSet(reg1, alloca1);
257
258 EXPECT_EQ(set.GetPointsToSet(reg0).Size(), 1u);
259 EXPECT_TRUE(set.GetPointsToSet(reg0).Contains(alloca0));
260
261 EXPECT_TRUE(set.MakePointsToSetSuperset(reg0, reg1));
262 EXPECT_EQ(set.GetPointsToSet(reg1).Size(), 1u);
263 EXPECT_EQ(set.GetPointsToSet(reg0).Size(), 2u);
264 EXPECT_TRUE(set.GetPointsToSet(reg0).Contains(alloca1));
265
266 // Calling it again without changing reg1 makes no difference, returns false
267 EXPECT_FALSE(set.MakePointsToSetSuperset(reg0, reg1));
268
269 // Add an additional member to P(reg1)
270 set.AddToPointsToSet(reg1, alloca2);
271 EXPECT_TRUE(set.MakePointsToSetSuperset(reg0, reg1));
272 EXPECT_TRUE(set.GetPointsToSet(reg0).Contains(alloca2));
273}
274
275TEST(PointerObjectSetTests, TestClonePointerObjectSet)
276{
277 using namespace jlm::llvm::aa;
279 rvsdg.InitializeTest();
280
282 const auto register0 = set.CreateRegisterPointerObject(rvsdg.GetAllocaOutput());
283 const auto dummy0 = set.CreateDummyRegisterPointerObject();
284 const auto alloca0 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(), false);
285 const auto malloc0 = set.CreateMallocMemoryObject(rvsdg.GetMallocNode(), true);
286 const auto delta0 = set.CreateGlobalMemoryObject(rvsdg.GetDeltaNode(), false);
287 const auto lambda0 = set.CreateFunctionMemoryObject(rvsdg.GetLambdaNode());
288 const auto import0 = set.CreateImportMemoryObject(rvsdg.GetImportOutput(), false);
289
290 set.AddToPointsToSet(register0, alloca0);
291 set.UnifyPointerObjects(delta0, import0);
292
293 auto clonedSet = set.Clone();
294
295 // All mappings are identical, since PointerObjects are referenced by index
296 EXPECT_EQ(clonedSet->NumPointerObjects(), set.NumPointerObjects());
297 EXPECT_EQ(clonedSet->GetRegisterMap(), set.GetRegisterMap());
298 EXPECT_EQ(clonedSet->GetAllocaMap(), set.GetAllocaMap());
299 EXPECT_EQ(clonedSet->GetMallocMap(), set.GetMallocMap());
300 EXPECT_EQ(clonedSet->GetGlobalMap(), set.GetGlobalMap());
301 EXPECT_EQ(clonedSet->GetFunctionMap(), set.GetFunctionMap());
302 EXPECT_EQ(clonedSet->GetImportMap(), set.GetImportMap());
303
304 // In the cloned set, each points-to set should be identical
305 EXPECT_EQ(clonedSet->GetPointsToSet(register0), set.GetPointsToSet(register0));
306
307 // Unifications are maintained
308 EXPECT_EQ(clonedSet->GetUnificationRoot(delta0), clonedSet->GetUnificationRoot(import0));
309
310 // Additional changes only affect the set they are applied to
311 set.AddToPointsToSet(register0, lambda0);
312 EXPECT_FALSE(clonedSet->GetPointsToSet(register0).Contains(lambda0));
313
314 clonedSet->UnifyPointerObjects(delta0, dummy0);
315 EXPECT_NE(set.GetUnificationRoot(delta0), set.GetUnificationRoot(dummy0));
316
317 set.MarkAsPointingToExternal(malloc0);
318 EXPECT_FALSE(clonedSet->IsPointingToExternal(malloc0));
319}
320
321// Test the SupersetConstraint's Apply function
322TEST(PointerObjectSetTests, TestSupersetConstraint)
323{
324 using namespace jlm::llvm::aa;
325
327 rvsdg.InitializeTest();
328
330 const auto alloca0 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(0), true);
331 const auto reg0 = set.CreateRegisterPointerObject(rvsdg.GetAllocaOutput(0));
332 const auto alloca1 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(1), true);
333 const auto reg1 = set.CreateRegisterPointerObject(rvsdg.GetAllocaOutput(1));
334 const auto alloca2 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(2), true);
335 const auto reg2 = set.CreateRegisterPointerObject(rvsdg.GetAllocaOutput(2));
336
337 set.AddToPointsToSet(reg0, alloca0);
338 set.AddToPointsToSet(reg1, alloca1);
339 set.AddToPointsToSet(reg2, alloca2);
340
341 // Make alloca0 point to everything reg1 points to
342 SupersetConstraint c1(alloca0, reg1);
343 EXPECT_TRUE(c1.ApplyDirectly(set));
344 while (c1.ApplyDirectly(set))
345 ;
346 // For now this only makes alloca0 point to alloca1
347 EXPECT_EQ(set.GetPointsToSet(alloca0).Size(), 1u);
348 EXPECT_TRUE(set.GetPointsToSet(alloca0).Contains(alloca1));
349
350 // Make reg1 point to everything reg2 points to
351 SupersetConstraint c2(reg1, reg2);
352 EXPECT_TRUE(c2.ApplyDirectly(set));
353 while (c2.ApplyDirectly(set))
354 ;
355 // This makes alloca2 a member of P(reg1)
356 EXPECT_TRUE(set.GetPointsToSet(reg1).Contains(alloca2));
357
358 // Apply c1 again
359 EXPECT_TRUE(c1.ApplyDirectly(set));
360 while (c1.ApplyDirectly(set))
361 ;
362 // Now alloca0 should also point to alloca2
363 EXPECT_EQ(set.GetPointsToSet(alloca0).Size(), 2u);
364 EXPECT_TRUE(set.GetPointsToSet(alloca0).Contains(alloca2));
365
366 // Make reg2 point to external, and propagate through constraints
367 set.MarkAsPointingToExternal(reg2);
368 EXPECT_TRUE(c2.ApplyDirectly(set));
369 while (c2.ApplyDirectly(set))
370 ;
371 EXPECT_TRUE(c1.ApplyDirectly(set));
372 while (c1.ApplyDirectly(set))
373 ;
374 // Now both reg1 and alloca0 may point to external
375 EXPECT_TRUE(set.IsPointingToExternal(reg1));
376 EXPECT_TRUE(set.IsPointingToExternal(alloca0));
377}
378
379TEST(PointerObjectSetTests, TestStoreConstraintDirectly)
380{
381 using namespace jlm::llvm::aa;
382
384 rvsdg.InitializeTest();
385
387 const auto alloca0 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(0), true);
388 const auto reg0 = set.CreateRegisterPointerObject(rvsdg.GetAllocaOutput(0));
389 const auto alloca1 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(1), true);
390 const auto reg1 = set.CreateRegisterPointerObject(rvsdg.GetAllocaOutput(1));
391 const auto alloca2 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(2), true);
392 const auto reg2 = set.CreateRegisterPointerObject(rvsdg.GetAllocaOutput(2));
393
394 set.AddToPointsToSet(reg0, alloca0);
395 set.AddToPointsToSet(reg1, alloca1);
396 set.AddToPointsToSet(reg2, alloca2);
397
398 // Add *alloca0 = reg2, which will do nothing, since alloca0 can't be pointing to anything yet
399 StoreConstraint c1(alloca0, reg2);
400 EXPECT_FALSE(c1.ApplyDirectly(set));
401
402 // This means *reg0 = reg1, and as we know, reg0 points to alloca0
403 // This should make alloca0 point to anything reg1 points to, aka alloca1
404 StoreConstraint c2(reg0, reg1);
405 EXPECT_TRUE(c2.ApplyDirectly(set));
406 while (c2.ApplyDirectly(set))
407 ;
408 EXPECT_EQ(set.GetPointsToSet(alloca0).Size(), 1u);
409 EXPECT_TRUE(set.GetPointsToSet(alloca0).Contains(alloca1));
410
411 // Do c1 again, now that alloca0 points to alloca1
412 EXPECT_TRUE(c1.ApplyDirectly(set));
413 while (c1.ApplyDirectly(set))
414 ;
415 EXPECT_EQ(set.GetPointsToSet(alloca1).Size(), 1u);
416 EXPECT_TRUE(set.GetPointsToSet(alloca1).Contains(alloca2));
417}
418
419TEST(PointerObjectSetTests, TestLoadConstraintDirectly)
420{
421 using namespace jlm::llvm::aa;
422
424 rvsdg.InitializeTest();
425
427 const auto alloca0 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(0), true);
428 const auto reg0 = set.CreateRegisterPointerObject(rvsdg.GetAllocaOutput(0));
429 const auto alloca1 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(1), true);
430 const auto reg1 = set.CreateRegisterPointerObject(rvsdg.GetAllocaOutput(1));
431 const auto alloca2 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(2), true);
432 const auto reg2 = set.CreateRegisterPointerObject(rvsdg.GetAllocaOutput(2));
433
434 set.AddToPointsToSet(reg0, alloca0);
435 set.AddToPointsToSet(reg1, alloca1);
436 set.AddToPointsToSet(reg2, alloca2);
437
438 // Makes reg1 = *reg0, where reg0 currently just points to alloca0
439 // Since alloca0 currently has no pointees, this does nothing
440 LoadConstraint c1(reg1, reg0);
441 EXPECT_FALSE(c1.ApplyDirectly(set));
442
443 // Make alloca0 point to something: alloca2
444 set.AddToPointsToSet(alloca0, alloca2);
445 // The constraint now makes a difference
446 EXPECT_TRUE(c1.ApplyDirectly(set));
447 while (c1.ApplyDirectly(set))
448 ;
449 EXPECT_EQ(set.GetPointsToSet(reg1).Size(), 2u);
450 EXPECT_TRUE(set.GetPointsToSet(reg1).Contains(alloca2));
451}
452
453TEST(PointerObjectSetTests, TestEscapedFunctionConstraint)
454{
455 using namespace jlm::llvm::aa;
456
458 rvsdg.InitializeTest();
459
460 const auto & localFunction = rvsdg.GetLocalFunction();
461 const auto & localFunctionRegister = rvsdg.GetLocalFunctionRegister();
462 const auto & exportedFunction = rvsdg.GetExportedFunction();
463 const auto & exportedFunctionReturn = *exportedFunction.GetFunctionResults()[0]->origin();
464
466 const auto localFunctionPO = set.CreateFunctionMemoryObject(localFunction);
467 const auto localFunctionRegisterPO = set.CreateRegisterPointerObject(localFunctionRegister);
468 const auto exportedFunctionPO = set.CreateFunctionMemoryObject(exportedFunction);
469 set.MapRegisterToExistingPointerObject(exportedFunctionReturn, localFunctionRegisterPO);
470
471 // Make localFunc's output point to localFunc
472 set.AddToPointsToSet(localFunctionRegisterPO, localFunctionPO);
473
474 bool result = EscapedFunctionConstraint::PropagateEscapedFunctionsDirectly(set);
475
476 // Nothing has happened yet, since the exported function is yet to be marked as escaped
477 EXPECT_FALSE(result);
478 EXPECT_FALSE(set.HasEscaped(localFunctionRegisterPO));
479 EXPECT_FALSE(set.HasEscaped(exportedFunctionPO));
480
481 set.MarkAsEscaped(exportedFunctionPO);
482
483 // Use both EscapedFunctionConstraint and EscapeFlagConstraint to propagate flags
484 result = EscapedFunctionConstraint::PropagateEscapedFunctionsDirectly(set);
485 EXPECT_TRUE(result);
486
487 // Now the return value is marked as all pointees escaping, so make that happen
488 result = EscapeFlagConstraint::PropagateEscapedFlagsDirectly(set);
489 // Now the local function has been marked as escaped as well, since it is the return value
490 EXPECT_TRUE(result);
491 EXPECT_TRUE(set.HasEscaped(localFunctionPO));
492}
493
494TEST(PointerObjectSetTests, TestStoredAsScalarFlag)
495{
496 using namespace jlm::llvm::aa;
497
499 rvsdg.InitializeTest();
500
502 const auto p0 = set.CreateDummyRegisterPointerObject();
503 const auto p1 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(0), true);
504 const auto p11 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(1), true);
505 const auto p2 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(2), true);
506
507 set.AddToPointsToSet(p0, p1);
508 set.AddToPointsToSet(p1, p11);
509 set.AddToPointsToSet(p0, p2);
510
511 bool result = EscapeFlagConstraint::PropagateEscapedFlagsDirectly(set);
512 EXPECT_FALSE(result);
513
514 set.MarkAsStoringAsScalar(p0);
515 result = EscapeFlagConstraint::PropagateEscapedFlagsDirectly(set);
516 EXPECT_TRUE(result);
517
518 // p0 should only have the single stored as scalar flag
519 EXPECT_FALSE(set.HasEscaped(p0));
520 EXPECT_FALSE(set.HasPointeesEscaping(p0));
521 EXPECT_FALSE(set.IsPointingToExternal(p0));
522 EXPECT_FALSE(set.IsLoadedAsScalar(p0));
523 EXPECT_TRUE(set.IsStoredAsScalar(p0));
524
525 // p1 and p2 should both point to external, but not any other flags
526 EXPECT_FALSE(set.HasEscaped(p1));
527 EXPECT_FALSE(set.HasPointeesEscaping(p1));
528 EXPECT_TRUE(set.IsPointingToExternal(p1));
529 EXPECT_FALSE(set.IsLoadedAsScalar(p1));
530 EXPECT_FALSE(set.IsStoredAsScalar(p1));
531
532 EXPECT_FALSE(set.HasEscaped(p2));
533 EXPECT_FALSE(set.HasPointeesEscaping(p2));
534 EXPECT_TRUE(set.IsPointingToExternal(p2));
535 EXPECT_FALSE(set.IsLoadedAsScalar(p2));
536 EXPECT_FALSE(set.IsStoredAsScalar(p2));
537
538 // p11 should have no flags
539 EXPECT_FALSE(set.HasEscaped(p11));
540 EXPECT_FALSE(set.HasPointeesEscaping(p11));
541 EXPECT_FALSE(set.IsPointingToExternal(p11));
542 EXPECT_FALSE(set.IsLoadedAsScalar(p11));
543 EXPECT_FALSE(set.IsStoredAsScalar(p11));
544
545 // Applying again does nothing
546 result = EscapeFlagConstraint::PropagateEscapedFlagsDirectly(set);
547 EXPECT_FALSE(result);
548}
549
550TEST(PointerObjectSetTests, TestLoadedAsScalarFlag)
551{
552 using namespace jlm::llvm::aa;
553
555 rvsdg.InitializeTest();
556
558 const auto p0 = set.CreateDummyRegisterPointerObject();
559 const auto p1 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(0), true);
560 const auto p11 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(1), true);
561 const auto p12 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(2), true);
562 const auto p2 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(3), true);
563 const auto p21 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(4), true);
564
565 set.AddToPointsToSet(p0, p1);
566 set.AddToPointsToSet(p1, p11);
567 set.AddToPointsToSet(p1, p12);
568 set.AddToPointsToSet(p0, p2);
569 set.AddToPointsToSet(p2, p21);
570
571 bool result = EscapeFlagConstraint::PropagateEscapedFlagsDirectly(set);
572 EXPECT_FALSE(result);
573
574 set.MarkAsLoadingAsScalar(p0);
575 result = EscapeFlagConstraint::PropagateEscapedFlagsDirectly(set);
576 EXPECT_TRUE(result);
577
578 // p0 should only have the single loaded as scalar flag
579 EXPECT_FALSE(set.HasEscaped(p0));
580 EXPECT_FALSE(set.HasPointeesEscaping(p0));
581 EXPECT_FALSE(set.IsPointingToExternal(p0));
582 EXPECT_FALSE(set.IsStoredAsScalar(p0));
583 EXPECT_TRUE(set.IsLoadedAsScalar(p0));
584
585 // p1 should only have the pointees escape flag
586 EXPECT_FALSE(set.HasEscaped(p1));
587 EXPECT_TRUE(set.HasPointeesEscaping(p1));
588 EXPECT_FALSE(set.IsPointingToExternal(p1));
589 EXPECT_FALSE(set.IsStoredAsScalar(p1));
590 EXPECT_FALSE(set.IsLoadedAsScalar(p1));
591
592 // p11, p12, p21 should have escaped, but not be flagged using the store or load flags
593 EXPECT_TRUE(set.HasEscaped(p11));
594 EXPECT_FALSE(set.IsLoadedAsScalar(p11));
595 EXPECT_FALSE(set.IsStoredAsScalar(p11));
596
597 EXPECT_TRUE(set.HasEscaped(p12));
598 EXPECT_FALSE(set.IsLoadedAsScalar(p12));
599 EXPECT_FALSE(set.IsStoredAsScalar(p12));
600
601 EXPECT_TRUE(set.HasEscaped(p21));
602 EXPECT_FALSE(set.IsLoadedAsScalar(p21));
603 EXPECT_FALSE(set.IsStoredAsScalar(p21));
604
605 // Applying again does nothing
606 result = EscapeFlagConstraint::PropagateEscapedFlagsDirectly(set);
607 EXPECT_FALSE(result);
608}
609
610TEST(PointerObjectSetTests, TestFunctionCallConstraint)
611{
612 using namespace jlm::llvm::aa;
613
615 rvsdg.InitializeTest();
616
618 const auto lambdaF = set.CreateFunctionMemoryObject(*rvsdg.lambda_f);
619 const auto lambdaFRegister = set.CreateRegisterPointerObject(*rvsdg.lambda_f->output());
620 const auto lambdaFArgumentX =
622 const auto lambdaFArgumentY =
624 const auto allocaX = set.CreateAllocaMemoryObject(*rvsdg.alloca_x, true);
625 const auto allocaY = set.CreateAllocaMemoryObject(*rvsdg.alloca_y, true);
626 const auto allocaXRegister = set.CreateRegisterPointerObject(*rvsdg.alloca_x->output(0));
627 const auto allocaYRegister = set.CreateRegisterPointerObject(*rvsdg.alloca_y->output(0));
628
629 // Associate the allocas and lambda with their output
630 set.AddToPointsToSet(lambdaFRegister, lambdaF);
631 set.AddToPointsToSet(allocaXRegister, allocaX);
632 set.AddToPointsToSet(allocaYRegister, allocaY);
633
634 FunctionCallConstraint c(lambdaFRegister, rvsdg.CallF());
635 EXPECT_TRUE(c.ApplyDirectly(set));
636 while (c.ApplyDirectly(set))
637 ;
638
639 // The arguments to f should now be pointing to exactly the corresponding allocas
640 EXPECT_TRUE(set.GetPointsToSet(lambdaFArgumentX).Contains(allocaX));
641 EXPECT_TRUE(set.GetPointsToSet(lambdaFArgumentY).Contains(allocaY));
642 EXPECT_FALSE(set.GetPointsToSet(lambdaFArgumentX).Contains(allocaY));
643 EXPECT_FALSE(set.GetPointsToSet(lambdaFArgumentY).Contains(allocaX));
644}
645
646TEST(PointerObjectSetTests, TestAddPointsToExternalConstraint)
647{
648 using namespace jlm::llvm::aa;
649
651 rvsdg.InitializeTest();
652
654 const auto alloca0 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(0), true);
655 const auto reg0 = set.CreateRegisterPointerObject(rvsdg.GetAllocaOutput(0));
656 const auto alloca1 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(1), true);
657 const auto reg1 = set.CreateRegisterPointerObject(rvsdg.GetAllocaOutput(1));
658
659 PointerObjectConstraintSet constraints(set);
660 constraints.AddPointerPointeeConstraint(reg0, alloca0);
661 constraints.AddPointerPointeeConstraint(reg1, alloca1);
662
663 constraints.AddPointsToExternalConstraint(reg0);
664 constraints.SolveNaively();
665
666 // Make sure only reg0 points to external, and nothing has escaped
667 EXPECT_TRUE(set.IsPointingToExternal(reg0));
668 EXPECT_FALSE(set.IsPointingToExternal(reg1));
669 EXPECT_FALSE(set.IsPointingToExternal(alloca0));
670 EXPECT_FALSE(set.IsPointingToExternal(alloca1));
671
672 EXPECT_FALSE(set.HasEscaped(reg0));
673 EXPECT_FALSE(set.HasEscaped(reg1));
674 EXPECT_FALSE(set.HasEscaped(alloca0));
675 EXPECT_FALSE(set.HasEscaped(alloca1));
676
677 // Add a *reg0 = reg1 store
678 constraints.AddConstraint(StoreConstraint(reg0, reg1));
679 constraints.SolveNaively();
680
681 // Now alloca1 is marked as escaped, due to being written to a pointer that might point to
682 // external
683 EXPECT_TRUE(set.HasEscaped(alloca1));
684 // The other alloca has not escaped
685 EXPECT_FALSE(set.HasEscaped(alloca0));
686}
687
688TEST(PointerObjectSetTests, TestAddRegisterContentEscapedConstraint)
689{
690 using namespace jlm::llvm::aa;
691
693 rvsdg.InitializeTest();
694
696 const auto alloca0 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(0), false);
697 const auto reg0 = set.CreateRegisterPointerObject(rvsdg.GetAllocaOutput(0));
698 const auto alloca1 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(1), false);
699 const auto reg1 = set.CreateRegisterPointerObject(rvsdg.GetAllocaOutput(1));
700
701 PointerObjectConstraintSet constraints(set);
702 constraints.AddPointerPointeeConstraint(reg0, alloca0);
703 constraints.AddPointerPointeeConstraint(reg1, alloca1);
704
705 // return reg0, making alloca0 escapce
706 constraints.AddRegisterContentEscapedConstraint(reg0);
707 constraints.SolveNaively();
708
709 // Make sure only alloca0 has escaped
710 EXPECT_TRUE(set.HasEscaped(alloca0));
711 EXPECT_FALSE(set.HasEscaped(alloca1));
712
713 // Add a alloca0 = reg1 store
714 constraints.AddConstraint(StoreConstraint(reg0, reg1));
715 constraints.SolveNaively();
716
717 // Now both are marked as escaped
718 EXPECT_TRUE(set.HasEscaped(alloca0));
719 EXPECT_TRUE(set.HasEscaped(alloca1));
720}
721
722TEST(PointerObjectSetTests, TestDrawSubsetGraph)
723{
724 using namespace jlm::llvm::aa;
725 using namespace jlm::util;
727 rvsdg.InitializeTest();
728
729 // Arrange
731 const auto alloca0 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(), true);
732 const auto allocaReg0 = set.CreateRegisterPointerObject(rvsdg.GetAllocaOutput());
733
734 const auto dummy0 = set.CreateDummyRegisterPointerObject();
735 const auto dummy1 = set.CreateDummyRegisterPointerObject();
736 const auto root = set.UnifyPointerObjects(dummy0, dummy1);
737 const auto nonRoot = dummy0 + dummy1 - root;
738
739 const auto storeValue = set.CreateDummyRegisterPointerObject();
740 const auto storePointer = set.CreateDummyRegisterPointerObject();
741
742 const auto loadValue = set.CreateDummyRegisterPointerObject();
743 const auto loadPointer = set.CreateDummyRegisterPointerObject();
744
745 const auto function0 = set.CreateFunctionMemoryObject(rvsdg.GetLambdaNode());
746
747 const auto import0 = set.CreateImportMemoryObject(rvsdg.GetImportOutput(), false);
748
749 PointerObjectConstraintSet constraints(set);
750 constraints.AddPointsToExternalConstraint(nonRoot);
751 constraints.AddPointerPointeeConstraint(allocaReg0, alloca0);
752 constraints.AddConstraint(SupersetConstraint(import0, allocaReg0));
753 constraints.AddConstraint(StoreConstraint(storePointer, storeValue));
754 constraints.AddConstraint(LoadConstraint(loadValue, loadPointer));
755
756 // Act
757 graph::Writer writer;
758 auto & graph = constraints.DrawSubsetGraph(writer);
759
760 // Assert
761 EXPECT_EQ(graph.NumNodes(), set.NumPointerObjects());
762
763 // Check that the unified node that is not the GetRootRegion, contains the index of the
764 // GetRootRegion
765 EXPECT_TRUE(StringContains(graph.GetNode(nonRoot).GetLabel(), "#" + std::to_string(root)));
766
767 // Check that the unification GetRootRegion's label indicates pointing to external
768 EXPECT_TRUE(StringContains(graph.GetNode(root).GetLabel(), "{+}"));
769
770 // Check that allocaReg0 points to alloca0
771 EXPECT_TRUE(StringContains(graph.GetNode(allocaReg0).GetLabel(), strfmt("{", alloca0, "}")));
772
773 // Check that a regular edge connects allocaReg0 to the importNode
774 auto * supersetEdge = graph.GetEdgeBetween(graph.GetNode(allocaReg0), graph.GetNode(import0));
775 EXPECT_TRUE(supersetEdge);
776 EXPECT_TRUE(supersetEdge->IsDirected());
777 EXPECT_EQ(supersetEdge->GetAttributeString("style").value_or("solid"), "solid");
778
779 // Check that a store edge connects storeValue to storePointer
780 auto * storeEdge = graph.GetEdgeBetween(graph.GetNode(storeValue), graph.GetNode(storePointer));
781 EXPECT_TRUE(storeEdge);
782 EXPECT_TRUE(storeEdge->IsDirected());
783 EXPECT_TRUE(storeEdge->GetAttributeString("style") == graph::Edge::Style::Dashed);
784 EXPECT_TRUE(StringContains(storeEdge->GetAttributeString("arrowhead").value(), "dot"));
785
786 // Check that a load edge connects loadPointer to loadValue
787 auto * loadEdge = graph.GetEdgeBetween(graph.GetNode(loadPointer), graph.GetNode(loadValue));
788 EXPECT_TRUE(loadEdge);
789 EXPECT_TRUE(loadEdge->IsDirected());
790 EXPECT_TRUE(loadEdge->GetAttributeString("style") == graph::Edge::Style::Dashed);
791 EXPECT_TRUE(StringContains(loadEdge->GetAttributeString("arrowtail").value(), "dot"));
792
793 // Check that the function contains the word "function0"
794 auto & functionNode = graph.GetNode(function0);
795 EXPECT_TRUE(StringContains(functionNode.GetLabel(), "function0"));
796 // Since functions don't track pointees, they should have CantPoint
797 EXPECT_TRUE(StringContains(functionNode.GetLabel(), "CantPoint"));
798 // They should also both point to external, and escape all pointees
799 EXPECT_TRUE(StringContains(functionNode.GetLabel(), "{+}e"));
800
801 // Check that the import PointerObject has a fill color, since it has escaped
802 auto & importNode = graph.GetNode(import0);
803 EXPECT_TRUE(importNode.HasAttribute("fillcolor"));
804}
805
806// Tests crating a ConstraintSet with multiple different constraints and calling Solve()
807template<jlm::llvm::aa::Andersen::Configuration::Solver solver, typename... Args>
808static void
810{
811 using namespace jlm::llvm::aa;
812
813 // Create a graph with 11 different registers, and 4 allocas.
815 rvsdg.InitializeTest();
816
818 PointerObjectIndex reg[11];
819 for (unsigned int & i : reg)
821
822 // %0 is a function parameter
823 // %1 = alloca 8 (variable v1)
824 // %2 = alloca 8 (variable v2)
825 // %3 = alloca 8 (variable v3)
826 // %4 = alloca 8 (variable v4)
827 const auto alloca1 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(0), true);
828 const auto alloca2 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(1), true);
829 const auto alloca3 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(2), true);
830 const auto alloca4 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(3), true);
831
832 // Now start building constraints based on instructions
833 PointerObjectConstraintSet constraints(set);
834
835 // the function parameter is marked as pointing to external
836 constraints.AddPointsToExternalConstraint(reg[0]);
837
838 // all alloca outputs point to the alloca memory object
839 constraints.AddPointerPointeeConstraint(reg[1], alloca1);
840 constraints.AddPointerPointeeConstraint(reg[2], alloca2);
841 constraints.AddPointerPointeeConstraint(reg[3], alloca3);
842 constraints.AddPointerPointeeConstraint(reg[4], alloca4);
843
844 // store [%1], %2 (alloca1 now points to alloca2)
845 constraints.AddConstraint(StoreConstraint(reg[1], reg[2]));
846 // store [%2], %3 (alloca2 now points to alloca3)
847 constraints.AddConstraint(StoreConstraint(reg[2], reg[3]));
848 // store [%3], %4 (alloca3 now points to alloca4)
849 constraints.AddConstraint(StoreConstraint(reg[3], reg[4]));
850
851 // %5 = load [%1] (loads v1, which is %2, the pointer to alloca2)
852 constraints.AddConstraint(LoadConstraint(reg[5], reg[1]));
853 // %6 = load [%3] (loads v3, which is %4, the pointer to alloca4)
854 constraints.AddConstraint(LoadConstraint(reg[6], reg[3]));
855
856 // %7 = phi %5/%6 (either points to alloca2 or alloca4)
857 constraints.AddConstraint(SupersetConstraint(reg[7], reg[5]));
858 constraints.AddConstraint(SupersetConstraint(reg[7], reg[6]));
859
860 // %8 = phi %0/%4 (either the function argument or pointer to alloca4)
861 constraints.AddConstraint(SupersetConstraint(reg[8], reg[0]));
862 constraints.AddConstraint(SupersetConstraint(reg[8], reg[4]));
863
864 // %9 = load [%7] (either loads alloca2 or alloca4, the first is a pointer to alloca3)
865 constraints.AddConstraint(LoadConstraint(reg[9], reg[7]));
866
867 // store [%8], %9 (stores what might be a pointer to alloca3 into what's either alloca4 or the
868 // pointer argument)
869 constraints.AddConstraint(StoreConstraint(reg[8], reg[9]));
870
871 // %10 = load [%8] (loads from possibly external, should also point to external)
872 constraints.AddConstraint(LoadConstraint(reg[10], reg[8]));
873
874 // Find a solution to all the constraints
875 if constexpr (solver == Andersen::Configuration::Solver::Worklist)
876 {
877 constraints.SolveUsingWorklist(args...);
878 }
879 else if constexpr (solver == Andersen::Configuration::Solver::Naive)
880 {
881 static_assert(sizeof...(args) == 0, "The naive solver takes no arguments");
882 constraints.SolveNaively();
883 }
884 else
885 {
886 JLM_UNREACHABLE("Unknown solver");
887 }
888
889 // alloca1 should point to alloca2, etc
890 EXPECT_LE(set.GetPointsToSet(alloca1).Size(), 1u);
891 EXPECT_TRUE(set.IsPointingTo(alloca1, alloca2));
892 EXPECT_LE(set.GetPointsToSet(alloca2).Size(), 1u);
893 EXPECT_TRUE(set.IsPointingTo(alloca2, alloca3));
894 EXPECT_LE(set.GetPointsToSet(alloca3).Size(), 1u);
895 EXPECT_TRUE(set.IsPointingTo(alloca3, alloca4));
896
897 // %5 is a load of alloca1, and should only be a pointer to alloca2
898 EXPECT_LE(set.GetPointsToSet(reg[5]).Size(), 1u);
899 EXPECT_TRUE(set.IsPointingTo(reg[5], alloca2));
900
901 // %6 is a load of alloca3, and should only be a pointer to alloca4
902 EXPECT_LE(set.GetPointsToSet(reg[6]).Size(), 1u);
903 EXPECT_TRUE(set.IsPointingTo(reg[6], alloca4));
904
905 // %7 can point to either alloca2 or alloca4
906 EXPECT_LE(set.GetPointsToSet(reg[7]).Size(), 2u);
907 EXPECT_TRUE(set.IsPointingTo(reg[7], alloca2));
908 EXPECT_TRUE(set.IsPointingTo(reg[7], alloca4));
909
910 // %8 should point to external, since it points to the superset of %0 and %1
911 EXPECT_TRUE(set.IsPointingToExternal(reg[8]));
912 // %8 may also point to alloca4
913 EXPECT_LE(set.GetPointsToSet(reg[8]).Size(), 1u);
914 EXPECT_TRUE(set.IsPointingTo(reg[8], alloca4));
915
916 // %9 may point to v3
917 EXPECT_TRUE(set.IsPointingTo(reg[9], alloca3));
918
919 // Due to the store of %9 into [%8], alloca4 may now point back to alloca3
920 EXPECT_LE(set.GetPointsToSet(alloca4).Size(), 1u);
921 EXPECT_TRUE(set.IsPointingTo(alloca4, alloca3));
922
923 // Also due to the same store, alloca3 might have escaped
924 EXPECT_TRUE(set.HasEscaped(alloca3));
925 // Due to alloca3 pointing to alloca4, it too should have been marked as escaped
926 EXPECT_TRUE(set.HasEscaped(alloca4));
927 // Check that the other two allocas haven't escaped
928 EXPECT_FALSE(set.HasEscaped(alloca1));
929 EXPECT_FALSE(set.HasEscaped(alloca2));
930
931 // Make sure only the escaped allocas are marked as pointing to external
932 EXPECT_FALSE(set.IsPointingToExternal(alloca1));
933 EXPECT_FALSE(set.IsPointingToExternal(alloca2));
934 EXPECT_TRUE(set.IsPointingToExternal(alloca3));
935 EXPECT_TRUE(set.IsPointingToExternal(alloca4));
936
937 // %10 should also point to external, since it might have been loaded from external
938 EXPECT_TRUE(set.IsPointingToExternal(reg[10]));
939}
940
941TEST(PointerObjectSetTests, TestPointerObjectConstraintSetSolveNaive)
942{
943 using Configuration = jlm::llvm::aa::Andersen::Configuration;
944 TestPointerObjectConstraintSetSolve<Configuration::Solver::Naive>();
945}
946
947TEST(PointerObjectSetTests, TestPointerObjectConstraintSetSolveWorklist)
948{
949 using Configuration = jlm::llvm::aa::Andersen::Configuration;
950
952 for (const auto & config : allConfigs)
953 {
954 // Ignore all configs that enable features that do not affect SolveUsingWorklist()
956 continue;
957 if (config.IsOfflineVariableSubstitutionEnabled())
958 continue;
959 if (config.IsOfflineConstraintNormalizationEnabled())
960 continue;
961
962 TestPointerObjectConstraintSetSolve<Configuration::Solver::Worklist>(
963 config.GetWorklistSoliverPolicy(),
964 config.IsOnlineCycleDetectionEnabled(),
965 config.IsHybridCycleDetectionEnabled(),
966 config.IsLazyCycleDetectionEnabled(),
967 config.IsDifferencePropagationEnabled(),
968 config.IsPreferImplicitPointeesEnabled());
969 }
970}
971
972TEST(PointerObjectSetTests, TestClonePointerObjectConstraintSet)
973{
974 using namespace jlm::llvm::aa;
976 rvsdg.InitializeTest();
977
979 const auto register0 = set.CreateRegisterPointerObject(rvsdg.GetAllocaOutput());
980 const auto alloca0 = set.CreateAllocaMemoryObject(rvsdg.GetAllocaNode(), true);
981 set.AddToPointsToSet(register0, alloca0);
982
983 // Create a dummy register that will point to alloca0 after solving
984 const auto dummy0 = set.CreateDummyRegisterPointerObject();
985
986 PointerObjectConstraintSet constraints(set);
987 constraints.AddConstraint(SupersetConstraint(dummy0, register0));
988
989 // Make a clone of everything
990 auto [setClone, constraintsClone] = constraints.Clone();
991
992 // Modifying the copy doesn't affect the original
993 constraintsClone->AddConstraint(LoadConstraint(register0, alloca0));
994 EXPECT_EQ(constraintsClone->GetConstraints().size(), 2u);
995 EXPECT_EQ(constraints.GetConstraints().size(), 1u);
996
997 // Solving only affects the PointerObjectSet belonging to that constraint set
998 constraints.SolveNaively();
999 EXPECT_TRUE(set.GetPointsToSet(dummy0).Contains(alloca0));
1000 EXPECT_TRUE(setClone->GetPointsToSet(dummy0).IsEmpty());
1001
1002 constraintsClone->SolveNaively();
1003 EXPECT_TRUE(setClone->GetPointsToSet(dummy0).Contains(alloca0));
1004}
std::int64_t c1
std::int64_t c2
static void TestPointerObjectConstraintSetSolve(Args... args)
static bool StringContains(std::string_view haystack, std::string_view needle)
TEST(PointerObjectSetTests, TestFlagFunctions)
RVSDG module with one of each memory node type.
const rvsdg::SimpleNode & GetMallocNode() const noexcept
const jlm::rvsdg::DeltaNode & GetDeltaNode() const noexcept
const rvsdg::SimpleNode & GetAllocaNode() const noexcept
const rvsdg::Output & GetDeltaOutput() const noexcept
const jlm::rvsdg::Output & GetAllocaOutput() const noexcept
const llvm::LlvmGraphImport & GetImportOutput() const noexcept
const jlm::rvsdg::LambdaNode & GetLambdaNode() const noexcept
CallTest1 class.
rvsdg::SimpleNode * alloca_y
jlm::rvsdg::LambdaNode * lambda_f
rvsdg::SimpleNode * alloca_x
const rvsdg::SimpleNode & CallF() const noexcept
RVSDG module with a static function escaping through another function.
const jlm::rvsdg::LambdaNode & GetExportedFunction() const noexcept
const jlm::rvsdg::Output & GetLocalFunctionRegister() const noexcept
const jlm::rvsdg::LambdaNode & GetLocalFunction() const noexcept
RVSDG module with an arbitrary amount of alloca nodes.
const rvsdg::SimpleNode & GetAllocaNode(size_t index) const noexcept
const jlm::rvsdg::Output & GetAllocaOutput(size_t index) const noexcept
static std::vector< Configuration > GetAllConfigurations()
Definition Andersen.cpp:76
bool ApplyDirectly(PointerObjectSet &set)
void AddPointerPointeeConstraint(PointerObjectIndex pointer, PointerObjectIndex pointee)
util::graph::Graph & DrawSubsetGraph(util::graph::Writer &writer) const
std::pair< std::unique_ptr< PointerObjectSet >, std::unique_ptr< PointerObjectConstraintSet > > Clone() const
WorklistStatistics SolveUsingWorklist(WorklistSolverPolicy policy, bool enableOnlineCycleDetection, bool enableHybridCycleDetection, bool enableLazyCycleDetection, bool enableDifferencePropagation, bool enablePreferImplicitPropation)
const std::vector< ConstraintVariant > & GetConstraints() const noexcept
void AddPointsToExternalConstraint(PointerObjectIndex pointer)
void AddRegisterContentEscapedConstraint(PointerObjectIndex registerIndex)
const util::BijectiveMap< const rvsdg::LambdaNode *, PointerObjectIndex > & GetFunctionMap() const noexcept
PointerObjectIndex CreateMallocMemoryObject(const rvsdg::SimpleNode &mallocNode, bool canPoint)
PointerObjectIndex UnifyPointerObjects(PointerObjectIndex object1, PointerObjectIndex object2)
bool IsPointingTo(PointerObjectIndex pointer, PointerObjectIndex pointee) const
PointerObjectIndex CreateDummyRegisterPointerObject()
PointerObjectKind GetPointerObjectKind(PointerObjectIndex index) const noexcept
const std::unordered_map< const rvsdg::SimpleNode *, PointerObjectIndex > & GetMallocMap() const noexcept
bool IsPointerObjectRegister(PointerObjectIndex index) const noexcept
PointerObjectIndex CreateGlobalMemoryObject(const rvsdg::DeltaNode &deltaNode, bool canPoint)
void MapRegisterToExistingPointerObject(const rvsdg::Output &rvsdgOutput, PointerObjectIndex pointerObject)
const util::HashSet< PointerObjectIndex > & GetPointsToSet(PointerObjectIndex index) const
bool HasPointeesEscaping(PointerObjectIndex index) const noexcept
bool IsLoadedAsScalar(PointerObjectIndex index) const noexcept
PointerObjectIndex CreateRegisterPointerObject(const rvsdg::Output &rvsdgOutput)
bool CanPoint(PointerObjectIndex index) const noexcept
bool AddToPointsToSet(PointerObjectIndex pointer, PointerObjectIndex pointee)
PointerObjectIndex GetRegisterPointerObject(const rvsdg::Output &rvsdgOutput) const
bool MarkAsLoadingAsScalar(PointerObjectIndex index)
const std::unordered_map< const rvsdg::SimpleNode *, PointerObjectIndex > & GetAllocaMap() const noexcept
std::unique_ptr< PointerObjectSet > Clone() const
bool IsPointingToExternal(PointerObjectIndex index) const noexcept
bool IsStoredAsScalar(PointerObjectIndex index) const noexcept
bool HasEscaped(PointerObjectIndex index) const noexcept
bool MarkAsPointeesEscaping(PointerObjectIndex index)
const std::unordered_map< const LlvmGraphImport *, PointerObjectIndex > & GetImportMap() const noexcept
const std::unordered_map< const rvsdg::DeltaNode *, PointerObjectIndex > & GetGlobalMap() const noexcept
PointerObjectIndex CreateAllocaMemoryObject(const rvsdg::SimpleNode &allocaNode, bool canPoint)
bool MarkAsStoringAsScalar(PointerObjectIndex index)
bool MarkAsPointingToExternal(PointerObjectIndex index)
PointerObjectIndex GetUnificationRoot(PointerObjectIndex index) const noexcept
bool IsUnificationRoot(PointerObjectIndex index) const noexcept
PointerObjectIndex CreateFunctionMemoryObject(const rvsdg::LambdaNode &lambdaNode)
size_t NumPointerObjects() const noexcept
const rvsdg::LambdaNode & GetLambdaNodeFromFunctionMemoryObject(PointerObjectIndex index) const
const std::unordered_map< const rvsdg::Output *, PointerObjectIndex > & GetRegisterMap() const noexcept
PointerObjectIndex CreateImportMemoryObject(const LlvmGraphImport &importNode, bool canPoint)
bool MarkAsEscaped(PointerObjectIndex index)
PointerObjectIndex GetFunctionMemoryObject(const rvsdg::LambdaNode &lambdaNode) const
bool MakePointsToSetSuperset(PointerObjectIndex superset, PointerObjectIndex subset)
std::optional< PointerObjectIndex > TryGetRegisterPointerObject(const rvsdg::Output &rvsdgOutput) const
size_t NumPointerObjectsOfKind(PointerObjectKind kind) const noexcept
std::vector< rvsdg::Output * > GetFunctionArguments() const
Definition lambda.cpp:58
std::vector< rvsdg::Input * > GetFunctionResults() const
Definition lambda.cpp:70
rvsdg::Output * output() const noexcept
Definition lambda.cpp:177
NodeOutput * output(size_t index) const noexcept
std::size_t Size() const noexcept
Definition HashSet.hpp:187
bool Contains(const ItemType &item) const noexcept
Definition HashSet.hpp:150
#define JLM_UNREACHABLE(msg)
Definition common.hpp:43
uint32_t PointerObjectIndex