Jlm
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 
10 #include <jlm/llvm/TestRvsdgs.hpp>
11 
12 #include <cassert>
13 
14 static bool
15 StringContains(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
21 TEST(PointerObjectSetTests, TestFlagFunctions)
22 {
23  using namespace jlm::llvm::aa;
24 
26  rvsdg.InitializeTest();
27 
28  PointerObjectSet set;
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
68 TEST(PointerObjectSetTests, TestCreatePointerObjects)
69 {
70  using namespace jlm::llvm::aa;
71 
73  rvsdg.InitializeTest();
74 
75  PointerObjectSet set;
76  // Register PointerObjects have some extra ways of being created: Dummy and mapping
77  const auto register0 = set.CreateRegisterPointerObject(rvsdg.GetAllocaOutput());
78  set.MapRegisterToExistingPointerObject(rvsdg.GetDeltaOutput(), register0);
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);
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 
141 TEST(PointerObjectSetTests, TestPointerObjectUnification)
142 {
143  using namespace jlm::llvm::aa;
144 
145  PointerObjectSet set;
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 
174 TEST(PointerObjectSetTests, TestPointerObjectUnificationPointees)
175 {
176  using namespace jlm::llvm::aa;
177 
179  rvsdg.InitializeTest();
180 
181  PointerObjectSet set;
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
219 TEST(PointerObjectSetTests, TestAddToPointsToSet)
220 {
221  using namespace jlm::llvm::aa;
222 
224  rvsdg.InitializeTest();
225 
226  PointerObjectSet set;
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
241 TEST(PointerObjectSetTests, TestMakePointsToSetSuperset)
242 {
243  using namespace jlm::llvm::aa;
244 
246  rvsdg.InitializeTest();
247 
248  PointerObjectSet set;
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 
275 TEST(PointerObjectSetTests, TestClonePointerObjectSet)
276 {
277  using namespace jlm::llvm::aa;
279  rvsdg.InitializeTest();
280 
281  PointerObjectSet set;
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
322 TEST(PointerObjectSetTests, TestSupersetConstraint)
323 {
324  using namespace jlm::llvm::aa;
325 
327  rvsdg.InitializeTest();
328 
329  PointerObjectSet set;
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 
379 TEST(PointerObjectSetTests, TestStoreConstraintDirectly)
380 {
381  using namespace jlm::llvm::aa;
382 
384  rvsdg.InitializeTest();
385 
386  PointerObjectSet set;
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 
419 TEST(PointerObjectSetTests, TestLoadConstraintDirectly)
420 {
421  using namespace jlm::llvm::aa;
422 
424  rvsdg.InitializeTest();
425 
426  PointerObjectSet set;
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 
453 TEST(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 
465  PointerObjectSet set;
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 
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
485  EXPECT_TRUE(result);
486 
487  // Now the return value is marked as all pointees escaping, so make that happen
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 
494 TEST(PointerObjectSetTests, TestStoredAsScalarFlag)
495 {
496  using namespace jlm::llvm::aa;
497 
499  rvsdg.InitializeTest();
500 
501  PointerObjectSet set;
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 
512  EXPECT_FALSE(result);
513 
514  set.MarkAsStoringAsScalar(p0);
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
547  EXPECT_FALSE(result);
548 }
549 
550 TEST(PointerObjectSetTests, TestLoadedAsScalarFlag)
551 {
552  using namespace jlm::llvm::aa;
553 
555  rvsdg.InitializeTest();
556 
557  PointerObjectSet set;
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 
572  EXPECT_FALSE(result);
573 
574  set.MarkAsLoadingAsScalar(p0);
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
607  EXPECT_FALSE(result);
608 }
609 
610 TEST(PointerObjectSetTests, TestFunctionCallConstraint)
611 {
612  using namespace jlm::llvm::aa;
613 
614  jlm::llvm::CallTest1 rvsdg;
615  rvsdg.InitializeTest();
616 
617  PointerObjectSet set;
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 
646 TEST(PointerObjectSetTests, TestAddPointsToExternalConstraint)
647 {
648  using namespace jlm::llvm::aa;
649 
651  rvsdg.InitializeTest();
652 
653  PointerObjectSet set;
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 
688 TEST(PointerObjectSetTests, TestAddRegisterContentEscapedConstraint)
689 {
690  using namespace jlm::llvm::aa;
691 
693  rvsdg.InitializeTest();
694 
695  PointerObjectSet set;
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 
722 TEST(PointerObjectSetTests, TestDrawSubsetGraph)
723 {
724  using namespace jlm::llvm::aa;
725  using namespace jlm::util;
727  rvsdg.InitializeTest();
728 
729  // Arrange
730  PointerObjectSet set;
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()
807 template<jlm::llvm::aa::Andersen::Configuration::Solver solver, typename... Args>
808 static void
810 {
811  using namespace jlm::llvm::aa;
812 
813  // Create a graph with 11 different registers, and 4 allocas.
815  rvsdg.InitializeTest();
816 
817  PointerObjectSet set;
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 
941 TEST(PointerObjectSetTests, TestPointerObjectConstraintSetSolveNaive)
942 {
943  using Configuration = jlm::llvm::aa::Andersen::Configuration;
944  TestPointerObjectConstraintSetSolve<Configuration::Solver::Naive>();
945 }
946 
947 TEST(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 
972 TEST(PointerObjectSetTests, TestClonePointerObjectConstraintSet)
973 {
974  using namespace jlm::llvm::aa;
976  rvsdg.InitializeTest();
977 
978  PointerObjectSet set;
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 }
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 & GetAllocaNode() const noexcept
const jlm::rvsdg::LambdaNode & GetLambdaNode() const noexcept
const jlm::rvsdg::Output & GetAllocaOutput() const noexcept
const rvsdg::Output & GetDeltaOutput() const noexcept
const llvm::LlvmGraphImport & GetImportOutput() const noexcept
const rvsdg::SimpleNode & GetMallocNode() const noexcept
const jlm::rvsdg::DeltaNode & GetDeltaNode() const noexcept
CallTest1 class.
Definition: TestRvsdgs.hpp:425
rvsdg::SimpleNode * alloca_y
Definition: TestRvsdgs.hpp:448
jlm::rvsdg::LambdaNode * lambda_f
Definition: TestRvsdgs.hpp:443
rvsdg::SimpleNode * alloca_x
Definition: TestRvsdgs.hpp:447
const rvsdg::SimpleNode & CallF() const noexcept
Definition: TestRvsdgs.hpp:432
RVSDG module with a static function escaping through another function.
const jlm::rvsdg::LambdaNode & GetExportedFunction() const noexcept
const jlm::rvsdg::LambdaNode & GetLocalFunction() const noexcept
const jlm::rvsdg::Output & GetLocalFunctionRegister() 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
static bool PropagateEscapedFlagsDirectly(PointerObjectSet &set)
static bool PropagateEscapedFunctionsDirectly(PointerObjectSet &set)
bool ApplyDirectly(PointerObjectSet &set)
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
bool ApplyDirectly(PointerObjectSet &set)
bool ApplyDirectly(PointerObjectSet &set)
std::vector< rvsdg::Output * > GetFunctionArguments() const
Definition: lambda.cpp:57
std::vector< rvsdg::Input * > GetFunctionResults() const
Definition: lambda.cpp:69
rvsdg::Output * output() const noexcept
Definition: lambda.cpp:176
NodeOutput * output(size_t index) const noexcept
Definition: simple-node.hpp:88
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
static std::string strfmt(Args... args)
Definition: strfmt.hpp:35