Nearly CC
An educational compiler skeleton
dataflow.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 DATAFLOW_H
22 #define DATAFLOW_H
23 
24 #include <cassert>
25 #include <algorithm>
26 #include <memory>
27 #include <vector>
28 #include <bitset>
29 #include "cfg.h"
30 
34 
36 enum class DataflowDirection {
37  FORWARD,
38  BACKWARD,
39 };
40 
44 public:
49  std::shared_ptr<InstructionSequence> get_start_block(const std::shared_ptr<ControlFlowGraph> &cfg) const {
50  return cfg->get_entry_block();
51  }
52 
59  const ControlFlowGraph::EdgeList &get_edges(const std::shared_ptr<ControlFlowGraph> &cfg, std::shared_ptr<InstructionSequence> bb) const {
60  return cfg->get_outgoing_edges(bb);
61  }
62 
68  std::shared_ptr<InstructionSequence> get_block(const Edge *edge) const {
69  return edge->get_target();
70  }
71 };
72 
76 public:
81  std::shared_ptr<InstructionSequence> get_start_block(const std::shared_ptr<ControlFlowGraph> &cfg) const {
82  return cfg->get_exit_block();
83  }
84 
91  const ControlFlowGraph::EdgeList &get_edges(const std::shared_ptr<ControlFlowGraph> &cfg, std::shared_ptr<InstructionSequence> bb) const {
92  return cfg->get_incoming_edges(bb);
93  }
94 
100  std::shared_ptr<InstructionSequence> get_block(const Edge *edge) const {
101  return edge->get_source();
102  }
103 };
104 
107 public:
109  const static DataflowDirection DIRECTION = DataflowDirection::FORWARD;
110 
114 
119  InstructionIterator begin(std::shared_ptr<InstructionSequence> bb) const { return bb->cbegin(); }
120 
125  InstructionIterator end(std::shared_ptr<InstructionSequence> bb) const { return bb->cend(); }
126 
127  // Logical navigation forward and backward (program order)
128 
132 
136 };
137 
140 public:
142  const static DataflowDirection DIRECTION = DataflowDirection::BACKWARD;
143 
147 
152  InstructionIterator begin(std::shared_ptr<InstructionSequence> bb) const { return bb->crbegin(); }
153 
158  InstructionIterator end(std::shared_ptr<InstructionSequence> bb) const { return bb->crend(); }
159 
160  // Logical navigation forward and backward (reverse of program order)
161 
165 
169 };
170 
176 template<typename DataflowType>
178 public:
179  DataflowType &m_dataflow;
180 
183  DataflowAnnotator(DataflowType &dataflow)
184  : m_dataflow(dataflow) {
185  }
186 
187  std::string get_instruction_annotation(std::shared_ptr<InstructionSequence> bb, Instruction *ins) const {
188  // get dataflow fact just before the instruction
189  typename DataflowType::FactType fact = m_dataflow.get_fact_before_instruction(bb, ins);
190  std::string s = DataflowType::fact_to_string(fact);
191  //printf("annotation: %s\n", s.c_str());
192  return s;
193  }
194 
201  virtual std::string get_block_begin_annotation(std::shared_ptr<InstructionSequence> bb) const {
202  typename DataflowType::FactType fact = m_dataflow.get_fact_at_beginning_of_block(bb);
203  return DataflowType::fact_to_string(fact);
204  }
205 
212  virtual std::string get_block_end_annotation(std::shared_ptr<InstructionSequence> bb) const {
213  typename DataflowType::FactType fact = m_dataflow.get_fact_at_end_of_block(bb);
214  return DataflowType::fact_to_string(fact);
215  }
216 };
217 
232 template<typename Analysis>
233 class Dataflow {
234 public:
237  typedef typename Analysis::FactType FactType;
238 
240  static const unsigned MAX_BLOCKS = 1024;
241 
242 private:
243  // The Analysis object encapsulates all of the details about the
244  // analysis to be performed: direction (forward or backward),
245  // the fact type, how to model an instruction, how to combine
246  // dataflow facts, etc.
247  Analysis m_analysis;
248 
249  // The control flow graph to analyze
250  std::shared_ptr<ControlFlowGraph> m_cfg;
251 
252  // facts at end and beginning of each basic block
253  std::vector<FactType> m_endfacts, m_beginfacts;
254 
255  // block iteration order
256  std::vector<unsigned> m_iter_order;
257 
258 public:
261  Dataflow(const std::shared_ptr<ControlFlowGraph> &cfg);
262  ~Dataflow();
263 
265  void execute();
266 
270  const FactType &get_fact_at_end_of_block(std::shared_ptr<InstructionSequence> bb) const;
271 
275  const FactType &get_fact_at_beginning_of_block(std::shared_ptr<InstructionSequence> bb) const;
276 
282  FactType get_fact_after_instruction(std::shared_ptr<InstructionSequence> bb, Instruction *ins) const;
283 
289  FactType get_fact_before_instruction(std::shared_ptr<InstructionSequence> bb, Instruction *ins) const;
290 
294  static std::string fact_to_string(const FactType &fact);
295 
296 private:
297  // Helpers to get the vector containing the facts known at "logical"
298  // beginning and end of each basic block. In this context, "beginning" means
299  // where analysis of the block should start (beginning for forward analyses,
300  // end for backward analyses), "end" means where analysis ends.
301 
302  std::vector<typename Analysis::FactType> &get_logical_begin_facts() {
303  return Analysis::DIRECTION == DataflowDirection::FORWARD
304  ? m_beginfacts
305  : m_endfacts;
306  }
307 
308  std::vector<typename Analysis::FactType> &get_logical_end_facts() {
309  return Analysis::DIRECTION == DataflowDirection::FORWARD
310  ? m_endfacts
311  : m_beginfacts;
312  }
313 
314  const std::vector<typename Analysis::FactType> &get_logical_begin_facts() const {
315  return Analysis::DIRECTION == DataflowDirection::FORWARD
316  ? m_beginfacts
317  : m_endfacts;
318  }
319 
320  const std::vector<typename Analysis::FactType> &get_logical_end_facts() const {
321  return Analysis::DIRECTION == DataflowDirection::FORWARD
322  ? m_endfacts
323  : m_beginfacts;
324  }
325 
326  // Helper for get_fact_after_instruction() and get_fact_before_instruction()
327  FactType get_instruction_fact(std::shared_ptr<InstructionSequence> bb, Instruction *ins, bool after_in_logical_order) const;
328 
329  // Compute the iteration order
330  void compute_iter_order();
331 
332  // Postorder traversal on the CFG (or reversed CFG, depending on
333  // analysis direction)
334  void postorder_on_cfg(std::bitset<MAX_BLOCKS> &visited, std::shared_ptr<InstructionSequence> bb);
335 };
336 
337 template<typename Analysis>
338 Dataflow<Analysis>::Dataflow(const std::shared_ptr<ControlFlowGraph> &cfg)
339  : m_cfg(cfg) {
340  for (unsigned i = 0; i < cfg->get_num_blocks(); ++i) {
341  m_beginfacts.push_back(m_analysis.get_top_fact());
342  m_endfacts.push_back(m_analysis.get_top_fact());
343  }
344 }
345 
346 template<typename Analysis>
348 }
349 
350 template<typename Analysis>
352  compute_iter_order();
353 
354  std::vector<FactType> &logical_begin_facts = get_logical_begin_facts(),
355  &logical_end_facts = get_logical_end_facts();
356 
357  const auto &to_logical_predecessors = m_analysis.LOGICAL_BACKWARD;
358 
359  bool done = false;
360 
361  unsigned num_iters = 0;
362  while (!done) {
363  num_iters++;
364  bool change = false;
365 
366  // For each block (according to the iteration order)...
367  for (auto i = m_iter_order.begin(); i != m_iter_order.end(); i++) {
368  unsigned id = *i;
369  std::shared_ptr<InstructionSequence> bb = m_cfg->get_block(id);
370 
371  // Combine facts known from control edges from the "logical" predecessors
372  // (which are the successors for backward analyses)
373  FactType fact = m_analysis.get_top_fact();
374  const ControlFlowGraph::EdgeList &logical_predecessor_edges = to_logical_predecessors.get_edges(m_cfg, bb);
375  for (auto j = logical_predecessor_edges.cbegin(); j != logical_predecessor_edges.cend(); j++) {
376  const Edge *e = *j;
377  std::shared_ptr<InstructionSequence> logical_predecessor = to_logical_predecessors.get_block(e);
378  fact = m_analysis.combine_facts(fact, logical_end_facts[logical_predecessor->get_block_id()]);
379  }
380 
381  // Update (currently-known) fact at the "beginning" of this basic block
382  // (which will actually be the end of the basic block for backward analyses)
383  logical_begin_facts[id] = fact;
384 
385  // For each Instruction in the basic block (in the appropriate analysis order)...
386  for (auto j = m_analysis.begin(bb); j != m_analysis.end(bb); ++j) {
387  Instruction *ins = *j;
388 
389  // model the instruction
390  m_analysis.model_instruction(ins, fact);
391  }
392 
393  // Did the fact at the logical "end" of the block change?
394  // If so, at least one more round will be needed.
395  if (fact != logical_end_facts[id]) {
396  change = true;
397  logical_end_facts[id] = fact;
398  }
399  }
400 
401  // If there was no change in the computed dataflow fact at the beginning
402  // of any block, then the analysis has terminated
403  if (!change) {
404  done = true;
405  }
406  }
407 }
408 
409 template<typename Analysis>
410 const typename Dataflow<Analysis>::FactType &Dataflow<Analysis>::get_fact_at_end_of_block(std::shared_ptr<InstructionSequence> bb) const {
411  return m_endfacts.at(bb->get_block_id());
412 }
413 
414 template<typename Analysis>
415 const typename Dataflow<Analysis>::FactType &Dataflow<Analysis>::get_fact_at_beginning_of_block(std::shared_ptr<InstructionSequence> bb) const {
416  return m_beginfacts.at(bb->get_block_id());
417 }
418 
419 template<typename Analysis>
420 typename Dataflow<Analysis>::FactType Dataflow<Analysis>::get_fact_after_instruction(std::shared_ptr<InstructionSequence> bb, Instruction *ins) const {
421  // For a backward analysis, we want the fact that is true "before" (logically)
422  // the specified instruction.
423  bool after_in_logical_order = (Analysis::DIRECTION == DataflowDirection::FORWARD);
424  return get_instruction_fact(bb, ins, after_in_logical_order);
425 }
426 
427 template<typename Analysis>
428 typename Dataflow<Analysis>::FactType Dataflow<Analysis>::get_fact_before_instruction(std::shared_ptr<InstructionSequence> bb, Instruction *ins) const {
429  // For a backward analysis, we want the fact that is true "after" (logically)
430  // the specified instruction.
431  bool after_in_logical_order = (Analysis::DIRECTION == DataflowDirection::BACKWARD);
432  return get_instruction_fact(bb, ins, after_in_logical_order);
433 }
434 
435 template<typename Analysis>
437  Analysis analysis;
438  return analysis.fact_to_string(fact);
439 }
440 
441 // Compute the dataflow fact immediately before or after the specified instruction,
442 // in the "logical" order (corresponding to the analysis direction) rather than
443 // program order.
444 template<typename Analysis>
445 typename Analysis::FactType Dataflow<Analysis>::get_instruction_fact(std::shared_ptr<InstructionSequence> bb,
446  Instruction *ins,
447  bool after_in_logical_order) const {
448  const std::vector<FactType> &logical_begin_facts = get_logical_begin_facts();
449 
450  FactType fact = logical_begin_facts[bb->get_block_id()];
451 
452  for (auto i = m_analysis.begin(bb); i != m_analysis.end(bb); ++i) {
453  Instruction *bb_ins = *i;
454  bool at_instruction = (bb_ins == ins);
455 
456  if (at_instruction && !after_in_logical_order) break;
457 
458  m_analysis.model_instruction(bb_ins, fact);
459 
460  if (at_instruction) {
461  assert(after_in_logical_order);
462  break;
463  }
464  }
465 
466  return fact;
467 }
468 
469 template<typename Analysis>
471  std::bitset<MAX_BLOCKS> visited;
472 
473  const auto &to_logical_successors = m_analysis.LOGICAL_FORWARD;
474 
475  std::shared_ptr<InstructionSequence> logical_entry_block = to_logical_successors.get_start_block(m_cfg);
476 
477  postorder_on_cfg(visited, logical_entry_block);
478 
479  std::reverse(m_iter_order.begin(), m_iter_order.end());
480 }
481 
482 template<typename Analysis>
483 void Dataflow<Analysis>::postorder_on_cfg(std::bitset<MAX_BLOCKS> &visited, std::shared_ptr<InstructionSequence> bb) {
484  const auto &to_logical_successors = m_analysis.LOGICAL_FORWARD;
485 
486  // already arrived at this block?
487  if (visited.test(bb->get_block_id())) {
488  return;
489  }
490 
491  // this block is now guaranteed to be visited
492  visited.set(bb->get_block_id());
493 
494  // recursively visit (logical) successors
495  const ControlFlowGraph::EdgeList &logical_successor_edges = to_logical_successors.get_edges(m_cfg, bb);
496  for (auto i = logical_successor_edges.begin(); i != logical_successor_edges.end(); i++) {
497  const Edge *e = *i;
498  std::shared_ptr<InstructionSequence> next_bb = to_logical_successors.get_block(e);
499  postorder_on_cfg(visited, next_bb);
500  }
501 
502  // add this block to the order
503  m_iter_order.push_back(bb->get_block_id());
504 }
505 
506 #endif // DATAFLOW_H
ControlFlowGraph and associated types.
Base class for backward analyses.
Definition: dataflow.h:139
InstructionIterator begin(std::shared_ptr< InstructionSequence > bb) const
Get a begin iterator over the instructions in a basic block in "analysis" order.
Definition: dataflow.h:152
static const DataflowDirection DIRECTION
Analysis direction is backward.
Definition: dataflow.h:142
BackwardNavigation LOGICAL_FORWARD
Logical forward navigation through the ControlFlowGraph in analysis order.
Definition: dataflow.h:164
ForwardNavigation LOGICAL_BACKWARD
Logical backward navigation through the ControlFlowGraph in analysis order.
Definition: dataflow.h:168
InstructionIterator end(std::shared_ptr< InstructionSequence > bb) const
Get an end iterator over the instructions in a basic block in "analysis" order.
Definition: dataflow.h:158
InstructionSequence::const_reverse_iterator InstructionIterator
Iterator type for iterating over instructions in a basic block in "analysis" order.
Definition: dataflow.h:146
Backward navigation in the control-flow graph (from successors back to predecessors).
Definition: dataflow.h:75
std::shared_ptr< InstructionSequence > get_start_block(const std::shared_ptr< ControlFlowGraph > &cfg) const
Get the "start" block in the ControlFlowGraph, which for a backward analysis is the exit block.
Definition: dataflow.h:81
const ControlFlowGraph::EdgeList & get_edges(const std::shared_ptr< ControlFlowGraph > &cfg, std::shared_ptr< InstructionSequence > bb) const
Get the control edges for a given block which lead to the "logical successors" of the block.
Definition: dataflow.h:91
std::shared_ptr< InstructionSequence > get_block(const Edge *edge) const
Get the basic block that is a "logical" successor from an Edge returned from the get_edges member fun...
Definition: dataflow.h:100
std::vector< Edge * > EdgeList
Data type for a vector of edges.
Definition: cfg.h:92
Annotator to return a stringified dataflow fact for a specific instruction in a basic block.
Definition: dataflow.h:177
virtual std::string get_block_end_annotation(std::shared_ptr< InstructionSequence > bb) const
Get an end annotation for a basic block.
Definition: dataflow.h:212
virtual std::string get_block_begin_annotation(std::shared_ptr< InstructionSequence > bb) const
Get a begin annotation for a basic block.
Definition: dataflow.h:201
DataflowAnnotator(DataflowType &dataflow)
Constructor.
Definition: dataflow.h:183
An instance of Dataflow performs a dataflow analysis on the basic blocks of a control flow graph and ...
Definition: dataflow.h:233
static const unsigned MAX_BLOCKS
We assume there won't be more than this many basic blocks.
Definition: dataflow.h:240
FactType get_fact_before_instruction(std::shared_ptr< InstructionSequence > bb, Instruction *ins) const
Get dataflow fact at the location immediately before the specified instruction (in program order).
Definition: dataflow.h:428
FactType get_fact_after_instruction(std::shared_ptr< InstructionSequence > bb, Instruction *ins) const
Get dataflow fact at the location immediately after the specified instruction (in program order).
Definition: dataflow.h:420
void execute()
Execute the analysis.
Definition: dataflow.h:351
const FactType & get_fact_at_beginning_of_block(std::shared_ptr< InstructionSequence > bb) const
Get dataflow fact at beginning of specific block.
Definition: dataflow.h:415
static std::string fact_to_string(const FactType &fact)
Convert dataflow fact to a string.
Definition: dataflow.h:436
Dataflow(const std::shared_ptr< ControlFlowGraph > &cfg)
Constructor.
Definition: dataflow.h:338
const FactType & get_fact_at_end_of_block(std::shared_ptr< InstructionSequence > bb) const
Get dataflow fact at end of specified block.
Definition: dataflow.h:410
Analysis::FactType FactType
Data type representing a dataflow fact.
Definition: dataflow.h:237
Control-flow graph edge data type.
Definition: cfg.h:56
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
Base class for forward analyses.
Definition: dataflow.h:106
ForwardNavigation LOGICAL_FORWARD
Logical forward navigation through the ControlFlowGraph in analysis order.
Definition: dataflow.h:131
InstructionIterator end(std::shared_ptr< InstructionSequence > bb) const
Get an end iterator over the instructions in a basic block in "analysis" order.
Definition: dataflow.h:125
InstructionIterator begin(std::shared_ptr< InstructionSequence > bb) const
Get a begin iterator over the instructions in a basic block in "analysis" order.
Definition: dataflow.h:119
InstructionSequence::const_iterator InstructionIterator
Iterator type for iterating over instructions in a basic block in "analysis" order.
Definition: dataflow.h:113
static const DataflowDirection DIRECTION
Analysis direction is forward.
Definition: dataflow.h:109
BackwardNavigation LOGICAL_BACKWARD
Logical backward navigation through the ControlFlowGraph in analysis order.
Definition: dataflow.h:135
Forward navigation in the control-flow graph (from predecessors towards successors).
Definition: dataflow.h:43
std::shared_ptr< InstructionSequence > get_block(const Edge *edge) const
Get the basic block that is a "logical" successor from an Edge returned from the get_edges member fun...
Definition: dataflow.h:68
std::shared_ptr< InstructionSequence > get_start_block(const std::shared_ptr< ControlFlowGraph > &cfg) const
Get the "start" block in the ControlFlowGraph, which for a forward analysis is the entry block.
Definition: dataflow.h:49
const ControlFlowGraph::EdgeList & get_edges(const std::shared_ptr< ControlFlowGraph > &cfg, std::shared_ptr< InstructionSequence > bb) const
Get the control edges for a given block which lead to the "logical successors" of the block.
Definition: dataflow.h:59
Definition: instruction_seq_iter.h:37
Instruction object type.
Definition: instruction.h:31
DataflowDirection
Dataflow analysis direction.
Definition: dataflow.h:36