PandA-2024.02
mem_dominator_allocation.cpp
Go to the documentation of this file.
1 /*
2  *
3  * _/_/_/ _/_/ _/ _/ _/_/_/ _/_/
4  * _/ _/ _/ _/ _/_/ _/ _/ _/ _/ _/
5  * _/_/_/ _/_/_/_/ _/ _/_/ _/ _/ _/_/_/_/
6  * _/ _/ _/ _/ _/ _/ _/ _/ _/
7  * _/ _/ _/ _/ _/ _/_/_/ _/ _/
8  *
9  * ***********************************************
10  * PandA Project
11  * URL: http://panda.dei.polimi.it
12  * Politecnico di Milano - DEIB
13  * System Architectures Group
14  * ***********************************************
15  * Copyright (C) 2004-2024 Politecnico di Milano
16  *
17  * This file is part of the PandA framework.
18  *
19  * The PandA framework is free software; you can redistribute it and/or modify
20  * it under the terms of the GNU General Public License as published by
21  * the Free Software Foundation; either version 3 of the License, or
22  * (at your option) any later version.
23  *
24  * This program is distributed in the hope that it will be useful,
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27  * GNU General Public License for more details.
28  *
29  * You should have received a copy of the GNU General Public License
30  * along with this program. If not, see <http://www.gnu.org/licenses/>.
31  *
32  */
44 
45 #include "Dominance.hpp"
46 #include "Parameter.hpp"
47 #include "behavioral_helper.hpp"
48 #include "call_graph.hpp"
49 #include "call_graph_manager.hpp"
50 #include "cpu_time.hpp"
51 #include "function_behavior.hpp"
52 #include "functions.hpp"
53 #include "generic_device.hpp"
54 #include "hls.hpp"
55 #include "hls_constraints.hpp"
56 #include "hls_device.hpp"
57 #include "hls_manager.hpp"
58 #include "math_function.hpp"
59 #include "memory.hpp"
60 #include "op_graph.hpp"
61 #include "polixml.hpp"
62 #include "string_manipulation.hpp"
63 #include "technology_manager.hpp"
64 #include "technology_node.hpp"
65 #include "tree_helper.hpp"
66 #include "tree_manager.hpp"
67 #include "tree_node.hpp"
68 #include "tree_reindex.hpp"
69 #include "var_pp_functor.hpp"
70 #include "xml_dom_parser.hpp"
71 #include "xml_helper.hpp"
72 
74 #include <algorithm>
75 #include <filesystem>
76 #include <limits>
77 #include <list>
78 #include <string>
79 #include <utility>
80 
81 #include "custom_map.hpp"
82 #include "custom_set.hpp"
83 
85  const ParameterConstRef _parameters, const HLS_managerRef _HLSMgr,
86  const DesignFlowManagerConstRef _design_flow_manager,
87  const HLSFlowStepSpecializationConstRef _hls_flow_step_specialization, const HLSFlowStep_Type _hls_flow_step_type)
88  : memory_allocation(_parameters, _HLSMgr, _design_flow_manager, _hls_flow_step_type, _hls_flow_step_specialization),
89  user_defined_base_address(UINT64_MAX)
90 {
91  debug_level = _parameters->get_class_debug_level(GET_CLASS(*this));
92 }
93 
95 
96 bool mem_dominator_allocation::is_internal_obj(unsigned int var_id, unsigned int fun_id, bool multiple_top_call_graph)
97 {
98  const auto TM = HLSMgr->get_tree_manager();
99  const auto FB = HLSMgr->CGetFunctionBehavior(fun_id);
100  const auto BH = FB->CGetBehavioralHelper();
101  const auto var_name = BH->PrintVariable(var_id);
102  const auto fun_name = BH->get_function_name();
103  const auto memory_allocation_policy = FB->GetMemoryAllocationPolicy();
104 
105  bool is_internal = false;
106  if(user_external_objects.count(fun_name) && user_external_objects.at(fun_name).count(var_name))
107  {
108  return false;
109  }
110  if(user_external_objects.count("*") && user_external_objects.at("*").count(var_name))
111  {
112  return false;
113  }
114  if(user_internal_objects.count(fun_name) && user_internal_objects.at(fun_name).count(var_name))
115  {
116  return true;
117  }
118  if(!multiple_top_call_graph)
119  {
120  const auto tn = TM->CGetTreeNode(var_id);
121  switch(memory_allocation_policy)
122  {
124  {
125  const auto vd = GetPointer<const var_decl>(tn);
126  if(vd && (vd->static_flag || (vd->scpe && GET_CONST_NODE(vd->scpe)->get_kind() != translation_unit_decl_K)))
127  {
128  is_internal = true;
129  }
130  if(GetPointer<const string_cst>(tn))
131  {
132  is_internal = true;
133  }
134  if(HLSMgr->Rmem->is_parm_decl_copied(var_id) || HLSMgr->Rmem->is_parm_decl_stored(var_id))
135  {
136  is_internal = true;
137  }
138  break;
139  }
141  {
142  const auto vd = GetPointer<const var_decl>(tn);
143  if(vd && (vd->static_flag || !vd->scpe || GET_CONST_NODE(vd->scpe)->get_kind() == translation_unit_decl_K))
144  {
145  is_internal = true;
146  }
147  if(GetPointer<const string_cst>(tn))
148  {
149  is_internal = true;
150  }
151  if(HLSMgr->Rmem->is_parm_decl_copied(var_id) || HLSMgr->Rmem->is_parm_decl_stored(var_id))
152  {
153  is_internal = true;
154  }
155  break;
156  }
158  {
159  is_internal = true;
160  break;
161  }
164  {
165  is_internal = false;
166  break;
167  }
169  default:
170  THROW_UNREACHABLE("not supported memory allocation policy");
171  }
172  // The address of the call site is internal.
173  // We are using the address of the call site in the notification
174  // mechanism of the hw call between accelerators.
175  if(GetPointer<const gimple_call>(tn))
176  {
177  is_internal = true;
178  }
179  }
180  return is_internal;
181 }
182 
185 {
186  const auto current_function_ID = CG->get_function(current_vertex);
187  const auto current_function_name = functions::GetFUName(current_function_ID, HLSMgr);
188  if(HLSMgr->Rfuns->is_a_proxied_function(current_function_name))
189  {
190  return CG->GetVertex(HLSMgr->Rfuns->get_proxy_mapping(current_function_name));
191  }
192  return current_vertex;
193 }
194 
196 {
197  if(parameters->isOption(OPT_xml_memory_allocation))
198  {
199  const auto XMLfilename = parameters->getOption<std::string>(OPT_xml_memory_allocation);
200  INDENT_DBG_MEX(DEBUG_LEVEL_VERY_PEDANTIC, debug_level, "-->parsing " + XMLfilename);
201  XMLDomParser parser(XMLfilename);
202  parser.Exec();
203  if(parser)
204  {
205  const auto node = parser.get_document()->get_root_node(); // deleted by DomParser.
206  const auto& list = node->get_children();
207  for(const auto& l : list)
208  {
209  const auto child = GetPointer<xml_element>(l);
210  if(!child)
211  {
212  continue;
213  }
214  if(child->get_name() == "memory_allocation")
215  {
216  auto base_address = UINT64_MAX;
217  if(CE_XVM(base_address, child))
218  {
219  LOAD_XVM(base_address, child);
220  }
221  user_defined_base_address = base_address;
222  for(const auto& it : child->get_children())
223  {
224  const auto mem_node = GetPointer<xml_element>(it);
225  if(!mem_node)
226  {
227  continue;
228  }
229  if(mem_node->get_name() == "object")
230  {
231  std::string is_internal;
232  if(!CE_XVM(is_internal, mem_node))
233  {
234  THROW_ERROR("expected the is_internal attribute");
235  }
236  LOAD_XVM(is_internal, mem_node);
237  if(is_internal == "T")
238  {
239  if(!CE_XVM(scope, mem_node))
240  {
241  THROW_ERROR("expected the scope attribute when the object is internal");
242  }
243  std::string scope;
244  LOAD_XVM(scope, mem_node);
245  if(!CE_XVM(name, mem_node))
246  {
247  THROW_ERROR("expected the name attribute");
248  }
249  std::string name;
250  LOAD_XVM(name, mem_node);
251  user_internal_objects[scope].insert(name);
252  }
253  else if(is_internal == "F")
254  {
255  std::string scope;
256  if(CE_XVM(scope, mem_node))
257  {
258  LOAD_XVM(scope, mem_node);
259  }
260  else
261  {
262  scope = "*";
263  }
264  if(!CE_XVM(name, mem_node))
265  {
266  THROW_ERROR("expected the name attribute");
267  }
268  std::string name;
269  LOAD_XVM(name, mem_node);
270  user_external_objects[scope].insert(name);
271  }
272  else
273  {
274  THROW_ERROR("unexpected value for is_internal attribute");
275  }
276  }
277  }
278  }
279  }
281  for(const auto& user_obj : user_internal_objects)
282  {
283  for(const auto& var_obj : user_obj.second)
284  {
285  if(user_external_objects.find(user_obj.first) != user_external_objects.end() &&
286  user_external_objects.find(user_obj.first)->second.find(var_obj) !=
287  user_external_objects.find(user_obj.first)->second.end())
288  {
289  THROW_ERROR("An allocated object cannot be both internal and external: " + var_obj + " in function " +
290  user_obj.first);
291  }
292  if(user_external_objects.find("*") != user_external_objects.end() &&
293  user_external_objects.find("*")->second.find(var_obj) !=
294  user_external_objects.find("*")->second.end())
295  {
296  THROW_ERROR("An allocated object cannot be both internal and external: " + var_obj + " in function " +
297  user_obj.first);
298  }
299  }
300  }
301  }
302  INDENT_DBG_MEX(DEBUG_LEVEL_VERY_PEDANTIC, debug_level, "<--parsed file " + XMLfilename);
303  }
304 }
305 
307 {
309  using func_id_t = unsigned int;
310  using top_id_t = func_id_t;
311  using var_id_t = unsigned int;
312 
313  long int step_time = 0;
315  {
316  START_TIME(step_time);
317  }
319  INDENT_OUT_MEX(OUTPUT_LEVEL_MINIMUM, output_level, "-->Memory allocation information:");
320  const auto TM = HLSMgr->get_tree_manager();
321  const auto HLS_D = HLSMgr->get_HLS_device();
322 
323  const auto initial_internal_address_p = parameters->isOption(OPT_initial_internal_address);
324  const auto initial_internal_address = initial_internal_address_p ?
325  parameters->getOption<unsigned int>(OPT_initial_internal_address) :
327  const auto unaligned_access_p =
328  parameters->isOption(OPT_unaligned_access) && parameters->getOption<bool>(OPT_unaligned_access);
329  const auto assume_aligned_access_p =
330  parameters->isOption(OPT_aligned_access) && parameters->getOption<bool>(OPT_aligned_access);
331  const auto max_bram = HLS_D->get_parameter<unsigned int>("BRAM_bitsize_max");
333  HLSMgr->base_address = user_defined_base_address != UINT64_MAX ?
335  parameters->getOption<unsigned long long int>(OPT_base_address);
336  const auto null_pointer_check = [&]() {
337  if(parameters->isOption(OPT_gcc_optimizations))
338  {
339  const auto gcc_parameters = parameters->getOption<CustomSet<std::string>>(OPT_gcc_optimizations);
340  if(gcc_parameters.find("no-delete-null-pointer-checks") != gcc_parameters.end())
341  {
342  return false;
343  }
344  }
345  return true;
346  }();
348  const memoryRef prevRmem = HLSMgr->Rmem;
349  HLSMgr->Rmem =
350  memory::create_memory(parameters, TM, HLSMgr->base_address, max_bram, null_pointer_check,
351  initial_internal_address_p, initial_internal_address, HLSMgr->Rget_address_bitsize());
353 
354  const auto CGM = HLSMgr->CGetCallGraphManager();
359  CustomUnorderedSet<vertex> all_reachable_vertices;
361  std::set<func_id_t> top_functions;
362  CustomMap<top_id_t, std::vector<func_id_t>> function_allocation_order;
363 
364  const auto root_functions = CGM->GetRootFunctions();
365  top_functions.insert(root_functions.begin(), root_functions.end());
366  const auto subgraph_from = [&](const vertex& top_vertex, bool is_addr = false, bool include_shared = true) {
367  const auto top_function = CGM->get_function(top_vertex);
369  preset.insert(top_vertex);
370  const auto reached_from_top = CGM->GetReachedFunctionsFrom(top_function);
371  const auto is_dominated = [&](const vertex& v) {
372  BOOST_FOREACH(EdgeDescriptor e, boost::in_edges(v, *CGM->CGetCallGraph()))
373  {
374  if(!reached_from_top.count(CGM->get_function(boost::source(e, *CGM->CGetCallGraph()))))
375  {
376  return false;
377  }
378  }
379  return true;
380  };
381  for(const auto& funID : reached_from_top)
382  {
383  if(!top_functions.count(funID))
384  {
385  const auto funV = CGM->GetVertex(funID);
386  if(include_shared || is_dominated(funV))
387  {
388  preset.insert(funV);
389  }
390  }
391  }
392  const auto presub = CGM->CGetCallSubGraph(preset);
394  subset.insert(top_vertex);
395  const auto update_vertices = !is_addr;
396  if(update_vertices)
397  {
398  reachable_vertices[top_function].insert(top_vertex);
399  all_reachable_vertices.insert(top_vertex);
400  }
401  for(const auto& v : preset)
402  {
403  if(presub->IsReachable(top_vertex, v))
404  {
405  subset.insert(v);
406  if(update_vertices)
407  {
408  reachable_vertices[top_function].insert(v);
409  all_reachable_vertices.insert(v);
410  }
411  }
412  }
413  return CGM->CGetCallSubGraph(subset);
414  };
415  const auto push_allocation_order = [&](std::vector<func_id_t>& allocation_order, top_id_t top_function,
416  bool include_shared = true) {
417  const auto top_vertex = CGM->GetVertex(top_function);
418  const auto subgraph = subgraph_from(top_vertex, false, include_shared);
419  cg[top_function] = subgraph;
421  cg_dominators[top_function] =
422  refcount<dominance<graph>>(new dominance<graph>(*subgraph, top_vertex, NULL_VERTEX, parameters));
423  cg_dominators.at(top_function)->calculate_dominance_info(dominance<graph>::CDI_DOMINATORS);
424  cg_dominator_map[top_function] = cg_dominators.at(top_function)->get_dominator_map();
425  std::list<vertex> sorted;
426  subgraph->TopologicalSort(sorted);
427  for(const auto& v : sorted)
428  {
429  allocation_order.push_back(CGM->get_function(v));
430  }
431  };
432  for(const auto& top_function : root_functions)
433  {
434  push_allocation_order(function_allocation_order[top_function], top_function);
435  }
436  for(const auto& addr_func : CGM->GetAddressedFunctions())
437  {
438  const auto top_vertex = CGM->GetVertex(addr_func);
439  const auto subgraph = subgraph_from(top_vertex, true);
440  cg[addr_func] = subgraph;
441  std::list<vertex> sorted;
442  subgraph->TopologicalSort(sorted);
443  for(const auto& v : sorted)
444  {
445  const auto v_id = CGM->get_function(v);
446  if(!std::count_if(function_allocation_order.begin(), function_allocation_order.end(),
447  [&](const std::pair<top_id_t, std::vector<func_id_t>>& p) {
448  return std::count(p.second.begin(), p.second.end(), v_id);
449  }))
450  {
451  function_allocation_order[addr_func].push_back(v_id);
452  }
453  }
454  }
455 
456 #if HAVE_ASSERTS
457  for(const auto& f_order : function_allocation_order)
458  {
459  CustomSet<unsigned int> check_order;
460  for(const auto& f : f_order.second)
461  {
462  const auto fname = HLSMgr->GetFunctionBehavior(f)->CGetBehavioralHelper()->get_function_name();
463  THROW_ASSERT(func_list.count(f), "Function " + fname + " not present in function list");
464  THROW_ASSERT(check_order.insert(f).second, "Duplicate fuction in allocation order: " + STR(f) + " " + fname);
465  }
466  }
467 #endif
468 
471  const auto compute_var_usage = [&](const func_id_t f_id, const top_id_t top_id) {
472  const auto function_behavior = HLSMgr->CGetFunctionBehavior(f_id);
473  const auto BH = function_behavior->CGetBehavioralHelper();
474  INDENT_DBG_MEX(DEBUG_LEVEL_VERBOSE, debug_level, "-->Analyzing function: " + BH->get_function_name());
475  CustomSet<vertex> vert_dominator;
476  const auto current_vertex = get_remapped_vertex(CGM->GetVertex(f_id), CGM, HLSMgr);
477  const auto projection_in_degree = boost::in_degree(current_vertex, *cg.at(top_id));
478 
479  if(projection_in_degree != 1)
480  {
481  if(cg_dominator_map.at(top_id).find(current_vertex) != cg_dominator_map.at(top_id).end())
482  {
483  vert_dominator.insert(get_remapped_vertex(cg_dominator_map.at(top_id).at(current_vertex), CGM, HLSMgr));
484  }
485  }
486  else
487  {
488  vert_dominator.insert(current_vertex);
489  }
490 
491  const auto& function_mem = function_behavior->get_function_mem();
492  for(const auto v : function_mem)
493  {
494  if(function_behavior->is_a_state_variable(v))
495  {
496  var_map[top_id][v].insert(vert_dominator.begin(), vert_dominator.end());
497  }
498  else
499  {
500  var_map[top_id][v].insert(current_vertex);
501  }
502  where_used[top_id][v].insert(f_id);
504  "---Variable : " + BH->PrintVariable(v) + " used in function " +
505  function_behavior->CGetBehavioralHelper()->get_function_name());
506  // INDENT_DBG_MEX(DEBUG_LEVEL_VERY_PEDANTIC, debug_level, "---Dominator Vertex: " +
507  // HLSMgr->CGetFunctionBehavior(CG->get_function(vert_dominator))->CGetBehavioralHelper()->get_function_name()
508  // + " - Variable to be stored: " + BH->PrintVariable(*v));
509  }
510  INDENT_DBG_MEX(DEBUG_LEVEL_VERBOSE, debug_level, "<--Analyzed function: " + BH->get_function_name());
511  };
512  INDENT_DBG_MEX(DEBUG_LEVEL_VERBOSE, debug_level, "Variable use analysis...");
513  for(const auto& top_order : function_allocation_order)
514  {
515  const auto& top_id = top_order.first;
517  "-->Analyzing top function: " +
518  HLSMgr->CGetFunctionBehavior(top_id)->CGetBehavioralHelper()->get_function_name());
519  for(const auto& f_id : top_order.second)
520  {
521  compute_var_usage(f_id, top_id);
522  }
524  "<--Analyzed top function: " +
525  HLSMgr->CGetFunctionBehavior(top_id)->CGetBehavioralHelper()->get_function_name());
526  }
527  INDENT_DBG_MEX(DEBUG_LEVEL_VERBOSE, debug_level, "Variable use analysis completed");
528 
529  bool all_pointers_resolved = true;
533  INDENT_DBG_MEX(DEBUG_LEVEL_VERBOSE, debug_level, "Pointers classification...");
534  for(const auto& fun_id : func_list)
535  {
536  const auto function_behavior = HLSMgr->CGetFunctionBehavior(fun_id);
537  const auto BH = function_behavior->CGetBehavioralHelper();
538  INDENT_DBG_MEX(DEBUG_LEVEL_VERBOSE, debug_level, "-->Analyzing function: " + BH->get_function_name());
539  // if(function_behavior->get_has_globals())
540  // {
541  // INDENT_DBG_MEX(DEBUG_LEVEL_VERBOSE, debug_level, "---Pointers not resolved: it has global variables");
542  // all_pointers_resolved = false;
543  // }
544  if(function_behavior->get_has_undefined_function_receiving_pointers())
545  {
547  "---Pointers not resolved: it has undefined function receiving pointers");
548  all_pointers_resolved = false;
549  }
550 
551  const auto g = function_behavior->CGetOpGraph(FunctionBehavior::CFG);
552  BOOST_FOREACH(const vertex v, boost::vertices(*g))
553  {
554  const auto current_op = g->CGetOpNodeInfo(v)->GetOperation();
556  if(current_op == "__builtin_printf" || current_op == BUILTIN_WAIT_CALL || current_op == MEMSET ||
557  current_op == MEMCMP || current_op == MEMCPY)
558  {
560  "---Pointers not resolved: it uses printf/builtin-wait-call/memset/memcpy/memcmp");
561  all_pointers_resolved = false;
562  }
563  if(GET_TYPE(g, v) & (TYPE_LOAD | TYPE_STORE))
564  {
565  const auto curr_tn = TM->CGetTreeNode(g->CGetOpNodeInfo(v)->GetNodeId());
567  "-->Analyzing statement " + GET_NAME(g, v) + ": " + STR(curr_tn));
568  const auto me = GetPointer<const gimple_assign>(curr_tn);
569  THROW_ASSERT(me, "only gimple_assign's are allowed as memory operations");
571 
572  if(GET_TYPE(g, v) & TYPE_STORE)
573  {
574  if(!tree_helper::IsPointerResolved(me->op0, res_set))
575  {
576  const auto var = tree_helper::GetBaseVariable(me->op0);
578  if(var && function_behavior->is_variable_mem(var->index))
579  {
580  res_set.insert(var->index);
581  }
582  else
583  {
585  "---Pointers not resolved: point-to-set not resolved");
586  all_pointers_resolved = false;
587  }
588  }
589  }
590  else
591  {
592  if(!tree_helper::IsPointerResolved(me->op1, res_set))
593  {
594  const auto var = tree_helper::GetBaseVariable(me->op1);
596  if(var && function_behavior->is_variable_mem(var->index))
597  {
598  res_set.insert(var->index);
599  }
600  else
601  {
603  "---Pointers not resolved: point-to-set not resolved " + me->ToString());
604  all_pointers_resolved = false;
605  }
606  }
607  }
608  for(const auto& var : res_set)
609  {
610  THROW_ASSERT(var, "");
611  THROW_ASSERT(function_behavior->is_variable_mem(var),
612  "unexpected condition: " + STR(TM->CGetTreeNode(var)) + " is not a memory variable");
613  if(HLSMgr->Rmem->has_sds_var(var) && !HLSMgr->Rmem->is_sds_var(var))
614  {
615  continue;
616  }
617  INDENT_DBG_MEX(DEBUG_LEVEL_VERY_PEDANTIC, debug_level, "---Variable is " + STR(var));
618  THROW_ASSERT(g->CGetOpNodeInfo(v), "unexpected condition");
619  const auto node_id = g->CGetOpNodeInfo(v)->GetNodeId();
620  const auto node = TM->CGetTreeNode(node_id);
621  const auto gm = GetPointer<const gimple_assign>(node);
622  THROW_ASSERT(gm, "only gimple_assign's are allowed as memory operations");
623  unsigned long long value_bitsize;
624  unsigned long long alignment = 8ULL;
625  if(GET_TYPE(g, v) & TYPE_STORE)
626  {
627  auto n_last_zerobits = 0u;
628  if(GET_NODE(gm->op0)->get_kind() == mem_ref_K)
629  {
630  const auto mr = GetPointer<mem_ref>(GET_NODE(gm->op0));
631  THROW_ASSERT(GetPointer<integer_cst>(GET_NODE(mr->op1)), "unexpected condition");
632  THROW_ASSERT(tree_helper::GetConstValue(mr->op1) == 0, "unexpected condition");
633  if(GET_NODE(mr->op0)->get_kind() == ssa_name_K)
634  {
635  const auto ssa_addr = GetPointer<const ssa_name>(GET_CONST_NODE(mr->op0));
637  "---STORE SSA pointer " + mr->op0->ToString() +
638  " bit_values=" + ssa_addr->bit_values);
639  if(ssa_addr->bit_values.find_first_not_of('0') == std::string::npos)
640  {
641  n_last_zerobits = 60; // infinite alignment
642  }
643  else
644  {
645  for(auto it = ssa_addr->bit_values.rbegin(); it != ssa_addr->bit_values.rend(); ++it)
646  {
647  if(*it == '0' || *it == 'X')
648  {
649  ++n_last_zerobits;
650  }
651  else
652  {
653  break;
654  }
655  }
656  }
657  }
658  else
659  {
660  THROW_ERROR("unexpected condition");
661  }
662  }
663  else
664  {
665  THROW_ERROR("unexpected condition" + GET_CONST_NODE(gm->op0)->get_kind_text() + " " +
666  gm->ToString());
667  }
668  alignment = (1ull << n_last_zerobits) * 8;
669  const auto var_read = HLSMgr->get_required_values(fun_id, v);
670  const auto size_var = std::get<0>(var_read[0]);
671  const auto size_type = tree_helper::CGetType(TM->CGetTreeReindex(size_var));
672  value_bitsize = tree_helper::Size(size_type);
673  const auto fd = GetPointer<const field_decl>(GET_CONST_NODE(size_type));
674  if(!fd || !fd->is_bitfield())
675  {
676  value_bitsize = std::max(8ull, value_bitsize);
677  }
678  HLSMgr->Rmem->add_source_value(var, size_var);
679  }
680  else
681  {
682  auto n_last_zerobits = 0u;
683  if(GET_CONST_NODE(gm->op1)->get_kind() == mem_ref_K)
684  {
685  const auto mr = GetPointer<const mem_ref>(GET_CONST_NODE(gm->op1));
686  THROW_ASSERT(GetPointer<const integer_cst>(GET_CONST_NODE(mr->op1)), "unexpected condition");
687  THROW_ASSERT(tree_helper::GetConstValue(mr->op1) == 0, "unexpected condition");
688  if(GET_CONST_NODE(mr->op0)->get_kind() == ssa_name_K)
689  {
690  const auto ssa_addr = GetPointer<const ssa_name>(GET_CONST_NODE(mr->op0));
692  "---LOAD SSA pointer " + mr->op0->ToString() +
693  " bit_values=" + ssa_addr->bit_values);
694  if(ssa_addr->bit_values.find_first_not_of('0') == std::string::npos)
695  {
696  n_last_zerobits = 60; // infinite alignment
697  }
698  else
699  {
700  for(auto it = ssa_addr->bit_values.rbegin(); it != ssa_addr->bit_values.rend(); ++it)
701  {
702  if(*it == '0' || *it == 'X')
703  {
704  ++n_last_zerobits;
705  }
706  else
707  {
708  break;
709  }
710  }
711  }
712  }
713  else
714  {
715  THROW_ERROR("unexpected condition");
716  }
717  }
718  else
719  {
720  THROW_ERROR("unexpected condition " + GET_CONST_NODE(gm->op1)->get_kind_text() + " " +
721  gm->ToString());
722  }
723  alignment = (1ull << n_last_zerobits) * 8;
724  const auto size_var = HLSMgr->get_produced_value(fun_id, v);
725  const auto size_type = tree_helper::CGetType(TM->CGetTreeReindex(size_var));
726  value_bitsize = tree_helper::Size(size_type);
727  const auto fd = GetPointer<const field_decl>(GET_CONST_NODE(size_type));
728  if(!fd || !fd->is_bitfield())
729  {
730  value_bitsize = std::max(8ull, value_bitsize);
731  }
732  }
733 
734  if(var_size.find(var) == var_size.end())
735  {
736  const auto type_node = tree_helper::CGetType(TM->CGetTreeReindex(var));
737  const auto is_a_struct_union =
741  const auto elmt_bitsize = tree_helper::AccessedMaximumBitsize(type_node, 1);
742  const auto min_elmt_bitsize = tree_helper::AccessedMinimunBitsize(type_node, elmt_bitsize);
743  const auto elts_size = tree_helper::IsArrayEquivType(type_node) ?
745  elmt_bitsize;
746  if(unaligned_access_p)
747  {
748  if(assume_aligned_access_p)
749  {
750  THROW_ERROR("Option --aligned-access have been specified on a function with unaligned "
751  "accesses:\n\tVariable " +
752  BH->PrintVariable(var) + " could be accessed in unaligned way");
753  }
754  HLSMgr->Rmem->set_sds_var(var, false);
756  "---Variable " + STR(var) + " not sds because unaligned_access option specified");
757  }
758  else if(min_elmt_bitsize != elmt_bitsize || is_a_struct_union || elts_size != elmt_bitsize)
759  {
760  if(assume_aligned_access_p)
761  {
762  THROW_ERROR("Option --aligned-access have been specified on a function with unaligned "
763  "accesses:\n\tVariable " +
764  BH->PrintVariable(var) + " could be accessed in unaligned way");
765  }
766  HLSMgr->Rmem->set_sds_var(var, false);
768  "---Variable " + STR(var) + " not sds " + STR(elmt_bitsize) + " vs " +
769  STR(min_elmt_bitsize) + " vs " + STR(elts_size));
770  }
771  else if(value_bitsize != elmt_bitsize)
772  {
773  if(assume_aligned_access_p)
774  {
775  THROW_ERROR("Option --aligned-access have been specified on a function with unaligned "
776  "accesses:\n\tVariable " +
777  BH->PrintVariable(var) +
778  " could be accessed in unaligned way: " + curr_tn->ToString());
779  }
780  HLSMgr->Rmem->set_sds_var(var, false);
782  "---Variable " + STR(var) + " not sds " + STR(value_bitsize) + " vs " +
783  STR(elmt_bitsize));
784  }
785  else if(alignment < value_bitsize)
786  {
787  if(assume_aligned_access_p)
788  {
789  THROW_WARNING("Option --aligned-access have been specified on a function with not "
790  "compiler-proved unaligned accesses:\n\tVariable " +
791  BH->PrintVariable(var) + " could be accessed in unaligned way");
792  THROW_WARNING("\tStatement is " + gm->ToString());
793  }
794  else
795  {
796  HLSMgr->Rmem->set_sds_var(var, false);
799  "---Variable " + STR(var) + " not sds because alignment=" + STR(alignment) +
800  " is less than the value loaded or written or than the size of the array elements=" +
801  STR(value_bitsize));
802  }
803  }
804  else
805  {
807  "---Variable " + STR(var) + " sds " + STR(value_bitsize) + " vs " +
808  STR(elmt_bitsize));
809  HLSMgr->Rmem->set_sds_var(var, true);
810  }
811  var_size[var] = value_bitsize;
812  }
813  else
814  {
815  if(var_size.at(var) != value_bitsize)
816  {
817  if(assume_aligned_access_p)
818  {
819  THROW_ERROR("Option --aligned-access have been specified on a function with unaligned "
820  "accesses:\n\tVariable " +
821  BH->PrintVariable(var) + " could be accessed in unaligned way");
822  }
823  HLSMgr->Rmem->set_sds_var(var, false);
825  "---Variable " + STR(var) + " not sds " + STR(value_bitsize) + " vs " +
826  STR(var_size.at(var)));
827  }
828  else if(alignment < value_bitsize)
829  {
830  if(assume_aligned_access_p)
831  {
832  THROW_WARNING("Option --aligned-access have been specified on a function with not "
833  "compiler-proved unaligned accesses:\n\tVariable " +
834  BH->PrintVariable(var) + " could be accessed in unaligned way");
835  THROW_WARNING("\tStatement is " + gm->ToString());
836  }
837  else
838  {
839  HLSMgr->Rmem->set_sds_var(var, false);
841  "---Variable " + STR(var) + " not sds " + STR(value_bitsize) + " vs " +
842  STR(var_size.at(var)) + " alignment=" + STR(alignment) +
843  " value_bitsize2=" + STR(var_size.at(var)));
844  }
845  }
846  else
847  {
849  "---Variable " + STR(var) + " sds " + STR(value_bitsize));
850  }
851  }
853  var_referring_vertex_map[var][fun_id].insert(v);
854  if(GET_TYPE(g, v) & TYPE_LOAD)
855  {
856  var_load_vertex_map[var][fun_id].insert(v);
857  }
858  }
859  INDENT_DBG_MEX(DEBUG_LEVEL_VERY_PEDANTIC, debug_level, "<--Analyzed statement " + GET_NAME(g, v));
860  }
861  }
862  INDENT_DBG_MEX(DEBUG_LEVEL_VERBOSE, debug_level, "<--Analyzed function: " + BH->get_function_name());
863  }
864  HLSMgr->Rmem->set_all_pointers_resolved(all_pointers_resolved);
865 
866  if(all_pointers_resolved)
867  {
868  for(const auto fun_id : func_list)
869  {
870  const auto function_behavior = HLSMgr->CGetFunctionBehavior(fun_id);
871  const auto BH = function_behavior->CGetBehavioralHelper();
872  INDENT_DBG_MEX(DEBUG_LEVEL_VERBOSE, debug_level, "-->Analyzing function: " + BH->get_function_name());
873  const auto g = function_behavior->CGetOpGraph(FunctionBehavior::CFG);
874  BOOST_FOREACH(const vertex v, boost::vertices(*g))
875  {
876  if(GET_TYPE(g, v) & (TYPE_LOAD | TYPE_STORE))
877  {
878  INDENT_DBG_MEX(DEBUG_LEVEL_VERY_PEDANTIC, debug_level, "-->Analyzing statement " + GET_NAME(g, v));
879  const auto curr_tn = TM->CGetTreeNode(g->CGetOpNodeInfo(v)->GetNodeId());
880  const auto me = GetPointer<const gimple_assign>(curr_tn);
881  THROW_ASSERT(me, "only gimple_assign's are allowed as memory operations");
882  tree_nodeRef expr;
883  if(GET_TYPE(g, v) & TYPE_STORE)
884  {
885  expr = me->op0;
886  }
887  else
888  {
889  expr = me->op1;
890  }
892  if(!tree_helper::IsPointerResolved(expr, used_set))
893  {
894  const auto var = tree_helper::GetBaseVariable(expr);
895  THROW_ASSERT(var, "unexpected condition");
896  used_set.insert(var->index);
897  }
898  THROW_ASSERT(!used_set.empty(), "");
899  if(used_set.size() > 1)
900  {
901  for(const auto used_var : used_set)
902  {
904  "---Variable need the bus for loads and stores " + BH->PrintVariable(used_var));
905  HLSMgr->Rmem->add_need_bus(used_var);
906  }
907  }
908  INDENT_DBG_MEX(DEBUG_LEVEL_VERY_PEDANTIC, debug_level, "<--Analyzed statement " + GET_NAME(g, v));
909  }
910  }
911  INDENT_DBG_MEX(DEBUG_LEVEL_VERBOSE, debug_level, "<--Analyzed function: " + BH->get_function_name());
912  }
913  }
914  INDENT_DBG_MEX(DEBUG_LEVEL_VERBOSE, debug_level, "Pointers classification completed");
915 
919  for(const auto root_function : root_functions)
920  {
921  num_instances[root_function][CGM->GetVertex(root_function)] = 1;
922  }
923  for(const auto addr_function : CGM->GetAddressedFunctions())
924  {
925  num_instances[addr_function][CGM->GetVertex(addr_function)] = 1;
926  }
927  const auto compute_instances = [&](const unsigned int f_id, const unsigned int top_id) {
928  memory_allocation_map[top_id][f_id];
929  const auto top_cg = cg.at(top_id);
930  const auto f_v = CGM->GetVertex(f_id);
931  if(boost::out_degree(f_v, *top_cg))
932  {
933  THROW_ASSERT(HLSMgr->get_HLS(f_id),
934  "Missing HLS initialization for " +
935  HLSMgr->CGetFunctionBehavior(f_id)->CGetBehavioralHelper()->get_function_name());
936  const auto HLS_C = HLSMgr->get_HLS(f_id)->HLS_C;
937  THROW_ASSERT(num_instances.at(top_id).count(f_v),
938  "missing number of instances of function " +
939  HLSMgr->CGetFunctionBehavior(f_id)->CGetBehavioralHelper()->get_function_name());
940  const auto cur_instances = num_instances.at(top_id).at(f_v);
941  BOOST_FOREACH(EdgeDescriptor e, boost::out_edges(f_v, *top_cg))
942  {
943  const auto tgt = boost::target(e, *top_cg);
944  const auto tgt_fu_name = functions::GetFUName(CGM->get_function(tgt), HLSMgr);
945  if(HLSMgr->Rfuns->is_a_proxied_function(tgt_fu_name) ||
946  (parameters->getOption<bool>(OPT_memory_mapped_top) && HLSMgr->hasToBeInterfaced(f_id)))
947  {
948  num_instances.at(top_id)[tgt] = 1;
949  }
950  else if(HLS_C->get_number_fu(tgt_fu_name, WORK_LIBRARY) == 1)
951  {
952  num_instances.at(top_id)[tgt] = 1;
953  }
954  else
955  {
956  const auto n_call_points = static_cast<unsigned int>(
957  Cget_edge_info<FunctionEdgeInfo, const CallGraph>(e, *top_cg)->direct_call_points.size());
958  if(!num_instances.at(top_id).count(tgt))
959  {
960  num_instances.at(top_id)[tgt] = cur_instances * n_call_points;
961  }
962  else
963  {
964  num_instances.at(top_id)[tgt] += cur_instances * n_call_points;
965  }
966  }
967  }
968  }
969  };
970  for(const auto& f_order : function_allocation_order)
971  {
972  const auto top_id = f_order.first;
973  for(const auto f_id : f_order.second)
974  {
975  compute_instances(f_id, top_id);
976  }
977  }
978 
980  const auto no_private_mem =
981  parameters->IsParameter("no-private-mem") && parameters->GetParameter<bool>("no-private-mem");
982  const auto no_local_mem = parameters->IsParameter("no-local-mem") && parameters->GetParameter<bool>("no-local-mem");
983  INDENT_DBG_MEX(DEBUG_LEVEL_VERBOSE, debug_level, "Variable dominator computation...");
984  for(const auto& top_vars : var_map)
985  {
986  const auto& top_id = top_vars.first;
988  "-->Analyzing top function: " +
989  HLSMgr->CGetFunctionBehavior(top_id)->CGetBehavioralHelper()->get_function_name());
990  for(const auto& var_uses : top_vars.second)
991  {
992  const auto& var_id = var_uses.first;
993  THROW_ASSERT(var_id, "null var index unexpected");
994  const auto& uses = var_uses.second;
996  "-->Finding common dominator for " + STR(TM->CGetTreeReindex(var_id)) + " (id: " + STR(var_id) +
997  ", uses: " + STR(uses.size()) + ")");
998  CustomSet<unsigned int> filtered_top_functions;
999  for(const auto& reachable_vertex : reachable_vertices)
1000  {
1001  for(const auto use_vertex : uses)
1002  {
1003  if(reachable_vertex.second.count(use_vertex))
1004  {
1005  filtered_top_functions.insert(reachable_vertex.first);
1006  }
1007  }
1008  }
1009  auto funID = top_id;
1010  bool multiple_top_call_graph = false;
1011  if(no_local_mem)
1012  {
1013  if(filtered_top_functions.size() != 1)
1014  {
1015  multiple_top_call_graph = true;
1016  }
1017  }
1018  else
1019  {
1020  const auto is_written = HLSMgr->get_written_objects().count(var_id) || no_private_mem;
1021  if(uses.size() == 1)
1022  {
1023  auto cur = *uses.begin();
1025  "---Current function(0): " + HLSMgr->CGetFunctionBehavior(CGM->get_function(cur))
1026  ->CGetBehavioralHelper()
1027  ->get_function_name());
1028  const auto fun_behavior = HLSMgr->CGetFunctionBehavior(CGM->get_function(cur));
1030  if(is_written && fun_behavior->is_a_state_variable(var_id))
1031  {
1032  while(num_instances.at(top_id).at(cur) != 1)
1033  {
1034  cur = get_remapped_vertex(cg_dominator_map.at(top_id).at(cur), CGM, HLSMgr);
1036  "---Current function(1): " + HLSMgr->CGetFunctionBehavior(CGM->get_function(cur))
1037  ->CGetBehavioralHelper()
1038  ->get_function_name());
1039  }
1040  }
1041  funID = CGM->get_function(cur);
1042  }
1043  else
1044  {
1045  if(filtered_top_functions.size() != 1)
1046  {
1047  multiple_top_call_graph = true;
1048  }
1049  else
1050  {
1051  const auto top_vertex = CGM->GetVertex(top_id);
1052  const auto vert_it_end = uses.end();
1053  auto vert_it = uses.begin();
1054  std::list<vertex> dominator_list1;
1055  auto cur = *vert_it;
1057  "---Current function(2a): " + HLSMgr->CGetFunctionBehavior(CGM->get_function(cur))
1058  ->CGetBehavioralHelper()
1059  ->get_function_name());
1060  dominator_list1.push_front(cur);
1061  do
1062  {
1063  cur = get_remapped_vertex(cg_dominator_map.at(top_id).at(cur), CGM, HLSMgr);
1065  "---Current function(2b): " + HLSMgr->CGetFunctionBehavior(CGM->get_function(cur))
1066  ->CGetBehavioralHelper()
1067  ->get_function_name());
1068  dominator_list1.push_front(cur);
1069  } while(cur != top_vertex);
1070  ++vert_it;
1071  auto last = dominator_list1.end();
1072  for(; vert_it != vert_it_end; ++vert_it)
1073  {
1074  std::list<vertex> dominator_list2;
1075  cur = *vert_it;
1077  "---Current function(2c): " + HLSMgr->CGetFunctionBehavior(CGM->get_function(cur))
1078  ->CGetBehavioralHelper()
1079  ->get_function_name());
1080  dominator_list2.push_front(cur);
1081  do
1082  {
1083  cur = get_remapped_vertex(cg_dominator_map.at(top_id).at(cur), CGM, HLSMgr);
1085  "---Current function(2d): " +
1086  HLSMgr->CGetFunctionBehavior(CGM->get_function(cur))
1087  ->CGetBehavioralHelper()
1088  ->get_function_name());
1089  dominator_list2.push_front(cur);
1090  } while(cur != top_vertex);
1092  auto dl1_it = dominator_list1.begin(), dl2_it = dominator_list2.begin(),
1093  dl2_it_end = dominator_list2.end(), cur_last = dominator_list1.begin();
1094  while(dl1_it != last && dl2_it != dl2_it_end && *dl1_it == *dl2_it &&
1095  (num_instances.at(top_id).at(*dl1_it) == 1 || !is_written))
1096  {
1097  cur = *dl1_it;
1099  "---Current function(2e): " +
1100  HLSMgr->CGetFunctionBehavior(CGM->get_function(cur))
1101  ->CGetBehavioralHelper()
1102  ->get_function_name() +
1103  " num instances: " + STR(num_instances.at(top_id).at(cur)));
1104  ++dl1_it;
1105  cur_last = dl1_it;
1106  ++dl2_it;
1107  }
1108  last = cur_last;
1109  funID = CGM->get_function(cur);
1110  if(cur == top_vertex)
1111  {
1112  break;
1113  }
1114  }
1115  }
1116  }
1117  }
1118  THROW_ASSERT(funID, "null function id index unexpected");
1119  const auto is_internal = is_internal_obj(var_id, funID, multiple_top_call_graph);
1120  memory_allocation_map.at(top_id)[funID].push_back(std::make_pair(var_id, is_internal));
1122  "<--Found common dominator for " +
1123  HLSMgr->CGetFunctionBehavior(funID)->CGetBehavioralHelper()->get_function_name() + " -> " +
1124  STR(TM->CGetTreeReindex(var_id)) + " (scope: " + (is_internal ? "internal" : "external") +
1125  ")");
1126  }
1128  "<--Analyzed top function: " +
1129  HLSMgr->CGetFunctionBehavior(top_id)->CGetBehavioralHelper()->get_function_name());
1130  }
1131  INDENT_DBG_MEX(DEBUG_LEVEL_VERBOSE, debug_level, "Variable dominator computation completed");
1132 
1133  const auto classify_variables = [&](const func_id_t f_id, const top_id_t top_id) {
1134  const auto it = memory_allocation_map.at(top_id).find(f_id);
1135  if(it != memory_allocation_map.at(top_id).end())
1136  {
1137  for(const auto& mem_map : it->second)
1138  {
1139  const auto var_id = mem_map.first;
1140  THROW_ASSERT(var_id, "null var index unexpected");
1141  const auto is_internal = mem_map.second;
1142  auto is_dynamic_address_used = false;
1143 
1144  if(is_internal)
1145  {
1146  THROW_ASSERT(where_used.at(top_id)[var_id].size() > 0, "variable not used anywhere");
1148  const auto wiu_it_end = where_used.at(top_id).at(var_id).end();
1149  for(auto wiu_it = where_used.at(top_id).at(var_id).begin();
1150  wiu_it != wiu_it_end && !is_dynamic_address_used; ++wiu_it)
1151  {
1152  const auto cur_function_behavior = HLSMgr->CGetFunctionBehavior(*wiu_it);
1153  if(cur_function_behavior->get_dynamic_address().find(var_id) !=
1154  cur_function_behavior->get_dynamic_address().end() ||
1155  (GetPointer<const var_decl>(TM->CGetTreeNode(var_id)) &&
1156  GetPointerS<const var_decl>(TM->CGetTreeNode(var_id))->addr_taken))
1157  {
1159  "---Found dynamic use of variable: " +
1160  cur_function_behavior->CGetBehavioralHelper()->PrintVariable(var_id) + " - " +
1161  STR(var_id) + " - " +
1162  HLSMgr->CGetFunctionBehavior(*(where_used.at(top_id).at(var_id).begin()))
1163  ->CGetBehavioralHelper()
1164  ->PrintVariable(var_id) +
1165  " in function " +
1166  cur_function_behavior->CGetBehavioralHelper()->get_function_name());
1167  is_dynamic_address_used = true;
1168  }
1169  }
1170 
1171  if(is_dynamic_address_used && !all_pointers_resolved && !assume_aligned_access_p)
1172  {
1173  HLSMgr->Rmem->set_sds_var(var_id, false);
1174  INDENT_DBG_MEX(DEBUG_LEVEL_VERY_PEDANTIC, debug_level, "---Variable " + STR(var_id) + " not sds-A");
1175  }
1176 
1177  if(!HLSMgr->Rmem->has_sds_var(var_id) && assume_aligned_access_p)
1178  {
1179  HLSMgr->Rmem->set_sds_var(var_id, true);
1180  }
1181  else if(!HLSMgr->Rmem->has_sds_var(var_id))
1182  {
1183  HLSMgr->Rmem->set_sds_var(var_id, false);
1184  INDENT_DBG_MEX(DEBUG_LEVEL_VERY_PEDANTIC, debug_level, "---Variable " + STR(var_id) + " not sds-B");
1185  }
1186 
1187  if(!no_private_mem && !no_local_mem)
1188  {
1189  if(!is_dynamic_address_used &&
1190  HLSMgr->get_written_objects().find(var_id) ==
1191  HLSMgr->get_written_objects().end()
1192  )
1193  {
1194  if(!GetPointer<const gimple_call>(TM->CGetTreeNode(var_id)))
1195  {
1196  HLSMgr->Rmem->add_private_memory(var_id);
1197  }
1198  }
1199  else if(CGM->ExistsAddressedFunction())
1200  {
1201  if(var_map.at(top_id).at(var_id).size() == 1 && where_used.at(top_id).at(var_id).size() == 1 &&
1202  !is_dynamic_address_used &&
1203  (*(where_used.at(top_id).at(var_id).begin()) == f_id) &&
1204  !GetPointer<const gimple_call>(TM->CGetTreeNode(var_id)))
1205  {
1206  HLSMgr->Rmem->add_private_memory(var_id);
1207  }
1208  }
1209  else
1210  {
1211  if(!is_dynamic_address_used &&
1212  !GetPointer<const gimple_call>(TM->CGetTreeNode(var_id)))
1213  {
1214  HLSMgr->Rmem->add_private_memory(var_id);
1215  }
1216  }
1217  }
1218  }
1219  else
1220  {
1221  is_dynamic_address_used = true;
1222  if(assume_aligned_access_p)
1223  {
1224  HLSMgr->Rmem->set_sds_var(var_id, true);
1225  }
1226  else
1227  {
1228  HLSMgr->Rmem->set_sds_var(var_id, false);
1229  INDENT_DBG_MEX(DEBUG_LEVEL_VERY_PEDANTIC, debug_level, "---Variable " + STR(var_id) + " not sds-C");
1230  }
1231  }
1232  const auto vd = GetPointer<const var_decl>(TM->CGetTreeNode(var_id));
1233  if((HLSMgr->get_written_objects().find(var_id) == HLSMgr->get_written_objects().end()) &&
1234  (!is_dynamic_address_used || (vd && vd->readonly_flag)))
1235  {
1236  HLSMgr->Rmem->add_read_only_variable(var_id);
1237  }
1238  }
1239  }
1240  };
1241 
1243  INDENT_DBG_MEX(DEBUG_LEVEL_VERBOSE, debug_level, "Variable classification...");
1244  for(const auto& top_order : function_allocation_order)
1245  {
1246  const auto& top_id = top_order.first;
1247  if(memory_allocation_map.find(top_id) != memory_allocation_map.end())
1248  {
1249  for(const auto& f_id : top_order.second)
1250  {
1251  classify_variables(f_id, top_id);
1252  }
1253  }
1254  }
1255  INDENT_DBG_MEX(DEBUG_LEVEL_VERBOSE, debug_level, "Variable classification completed");
1256 
1258  if(parameters->isOption(OPT_sparse_memory) && parameters->getOption<bool>(OPT_sparse_memory))
1259  {
1261  auto max_byte_size = HLSMgr->Rmem->get_internal_base_address_alignment();
1262  for(const auto& mem_map : memory_allocation_map)
1263  {
1264  for(const auto& f_pair : mem_map.second)
1265  {
1266  for(const auto& pair : f_pair.second)
1267  {
1268  const auto& var_id = pair.first;
1269  THROW_ASSERT(var_id, "null var index unexpected");
1270  if(pair.second && (!HLSMgr->Rmem->is_private_memory(var_id) || null_pointer_check))
1271  {
1272  const auto curr_size = compute_n_bytes(tree_helper::Size(TM->CGetTreeReindex(var_id)));
1273  max_byte_size = std::max(static_cast<unsigned long long>(curr_size), max_byte_size);
1274  }
1275  }
1276  }
1277  }
1279  max_byte_size = ceil_pow2(max_byte_size);
1280  HLSMgr->Rmem->set_internal_base_address_alignment(max_byte_size);
1282  "Sparse memory alignemnt set to " + STR(max_byte_size) + " bytes");
1283  }
1284 
1285  const auto memory_mapped_top_if = parameters->getOption<bool>(OPT_memory_mapped_top);
1286  const auto allocate_function_mem = [&](const func_id_t f_id, const top_id_t top_id, const memoryRef& Rmem) {
1287 #ifndef NDEBUG
1288  const auto dbg_lvl = Rmem == HLSMgr->Rmem ? debug_level : DEBUG_LEVEL_NONE;
1289 #endif
1290  const auto out_lvl = Rmem == HLSMgr->Rmem ? output_level : OUTPUT_LEVEL_NONE;
1291  THROW_ASSERT(memory_allocation_map.count(top_id), "Invalid top function id.");
1292  const auto func_mem_map = memory_allocation_map.at(top_id).find(f_id);
1293  if(func_mem_map != memory_allocation_map.at(top_id).end())
1294  {
1295  for(const auto& mem_map : func_mem_map->second)
1296  {
1297  const auto var_id = mem_map.first;
1298  THROW_ASSERT(var_id, "null var index unexpected");
1299  const auto var_node = TM->CGetTreeReindex(var_id);
1300  const auto is_internal = mem_map.second;
1301  auto is_dynamic_address_used = false;
1302 
1303  if(is_internal)
1304  {
1305  THROW_ASSERT(where_used.at(top_id)[var_id].size() > 0, "variable not used anywhere");
1306  const auto function_behavior = HLSMgr->CGetFunctionBehavior(*(where_used.at(top_id).at(var_id).begin()));
1307  const auto BH = function_behavior->CGetBehavioralHelper();
1308  const auto var_id_string = BH->PrintVariable(var_id);
1310  INDENT_DBG_MEX(DEBUG_LEVEL_VERY_PEDANTIC, dbg_lvl, "---Check dynamic use for var " + var_id_string);
1311  const auto wiu_it_end = where_used.at(top_id).at(var_id).end();
1312  for(auto wiu_it = where_used.at(top_id).at(var_id).begin();
1313  wiu_it != wiu_it_end && !is_dynamic_address_used; ++wiu_it)
1314  {
1315  const auto cur_function_behavior = HLSMgr->CGetFunctionBehavior(*wiu_it);
1317  "---Analyzing function " +
1318  cur_function_behavior->CGetBehavioralHelper()->get_function_name());
1319  if(cur_function_behavior->get_dynamic_address().count(var_id) ||
1320  (GetPointer<const var_decl>(GET_CONST_NODE(var_node)) &&
1321  GetPointerS<const var_decl>(GET_CONST_NODE(var_node))->addr_taken))
1322  {
1324  "---Found dynamic use of variable: " +
1325  cur_function_behavior->CGetBehavioralHelper()->PrintVariable(var_id) + " - " +
1326  STR(var_id) + " - " + var_id_string + " in function " +
1327  cur_function_behavior->CGetBehavioralHelper()->get_function_name());
1328  is_dynamic_address_used = true;
1329  }
1330  }
1331 
1332  if(!is_dynamic_address_used &&
1333  !HLSMgr->get_written_objects().count(var_id)
1334  && (!no_private_mem && !no_local_mem))
1335  {
1336  for(auto wiu_it = where_used.at(top_id).at(var_id).begin(); wiu_it != wiu_it_end; ++wiu_it)
1337  {
1338  const auto cur_function_behavior = HLSMgr->CGetFunctionBehavior(*wiu_it);
1339  const auto cur_BH = cur_function_behavior->CGetBehavioralHelper();
1341  "---Internal variable: " + cur_BH->PrintVariable(var_id) + " - " + STR(var_id) +
1342  " - " + var_id_string + " in function " + cur_BH->get_function_name());
1343  Rmem->add_internal_variable(*wiu_it, var_id, cur_BH->PrintVariable(var_id));
1344  }
1345  INDENT_OUT_MEX(OUTPUT_LEVEL_VERBOSE, out_lvl, "-->");
1346  }
1347  else if(CGM->ExistsAddressedFunction())
1348  {
1349  const auto cur_function_behavior = HLSMgr->CGetFunctionBehavior(f_id);
1350  const auto cur_BH = cur_function_behavior->CGetBehavioralHelper();
1352  "-->Internal variable: " + cur_BH->PrintVariable(var_id) + " - " + STR(var_id) +
1353  " - " + var_id_string + " in function " + cur_BH->get_function_name());
1354  Rmem->add_internal_variable(f_id, var_id, cur_BH->PrintVariable(var_id));
1355  }
1356  else
1357  {
1358  const auto cur_function_behavior = HLSMgr->CGetFunctionBehavior(f_id);
1359  const auto cur_BH = cur_function_behavior->CGetBehavioralHelper();
1361  "-->Internal variable: " + cur_BH->PrintVariable(var_id) + " - " + STR(var_id) +
1362  " - " + var_id_string + " in function " + cur_BH->get_function_name());
1363  Rmem->add_internal_variable(f_id, var_id, cur_BH->PrintVariable(var_id));
1365  if(!no_private_mem && !no_local_mem)
1366  {
1367  for(const auto& wu_id : where_used.at(top_id)[var_id])
1368  {
1369  if(wu_id != f_id)
1370  {
1371  Rmem->add_internal_variable_proxy(wu_id, var_id);
1372  }
1373  }
1374  }
1375  }
1376  }
1377  else
1378  {
1379  const auto function_behavior = HLSMgr->CGetFunctionBehavior(f_id);
1380  const auto BH = function_behavior->CGetBehavioralHelper();
1381  const auto var_id_string = BH->PrintVariable(var_id);
1383  "-->Variable external to the top module: " + BH->PrintVariable(var_id) + " - " +
1384  STR(var_id) + " - " + var_id_string);
1385  Rmem->add_external_variable(var_id, BH->PrintVariable(var_id));
1386  is_dynamic_address_used = true;
1387  }
1388  const auto is_packed =
1389  GetPointer<const decl_node>(GET_CONST_NODE(var_node)) && tree_helper::IsPackedType(var_node);
1390  Rmem->set_packed_vars(is_packed);
1391 
1392  INDENT_OUT_MEX(OUTPUT_LEVEL_VERBOSE, out_lvl, "---Id: " + STR(var_id));
1394  "---Base Address: " + STR(Rmem->get_base_address(var_id, f_id)));
1396  "---Size: " + STR(compute_n_bytes(tree_helper::Size(var_node))));
1397  if(HLSMgr->Rmem->is_private_memory(var_id))
1398  {
1399  INDENT_OUT_MEX(OUTPUT_LEVEL_VERBOSE, out_lvl, "---Is a private memory");
1400  }
1401  if(HLSMgr->Rmem->is_a_proxied_variable(var_id))
1402  {
1403  INDENT_OUT_MEX(OUTPUT_LEVEL_VERBOSE, out_lvl, "---Has proxied accesses");
1404  }
1405  if(HLSMgr->Rmem->is_read_only_variable(var_id))
1406  {
1407  INDENT_OUT_MEX(OUTPUT_LEVEL_VERBOSE, out_lvl, "---Is a Read Only Memory");
1408  }
1409  if(HLSMgr->Rmem->is_parm_decl_copied(var_id))
1410  {
1411  INDENT_OUT_MEX(OUTPUT_LEVEL_VERBOSE, out_lvl, "---Is a parm decl copied");
1412  }
1413  if(HLSMgr->Rmem->is_actual_parm_loaded(var_id))
1414  {
1415  INDENT_OUT_MEX(OUTPUT_LEVEL_VERBOSE, out_lvl, "---Is an actual parm decl loaded");
1416  }
1417  if(HLSMgr->Rmem->is_parm_decl_stored(var_id))
1418  {
1419  INDENT_OUT_MEX(OUTPUT_LEVEL_VERBOSE, out_lvl, "---Is a parm decl stored");
1420  }
1421  if(is_dynamic_address_used)
1422  {
1423  INDENT_OUT_MEX(OUTPUT_LEVEL_VERBOSE, out_lvl, "---Used &(object)");
1424  }
1425  if(HLSMgr->Rmem->is_sds_var(var_id))
1426  {
1428  "---The variable is always accessed with the same data size");
1429  const auto vd = GetPointer<const var_decl>(GET_CONST_NODE(var_node));
1430  if(vd && vd->bit_values.size() != 0)
1431  {
1433  "---The variable has been trimmed to bitsize: " + STR(vd->bit_values.size()) +
1434  " with bit-value pattern: " + vd->bit_values);
1435  }
1436  }
1437  if(var_referring_vertex_map.count(var_id))
1438  {
1440  "---Number of functions in which is used: " +
1441  STR(var_referring_vertex_map.at(var_id).size()));
1442  size_t max_references = 0;
1443  for(const auto& fun_vertex_set : var_referring_vertex_map.at(var_id))
1444  {
1445  max_references = max_references > static_cast<size_t>(fun_vertex_set.second.size()) ?
1446  max_references :
1447  static_cast<size_t>(fun_vertex_set.second.size());
1448  }
1449  Rmem->set_maximum_references(var_id, max_references);
1451  "---Maximum number of references per function: " + STR(max_references));
1452  }
1453  if(var_load_vertex_map.count(var_id))
1454  {
1455  size_t max_loads = 0;
1456  for(const auto& fun_vertex_set : var_load_vertex_map.at(var_id))
1457  {
1458  max_loads = max_loads > static_cast<size_t>(fun_vertex_set.second.size()) ?
1459  max_loads :
1460  static_cast<size_t>(fun_vertex_set.second.size());
1461  }
1462  Rmem->set_maximum_loads(var_id, max_loads);
1464  "---Maximum number of loads per function: " + STR(max_loads));
1465  }
1466  if(is_packed)
1467  {
1468  INDENT_OUT_MEX(OUTPUT_LEVEL_VERBOSE, out_lvl, "---Variable is packed");
1469  }
1470  INDENT_OUT_MEX(OUTPUT_LEVEL_VERBOSE, out_lvl, "<--");
1471  }
1472  }
1473  if(f_id && (top_functions.count(f_id) ? memory_mapped_top_if : HLSMgr->hasToBeInterfaced(f_id)))
1474  {
1475  allocate_parameters(f_id, Rmem);
1476  }
1477  };
1478 
1479  // Allocate memory for root functions
1480  INDENT_DBG_MEX(DEBUG_LEVEL_VERBOSE, debug_level, "Memory allocation phase...");
1482  "Internal memory address base: " + STR(HLSMgr->Rmem->get_next_internal_base_address()));
1483  INDENT_DBG_MEX(DEBUG_LEVEL_VERBOSE, debug_level, "Top functions");
1484  for(const auto& top_order : function_allocation_order)
1485  {
1486  const auto& top_id = top_order.first;
1487  if(memory_allocation_map.find(top_id) != memory_allocation_map.end())
1488  {
1489  for(const auto& f_id : top_order.second)
1490  {
1491  allocate_function_mem(f_id, top_id, HLSMgr->Rmem);
1492  }
1493  }
1494  }
1495  INDENT_DBG_MEX(DEBUG_LEVEL_VERBOSE, debug_level, "Memory allocation phase completed");
1496 
1498  {
1499  STOP_TIME(step_time);
1500  }
1504  {
1506  "---Time to perform memory allocation: " + print_cpu_time(step_time) + " seconds");
1507  }
1509  const auto changed = HLSMgr->Rmem->notEQ(prevRmem);
1510  if(changed)
1511  {
1512  HLSMgr->UpdateMemVersion();
1514  const auto TechM = HLS_D->get_technology_manager();
1515  TechM->erase_library(PROXY_LIBRARY);
1516  TechM->erase_library(WORK_LIBRARY);
1517  }
1519 }
#define GET_NODE(t)
Macro used to hide implementation details when accessing a tree_node from another tree_node...
Definition: tree_node.hpp:343
#define DEBUG_LEVEL_VERY_PEDANTIC
extremely verbose debugging print is performed.
static bool IsUnionType(const tree_nodeConstRef &type)
Return if treenode is an union.
const HLS_managerRef HLSMgr
information about all the HLS synthesis
Definition: hls_step.hpp:205
static bool IsComplexType(const tree_nodeConstRef &type)
Return if treenode is a complex.
Data structure representing the entire HLS information.
#define INDENT_DBG_MEX(dbgLevel, curDbgLevel, mex)
We are producing a debug version of the program, so the message is printed;.
#define GET_TYPE(data, vertex_index)
Helper macro returning the type associated with a node.
#define MEMCPY
constant string identifying the operation performed when two objects are memcopied.
Definition: op_graph.hpp:310
static unsigned long long AccessedMaximumBitsize(const tree_nodeConstRef &type_node, unsigned long long bitsize)
return the maximum bitsize associated with the elements accessible through type_node ...
Dominator o post-dominator data structure.
Definition: Dominance.hpp:627
DesignFlowStep_Status InternalExec() override
Execute the step.
#define OUTPUT_LEVEL_NONE
no output print is performed.
Step successfully executed.
string target
Definition: lenet_tvm.py:16
#define GET_CLASS(obj)
Macro returning the actual type of an object.
const int output_level
The output level.
static bool IsArrayEquivType(const tree_nodeConstRef &type)
Return true if treenode is an array or it is equivalent to an array (record recursively having a sing...
mathematical utility function not provided by standard libraries
#define INDENT_OUT_MEX(outLevel, curOutLevel, mex)
all objects that need to be stored in memory are allocated on an external pipelined memory ...
void finalize_memory_allocation()
Performs a final analysis of the memory allocation to finalize the data-structure.
virtual bool is_internal_obj(unsigned int var_id, unsigned int fun_id, bool multiple_top_call_graph)
function checking if the current variable has to allocated inside the accelerator or outside ...
#define GET_NAME(data, vertex_index)
Helper macro returning the name associated with a node.
CustomOrderedMap< T, U > CustomMap
Definition: custom_map.hpp:167
#define BUILTIN_WAIT_CALL
constant defining the builtin wait call intrinsic function
Definition: op_graph.hpp:358
static unsigned long long GetArrayElementSize(const tree_nodeConstRef &node)
Return the size (in bits) of the base element of the array.
#define CE_XVM(variable, node)
Check existence XML Value Macro. Check if an XML attribute is present in the XML tree.
Definition: xml_helper.hpp:88
Class specification of the manager of the technology library data structures.
all objects that need to be stored in memory are allocated on an external memory
#define OUTPUT_LEVEL_MINIMUM
minimum debugging print is performed.
redefinition of map to manage ordered/unordered structures
Include a set of utilities used to manage CPU time measures.
#define TYPE_LOAD
Constant string identifying a memory load operation.
Definition: op_graph.hpp:172
#define THROW_WARNING(str_expr)
helper function used to throw a warning in a standard way: though it uses PRINT_DBG_MEX, the debug level used is such that the message is always printed
Definition: exceptions.hpp:300
#define STR(s)
Macro which performs a lexical_cast to a string.
Auxiliary methods for manipulating string.
#define max
Definition: backprop.h:17
std::map< std::string, std::set< std::string > > user_internal_objects
#define MEMSET
constant string identifying the operation performed when two objects are memsetted.
Definition: op_graph.hpp:320
Allocation memory class.
void Initialize() override
Initialize the step (i.e., like a constructor, but executed just before exec.
#define START_TIME(time_var)
Macro used to store the start time into time_var.
Definition: cpu_time.hpp:133
#define THROW_UNREACHABLE(str_expr)
helper function used to specify that some points should never be reached
Definition: exceptions.hpp:292
static unsigned long long AccessedMinimunBitsize(const tree_nodeConstRef &type_node, unsigned long long bitsize)
return the minimum bitsize associated with the elements accessible through type_node ...
static unsigned long long Size(const tree_nodeConstRef &tn)
Return the size of a tree object.
~mem_dominator_allocation() override
Destructor.
Class to allocate memories in HLS based on dominators.
Control flow graph.
static memoryRef create_memory(const ParameterConstRef _parameters, const tree_managerConstRef _TreeM, unsigned long long int _off_base_address, unsigned int max_bram, bool _null_pointer_check, bool initial_internal_address_p, unsigned int initial_internal_address, const unsigned int &_address_bitsize)
Definition: memory.cpp:132
all global variables, static variables and strings are allocated on BRAMs
HLSFlowStep_Type
Definition: hls_step.hpp:95
Class specification of the data structures used to manage technology information. ...
redefinition of set to manage ordered/unordered structures
#define STOP_TIME(time_var)
Macro used to store the elapsed time into time_var.
Definition: cpu_time.hpp:136
Datastructure to describe functions allocation in high-level synthesis.
#define TYPE_STORE
Constant string identifying a memory store operation.
Definition: op_graph.hpp:177
XML DOM parser.
XML DOM parser.
boost::graph_traits< graph >::vertex_descriptor vertex
vertex definition.
Definition: graph.hpp:1303
void allocate_parameters(unsigned int functionId, memoryRef Rmem=nullptr)
Allocate parameters for given function.
#define MEMCMP
constant string identifying the operation performed when two objects are memcompared.
Definition: op_graph.hpp:315
static std::string GetFUName(const std::string &fname, const HLS_managerRef HLSMgr)
Return FU used to implement given function.
Definition: functions.cpp:118
#define GET_CONST_NODE(t)
Definition: tree_node.hpp:347
Classes specification of the tree_node data structures.
const ParameterConstRef parameters
Set of input parameters.
DesignFlowStep_Status
The status of a step.
Call graph hierarchy.
#define DEBUG_LEVEL_NONE
no debugging print is performed.
void Exec()
Parse an XML document from a file.
#define THROW_ERROR(str_expr)
helper function used to throw an error in a standard way
Definition: exceptions.hpp:263
Data structure definition for HLS constraints.
T compute_n_bytes(T bitsize)
This file collects some utility functions.
static vertex get_remapped_vertex(vertex current_vertex, const CallGraphManagerConstRef CG, const HLS_managerRef HLSMgr)
check if current_vertex is a proxied function
std::string print_cpu_time(long int t)
massage a long which represents a time interval in milliseconds, into a string suitable for output ...
Definition: cpu_time.hpp:110
std::map< _Key, _Tp, _Compare, _Alloc > OrderedMapStd
Definition: custom_map.hpp:60
static bool IsPackedType(const tree_nodeConstRef &type)
static tree_nodeConstRef GetBaseVariable(const tree_nodeConstRef &mem)
Retrun the base variable of a memory access.
CustomOrderedSet< unsigned int > func_list
list of functions to be analyzed
#define OUTPUT_LEVEL_PEDANTIC
verbose debugging print is performed.
struct definition of the type node structures.
Definition: tree_node.hpp:1318
Class specification of the tree_reindex support class.
#define WORK_LIBRARY
working library.
all local variables, static variables and strings are allocated on BRAMs
Data structures used in operations graph.
std::map< std::string, std::set< std::string > > user_external_objects
Template borrowed from the ANTLR library by Terence Parr (http://www.jGuru.com - Software rights: htt...
Definition: refcount.hpp:94
static tree_nodeConstRef CGetType(const tree_nodeConstRef &node)
Return the treenode of the type of node.
xml_documentRef get_document()
Obtain the parsed document.
static bool IsStructType(const tree_nodeConstRef &type)
Return true if treenode is a record.
this class is used to manage the command-line or XML options.
#define LOAD_XVM(variable, node)
LOAD XML Value Macro. Set a variable starting from an XML value. Conversion is performed if needed...
Definition: xml_helper.hpp:65
mem_dominator_allocation(const ParameterConstRef _parameters, const HLS_managerRef HLSMgr, const DesignFlowManagerConstRef design_flow_manager, const HLSFlowStepSpecializationConstRef hls_flow_step_specialization, const HLSFlowStep_Type hls_flow_step_type=HLSFlowStep_Type::DOMINATOR_MEMORY_ALLOCATION)
Constructor.
Some macro used to interface with the XML library.
Wrapper to call graph.
Generic device description.
static integer_cst_t GetConstValue(const tree_nodeConstRef &tn, bool is_signed=true)
Get value from integer constant.
int debug_level
The debug level.
#define OUTPUT_LEVEL_VERBOSE
verbose debugging print is performed.
all objects that need to be stored in memory are allocated on BRAMs
unsigned long long int user_defined_base_address
user defined base address
#define DEBUG_LEVEL_VERBOSE
verbose debugging print is performed.
static bool IsPointerResolved(const tree_nodeConstRef &ptr, CustomOrderedSet< unsigned int > &res_set)
Data structure definition for high-level synthesis flow.
#define NULL_VERTEX
null vertex definition
Definition: graph.hpp:1305
Datastructure to represent memory information in high-level synthesis.
#define PROXY_LIBRARY
proxy library
Class specification of the manager of the tree structures extracted from the raw file.
HLS specialization of generic_device.
A brief description of the C++ Header File.
void setup_memory_allocation()
Prepares the data structures for the memory allocation.
boost::graph_traits< graph >::edge_descriptor EdgeDescriptor
edge definition.
Definition: graph.hpp:1316
#define THROW_ASSERT(cond, str_expr)
helper function used to check an assert and if needed to throw an error in a standard way ...
Definition: exceptions.hpp:289

Generated on Mon Feb 12 2024 13:02:53 for PandA-2024.02 by doxygen 1.8.13