Spicy
cfg.h
1 // Copyright (c) 2020-now by the Zeek Project. See LICENSE for details.
2 
3 #pragma once
4 
5 #include <cstddef>
6 #include <cstdint>
7 #include <functional>
8 #include <memory>
9 #include <string>
10 #include <type_traits>
11 #include <unordered_map>
12 #include <unordered_set>
13 #include <utility>
14 
15 #include <hilti/ast/all.h>
16 #include <hilti/ast/forward.h>
17 #include <hilti/base/graph.h>
18 
19 namespace hilti {
20 
21 namespace node::tag {
22 constexpr Tag MetaNode = 20000;
23 constexpr Tag Start = 20001;
24 constexpr Tag End = 20002;
25 constexpr Tag Flow = 20003;
26 } // namespace node::tag
27 
28 namespace detail::cfg {
29 
36 struct MetaNode : Node {
37  // Some versions of GCC incorrectly diagnose maybe uninitialized members here.
38 #pragma GCC diagnostic push
39 #pragma GCC diagnostic ignored "-Wpragmas"
40 #pragma GCC diagnostic ignored "-Wunknown-warning-option"
41 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
42  MetaNode(node::Tags node_tags) : Node(nullptr, node_tags, {}, {}) {}
43 #pragma GCC diagnostic pop
44 
45  HILTI_NODE_0(MetaNode, override);
46 };
47 
51 struct Start : MetaNode {
52  Start() : MetaNode(NodeTags) {}
53  HILTI_NODE_1(Start, MetaNode, final);
54 };
55 
59 struct Flow : MetaNode {
60  Flow() : MetaNode(NodeTags) {}
61  HILTI_NODE_1(Flow, MetaNode, final);
62 };
63 
67 struct End : MetaNode {
68  End(const Node* scope) : MetaNode(NodeTags), scope(scope) {
69  assert(scope); // Should always contain a valid scope.
70  }
71 
72  HILTI_NODE_1(End, MetaNode, final);
73 
74  const Node* scope;
75 };
76 
82 class GraphNode {
83 public:
84  GraphNode(operator_::function::Call* x) : node(x) {}
85  GraphNode(Expression* x) : node(x) {}
86  GraphNode(statement::Return* x) : node(x) {}
87  GraphNode(Statement* x) : node(x) {}
88  GraphNode(MetaNode* x) : node(x) {}
89  GraphNode(declaration::LocalVariable* x) : node(x) {}
90  GraphNode(declaration::GlobalVariable* x) : node(x) {}
91 
92  GraphNode() = default;
93  GraphNode(const GraphNode&) = default;
94 
95  GraphNode& operator=(const GraphNode& x) {
96  if ( &x == this )
97  return *this;
98 
99  node = x.node;
100  return *this;
101  }
102 
103  Node* operator->() { return node; }
104  const Node* operator->() const { return node; }
105 
106  Node* value() const { return node; }
107 
108  friend bool operator==(const GraphNode& a, const GraphNode& b) { return a.node == b.node; }
109  friend bool operator!=(const GraphNode& a, const GraphNode& b) { return ! (a.node == b.node); }
110 
111  friend bool operator<(const GraphNode& a, const GraphNode& b) { return a.node < b.node; }
112 
113 private:
114  Node* node = nullptr;
115 };
116 
117 } // namespace detail::cfg
118 } // namespace hilti
119 
120 namespace std {
121 template<>
122 struct hash<hilti::detail::cfg::GraphNode> {
123  auto operator()(const hilti::detail::cfg::GraphNode& n) const { return n.value() ? n->identity() : 0; }
124 };
125 } // namespace std
126 
127 namespace hilti::detail::cfg {
128 
132 struct Transfer {
134  std::unordered_map<Declaration*, std::unordered_set<GraphNode>> in;
135 
137  std::unordered_map<Declaration*, std::unordered_set<GraphNode>> out;
138 
140  std::unordered_map<Declaration*, std::unordered_set<GraphNode>> kill;
141 
143  std::unordered_set<Declaration*> maybe_alias;
144 
151  std::unordered_map<Declaration*, GraphNode> gen;
152 
154  std::unordered_set<Declaration*> read;
155 
157  std::unordered_set<Declaration*> write;
158 
163  bool keep = false;
164 };
165 
169 class CFG {
170 public:
171  using NodeId = uint64_t;
172 
175 
184  CFG(const Node* root);
185 
193  void removeNode(Node* node);
194 
200  std::string dot() const;
201 
203  const auto& dataflow() const { return _dataflow; }
204 
206  const Graph& graph() const { return g; }
207 
208 private:
209  GraphNode _getOrAddNode(GraphNode n);
210  void _addEdge(const GraphNode& from, const GraphNode& to);
211  void _populateDataflow();
212 
213  GraphNode _addBlock(GraphNode predecessor, const Nodes& stmts, const Node* scope);
214  GraphNode _addFor(GraphNode predecessor, const statement::For& for_);
215  GraphNode _addWhile(GraphNode predecessor, const statement::While& while_, GraphNode scope_end);
216  GraphNode _addIf(GraphNode predecessor, const statement::If& if_);
217  GraphNode _addTryCatch(GraphNode predecessor, const statement::Try& try_);
218  GraphNode _addReturn(GraphNode predecessor, const statement::Return& return_);
219  GraphNode _addThrow(GraphNode predecessor, statement::Throw& throw_, GraphNode scope_end);
220  GraphNode _addCall(GraphNode predecessor, operator_::function::Call& call);
221 
222  // Add flow for globals if `root` corresponds to a global module block.
223  GraphNode _addGlobals(GraphNode predecessor, const Node& root);
224 
234  template<typename T, typename... Args>
235  MetaNode* _createMetaNode(Args... args)
236  requires(std::is_base_of_v<MetaNode, T>)
237  {
238  auto n = std::make_unique<T>(args...);
239  auto* r = n.get();
240  _meta_nodes.insert(std::move(n));
241  return r;
242  }
243 
244  Graph g;
245 
246  std::unordered_set<std::unique_ptr<MetaNode>> _meta_nodes;
247  std::unordered_map<GraphNode, Transfer> _dataflow;
248  GraphNode _begin;
249  GraphNode _end;
250 };
251 
252 } // namespace hilti::detail::cfg
Definition: expression.h:15
Definition: node.h:240
uint64_t identity() const
Definition: node.h:361
Node(ASTContext *ctx, node::Tags node_tags, Nodes children, Meta meta)
Definition: node.h:922
Definition: forward.h:758
Definition: statement.h:15
Definition: global-variable.h:17
Definition: local-variable.h:32
Definition: cfg.h:169
const Graph & graph() const
Definition: cfg.h:206
const auto & dataflow() const
Definition: cfg.h:203
void removeNode(Node *node)
Definition: cfg.cc:374
util::graph::DirectedGraph< GraphNode, NodeId > Graph
Definition: cfg.h:174
CFG(const Node *root)
Definition: cfg.cc:85
std::string dot() const
Definition: cfg.cc:389
Definition: cfg.h:82
Definition: for.h:15
Definition: if.h:16
Definition: return.h:14
Definition: throw.h:14
Definition: try.h:49
Definition: while.h:16
Definition: cfg.h:67
Definition: cfg.h:59
Definition: cfg.h:36
Definition: cfg.h:51
Definition: cfg.h:132
std::unordered_map< Declaration *, std::unordered_set< GraphNode > > in
Definition: cfg.h:134
std::unordered_map< Declaration *, std::unordered_set< GraphNode > > kill
Definition: cfg.h:140
std::unordered_map< Declaration *, std::unordered_set< GraphNode > > out
Definition: cfg.h:137
std::unordered_set< Declaration * > read
Definition: cfg.h:154
std::unordered_set< Declaration * > write
Definition: cfg.h:157
std::unordered_set< Declaration * > maybe_alias
Definition: cfg.h:143
std::unordered_map< Declaration *, GraphNode > gen
Definition: cfg.h:151
bool keep
Definition: cfg.h:163