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 
12 #include <jlm/rvsdg/node.hpp>
13 #include <jlm/util/common.hpp>
14 #include <jlm/util/HashSet.hpp>
16 #include <jlm/util/Math.hpp>
17 
18 #include <memory>
19 #include <optional>
20 #include <string>
21 #include <unordered_map>
22 
24 {
25 class Writer;
26 }
27 
28 namespace jlm::llvm
29 {
30 
31 class LlvmRvsdgModule;
32 
33 namespace aa
34 {
35 
46 class PointsToGraph final
47 {
48 public:
49  using NodeIndex = uint32_t;
50 
51  // The external node, representing all memory not represented by any other memory node
52  static constexpr NodeIndex externalMemoryNode = 0;
53 
54  enum class NodeKind : uint8_t
55  {
56  // Register nodes can never be the target of pointers, and cannot be externally available
57  RegisterNode = 0,
58  // All other kinds of nodes are memory nodes, representing one or more memory locations
59  AllocaNode,
60  DeltaNode,
61  ImportNode,
62  LambdaNode,
63  MallocNode,
64  // Only one external node exists. It represents all memory not represented by any other node.
66  COUNT
67  };
68 
69 private:
70  struct NodeData
71  {
72  // The kind of node
74 
75  // When set, the node is available from other modules in the program
76  uint8_t isExternallyAvailable : 1;
77 
78  // When set, the points-to graph node implicitly targets all externally available nodes
80 
81  // When set, the node represents constant memory / a constant register
82  uint8_t isConstant : 1;
83 
84  // The size of the memory allocation(s) represented by the node.
85  // If the size is unknown, or too large to represent, it is set to UnknownMemorySize
86  uint16_t memorySize : 10;
87  static constexpr uint16_t UnknownMemorySize = (1 << 10) - 1;
88 
90  NodeKind kind,
91  bool externallyAvailable,
92  bool targetsAllExternallyAvailable,
93  bool isConstant,
94  std::optional<size_t> memorySize)
95  : kind(kind),
96  isExternallyAvailable(externallyAvailable),
97  isTargetingAllExternallyAvailable(targetsAllExternallyAvailable),
99  memorySize(
102  {}
103  };
104 
105  static_assert(sizeof(NodeData) == sizeof(uint16_t), "NodeData must fit in 16 bits");
106 
107  using AllocaNodeMap = std::unordered_map<const rvsdg::SimpleNode *, NodeIndex>;
108  using DeltaNodeMap = std::unordered_map<const rvsdg::DeltaNode *, NodeIndex>;
109  using ImportNodeMap = std::unordered_map<const rvsdg::GraphImport *, NodeIndex>;
110  using LambdaNodeMap = std::unordered_map<const rvsdg::LambdaNode *, NodeIndex>;
111  using MallocNodeMap = std::unordered_map<const rvsdg::SimpleNode *, NodeIndex>;
112  using RegisterNodeMap = std::unordered_map<const rvsdg::Output *, NodeIndex>;
113 
116 
119 
122 
125 
128 
129  using RegisterNodeIterator = std::vector<NodeIndex>::const_iterator;
131 
132  using ExternallyAvailableIterator = std::vector<NodeIndex>::const_iterator;
134 
135  PointsToGraph();
136 
137 public:
138  PointsToGraph(const PointsToGraph &) = delete;
139 
141 
142  PointsToGraph &
143  operator=(const PointsToGraph &) = delete;
144 
145  PointsToGraph &
146  operator=(PointsToGraph &&) = delete;
147 
152  allocaNodes() const noexcept;
153 
158  deltaNodes() const noexcept;
159 
164  importNodes() const noexcept;
165 
170  lambdaNodes() const noexcept;
171 
176  mallocNodes() const noexcept;
177 
185  NodeIndex
186  getExternalMemoryNode() const noexcept;
187 
192  registerNodes() const noexcept;
193 
197  size_t
198  numAllocaNodes() const noexcept
199  {
200  return allocaMap_.size();
201  }
202 
206  size_t
207  numDeltaNodes() const noexcept
208  {
209  return deltaMap_.size();
210  }
211 
215  size_t
216  numImportNodes() const noexcept
217  {
218  return importMap_.size();
219  }
220 
224  size_t
225  numLambdaNodes() const noexcept
226  {
227  return lambdaMap_.size();
228  }
229 
233  size_t
234  numMallocNodes() const noexcept
235  {
236  return mallocMap_.size();
237  }
238 
242  [[nodiscard]] size_t
243  numRegisterNodes() const noexcept
244  {
245  return registerNodes_.size();
246  }
247 
251  [[nodiscard]] size_t
252  numMappedRegisters() const noexcept
253  {
254  return registerMap_.size();
255  }
256 
261  size_t
262  numMemoryNodes() const noexcept
263  {
265  + numMallocNodes() + 1; // The external node
266  }
267 
271  size_t
272  numNodes() const noexcept
273  {
274  JLM_ASSERT(nodeData_.size() == nodeExplicitTargets_.size());
275  JLM_ASSERT(nodeData_.size() == nodeObjects_.size());
276  return nodeData_.size();
277  }
278 
285  [[nodiscard]] bool
287  {
288  return allocaMap_.find(&node) != allocaMap_.end();
289  }
290 
296  [[nodiscard]] bool
297  hasNodeForDelta(const rvsdg::DeltaNode & node) const
298  {
299  return deltaMap_.find(&node) != deltaMap_.end();
300  }
301 
307  [[nodiscard]] bool
308  hasNodeForImport(const rvsdg::GraphImport & argument) const
309  {
310  return importMap_.find(&argument) != importMap_.end();
311  }
312 
318  [[nodiscard]] bool
320  {
321  return lambdaMap_.find(&node) != lambdaMap_.end();
322  }
323 
330  [[nodiscard]] bool
332  {
333  return mallocMap_.find(&node) != mallocMap_.end();
334  }
335 
341  [[nodiscard]] bool
342  hasNodeForRegister(const rvsdg::Output & output) const
343  {
344  return registerMap_.find(&output) != registerMap_.end();
345  }
346 
353  [[nodiscard]] NodeIndex
355  {
356  return allocaMap_.at(&node);
357  }
358 
365  NodeIndex
366  getNodeForDelta(const rvsdg::DeltaNode & node) const
367  {
368  return deltaMap_.at(&node);
369  }
370 
377  NodeIndex
378  getNodeForImport(const rvsdg::GraphImport & argument) const
379  {
380  return importMap_.at(&argument);
381  }
382 
389  NodeIndex
391  {
392  return lambdaMap_.at(&node);
393  }
394 
401  NodeIndex
403  {
404  return mallocMap_.at(&node);
405  }
406 
413  NodeIndex
414  getNodeForRegister(const rvsdg::Output & output) const
415  {
416  return registerMap_.at(&output);
417  }
418 
424  [[nodiscard]] std::optional<NodeIndex>
425  tryGetNodeForRegister(const rvsdg::Output & output) const noexcept
426  {
427  auto it = registerMap_.find(&output);
428  if (it != registerMap_.end())
429  return it->second;
430  return std::nullopt;
431  }
432 
438  [[nodiscard]] NodeKind
439  getNodeKind(NodeIndex index) const
440  {
441  JLM_ASSERT(index < nodeData_.size());
442  return nodeData_[index].kind;
443  }
444 
450  [[nodiscard]] bool
452  {
453  return getNodeKind(index) == NodeKind::RegisterNode;
454  }
455 
462  [[nodiscard]] bool
463  isMemoryNode(NodeIndex index) const
464  {
465  return !isRegisterNode(index);
466  }
467 
474  [[nodiscard]] bool
476  {
477  JLM_ASSERT(index < nodeData_.size());
478  return nodeData_[index].isExternallyAvailable;
479  }
480 
488  [[nodiscard]] bool
490  {
491  JLM_ASSERT(index < nodeData_.size());
492  return nodeData_[index].isTargetingAllExternallyAvailable;
493  }
494 
501  [[nodiscard]] bool
502  isNodeConstant(NodeIndex index) const noexcept
503  {
504  JLM_ASSERT(index < nodeData_.size());
505  return nodeData_[index].isConstant;
506  }
507 
515  [[nodiscard]] std::optional<size_t>
516  tryGetNodeSize(NodeIndex index) const noexcept
517  {
518  JLM_ASSERT(index < nodeData_.size());
519  const auto size = nodeData_[index].memorySize;
520  if (size == NodeData::UnknownMemorySize)
521  return std::nullopt;
522  return size;
523  }
524 
536  [[nodiscard]] const util::HashSet<NodeIndex> &
538  {
539  JLM_ASSERT(index < nodeExplicitTargets_.size());
540  return nodeExplicitTargets_[index];
541  }
542 
550  [[nodiscard]] bool
551  isTargeting(NodeIndex source, NodeIndex target) const
552  {
554  return true;
555  return getExplicitTargets(source).Contains(target);
556  }
557 
562  const std::vector<NodeIndex> &
564  {
566  }
567 
572  size_t
574  {
575  return externallyAvailableNodes_.size();
576  }
577 
582  size_t
584  {
586  }
587 
594  const rvsdg::SimpleNode &
596  {
597  JLM_ASSERT(index < nodeObjects_.size());
598  if (nodeData_[index].kind != NodeKind::AllocaNode)
599  throw std::logic_error("PointsToGraph node is not an AllocaNode");
600  return *static_cast<const rvsdg::SimpleNode *>(nodeObjects_[index]);
601  }
602 
609  const rvsdg::DeltaNode &
611  {
612  JLM_ASSERT(index < nodeObjects_.size());
613  if (nodeData_[index].kind != NodeKind::DeltaNode)
614  throw std::logic_error("PointsToGraph node is not a DeltaNode");
615  return *static_cast<const rvsdg::DeltaNode *>(nodeObjects_[index]);
616  }
617 
624  const rvsdg::GraphImport &
626  {
627  JLM_ASSERT(index < nodeObjects_.size());
628  if (nodeData_[index].kind != NodeKind::ImportNode)
629  throw std::logic_error("PointsToGraph node is not an ImportNode");
630  return *static_cast<const rvsdg::GraphImport *>(nodeObjects_[index]);
631  }
632 
639  const rvsdg::LambdaNode &
641  {
642  JLM_ASSERT(index < nodeObjects_.size());
643  if (nodeData_[index].kind != NodeKind::LambdaNode)
644  throw std::logic_error("PointsToGraph node is not a LambdaNode");
645  return *static_cast<const rvsdg::LambdaNode *>(nodeObjects_[index]);
646  }
647 
654  const rvsdg::SimpleNode &
656  {
657  JLM_ASSERT(index < nodeObjects_.size());
658  if (nodeData_[index].kind != NodeKind::MallocNode)
659  throw std::logic_error("PointsToGraph node is not a MallocNode");
660  return *static_cast<const rvsdg::SimpleNode *>(nodeObjects_[index]);
661  }
662 
670  NodeIndex
671  addNodeForAlloca(const rvsdg::SimpleNode & allocaNode, bool externallyAvailable);
672 
679  NodeIndex
680  addNodeForDelta(const rvsdg::DeltaNode & deltaNode, bool externallyAvailable);
681 
688  NodeIndex
689  addNodeForImport(const rvsdg::GraphImport & argument, bool externallyAvailable);
690 
697  NodeIndex
698  addNodeForLambda(const rvsdg::LambdaNode & lambdaNode, bool externallyAvailable);
699 
707  NodeIndex
708  addNodeForMalloc(const rvsdg::SimpleNode & mallocNode, bool externallyAvailable);
709 
715  [[nodiscard]] NodeIndex
717 
724  void
725  mapRegisterToNode(const rvsdg::Output & output, NodeIndex nodeIndex);
726 
732  void
734 
747  bool
748  addTarget(NodeIndex source, NodeIndex target);
749 
761  [[nodiscard]] std::pair<size_t, size_t>
762  numEdges() const noexcept;
763 
772  [[nodiscard]] std::string
773  getNodeDebugString(NodeIndex index, char separator = ' ') const;
774 
784  [[nodiscard]] bool
785  isSupergraphOf(const PointsToGraph & subgraph) const;
786 
791  static void
792  dumpGraph(util::graph::Writer & graphWriter, const PointsToGraph & pointsToGraph);
793 
798  static std::string
799  dumpDot(const PointsToGraph & pointsToGraph);
800 
801  static std::unique_ptr<PointsToGraph>
803  {
804  return std::unique_ptr<PointsToGraph>(new PointsToGraph());
805  }
806 
807 private:
808  NodeIndex
809  addNode(
810  NodeKind kind,
811  bool externallyAvailable,
812  bool isConstant,
813  std::optional<size_t> memorySize,
814  const void * object);
815 
816  // Vectors containing information per node
817  std::vector<NodeData> nodeData_;
818  // For each node, the set of memory nodes that it explicitly targets
819  std::vector<util::HashSet<NodeIndex>> nodeExplicitTargets_;
820  // For each memory node, this vector contains a pointer to the associated RVSDG object.
821  std::vector<const void *> nodeObjects_;
822 
823  // Mappings from RVSDG objects to their corresponding PointsToGraph NodeIndex
829 
830  // RegisterNode is the only kind of PointsToGraph nodes where multiple
831  // \ref rvsdg::Output* can be mapped to the same PointsToGraph node.
833 
834  // In-order list of node indices containing all nodes of RegisterNode kind
835  std::vector<NodeIndex> registerNodes_;
836  // In-order list of node indices containing all nodes that are flagged isExternallyAvailable
837  std::vector<NodeIndex> externallyAvailableNodes_;
838  // The number of nodes that have the isTargetingAllExternallyAvailable flag
840 };
841 
842 }
843 }
844 
845 #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
std::optional< NodeIndex > tryGetNodeForRegister(const rvsdg::Output &output) const noexcept
static constexpr NodeIndex externalMemoryNode
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)