Jlm
PointsToGraph.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2020 Nico Reißmann <nico.reissmann@gmail.com>
3  * Copyright 2025 Håvard Krogstie <krogstie.havard@gmail.com>
4  * See COPYING for terms of redistribution.
5  */
6 
7 #ifndef JLM_LLVM_OPT_ALIAS_ANALYSES_POINTSTOGRAPH_HPP
8 #define JLM_LLVM_OPT_ALIAS_ANALYSES_POINTSTOGRAPH_HPP
9 
11 #include <jlm/rvsdg/node.hpp>
12 #include <jlm/util/common.hpp>
13 #include <jlm/util/HashSet.hpp>
15 #include <jlm/util/Math.hpp>
16 
17 #include <memory>
18 #include <string>
19 #include <unordered_map>
20 
22 {
23 class Writer;
24 }
25 
26 namespace jlm::llvm
27 {
28 
29 class LlvmRvsdgModule;
30 
31 namespace aa
32 {
33 
44 class PointsToGraph final
45 {
46 public:
47  using NodeIndex = uint32_t;
48 
49  enum class NodeKind : uint8_t
50  {
51  // Register nodes can never be the target of pointers, and cannot be externally available
52  RegisterNode = 0,
53  // All other kinds of nodes are memory nodes, representing one or more memory locations
54  AllocaNode,
55  DeltaNode,
56  ImportNode,
57  LambdaNode,
58  MallocNode,
59  // Only one external node exists. It represents all memory not represented by any other node.
61  COUNT
62  };
63 
64 private:
65  struct NodeData
66  {
67  // The kind of node
69 
70  // When set, the node is available from other modules in the program
71  uint8_t isExternallyAvailable : 1;
72 
73  // When set, the points-to graph node implicitly targets all externally available nodes
75 
76  // When set, the node represents constant memory / a constant register
77  uint8_t isConstant : 1;
78 
79  // The size of the memory allocation(s) represented by the node.
80  // If the size is unknown, or too large to represent, it is set to UnknownMemorySize
81  uint16_t memorySize : 10;
82  static constexpr uint16_t UnknownMemorySize = (1 << 10) - 1;
83 
85  NodeKind kind,
86  bool externallyAvailable,
87  bool targetsAllExternallyAvailable,
88  bool isConstant,
89  std::optional<size_t> memorySize)
90  : kind(kind),
91  isExternallyAvailable(externallyAvailable),
92  isTargetingAllExternallyAvailable(targetsAllExternallyAvailable),
94  memorySize(
97  {}
98  };
99 
100  static_assert(sizeof(NodeData) == sizeof(uint16_t), "NodeData must fit in 16 bits");
101 
102  using AllocaNodeMap = std::unordered_map<const rvsdg::SimpleNode *, NodeIndex>;
103  using DeltaNodeMap = std::unordered_map<const rvsdg::DeltaNode *, NodeIndex>;
104  using ImportNodeMap = std::unordered_map<const rvsdg::GraphImport *, NodeIndex>;
105  using LambdaNodeMap = std::unordered_map<const rvsdg::LambdaNode *, NodeIndex>;
106  using MallocNodeMap = std::unordered_map<const rvsdg::SimpleNode *, NodeIndex>;
107  using RegisterNodeMap = std::unordered_map<const rvsdg::Output *, NodeIndex>;
108 
111 
114 
117 
120 
123 
124  using RegisterNodeIterator = std::vector<NodeIndex>::const_iterator;
126 
127  using ExternallyAvailableIterator = std::vector<NodeIndex>::const_iterator;
129 
130  PointsToGraph();
131 
132 public:
133  PointsToGraph(const PointsToGraph &) = delete;
134 
136 
137  PointsToGraph &
138  operator=(const PointsToGraph &) = delete;
139 
140  PointsToGraph &
141  operator=(PointsToGraph &&) = delete;
142 
147  allocaNodes() const noexcept;
148 
153  deltaNodes() const noexcept;
154 
159  importNodes() const noexcept;
160 
165  lambdaNodes() const noexcept;
166 
171  mallocNodes() const noexcept;
172 
180  NodeIndex
181  getExternalMemoryNode() const noexcept;
182 
187  registerNodes() const noexcept;
188 
192  size_t
193  numAllocaNodes() const noexcept
194  {
195  return allocaMap_.size();
196  }
197 
201  size_t
202  numDeltaNodes() const noexcept
203  {
204  return deltaMap_.size();
205  }
206 
210  size_t
211  numImportNodes() const noexcept
212  {
213  return importMap_.size();
214  }
215 
219  size_t
220  numLambdaNodes() const noexcept
221  {
222  return lambdaMap_.size();
223  }
224 
228  size_t
229  numMallocNodes() const noexcept
230  {
231  return mallocMap_.size();
232  }
233 
237  [[nodiscard]] size_t
238  numRegisterNodes() const noexcept
239  {
240  return registerNodes_.size();
241  }
242 
246  [[nodiscard]] size_t
247  numMappedRegisters() const noexcept
248  {
249  return registerMap_.size();
250  }
251 
256  size_t
257  numMemoryNodes() const noexcept
258  {
260  + numMallocNodes() + 1; // The external node
261  }
262 
266  size_t
267  numNodes() const noexcept
268  {
269  JLM_ASSERT(nodeData_.size() == nodeExplicitTargets_.size());
270  JLM_ASSERT(nodeData_.size() == nodeObjects_.size());
271  return nodeData_.size();
272  }
273 
280  [[nodiscard]] bool
282  {
283  return allocaMap_.find(&node) != allocaMap_.end();
284  }
285 
291  [[nodiscard]] bool
292  hasNodeForDelta(const rvsdg::DeltaNode & node) const
293  {
294  return deltaMap_.find(&node) != deltaMap_.end();
295  }
296 
302  [[nodiscard]] bool
303  hasNodeForImport(const rvsdg::GraphImport & argument) const
304  {
305  return importMap_.find(&argument) != importMap_.end();
306  }
307 
313  [[nodiscard]] bool
315  {
316  return lambdaMap_.find(&node) != lambdaMap_.end();
317  }
318 
325  [[nodiscard]] bool
327  {
328  return mallocMap_.find(&node) != mallocMap_.end();
329  }
330 
336  [[nodiscard]] bool
337  hasNodeForRegister(const rvsdg::Output & output) const
338  {
339  return registerMap_.find(&output) != registerMap_.end();
340  }
341 
348  [[nodiscard]] NodeIndex
350  {
351  return allocaMap_.at(&node);
352  }
353 
360  NodeIndex
361  getNodeForDelta(const rvsdg::DeltaNode & node) const
362  {
363  return deltaMap_.at(&node);
364  }
365 
372  NodeIndex
373  getNodeForImport(const rvsdg::GraphImport & argument) const
374  {
375  return importMap_.at(&argument);
376  }
377 
384  NodeIndex
386  {
387  return lambdaMap_.at(&node);
388  }
389 
396  NodeIndex
398  {
399  return mallocMap_.at(&node);
400  }
401 
408  NodeIndex
409  getNodeForRegister(const rvsdg::Output & output) const
410  {
411  return registerMap_.at(&output);
412  }
413 
419  [[nodiscard]] NodeKind
420  getNodeKind(NodeIndex index) const
421  {
422  JLM_ASSERT(index < nodeData_.size());
423  return nodeData_[index].kind;
424  }
425 
431  [[nodiscard]] bool
433  {
434  return getNodeKind(index) == NodeKind::RegisterNode;
435  }
436 
443  [[nodiscard]] bool
444  isMemoryNode(NodeIndex index) const
445  {
446  return !isRegisterNode(index);
447  }
448 
455  [[nodiscard]] bool
457  {
458  JLM_ASSERT(index < nodeData_.size());
459  return nodeData_[index].isExternallyAvailable;
460  }
461 
469  [[nodiscard]] bool
471  {
472  JLM_ASSERT(index < nodeData_.size());
473  return nodeData_[index].isTargetingAllExternallyAvailable;
474  }
475 
482  [[nodiscard]] bool
483  isNodeConstant(NodeIndex index) const noexcept
484  {
485  JLM_ASSERT(index < nodeData_.size());
486  return nodeData_[index].isConstant;
487  }
488 
496  [[nodiscard]] std::optional<size_t>
497  tryGetNodeSize(NodeIndex index) const noexcept
498  {
499  JLM_ASSERT(index < nodeData_.size());
500  const auto size = nodeData_[index].memorySize;
501  if (size == NodeData::UnknownMemorySize)
502  return std::nullopt;
503  return size;
504  }
505 
517  [[nodiscard]] const util::HashSet<NodeIndex> &
519  {
520  JLM_ASSERT(index < nodeExplicitTargets_.size());
521  return nodeExplicitTargets_[index];
522  }
523 
531  [[nodiscard]] bool
532  isTargeting(NodeIndex source, NodeIndex target) const
533  {
535  return true;
536  return getExplicitTargets(source).Contains(target);
537  }
538 
543  const std::vector<NodeIndex> &
545  {
547  }
548 
553  size_t
555  {
556  return externallyAvailableNodes_.size();
557  }
558 
563  size_t
565  {
567  }
568 
575  const rvsdg::SimpleNode &
577  {
578  JLM_ASSERT(index < nodeObjects_.size());
579  if (nodeData_[index].kind != NodeKind::AllocaNode)
580  throw std::logic_error("PointsToGraph node is not an AllocaNode");
581  return *static_cast<const rvsdg::SimpleNode *>(nodeObjects_[index]);
582  }
583 
590  const rvsdg::DeltaNode &
592  {
593  JLM_ASSERT(index < nodeObjects_.size());
594  if (nodeData_[index].kind != NodeKind::DeltaNode)
595  throw std::logic_error("PointsToGraph node is not a DeltaNode");
596  return *static_cast<const rvsdg::DeltaNode *>(nodeObjects_[index]);
597  }
598 
605  const rvsdg::GraphImport &
607  {
608  JLM_ASSERT(index < nodeObjects_.size());
609  if (nodeData_[index].kind != NodeKind::ImportNode)
610  throw std::logic_error("PointsToGraph node is not an ImportNode");
611  return *static_cast<const rvsdg::GraphImport *>(nodeObjects_[index]);
612  }
613 
620  const rvsdg::LambdaNode &
622  {
623  JLM_ASSERT(index < nodeObjects_.size());
624  if (nodeData_[index].kind != NodeKind::LambdaNode)
625  throw std::logic_error("PointsToGraph node is not a LambdaNode");
626  return *static_cast<const rvsdg::LambdaNode *>(nodeObjects_[index]);
627  }
628 
635  const rvsdg::SimpleNode &
637  {
638  JLM_ASSERT(index < nodeObjects_.size());
639  if (nodeData_[index].kind != NodeKind::MallocNode)
640  throw std::logic_error("PointsToGraph node is not a MallocNode");
641  return *static_cast<const rvsdg::SimpleNode *>(nodeObjects_[index]);
642  }
643 
651  NodeIndex
652  addNodeForAlloca(const rvsdg::SimpleNode & allocaNode, bool externallyAvailable);
653 
660  NodeIndex
661  addNodeForDelta(const rvsdg::DeltaNode & deltaNode, bool externallyAvailable);
662 
669  NodeIndex
670  addNodeForImport(const rvsdg::GraphImport & argument, bool externallyAvailable);
671 
678  NodeIndex
679  addNodeForLambda(const rvsdg::LambdaNode & lambdaNode, bool externallyAvailable);
680 
688  NodeIndex
689  addNodeForMalloc(const rvsdg::SimpleNode & mallocNode, bool externallyAvailable);
690 
696  [[nodiscard]] NodeIndex
698 
705  void
706  mapRegisterToNode(const rvsdg::Output & output, NodeIndex nodeIndex);
707 
713  void
715 
728  bool
729  addTarget(NodeIndex source, NodeIndex target);
730 
742  [[nodiscard]] std::pair<size_t, size_t>
743  numEdges() const noexcept;
744 
753  [[nodiscard]] std::string
754  getNodeDebugString(NodeIndex index, char separator = ' ') const;
755 
765  [[nodiscard]] bool
766  isSupergraphOf(const PointsToGraph & subgraph) const;
767 
772  static void
773  dumpGraph(util::graph::Writer & graphWriter, const PointsToGraph & pointsToGraph);
774 
779  static std::string
780  dumpDot(const PointsToGraph & pointsToGraph);
781 
782  static std::unique_ptr<PointsToGraph>
784  {
785  return std::unique_ptr<PointsToGraph>(new PointsToGraph());
786  }
787 
788 private:
789  NodeIndex
790  addNode(
791  NodeKind kind,
792  bool externallyAvailable,
793  bool isConstant,
794  std::optional<size_t> memorySize,
795  const void * object);
796 
797  // Vectors containing information per node
798  std::vector<NodeData> nodeData_;
799  // For each node, the set of memory nodes that it explicitly targets
800  std::vector<util::HashSet<NodeIndex>> nodeExplicitTargets_;
801  // For each memory node, this vector contains a pointer to the associated RVSDG object.
802  std::vector<const void *> nodeObjects_;
803 
804  // Mappings from RVSDG objects to their corresponding PointsToGraph NodeIndex
810 
811  // The external node, representing all memory not represented by any other memory node
813 
814  // RegisterNode is the only kind of PointsToGraph nodes where multiple
815  // \ref rvsdg::Output* can be mapped to the same PointsToGraph node.
817 
818  // In-order list of node indices containing all nodes of RegisterNode kind
819  std::vector<NodeIndex> registerNodes_;
820  // In-order list of node indices containing all nodes that are flagged isExternallyAvailable
821  std::vector<NodeIndex> externallyAvailableNodes_;
822  // The number of nodes that have the isTargetingAllExternallyAvailable flag
824 };
825 
826 }
827 }
828 
829 #endif
std::vector< NodeIndex >::const_iterator RegisterNodeIterator
std::unordered_map< const rvsdg::SimpleNode *, NodeIndex > AllocaNodeMap
std::optional< size_t > tryGetNodeSize(NodeIndex index) const noexcept
NodeIndex getNodeForLambda(const rvsdg::LambdaNode &node) const
RegisterNodeRange registerNodes() const noexcept
bool isTargeting(NodeIndex source, NodeIndex target) const
std::vector< NodeIndex >::const_iterator ExternallyAvailableIterator
size_t numLambdaNodes() const noexcept
PointsToGraph & operator=(PointsToGraph &&)=delete
const rvsdg::SimpleNode & getMallocForNode(NodeIndex index) const
size_t numNodesTargetingAllExternallyAvailable() const noexcept
static void dumpGraph(util::graph::Writer &graphWriter, const PointsToGraph &pointsToGraph)
size_t numNodes() const noexcept
const rvsdg::SimpleNode & getAllocaForNode(NodeIndex index) const
bool hasNodeForAlloca(const rvsdg::SimpleNode &node) const
size_t numMemoryNodes() const noexcept
const rvsdg::GraphImport & getImportForNode(NodeIndex index) const
const util::HashSet< NodeIndex > & getExplicitTargets(NodeIndex index) const
NodeIndex getNodeForRegister(const rvsdg::Output &output) const
NodeIndex addNodeForMalloc(const rvsdg::SimpleNode &mallocNode, bool externallyAvailable)
size_t numRegisterNodes() const noexcept
bool isExternallyAvailable(NodeIndex index) const
std::unordered_map< const rvsdg::Output *, NodeIndex > RegisterNodeMap
NodeIndex addNodeForImport(const rvsdg::GraphImport &argument, bool externallyAvailable)
size_t numMallocNodes() const noexcept
std::string getNodeDebugString(NodeIndex index, char separator=' ') const
NodeIndex getNodeForImport(const rvsdg::GraphImport &argument) const
NodeIndex addNode(NodeKind kind, bool externallyAvailable, bool isConstant, std::optional< size_t > memorySize, const void *object)
AllocaNodeRange allocaNodes() const noexcept
static std::unique_ptr< PointsToGraph > create()
bool isTargetingAllExternallyAvailable(NodeIndex index) const
bool hasNodeForLambda(const rvsdg::LambdaNode &node) const
const std::vector< NodeIndex > & getExternallyAvailableNodes() const noexcept
std::vector< NodeData > nodeData_
NodeIndex getExternalMemoryNode() const noexcept
size_t numExternallyAvailableNodes() const noexcept
bool hasNodeForImport(const rvsdg::GraphImport &argument) const
const rvsdg::LambdaNode & getLambdaForNode(NodeIndex index) const
NodeIndex addNodeForAlloca(const rvsdg::SimpleNode &allocaNode, bool externallyAvailable)
void mapRegisterToNode(const rvsdg::Output &output, NodeIndex nodeIndex)
NodeIndex addNodeForLambda(const rvsdg::LambdaNode &lambdaNode, bool externallyAvailable)
NodeIndex getNodeForDelta(const rvsdg::DeltaNode &node) const
NodeIndex addNodeForDelta(const rvsdg::DeltaNode &deltaNode, bool externallyAvailable)
bool isSupergraphOf(const PointsToGraph &subgraph) const
bool isNodeConstant(NodeIndex index) const noexcept
NodeIndex getNodeForMalloc(const rvsdg::SimpleNode &node) const
LambdaNodeRange lambdaNodes() const noexcept
std::unordered_map< const rvsdg::GraphImport *, NodeIndex > ImportNodeMap
size_t numDeltaNodes() const noexcept
std::vector< const void * > nodeObjects_
std::unordered_map< const rvsdg::LambdaNode *, NodeIndex > LambdaNodeMap
DeltaNodeRange deltaNodes() const noexcept
PointsToGraph(const PointsToGraph &)=delete
NodeIndex getNodeForAlloca(const rvsdg::SimpleNode &node) const
PointsToGraph(PointsToGraph &&)=delete
bool addTarget(NodeIndex source, NodeIndex target)
void markAsTargetsAllExternallyAvailable(NodeIndex index)
bool isRegisterNode(NodeIndex index) const
size_t numAllocaNodes() const noexcept
std::vector< NodeIndex > registerNodes_
static std::string dumpDot(const PointsToGraph &pointsToGraph)
PointsToGraph & operator=(const PointsToGraph &)=delete
NodeKind getNodeKind(NodeIndex index) const
bool isMemoryNode(NodeIndex index) const
bool hasNodeForMalloc(const rvsdg::SimpleNode &node) const
bool hasNodeForRegister(const rvsdg::Output &output) const
std::unordered_map< const rvsdg::DeltaNode *, NodeIndex > DeltaNodeMap
size_t numImportNodes() const noexcept
size_t numMappedRegisters() const noexcept
const rvsdg::DeltaNode & getDeltaForNode(NodeIndex index) const
std::vector< util::HashSet< NodeIndex > > nodeExplicitTargets_
std::unordered_map< const rvsdg::SimpleNode *, NodeIndex > MallocNodeMap
std::pair< size_t, size_t > numEdges() const noexcept
bool hasNodeForDelta(const rvsdg::DeltaNode &node) const
MallocNodeRange mallocNodes() const noexcept
ImportNodeRange importNodes() const noexcept
std::vector< NodeIndex > externallyAvailableNodes_
Delta node.
Definition: delta.hpp:129
Lambda node.
Definition: lambda.hpp:83
#define JLM_ASSERT(x)
Definition: common.hpp:16
Global memory state passed between functions.
static constexpr int BitWidthOfEnum(T endValue)
Definition: Math.hpp:107
static constexpr uint16_t UnknownMemorySize
NodeData(NodeKind kind, bool externallyAvailable, bool targetsAllExternallyAvailable, bool isConstant, std::optional< size_t > memorySize)