HAL  v4.5.0-124-g47ab54673
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
processing.cpp
Go to the documentation of this file.
2 
11 #include "hal_core/netlist/gate.h"
13 #include "hal_core/utilities/log.h"
14 
15 #include <iomanip>
16 #include <iostream>
17 #include <thread>
18 
19 namespace hal
20 {
21  namespace dataflow
22  {
23  namespace processing
24  {
25  namespace
26  {
33  std::unique_ptr<user_feedback::ProgressPrinter> m_progress_printer;
34 
35  void process_pass_configuration(const Configuration& config, Context& ctx)
36  {
37  u32 num_passes = ctx.current_passes.size();
38 
39  while (true)
40  {
41  u32 start_id;
42  u32 end_id;
43 
44  // fetch next work package
45  {
46  std::lock_guard<std::mutex> guard(ctx.progress_mutex);
47 
48  if (ctx.done)
49  {
50  break;
51  }
52 
53  // adaptive workload:
54  // every thread gets between 1 and 20 passes, depending on the number of remaining passes
55  // this improves cpu utilization and reduces number of mutex locks
56  start_id = ctx.pass_counter;
57  u32 remaining_passes = num_passes - start_id;
58 
59  u32 work = 20;
60 
61  if (remaining_passes < config.num_threads * work / 2)
62  {
63  work = std::max(1u, remaining_passes / config.num_threads);
64  }
65 
66  end_id = std::min(start_id + work, (u32)num_passes);
67 
68  ctx.pass_counter = end_id;
69  if (ctx.pass_counter >= num_passes)
70  {
71  std::lock_guard guard(ctx.result_mutex);
72  ctx.done = true;
73  }
74  }
75 
76  for (u32 current_id = start_id; current_id < end_id; ++current_id)
77  {
78  const auto& [current_state, current_pass] = ctx.current_passes[current_id];
79 
80  if (auto it = ctx.pass_outcome.find({current_state, current_pass.id}); it != ctx.pass_outcome.end())
81  {
82  {
83  // early exit, outcome is already known
84  std::lock_guard guard(ctx.result_mutex);
85  ctx.new_recurring_results.emplace_back(current_state, current_pass.id, it->second);
86  ctx.finished_passes++;
87  m_progress_printer->report((float)ctx.finished_passes / ctx.current_passes.size(),
88  std::to_string(ctx.finished_passes) + "\\" + std::to_string(ctx.current_passes.size()) + " ("
89  + std::to_string(ctx.new_unique_groupings.size()) + " new results)");
90  }
91  continue;
92  }
93 
94  // process work
95  auto new_state = current_pass.function(current_state);
96 
97  // aggregate result
98  std::shared_ptr<Grouping> duplicate = nullptr;
99  for (const auto& other : ctx.result.unique_groupings)
100  {
101  if (*new_state == *other)
102  {
103  duplicate = other;
104  break;
105  }
106  }
107  {
108  std::lock_guard guard(ctx.result_mutex);
109  if (duplicate == nullptr)
110  {
111  ctx.new_unique_groupings.emplace_back(current_state, current_pass.id, new_state);
112  }
113  else
114  {
115  ctx.new_recurring_results.emplace_back(current_state, current_pass.id, duplicate);
116  }
117 
118  ctx.finished_passes++;
119  m_progress_printer->report((float)ctx.finished_passes / ctx.current_passes.size(),
120  std::to_string(ctx.finished_passes) + "\\" + std::to_string(ctx.current_passes.size()) + " ("
121  + std::to_string(ctx.new_unique_groupings.size()) + " new results)");
122  }
123  }
124  }
125  }
126 
127  std::vector<std::pair<std::shared_ptr<Grouping>, PassConfiguration>>
128  generate_pass_combinations(Context& ctx, const Configuration& config, const std::shared_ptr<Grouping>& initial_grouping)
129  {
130  // create current layer of pass combinations;
131  std::vector<std::pair<std::shared_ptr<Grouping>, PassConfiguration>> output;
132 
133  if (initial_grouping != nullptr)
134  {
135  for (const auto& pass : pass_collection::get_passes(config, ctx.result.pass_combinations_leading_to_grouping[initial_grouping]))
136  {
137  output.emplace_back(initial_grouping, pass);
138  }
139  }
140  else
141  {
142  for (const auto& state : ctx.result.unique_groupings)
143  {
144  for (const auto& pass : pass_collection::get_passes(config, ctx.result.pass_combinations_leading_to_grouping[state]))
145  {
146  output.emplace_back(state, pass);
147  }
148  }
149  }
150 
151  return output;
152  }
153 
154  } // namespace
155 
156  Result run(const Configuration& config, const std::shared_ptr<Grouping>& initial_grouping)
157  {
158  log_info("dataflow", "starting pipeline with {} threads", config.num_threads);
159 
160  Context ctx;
161  ctx.num_iterations = 0;
162  ctx.phase = 0;
163  ctx.end_reached = false;
164 
165  for (u32 layer = 0; layer < config.pass_layers; layer++)
166  {
167  log_info("dataflow", "start processing layer {}", layer);
168  auto begin_time = std::chrono::high_resolution_clock::now();
169 
170  // get all pass combinations of layer
171  ctx.current_passes = generate_pass_combinations(ctx, config, (layer == 0) ? initial_grouping : nullptr);
172 
173  // preparations
174  ctx.done = false;
175  ctx.pass_counter = 0;
176  ctx.finished_passes = 0;
177 
178  m_progress_printer = std::make_unique<user_feedback::ProgressPrinter>("dataflow: processing …", 30);
179 
180  // spawn threads
181  std::vector<std::thread> workers;
182  for (u32 t = 0; t < config.num_threads - 1; ++t)
183  {
184  workers.emplace_back([&]() { process_pass_configuration(config, ctx); });
185  }
186 
187  process_pass_configuration(config, ctx);
188 
189  // wait for threads to finish
190  for (auto& worker : workers)
191  {
192  worker.join();
193  }
194 
195  m_progress_printer.reset();
196 
197  log_info("dataflow", " finished in {:3.2f}s, processed {} passes, filtering results...", seconds_since(begin_time), ctx.finished_passes, ctx.new_unique_groupings.size());
198 
199  auto all_new_results = ctx.new_recurring_results;
200 
201  begin_time = std::chrono::high_resolution_clock::now();
202 
203  // filter same results of different threads
204  u32 num_unique_filtered = 0;
205  std::vector<bool> do_not_consider(ctx.new_unique_groupings.size(), false);
206  for (u32 i = 0; i < ctx.new_unique_groupings.size(); ++i)
207  {
208  if (do_not_consider[i])
209  {
210  continue;
211  }
212  const auto& [start_state_i, pass_i, new_state_i] = ctx.new_unique_groupings[i];
213  for (u32 j = i + 1; j < ctx.new_unique_groupings.size(); ++j)
214  {
215  if (do_not_consider[j])
216  {
217  continue;
218  }
219  const auto& [start_state_j, pass_j, new_state_j] = ctx.new_unique_groupings[j];
220 
221  // j is a duplicate of i
222  if (*new_state_i == *new_state_j)
223  {
224  do_not_consider[j] = true;
225  all_new_results.emplace_back(start_state_j, pass_j, new_state_i);
226  }
227  }
228  ctx.result.unique_groupings.push_back(new_state_i);
229  all_new_results.push_back(ctx.new_unique_groupings[i]);
230  num_unique_filtered++;
231  }
232  log_info("dataflow", " filtered results in {:3.2f}s, got {} new unique results", seconds_since(begin_time), num_unique_filtered);
233 
234  begin_time = std::chrono::high_resolution_clock::now();
235  ctx.new_recurring_results.clear();
236  ctx.new_unique_groupings.clear();
237 
238  // fill results: compute path by appending pass id to the path of the prev round
239  for (const auto& [start_state, pass, new_state] : all_new_results)
240  {
241  ctx.pass_outcome[{start_state, pass}] = new_state;
242 
243  const auto& start_pass_combinations = ctx.result.pass_combinations_leading_to_grouping[start_state];
244  auto& new_pass_combinations = ctx.result.pass_combinations_leading_to_grouping[new_state];
245  if (start_pass_combinations.empty())
246  {
247  std::vector<pass_id> path{pass};
248  new_pass_combinations.push_back(path);
249  ctx.result.groupings[path] = new_state;
250  }
251  else
252  {
253  std::vector<std::vector<pass_id>> new_paths; // temporary memory to avoid modification while looping
254  new_paths.reserve(start_pass_combinations.size());
255  for (const auto& path : start_pass_combinations)
256  {
257  if (path.size() != layer)
258  {
259  continue;
260  }
261  std::vector<pass_id> new_path(path);
262  new_path.push_back(pass);
263  new_paths.push_back(new_path);
264  ctx.result.groupings[new_path] = new_state;
265  }
266  new_pass_combinations.insert(new_pass_combinations.end(), new_paths.begin(), new_paths.end());
267  }
268  }
269  log_info("dataflow", " total: {} unique states", ctx.result.unique_groupings.size());
270  }
271 
272  return ctx.result;
273  }
274 
275  void clear()
276  {
278  }
279  } // namespace processing
280  } // namespace dataflow
281 } // namespace hal
uint32_t u32
Definition: defines.h:41
#define log_info(channel,...)
Definition: log.h:70
std::vector< PassConfiguration > get_passes(const Configuration &config, const std::vector< std::vector< pass_id >> &previous_passes)
processing::Result run(const processing::Configuration &config, const std::shared_ptr< Grouping > &initial_grouping)
Definition: processing.cpp:156
Definition: defines.h:45
This file contains the struct that holds all information on the netlist abstraction used for dataflow...
This file contains the class that holds all information of a dataflow analysis grouping.
std::vector< std::pair< std::shared_ptr< Grouping >, PassConfiguration > > current_passes
Definition: context.h:76
std::vector< std::tuple< std::shared_ptr< Grouping >, pass_id, std::shared_ptr< Grouping > > > new_recurring_results
Definition: context.h:85
std::map< std::pair< std::shared_ptr< Grouping >, pass_id >, std::shared_ptr< Grouping > > pass_outcome
Definition: context.h:79
std::vector< std::tuple< std::shared_ptr< Grouping >, pass_id, std::shared_ptr< Grouping > > > new_unique_groupings
Definition: context.h:82
processing::Result result
Definition: context.h:98
std::map< std::shared_ptr< Grouping >, std::vector< std::vector< pass_id > > > pass_combinations_leading_to_grouping
Definition: result.h:52
std::map< std::vector< pass_id >, std::shared_ptr< Grouping > > groupings
Definition: result.h:55
std::vector< std::shared_ptr< Grouping > > unique_groupings
Definition: result.h:49
#define seconds_since(X)
Definition: timing_utils.h:38