Nearly CC
An educational compiler skeleton
cfg.h
Go to the documentation of this file.
1 // Copyright (c) 2021-2023, David H. Hovemeyer <david.hovemeyer@gmail.com>
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a
4 // copy of this software and associated documentation files (the "Software"),
5 // to deal in the Software without restriction, including without limitation
6 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 // and/or sell copies of the Software, and to permit persons to whom the
8 // Software is furnished to do so, subject to the following conditions:
9 //
10 // The above copyright notice and this permission notice shall be included
11 // in all copies or substantial portions of the Software.
12 //
13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
17 // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
18 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
19 // OTHER DEALINGS IN THE SOFTWARE.
20 
21 #ifndef CFG_H
22 #define CFG_H
23 
24 #include <cassert>
25 #include <vector>
26 #include <map>
27 #include <deque>
28 #include <string>
29 #include <memory>
30 #include "instruction.h"
31 #include "instruction_seq.h"
32 #include "operand.h"
33 
39 
48 enum EdgeKind {
49  EDGE_FALLTHROUGH,
50  EDGE_BRANCH,
51 };
52 
56 class Edge {
57 private:
58  EdgeKind m_kind;
59  std::shared_ptr<InstructionSequence> m_source, m_target;
60 
61 public:
66  Edge(std::shared_ptr<InstructionSequence> source, std::shared_ptr<InstructionSequence> target, EdgeKind kind);
67  ~Edge();
68 
71  EdgeKind get_kind() const { return m_kind; }
72 
75  std::shared_ptr<InstructionSequence> get_source() const { return m_source; }
76 
79  std::shared_ptr<InstructionSequence> get_target() const { return m_target; }
80 };
81 
87 public:
89  typedef std::vector<std::shared_ptr<InstructionSequence> > BlockList;
90 
92  typedef std::vector<Edge *> EdgeList;
93 
95  typedef std::map<std::shared_ptr<InstructionSequence> , EdgeList> EdgeMap;
96 
97 private:
98  BlockList m_basic_blocks;
99  std::shared_ptr<InstructionSequence> m_entry, m_exit;
100  EdgeMap m_incoming_edges;
101  EdgeMap m_outgoing_edges;
102  EdgeList m_empty_edge_list;
103 
104  // A "Chunk" is a collection of InstructionSequences
105  // connected by fall-through edges. All of the blocks
106  // in a Chunk must be emitted contiguously in the
107  // resulting InstructionSequence when the CFG is flattened.
108  struct Chunk {
110  bool is_exit;
111 
112  void append(std::shared_ptr<InstructionSequence> bb) {
113  blocks.push_back(bb);
114  if (bb->get_kind() == BASICBLOCK_EXIT) { is_exit = true; }
115  }
116  void prepend(std::shared_ptr<InstructionSequence> bb) {
117  blocks.insert(blocks.begin(), bb);
118  if (bb->get_kind() == BASICBLOCK_EXIT) { is_exit = true; }
119  }
120 
121  Chunk *merge_with(Chunk *other) {
122  Chunk *merged = new Chunk();
123  for (auto i = blocks.begin(); i != blocks.end(); i++) {
124  merged->append(*i);
125  }
126  for (auto i = other->blocks.begin(); i != other->blocks.end(); i++) {
127  merged->append(*i);
128  }
129  return merged;
130  }
131 
132  bool is_first(std::shared_ptr<InstructionSequence> bb) const {
133  return !blocks.empty() && blocks.front() == bb;
134  }
135 
136  bool is_last(std::shared_ptr<InstructionSequence> bb) const {
137  return !blocks.empty() && blocks.back() == bb;
138  }
139 
140  bool contains_exit_block() const { return is_exit; }
141  };
142 
143 public:
145  ~ControlFlowGraph();
146 
149  unsigned get_num_blocks() const { return unsigned(m_basic_blocks.size()); }
150 
153  std::shared_ptr<InstructionSequence> get_entry_block() const;
154 
157  std::shared_ptr<InstructionSequence> get_exit_block() const;
158 
162  std::shared_ptr<InstructionSequence> get_block(unsigned id) const {
163  assert(id < m_basic_blocks.size());
164  return m_basic_blocks[id];
165  }
166 
167  // iterator over pointers to InstructionSequences
168 
172  BlockList::const_iterator bb_begin() const { return m_basic_blocks.cbegin(); }
173 
177  BlockList::const_iterator bb_end() const { return m_basic_blocks.cend(); }
178 
191  std::shared_ptr<InstructionSequence> create_basic_block(BasicBlockKind kind, int code_order, const std::string &label = "");
192 
197  void adopt_basic_block(std::shared_ptr<InstructionSequence> &bb);
198 
206  Edge *create_edge(std::shared_ptr<InstructionSequence> source, std::shared_ptr<InstructionSequence> target, EdgeKind kind);
207 
214  Edge *lookup_edge(std::shared_ptr<InstructionSequence> source, std::shared_ptr<InstructionSequence> target) const;
215 
219  const EdgeList &get_outgoing_edges(std::shared_ptr<InstructionSequence> bb) const;
220 
224  const EdgeList &get_incoming_edges(std::shared_ptr<InstructionSequence> bb) const;
225 
229  // basic blocks
230  std::shared_ptr<InstructionSequence> create_instruction_sequence() const;
231 
232 private:
233  std::vector<std::shared_ptr<InstructionSequence> > get_blocks_in_code_order() const;
234  bool can_use_original_block_order() const;
235  std::shared_ptr<InstructionSequence> rebuild_instruction_sequence() const;
236  std::shared_ptr<InstructionSequence> reconstruct_instruction_sequence() const;
237  void append_basic_block(std::shared_ptr<InstructionSequence> &iseq, std::shared_ptr<InstructionSequence> bb, std::vector<bool> &finished_blocks) const;
238  void append_chunk(std::shared_ptr<InstructionSequence> &iseq, Chunk *chunk, std::vector<bool> &finished_blocks) const;
239  void visit_successors(std::shared_ptr<InstructionSequence> bb, std::deque<std::shared_ptr<InstructionSequence> > &work_list) const;
240  void delete_edges(EdgeMap &edge_map);
241 };
242 
243 #endif // CFG_H
EdgeKind
Control-flow graph edge kinds.
Definition: cfg.h:48
ControlFlowGraph: graph of basic blocks connected by control edges.
Definition: cfg.h:86
std::vector< Edge * > EdgeList
Data type for a vector of edges.
Definition: cfg.h:92
BlockList::const_iterator bb_end() const
Get an end iterator for the list of all basic blocks in the ControlFlowGraph.
Definition: cfg.h:177
const EdgeList & get_incoming_edges(std::shared_ptr< InstructionSequence > bb) const
Get vector of all incoming edges to given block.
Definition: cfg.cpp:135
std::vector< std::shared_ptr< InstructionSequence > > BlockList
Data type for a vector of basic blocks.
Definition: cfg.h:89
Edge * create_edge(std::shared_ptr< InstructionSequence > source, std::shared_ptr< InstructionSequence > target, EdgeKind kind)
Create Edge of given kind from source basic block to target basic block.
Definition: cfg.cpp:98
std::shared_ptr< InstructionSequence > create_basic_block(BasicBlockKind kind, int code_order, const std::string &label="")
Create a new InstructionSequence.
Definition: cfg.cpp:74
BlockList::const_iterator bb_begin() const
Get a begin iterator for the list of all basic blocks in the ControlFlowGraph.
Definition: cfg.h:172
unsigned get_num_blocks() const
Get total number of basic blocks (including entry and exit).
Definition: cfg.h:149
const EdgeList & get_outgoing_edges(std::shared_ptr< InstructionSequence > bb) const
Get vector of all outgoing edges from given block.
Definition: cfg.cpp:130
std::shared_ptr< InstructionSequence > get_entry_block() const
Get pointer to the dedicated empty entry block.
Definition: cfg.cpp:66
std::shared_ptr< InstructionSequence > create_instruction_sequence() const
Return a "flat" InstructionSequence created from this ControlFlowGraph.
Definition: cfg.cpp:140
std::shared_ptr< InstructionSequence > get_exit_block() const
Get pointer to the dedicated empty exit block.
Definition: cfg.cpp:70
std::shared_ptr< InstructionSequence > get_block(unsigned id) const
Get block with specified id.
Definition: cfg.h:162
std::map< std::shared_ptr< InstructionSequence >, EdgeList > EdgeMap
Data type for a map of basic blocks to an EdgeList.
Definition: cfg.h:95
Edge * lookup_edge(std::shared_ptr< InstructionSequence > source, std::shared_ptr< InstructionSequence > target) const
Look up edge from specified source block to target block.
Definition: cfg.cpp:114
void adopt_basic_block(std::shared_ptr< InstructionSequence > &bb)
Adopt a new basic block.
Definition: cfg.cpp:80
Control-flow graph edge data type.
Definition: cfg.h:56
Edge(std::shared_ptr< InstructionSequence > source, std::shared_ptr< InstructionSequence > target, EdgeKind kind)
Constructor.
Definition: cfg.cpp:38
EdgeKind get_kind() const
Get the EdgeKind of this Edge.
Definition: cfg.h:71
std::shared_ptr< InstructionSequence > get_source() const
Get the source basic block.
Definition: cfg.h:75
std::shared_ptr< InstructionSequence > get_target() const
Get the target basic block.
Definition: cfg.h:79
InstructionSequence and friends.
BasicBlockKind
Kinds of basic blocks.
Definition: instruction_seq.h:39
@ BASICBLOCK_EXIT
special "exit" block
Definition: instruction_seq.h:41