1 // Philipp Klaus Krause, philipp@informatik.uni-frankfurt.de, pkk@spth.de, 2011
3 // (c) 2011 Goethe-Universität Frankfurt
5 // This program is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU General Public License as published by the
7 // Free Software Foundation; either version 2, or (at your option) any
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 // Optimal placement of bank switching instructions for named address spaces.
24 // Philipp Klaus Krause,
25 // "Optimal Placement of Bank Selection Instructions in Polynomial Time",
26 // Proceedings of the 16th International Workshop on Software and Compilers for Embedded Systems,
27 // M-SCOPES '13, pp. 23-30.
28 // Association for Computing Machinery,
32 #define SDCCNADDR_HH 1
40 #include <boost/graph/graphviz.hpp>
45 #include "SDCCicode.h"
46 #include "SDCCBBlock.h"
51 typedef short int naddrspace_t
; // Named address spaces. -1: Undefined, Others: see map.
53 typedef std::set
<unsigned short int> naddrspaceset_t
;
55 struct assignment_naddr
58 naddrspaceset_t local
;
59 std::vector
<naddrspace_t
> global
;
61 bool operator<(const assignment_naddr
& a
) const
63 naddrspaceset_t::const_iterator i
, ai
, i_end
, ai_end
;
66 ai_end
= a
.local
.end();
68 for (i
= local
.begin(), ai
= a
.local
.begin();; ++i
, ++ai
)
80 if (global
[*i
] < a
.global
[*ai
])
82 if (global
[*i
] > a
.global
[*ai
])
88 bool assignments_naddr_locally_same(const assignment_naddr
&a1
, const assignment_naddr
&a2
)
90 if (a1
.local
!= a2
.local
)
93 naddrspaceset_t::const_iterator i
, i_end
;
94 for (i
= a1
.local
.begin(), i_end
= a1
.local
.end(); i
!= i_end
; ++i
)
95 if (a1
.global
[*i
] != a2
.global
[*i
])
101 struct cfg_naddr_node
104 naddrspaceset_t possible_naddrspaces
;
107 typedef std::list
<assignment_naddr
> assignment_list_naddr_t
;
109 struct tree_dec_naddr_node
111 std::set
<unsigned int> bag
;
112 assignment_list_naddr_t assignments
;
113 unsigned weight
; // The weight is the number of nodes at which intermediate results need to be remembered. In general, to minimize memory consumption, at join nodes the child with maximum weight should be processed first.
116 typedef boost::adjacency_list
<boost::vecS
, boost::vecS
, boost::bidirectionalS
, cfg_naddr_node
, float> cfg_t
; // The edge property is the cost of subdividing the edge and inserting a bank switching instruction.
117 typedef boost::adjacency_list
<boost::vecS
, boost::vecS
, boost::bidirectionalS
, tree_dec_naddr_node
> tree_dec_t
;
119 #ifdef HAVE_TREEDEC_COMBINATIONS_HPP
120 #include <treedec/treedec_traits.hpp>
121 TREEDEC_TREEDEC_BAG_TRAITS(tree_dec_t
, bag
);
124 #include "SDCCtree_dec.hpp"
126 // Annotate nodes of the control flow graph with the set of possible named address spaces active there.
127 void annotate_cfg_naddr(cfg_t
&cfg
, std::map
<naddrspace_t
, const symbol
*> &addrspaces
)
129 /* MSVC 2010 doesn't like the typename here, though it accepts it elsewhere */
130 typedef /*typename*/ boost::graph_traits
<cfg_t
>::vertex_descriptor vertex_t
;
132 std::map
<const symbol
*, naddrspace_t
> sym_to_index
;
133 naddrspace_t na_max
= -1;
135 std::vector
<bool> predetermined(boost::num_vertices (cfg
), false);
137 // Initialize the cfg vertices where there is information on the desired named address space.
138 for (vertex_t i
= 0; i
< boost::num_vertices (cfg
); i
++)
140 const iCode
*ic
= cfg
[i
].ic
;
141 const symbol
*addrspace
;
143 // We do not know the current named address space when entering a function or after calling one.
144 if (ic
->op
== CALL
|| ic
->op
== PCALL
|| ic
->op
== FUNCTION
)
145 predetermined
[i
] = true;
147 // Set the required named address spaces
148 if (addrspace
= getAddrspaceiCode (ic
))
152 if (sym_to_index
.find (addrspace
) == sym_to_index
.end ())
153 sym_to_index
[addrspace
] = ++na_max
;
154 na
= sym_to_index
[addrspace
];
155 addrspaces
[na
] = addrspace
;
157 cfg
[i
].possible_naddrspaces
.insert (na
);
158 predetermined
[i
] = true;
161 cfg
[i
].possible_naddrspaces
.insert(-1);
169 for (vertex_t i
= 0; i
< boost::num_vertices (cfg
); i
++)
171 if (predetermined
[i
])
174 size_t oldsize
= cfg
[i
].possible_naddrspaces
.size();
176 /* MSVC 2010 doesn't like the typename here, though it accepts it elsewhere */
177 typedef /*typename*/ boost::graph_traits
<cfg_t
>::out_edge_iterator n_iter_t
;
179 for (boost::tie(n
, n_end
) = boost::out_edges(i
, cfg
); n
!= n_end
; ++n
)
181 vertex_t v
= boost::target(*n
, cfg
);
182 cfg
[i
].possible_naddrspaces
.insert(cfg
[v
].possible_naddrspaces
.begin(), cfg
[v
].possible_naddrspaces
.end());
186 /* MSVC 2010 doesn't like the typename here, though it accepts it elsewhere */
187 typedef /*typename*/ boost::graph_traits
<cfg_t
>::in_edge_iterator n_iter_t
;
189 for (boost::tie(n
, n_end
) = boost::in_edges(i
, cfg
); n
!= n_end
; ++n
)
191 vertex_t v
= boost::source(*n
, cfg
);
192 cfg
[i
].possible_naddrspaces
.insert(cfg
[v
].possible_naddrspaces
.begin(), cfg
[v
].possible_naddrspaces
.end());
196 if (oldsize
!= cfg
[i
].possible_naddrspaces
.size())
203 // Handle Leaf nodes in the nice tree decomposition
204 template <class T_t
, class G_t
>
205 void tree_dec_naddrswitch_leaf(T_t
&T
, typename
boost::graph_traits
<T_t
>::vertex_descriptor t
, const G_t
&G
)
208 assignment_list_naddr_t
&alist
= T
[t
].assignments
;
211 a
.global
.resize(boost::num_vertices(G
), -2);
215 // Handle introduce nodes in the nice tree decomposition
216 template <class T_t
, class G_t
>
217 int tree_dec_naddrswitch_introduce(T_t
&T
, typename
boost::graph_traits
<T_t
>::vertex_descriptor t
, const G_t
&G
)
219 typedef typename
boost::graph_traits
<T_t
>::adjacency_iterator adjacency_iter_t
;
220 adjacency_iter_t c
, c_end
;
221 assignment_list_naddr_t::iterator ai
;
222 boost::tie(c
, c_end
) = adjacent_vertices(t
, T
);
224 assignment_list_naddr_t
&alist2
= T
[t
].assignments
;
225 assignment_list_naddr_t
&alist
= T
[*c
].assignments
;
227 std::set
<unsigned short> new_inst
;
228 std::set_difference(T
[t
].bag
.begin(), T
[t
].bag
.end(), T
[*c
].bag
.begin(), T
[*c
].bag
.end(), std::inserter(new_inst
, new_inst
.end()));
229 unsigned short int i
= *(new_inst
.begin());
231 for(ai
= alist
.begin(); ai
!= alist
.end(); ++ai
)
235 naddrspaceset_t::const_iterator ni
, ni_end
;
236 for(ni
= G
[i
].possible_naddrspaces
.begin(), ni_end
= G
[i
].possible_naddrspaces
.end(); ni
!= ni_end
; ++ni
)
239 alist2
.push_back(*ai
);
245 return((int)alist2
.size() <= options
.max_allocs_per_node
? 0 : -1);
248 // Handle forget nodes in the nice tree decomposition
249 template <class T_t
, class G_t
>
250 void tree_dec_naddrswitch_forget(T_t
&T
, typename
boost::graph_traits
<T_t
>::vertex_descriptor t
, const G_t
&G
)
252 typedef typename
boost::graph_traits
<T_t
>::adjacency_iterator adjacency_iter_t
;
253 adjacency_iter_t c
, c_end
;
254 boost::tie(c
, c_end
) = adjacent_vertices(t
, T
);
256 assignment_list_naddr_t
&alist
= T
[t
].assignments
;
258 std::swap(alist
, T
[*c
].assignments
);
260 std::set
<unsigned short int> old_inst
;
261 std::set_difference(T
[*c
].bag
.begin(), T
[*c
].bag
.end(), T
[t
].bag
.begin(), T
[t
].bag
.end(), std::inserter(old_inst
, old_inst
.end()));
262 unsigned short int i
= *(old_inst
.begin());
264 assignment_list_naddr_t::iterator ai
, aif
;
266 // Restrict assignments (locally) to current variables.
267 for (ai
= alist
.begin(); ai
!= alist
.end(); ++ai
)
271 typedef typename
boost::graph_traits
<cfg_t
>::out_edge_iterator n_iter_t
;
273 for (boost::tie(n
, n_end
) = boost::out_edges(i
, G
); n
!= n_end
; ++n
)
275 if (ai
->local
.find(boost::target(*n
, G
)) == ai
->local
.end() || ai
->global
[boost::target(*n
, G
)] == -1)
277 if (ai
->global
[boost::source(*n
, G
)] == ai
->global
[boost::target(*n
, G
)])
283 typedef typename
boost::graph_traits
<cfg_t
>::in_edge_iterator n_iter_t
;
285 for (boost::tie(n
, n_end
) = boost::in_edges(i
, G
); n
!= n_end
; ++n
)
287 if (ai
->local
.find(boost::source(*n
, G
)) == ai
->local
.end() || ai
->global
[boost::target(*n
, G
)] == -1)
289 if (ai
->global
[boost::source(*n
, G
)] == ai
->global
[boost::target(*n
, G
)])
298 // Collapse (locally) identical assignments.
299 for (ai
= alist
.begin(); ai
!= alist
.end();)
303 for (++ai
; ai
!= alist
.end() && assignments_naddr_locally_same(*aif
, *ai
);)
321 // Handle join nodes in the nice tree decomposition
322 template <class T_t
, class G_t
>
323 void tree_dec_naddrswitch_join(T_t
&T
, typename
boost::graph_traits
<T_t
>::vertex_descriptor t
, const G_t
&G
)
325 typedef typename
boost::graph_traits
<T_t
>::adjacency_iterator adjacency_iter_t
;
326 adjacency_iter_t c
, c_end
, c2
, c3
;
327 boost::tie(c
, c_end
) = adjacent_vertices(t
, T
);
333 assignment_list_naddr_t
&alist1
= T
[t
].assignments
;
334 assignment_list_naddr_t
&alist2
= T
[*c2
].assignments
;
335 assignment_list_naddr_t
&alist3
= T
[*c3
].assignments
;
340 assignment_list_naddr_t::iterator ai2
, ai3
;
341 for (ai2
= alist2
.begin(), ai3
= alist3
.begin(); ai2
!= alist2
.end() && ai3
!= alist3
.end();)
343 if (assignments_naddr_locally_same(*ai2
, *ai3
))
346 for (size_t i
= 0; i
< ai2
->global
.size(); i
++)
347 ai2
->global
[i
] = ((ai2
->global
[i
] != -2) ? ai2
->global
[i
] : ai3
->global
[i
]);
348 alist1
.push_back(*ai2
);
352 else if (*ai2
< *ai3
)
357 else if (*ai3
< *ai2
)
368 template <class T_t
, class G_t
>
369 int tree_dec_naddrswitch_nodes(T_t
&T
, typename
boost::graph_traits
<T_t
>::vertex_descriptor t
, const G_t
&G
)
371 typedef typename
boost::graph_traits
<T_t
>::adjacency_iterator adjacency_iter_t
;
373 adjacency_iter_t c
, c_end
;
374 typename
boost::graph_traits
<T_t
>::vertex_descriptor c0
, c1
;
376 boost::tie(c
, c_end
) = adjacent_vertices(t
, T
);
378 switch (out_degree(t
, T
))
381 tree_dec_naddrswitch_leaf(T
, t
, G
);
385 tree_dec_naddrswitch_nodes(T
, c0
, G
);
386 if (T
[c0
].bag
.size() < T
[t
].bag
.size())
388 if (tree_dec_naddrswitch_introduce(T
, t
, G
))
392 tree_dec_naddrswitch_forget(T
, t
, G
);
398 if (T
[c0
].weight
< T
[c1
].weight
) // Minimize memory consumption.
401 tree_dec_naddrswitch_nodes(T
, c0
, G
);
402 tree_dec_naddrswitch_nodes(T
, c1
, G
);
403 tree_dec_naddrswitch_join(T
, t
, G
);
406 std::cerr
<< "Not nice.\n";
413 static void implement_naddr_assignment(const assignment_naddr
&a
, const G_t
&G
, const std::map
<naddrspace_t
, const symbol
*>& addrspaces
)
415 typedef typename
boost::graph_traits
<G_t
>::vertex_descriptor vertex_t
;
416 typedef typename
boost::graph_traits
<G_t
>::edge_iterator ei_t
;
419 for(boost::tie(e
, e_end
) = boost::edges(G
); e
!= e_end
; ++e
)
421 const vertex_t source
= boost::source(*e
, G
);
422 const vertex_t target
= boost::target(*e
, G
);
423 const naddrspace_t sourcespace
= a
.global
[source
];
424 const naddrspace_t targetspace
= a
.global
[target
];
426 // Nothing to do if the space doesn't change, or we just forget it.
427 if(targetspace
== -1 || sourcespace
== targetspace
)
430 // This shouldn't happen with the CFGs sdcc generates and a cost function based on code size.
431 if(G
[source
].ic
->next
!= G
[target
].ic
)
432 std::cerr
<< "Trying to switch address space at weird edge in CFG.";
434 switchAddressSpaceAt(G
[target
].ic
, addrspaces
.find(targetspace
)->second
);
438 template <class T_t
, class G_t
>
439 int tree_dec_address_switch(T_t
&T
, const G_t
&G
, const std::map
<naddrspace_t
, const symbol
*>& addrspaces
)
441 if(tree_dec_naddrswitch_nodes(T
, find_root(T
), G
))
444 const assignment_naddr
&winner
= *(T
[find_root(T
)].assignments
.begin());
447 std::cout
<< "Winner: ";
448 for(unsigned int i
= 0; i
< boost::num_vertices(G
); i
++)
450 std::cout
<< "(" << i
<< ", " << int(winner
.global
[i
]) << ") ";
453 std::cout
<< "Cost: " << winner
.s
<< "\n";
457 implement_naddr_assignment(winner
, G
, addrspaces
);
462 // Dump cfg, with numbered nodes, show possible address spaces at each node.
463 void dump_cfg_naddr(const cfg_t
&cfg
)
465 std::ofstream
dump_file((std::string(dstFileName
) + ".dumpnaddrcfg" + (currFunc
? currFunc
->rname
: "__global") + ".dot").c_str());
467 std::string
*name
= new std::string
[num_vertices(cfg
)];
468 for (unsigned int i
= 0; i
< boost::num_vertices(cfg
); i
++)
470 std::ostringstream os
;
471 os
<< i
<< ", " << cfg
[i
].ic
->key
<< ": ";
472 naddrspaceset_t::const_iterator n
;
473 for (n
= cfg
[i
].possible_naddrspaces
.begin(); n
!= cfg
[i
].possible_naddrspaces
.end(); ++n
)
477 boost::write_graphviz(dump_file
, cfg
, boost::make_label_writer(name
), boost::default_writer(), cfg_titlewriter(currFunc
->rname
, " bank selection instr. placement"));
481 // Dump tree decomposition, show bag and live variables at each node.
482 static void dump_tree_decomposition_naddr(const tree_dec_t
&tree_dec
)
484 std::ofstream
dump_file((std::string(dstFileName
) + ".dumpnaddrdec" + currFunc
->rname
+ ".dot").c_str());
488 std::string
*name
= new std::string
[num_vertices(tree_dec
)];
489 for (unsigned int i
= 0; i
< boost::num_vertices(tree_dec
); i
++)
491 if (tree_dec
[i
].bag
.size() > w
)
492 w
= tree_dec
[i
].bag
.size();
493 std::ostringstream os
;
494 std::set
<unsigned int>::const_iterator v1
;
496 for (v1
= tree_dec
[i
].bag
.begin(); v1
!= tree_dec
[i
].bag
.end(); ++v1
)
500 boost::write_graphviz(dump_file
, tree_dec
, boost::make_label_writer(name
), boost::default_writer(), dec_titlewriter((w
- 1), currFunc
->rname
, " bank selection instr. placement"));