Jlm
Loading...
Searching...
No Matches
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{
25class Writer;
26}
27
28namespace jlm::llvm
29{
30
31class LlvmRvsdgModule;
32
33namespace aa
34{
35
46class PointsToGraph final
47{
48public:
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
64 // Only one external node exists. It represents all memory not represented by any other node.
66 COUNT
67 };
68
69private:
70 struct NodeData
71 {
72 // The kind of node
74
75 // When set, the node is available from other modules in the program
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
91 bool externallyAvailable,
92 bool targetsAllExternallyAvailable,
93 bool isConstant,
94 std::optional<size_t> memorySize)
95 : kind(kind),
96 isExternallyAvailable(externallyAvailable),
97 isTargetingAllExternallyAvailable(targetsAllExternallyAvailable),
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
136
137public:
138 PointsToGraph(const PointsToGraph &) = delete;
139
141
143 operator=(const PointsToGraph &) = delete;
144
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
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
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
367 {
368 return deltaMap_.at(&node);
369 }
370
378 getNodeForImport(const rvsdg::GraphImport & argument) const
379 {
380 return importMap_.at(&argument);
381 }
382
391 {
392 return lambdaMap_.at(&node);
393 }
394
403 {
404 return mallocMap_.at(&node);
405 }
406
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
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
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
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
671 addNodeForAlloca(const rvsdg::SimpleNode & allocaNode, bool externallyAvailable);
672
680 addNodeForDelta(const rvsdg::DeltaNode & deltaNode, bool externallyAvailable);
681
689 addNodeForImport(const rvsdg::GraphImport & argument, bool externallyAvailable);
690
698 addNodeForLambda(const rvsdg::LambdaNode & lambdaNode, bool externallyAvailable);
699
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
807private:
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
jlm::llvm::aa::PointsToGraph::NodeIndex NodeIndex
std::vector< NodeIndex >::const_iterator RegisterNodeIterator
std::unordered_map< const rvsdg::SimpleNode *, NodeIndex > AllocaNodeMap
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
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
bool hasNodeForAlloca(const rvsdg::SimpleNode &node) const
size_t numMemoryNodes() const noexcept
NodeIndex getNodeForRegister(const rvsdg::Output &output) const
NodeIndex addNodeForMalloc(const rvsdg::SimpleNode &mallocNode, bool externallyAvailable)
const rvsdg::LambdaNode & getLambdaForNode(NodeIndex index) const
size_t numRegisterNodes() const noexcept
bool isExternallyAvailable(NodeIndex index) const
std::unordered_map< const rvsdg::Output *, NodeIndex > RegisterNodeMap
std::optional< NodeIndex > tryGetNodeForRegister(const rvsdg::Output &output) const noexcept
NodeIndex addNodeForImport(const rvsdg::GraphImport &argument, bool externallyAvailable)
size_t numMallocNodes() const noexcept
std::string getNodeDebugString(NodeIndex index, char separator=' ') const
PointsToGraph & operator=(PointsToGraph &&)=delete
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
bool isTargetingAllExternallyAvailable(NodeIndex index) const
bool hasNodeForLambda(const rvsdg::LambdaNode &node) const
std::vector< NodeData > nodeData_
NodeIndex getExternalMemoryNode() const noexcept
size_t numExternallyAvailableNodes() const noexcept
const rvsdg::DeltaNode & getDeltaForNode(NodeIndex index) const
const rvsdg::SimpleNode & getAllocaForNode(NodeIndex index) const
bool hasNodeForImport(const rvsdg::GraphImport &argument) 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::optional< size_t > tryGetNodeSize(NodeIndex index) 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
const std::vector< NodeIndex > & getExternallyAvailableNodes() const noexcept
static std::unique_ptr< PointsToGraph > create()
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)
const util::HashSet< NodeIndex > & getExplicitTargets(NodeIndex index) const
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
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
static constexpr NodeIndex externalMemoryNode
PointsToGraph & operator=(const PointsToGraph &)=delete
const rvsdg::GraphImport & getImportForNode(NodeIndex index) const
ImportNodeRange importNodes() const noexcept
std::vector< NodeIndex > externallyAvailableNodes_
#define JLM_ASSERT(x)
Definition common.hpp:16
Global memory state passed between functions.
static constexpr int BitWidthOfEnum(T endValue)
Definition Math.hpp:110
static constexpr uint16_t UnknownMemorySize
NodeData(NodeKind kind, bool externallyAvailable, bool targetsAllExternallyAvailable, bool isConstant, std::optional< size_t > memorySize)