PIPS
bourdoncle.c
Go to the documentation of this file.
1 /*
2 
3  $Id: bourdoncle.c 23065 2016-03-02 09:05:50Z coelho $
4 
5  Copyright 1989-2016 MINES ParisTech
6 
7  This file is part of PIPS.
8 
9  PIPS is free software: you can redistribute it and/or modify it
10  under the terms of the GNU General Public License as published by
11  the Free Software Foundation, either version 3 of the License, or
12  any later version.
13 
14  PIPS is distributed in the hope that it will be useful, but WITHOUT ANY
15  WARRANTY; without even the implied warranty of MERCHANTABILITY or
16  FITNESS FOR A PARTICULAR PURPOSE.
17 
18  See the GNU General Public License for more details.
19 
20  You should have received a copy of the GNU General Public License
21  along with PIPS. If not, see <http://www.gnu.org/licenses/>.
22 
23 */
24 #ifdef HAVE_CONFIG_H
25  #include "pips_config.h"
26 #endif
27 /* bourdoncle.c
28 
29  Decomposition of a CFG into SCC's and sub-SCC's. Algorithm 3.9, Page
30  43, Francois Bourdoncle's PhD. Define heuristically SCC and sub-SCC
31  heads.
32 
33  Build a set of new data structures to represent any CFG as a recursive
34  structure of CFG's (scc_map) and one parent DAG (ndu) and one node
35  traversal order (partition list) and a correspondance between the new
36  nodes and the original nodes (ancestor_map).
37 
38  Some predecessors or successors must be deleted in the recursive
39  descent. These vertices are given a statement_undefined statement and
40  empty lists of predecessors and successors. Empty successors must be
41  maintained so as to know if they are true or false successors. I
42  suppose that we do not really care about irrelevant predecessors and
43  that we might as well delete them. In fact, only the true branch
44  successor absolutely must be preserved.
45 
46  Since the new CFG's are not deterministic, the number of successors is
47  not bounded to two and any statement can have more than one
48  successor. Standard consistency check are likely to fail if applied to
49  the unstructured produced by this decomposition.
50 
51  When a node pointing to a scc is replicated, two options are
52  available. Either all copies point to a unique scc (initial
53  implementation) or each control copy points towards its own copy of the
54  scc. In the initial implementation and first option, only ancestor
55  control nodes can point to a scc, and ancestor_map is used to reach the
56  scc_associated to a node. In the second case, any control node,
57  replicated or not, can point to a scc. The first option makes sense
58  when transformers are not context dependent: all identical cycles can
59  be analyzed as one cycle, although I do not understand the miracle for
60  the preconditions, since they clearly are context-sensitive. The second
61  option is useful when transformers are computed in context, since all
62  replicates of a cycle can operate in diferrent contexts. This is
63  performed as a post-processing step conditional to property
64  SEMANTICS_COMPUTE_TRANSFORMERS_IN_CONTEXT.
65 
66  Structure of the code:
67  bourdoncle_partition()
68  bourdoncle_visit()
69  bourdoncle_component()
70  bourdoncle_visit() recursively
71  update_partition()
72  scc_to_dag() [Not part of Bourdoncle's algorithm: transforms a scc
73  into a non-deterministic while loop]
74  replicate_cycles() [Not part of Bourdoncle's algorithm: allocate a copy
75  of each scc, i.e. cycle, i.e. while loop for each
76  of its "call" sites]
77 */
78 
79 #include <stdio.h>
80 #include <strings.h>
81 
82 #include "linear.h"
83 
84 #include "genC.h"
85 #include "ri.h"
86 #include "ri-util.h"
87 #include "control.h"
88 #include "properties.h"
89 #include "pipsdbm.h"
90 
91 #include "misc.h"
92 
93 ␌
94 /* Data structures for Bourdoncle's heuristics:
95  *
96  * dfn = depth first number
97  *
98  * num = current vertex number
99  *
100  * vertex_stack = stack of visited nodes
101  *
102  */
103 
105 
106 static void reset_dfn(control c)
107 {
108  hash_put(dfn, (void *) c, (void *) 0);
109 }
110 
111 static void copy_dfn(control new_c, control old_c)
112 {
113  _int d = 0;
114 
115  if((d = (_int) hash_get(dfn, (void *) old_c)) == (_int) (HASH_UNDEFINED_VALUE))
116  pips_internal_error("No dfn value for control %p", old_c);
117 
118  hash_put(dfn, (void *) new_c, (void *) d);
119 }
120 
122 {
123  _int d = 0;
124 
125  if((d = (_int) hash_get(dfn, (void *) c)) == (_int) (HASH_UNDEFINED_VALUE))
126  pips_internal_error("No dfn value for control %p", c);
127 
128  return d;
129 }
130 
131 static void update_dfn(control c, _int d)
132 {
133  hash_update(dfn, (void *) c, (void *) d);
134 }
135 
136 
137 static int num = 0;
138 
140 
141 ␌
142  /* With non-deterministic graph, the outcome of a test may be known
143  in advance. Never taken branches are filled in with meaningless
144  nodes. */
145 
147 {
148  control c = make_control(statement_undefined, preds, succs);
149  pips_assert("A meaningless control has no successors", ENDP(succs));
150  return c;
151 }
152 
153 /* Is exported to exploit non-deterministic control flow graphs */
155 {
157 }
158 
160 {
161  /* free_control() cannot be used because you do not want to free the
162  successors and the predecessors */
163  pips_assert("No statement is associated", statement_undefined_p(control_statement(c)));
168  free_control(c);
169 }
170 
171 ␌
172 /* Check that a node is used as a test in a CFG. The associated statement
173  must be a test, the number of successors must be two and the true and
174  false branches must be empty. Exported for user of ND CFG's. */
176 {
177  bool test_p = false;
178  if(!meaningless_control_p(c)) {
180  if(gen_length(control_successors(c))>=2) {
182  statement ts = test_true(t);
183  statement fs = test_false(t);
186  }
187  }
188  }
189  return test_p;
190 }
191 
192 ␌
193  /* Code partly replicated from semantics/unstructured.c to be able to
194  taylor it to the exact needs. */
195 
197 
199 {
200  if(!meaningless_control_p(c)) {
202  if(gen_length(control_successors(c))==2) {
203  /* The true and false branches should be empty most of the
204  time. But a structured test used a non-deterministic node might
205  very well have two successors or more. The assert below is too
206  strong. */
207  /*
208  test t = statement_test(control_statement(c));
209  statement ts = test_true(t);
210  statement fs = test_false(t);
211  */
212  /* Since some test statements are left structured and since
213  structure and unstructured test statements may be
214  non-deterministic here, nothing can be said. */
215  /*
216  pips_assert("The true and false branches must be empty",
217  empty_statement_or_labelless_continue_p(ts)
218  && empty_statement_or_labelless_continue_p(fs));
219  */
220  ;
221  }
222  else {
223  /* Non-deterministic environment */
224  /*
225  pips_assert("A structured test has one successor at most",
226  gen_length(control_successors(c))<2);
227  */
228  ;
229  }
230  }
231  else {
232  /* Non-deterministic environment */
233  /*
234  pips_assert("A non-test has at most one successor",
235  gen_length(control_successors(c))<2);
236  */
237  ;
238  }
239  }
240  else {
241  ifdebug(4) {
242  /* A meaningless control can be shared in intermediate states */
243  }
244  else ifdebug(1){
245  /* It should not be shared, not only because memory management would
246  be impossible locally but also because gen_recurse() would follow
247  forbidden paths. */
248  pips_assert("A meaningless control has one and ony one predecessor",
250  pips_assert("A meaningless control has no successor",
252  }
253 
254  }
255 
256  MAP(CONTROL, pred,
257  {
258  if(!gen_in_list_p(c, control_successors(pred))) {
260  pips_assert("c is a successor of each of its predecessors", false);
261  }
262  }
263  , control_predecessors(c));
264 
265  MAP(CONTROL, succ,
266  {
267  if(!gen_in_list_p(c, control_predecessors(succ))) {
269  pips_assert("c is a predecessor of each of its successors", false);
270  }
271  }
272  , control_successors(c));
273  return true;
274 }
275 
276 static void print_and_check_control_node(control c, bool check_p)
277 {
278  fprintf(stderr,
279  "ctr %p, %zd preds, %zd succs: %s",
280  c,
284  fprintf(stderr,"\tsuccessors:\n");
285  MAP(CONTROL, s, {
286  fprintf(stderr, "\t\t%p %s", s,
288  }, control_successors(c));
289  fprintf(stderr,"\tpredecessors:\n");
290  MAP(CONTROL, p, {
291  fprintf(stderr, "\t\t%p %s", p,
293  }, control_predecessors(c));
294  fprintf(stderr, "\n");
295  if(check_p)
296  (void) check_control_statement(c);
297 }
298 
300 {
302 }
303 
305 {
307 }
308 
309 /* Was moved into ri-util/control, minus the check_control_statement() */
310 #if 0
311 static void print_control_nodes(list l)
312 {
313  MAP(CONTROL, c, {
314  fprintf(stderr, "%p, %s", c,
316  (void) check_control_statement(c);
317  }, l);
318  fprintf(stderr, "\n");
319 }
320 #endif
321 
323 {
324  MAP(CONTROL, c, {
325  fprintf(stderr, "%p, %s", c,
327  }, l);
328  fprintf(stderr, "\n");
329 }
330 
332 {
333  pips_debug(2, "Unstructured %p with nodes:\n", u);
334  /* Do not go down into nested unstructured */
337  pips_debug(2, "With entry nodes\n");
339  pips_debug(2, "And exit node\n");
341 }
342 ␌
343 /* Output CFG in daVinci format */
344 static void davinci_print_control_node(control c, FILE * f,
345  bool entry_p,
346  bool exit_p,
347  bool fix_point_p)
348 {
349  char attributes[256];
350  bool arc_kind = true;
351 
352  /* The same node can be an entry and an exit node. */
353  attributes[0] = '\000';
354  if(entry_p) {
355  (void) strcpy(&attributes[0], ",a(\"BORDER\",\"double\")");
356  }
357  if(exit_p){
358  (void) strcat(&attributes[0], ",a(\"COLOR\",\"red\")");
359  }
360  if(fix_point_p){
361  (void) strcat(&attributes[0], ",a(\"FONTSTYLE\",\"bold_italic\")");
362  }
363 
364  if(meaningless_control_p(c)) {
365  fprintf(f, "l(\"%p\",n(\"\",[a(\"_GO\",\"text\"),a(\"OBJECT\",\"%p\")],\n\t[\n", c, c);
366  }
367  else if(control_test_p(c)) {
369  fprintf(f,
370  "l(\"%p\",n(\"\",[a(\"_GO\",\"rhombus\"),a(\"OBJECT\",\"%p\\n(%d,%d)\")%s],\n\t[\n",
371  c, c,
372  ORDERING_NUMBER(so), ORDERING_STATEMENT(so), attributes);
373  }
374  else {
376  fprintf(f, "l(\"%p\",n(\"\",[a(\"OBJECT\",\"%p\\n(%d,%d)\")%s],\n\t[\n", c, c,
377  ORDERING_NUMBER(so), ORDERING_STATEMENT(so), attributes);
378  }
379 
380  /* Declare arcs */
381  MAPL(c_s, {
382  control s = CONTROL(CAR(c_s));
383 
384  if(control_test_p(c)) {
385  (void) strcpy(&attributes[0], arc_kind? "green":"red");
386  fprintf(f, "\tl(\"%p->%p\",e(\"\",[a(\"EDGECOLOR\",\"%s\")],r(\"%p\")))",
387  c, s, attributes, s);
388  }
389  else {
390  fprintf(f, "\tl(\"%p->%p\",e(\"\",[],r(\"%p\")))", c, s, s);
391  }
392 
393  fprintf(f, "%s\n", ENDP(CDR(c_s))? "" : ",");
394  arc_kind = !arc_kind;
395  }, control_successors(c));
396 
397  fprintf(f,"\t]))");
398 
399  (void) check_control_statement(c);
400 }
401 
402 static int control_cons_compare(list l1, list l2)
403 {
404  int diff = 0;
405  control c1 = CONTROL(CAR(l1));
406  control c2 = CONTROL(CAR(l2));
407 
408  if(meaningless_control_p(c1)) {
409  diff = 0;
410  }
411  else if(meaningless_control_p(c2)) {
412  diff = 0;
413  }
414  else {
416  statement s2 = control_statement(c2);
417 
419  }
420  return diff;
421 }
422 
423 /* This counter is pre-incremented each time a new graph is stored to
424  generate a new file name. */
425 static int davinci_count = 0;
426 
427 static void set_davinci_count()
428 {
429  davinci_count = 0;
430 }
431 
432 
433 static void davinci_print_control_nodes(list l, string msg)
434 {
436  FILE * f = safe_fopen(concatenate(dn, "/",
439  "-",
440  i2a(++davinci_count),
441  ".daVinci", NULL),
442  "w");
443  list sl = gen_copy_seq(l);
444 
445  free(dn);
447 
448  fprintf(f, "[\n");
449  fprintf(f, "l(\"%s\",n(\"\",[a(\"_GO\",\"text\"),a(\"OBJECT\",\"%s\\nSerial number for module %s: %d\\nEntry node: double border\\nExit node: red\\nCycles: bold italic\\nTrue arc: green\\nFalse arc: red\")],\n\t[])),\n",
451  /*
452  fprintf(f, "l(\"%d\",n(\"\",[a(\"_GO\",\"text\"),a(\"OBJECT\",\"serial number: %d\")],\n\t[])),\n",
453  davinci_count, davinci_count);
454  */
455 
456  MAPL(c_c, {
457  control c = CONTROL(CAR(c_c));
458 
459  davinci_print_control_node(c, f, c==CONTROL(CAR(l)), false, false);
460  fprintf(f, "%s\n", ENDP(CDR(c_c))? "" : ",");
461  }, sl);
462 
463  fprintf(f, "]\n");
464  gen_free_list(sl);
466  "-",
468  ".daVinci", NULL));
469 
470 }
471 
473 (unstructured u, string msg, hash_table scc_map, hash_table ancestor_map)
474 {
476  FILE * f = safe_fopen(concatenate(dn, "/",
479  "-",
480  i2a(++davinci_count),
481  ".daVinci", NULL),
482  "w");
483  control entry_c = unstructured_control(u);
484  control exit_c = unstructured_exit(u);
485  list l = NIL;
486  list sl = list_undefined;
487 
488  free(dn);
489  forward_control_map_get_blocs(entry_c, &l);
490 
491  if(!gen_in_list_p(exit_c, l)) {
492  pips_debug(1, "Exit node %p in unstructured %p is not reachable\n", exit_c, u);
493  }
494 
495  sl = gen_copy_seq(l);
496 
498 
499  fprintf(f, "[\n");
500  fprintf(f, "l(\"%s\",n(\"\",[a(\"_GO\",\"text\"),a(\"OBJECT\",\"%s\\nSerial number for module %s: %d\\nEntry node: double border\\nExit node: red\\nCycles: bold italic\\nTrue arc: green\\nFalse arc: red\")],\n\t[])),\n",
502  /*
503  fprintf(f, "l(\"%d\",n(\"\",[a(\"_GO\",\"text\"),a(\"OBJECT\",\"serial number: %d\")],\n\t[])),\n",
504  davinci_count, davinci_count);
505  */
506 
507  MAPL(c_c, {
508  control c = CONTROL(CAR(c_c));
510 
511  if((a = hash_get(ancestor_map, c))==HASH_UNDEFINED_VALUE)
512  a = c;
513 
514  /* It may be called with an empty ancestor and scc maps. */
516  c==entry_c,
517  c==exit_c,
518  hash_table_entry_count(ancestor_map)!=0
519  && !meaningless_control_p(c)
520  && cycle_head_p(c, ancestor_map, scc_map));
521  fprintf(f, "%s\n", ENDP(CDR(c_c))? "" : ",");
522  }, sl);
523 
524  fprintf(f, "]\n");
525  gen_free_list(sl);
527  "-",
529  ".daVinci", NULL));
530 
531 }
532 ␌
534 
536 {
538 }
539 
540 static void print_embedding_graph(control c, string msg)
541 {
542  ifdebug(2) {
543  bool consistent_embedding_graph_p = true;
544 
545  pips_debug(2, "Embedding graph for vertex %p:\n", c);
546  /* Do not go down into nested unstructured */
549 
550  /* Check its structure: make sure that each node is a successor of its
551  predecessors and a predecessor of all its successors */
552  pips_assert("The embedding control list is NIL", embedding_control_list == NIL);
555  MAP(CONTROL, ec, {
556  MAP(CONTROL, pred, {
557  if(!gen_in_list_p(ec, control_successors(pred))){
558  consistent_embedding_graph_p = false;
559  fprintf(stderr, "Control %p is a predecessor of control %p "
560  "but control %p is not a successor of control %p\n",
561  pred, ec, ec, pred);
562  }
563 
564  }
565  , control_predecessors(ec));
566  MAP(CONTROL, succ, {
567  if(!gen_in_list_p(ec, control_predecessors(succ))){
568  consistent_embedding_graph_p = false;
569  fprintf(stderr, "Control %p is a successor of control %p "
570  "but control %p is not a predecessor of control %p\n",
571  succ, ec, ec, succ);
572  }
573 
574  }
575  , control_successors(ec));
576  }
578 
579  /* Make sure that no meaningful node only has meaningless successors */
580  MAP(CONTROL, ec, {
581  if(!meaningless_control_p(ec)) {
582  bool meaningless_p = !ENDP(control_successors(ec));
583 
584  MAP(CONTROL, succ, {
585  if(!meaningless_control_p(succ)) {
586  meaningless_p = false;
587  }
588  }, control_successors(ec));
589 
590  if(meaningless_p) {
591  consistent_embedding_graph_p = false;
592  fprintf(stderr, "Control %p only has meaningless successors\n",
593  ec);
594  }
595  }
597 
598  /* Make sure that no node has any meaningless predecessor */
599  MAP(CONTROL, ec, {
600  MAP(CONTROL, pred, {
601  if(meaningless_control_p(pred)) {
602  consistent_embedding_graph_p = false;
603  fprintf(stderr, "Control %p has meaningless predecessor %p\n",
604  ec, pred);
605  }
606  }, control_predecessors(ec));
608 
609  /* Make sure that destructured test statements always have two
610  successors... not ture anymore with non-determinacy. */
611  MAP(CONTROL, ec, {
612  (void) check_control_statement(ec);
614 
616 
617  pips_assert("The embedding graph is consistent", consistent_embedding_graph_p);
618 
621  pips_debug(2, "End: the embedding graph for vertex %p is consistent.\n", c);
622  }
623 }
624 ␌
625 /* Allocate a list of control nodes transitively linked to control c */
627 {
628  list l = list_undefined;
629 
630  pips_debug(8, "Linked nodes for vertex %p:\n", c);
631  pips_assert("The embedding control list is NIL", embedding_control_list == NIL);
632 
637 
638  ifdebug(8) {
639  pips_debug(8, "End with list:\n");
641  }
642 
643  return l;
644 }
645 ␌
646 /* Take care of two problems: meaningless control nodes may end up shared
647  by effective nodes and they may end up uselessly numerous.
648 
649  FI: Two more problems could be tackled. The same node should not be
650  twice a true or twice a false successor, although a given node can be a
651  true and a false successor. gen_once() should be used to build t_succs
652  and f_succs but the assertions and the memory management should then be
653  updated. Duplicate could be chained to the u_succs(), the useless successors */
654 
656 {
657  list succs = control_successors(c);
658  list t_succs = NIL; /* true branch successors */
659  list f_succs = NIL; /* false branch successors */
660  list e_succs = NIL; /* empty successors */
661  int ns = gen_length(succs);
662  int nts = 0;
663  int nfs = 0;
664  int nes = 0;
665  int nel = 0; /* Number of eliminations */
666  int rank = 0;
667 
668  pips_assert("A control test has at least two successors", ns>=2);
669 
670  /* Partition the successors */
671  MAP(CONTROL, s,
672  {
673  rank++;
674  if(meaningless_control_p(s)) {
675  e_succs = CONS(CONTROL, s, e_succs);
676  }
677  else if(rank%2==1) {
678  t_succs = CONS(CONTROL, s, t_succs);
679  }
680  else {
681  f_succs = CONS(CONTROL, s, f_succs);
682  }
683  }
684  , succs);
685 
686  nts = gen_length(t_succs);
687  nfs = gen_length(f_succs);
688  nes = gen_length(e_succs);
689  pips_assert("The rank of the last successors is the number of successors", rank==ns);
690  pips_assert("true, false and empty successors are a partition", ns==nts+nfs+nes);
691 
692  /* Rebuild the successor list if it can be useful */
693  if(nes > nts-nfs && nes > nfs-nts) {
694  list n_succs = NIL; /* new successor list */
695  list c_t_succs = t_succs;
696  list c_f_succs = f_succs;
697  list c_e_succs = e_succs;
698 
699  for(rank=1; rank <= nts || rank <= nfs; rank++) {
700  if(!ENDP(c_t_succs)) {
701  n_succs = gen_nconc(n_succs, CONS(CONTROL, CONTROL(CAR(c_t_succs)), NIL));
702  POP(c_t_succs);
703  }
704  else {
705  pips_assert("The empty successor list is not empty", !ENDP(c_e_succs));
706  n_succs = gen_nconc(n_succs, CONS(CONTROL, CONTROL(CAR(c_e_succs)), NIL));
707  POP(c_e_succs);
708  }
709  if(!ENDP(c_f_succs)) {
710  n_succs = gen_nconc(n_succs, CONS(CONTROL, CONTROL(CAR(c_f_succs)), NIL));
711  POP(c_f_succs);
712  }
713  else {
714  pips_assert("The empty successor list is not empty", !ENDP(c_e_succs));
715  n_succs = gen_nconc(n_succs, CONS(CONTROL, CONTROL(CAR(c_e_succs)), NIL));
716  POP(c_e_succs);
717  }
718  }
719  pips_assert("No more true successors", ENDP(c_t_succs));
720  pips_assert("No more false successors", ENDP(c_f_succs));
721  /* FI: I do not see why you are sure not to need all meaningless
722  controls... There might be no meaningless control at all. The next
723  two assertions seem too strong. */
724  pips_assert("More empty successors", !ENDP(c_e_succs));
725 
726  nel = ns - gen_length(n_succs);
727  pips_assert("The successor list is reduced", nel>0);
728 
729  /* Courageously, free the remaining meaningless successors */
730  MAP(CONTROL, e, {
731  ifdebug(9) fprintf(stderr,"control %p is consistent and is going to be freed\n", e);
733  }, c_e_succs);
734 
735  /* Update the successor list of c */
737  control_successors(c) = n_succs;
738  }
739  /* Free the partition lists */
740  gen_free_list(t_succs);
741  gen_free_list(f_succs);
742  gen_free_list(e_succs);
743 
744  return nel;
745 }
746 
748 {
749  list el = node_to_linked_nodes(c);
750  list tl = NIL;
751  int nor = 0; /* number of replications */
752  int nel = 0; /* number of eliminations */
753 
754  /* FI: A level of 1 makes more sense, but it is very slow on large
755  graphs. The same nodes are checked several times because the cache
756  managed by control_consistent_p() is lost between iterations. */
757  /* ifdebug(1) { */
758  ifdebug(8) {
759  pips_assert("c is consistent", control_consistent_p(c));
760  MAP(CONTROL, ec, {
761  pips_assert("ec is consistent first", control_consistent_p(ec));
762  ifdebug(10) fprintf(stderr,"control %p is consistent first\n", ec);
763  }
764  , el);
765  }
766 
767  /* A meaningless control must have only one predecessor and no
768  successor. */
769  MAP(CONTROL, ec, {
770  if(meaningless_control_p(ec)) {
771  pips_assert("No successor", ENDP(control_successors(ec)));
772 
773  if(gen_length(control_predecessors(ec))>1) {
774  list c_pred = list_undefined;
775 
776  for(c_pred=CDR(control_predecessors(ec)); !ENDP(c_pred); POP(c_pred)) {
777  control pred = CONTROL(CAR(c_pred));
778  control new_ec = make_meaningless_control(CONS(CONTROL, pred, NIL), NIL);
779  gen_list_patch(control_successors(pred), ec, new_ec);
780  nor++;
781  }
784  }
785  }
786  }
787  , el);
788 
789  ifdebug(1) {
790  MAP(CONTROL, ec, {
791  pips_assert("ec is consistent second", control_consistent_p(ec));
792  ifdebug(9) fprintf(stderr,"control %p is consistent second\n", ec);
793  }
794  , el);
795  }
796 
797  /* A non-deterministic test does not need too many meaningless
798  successors. A new list must be built first because
799  clean_up_control_test() destroys elements of el, which cannot be
800  scanned any longer. */
801  MAP(CONTROL, ec, {
802  ifdebug(10) fprintf(stderr,"is control %p is consistent?\n", ec);
803  ifdebug(1) pips_assert("ec is consistent third", control_consistent_p(ec));
804  if(control_test_p(ec)) {
805  tl = CONS(CONTROL, ec, tl);
806  }
807  }
808  , el);
809 
810  MAP(CONTROL, ec, {
811  ifdebug(10) fprintf(stderr,"is control %p is consistent?\n", ec);
812  ifdebug(1) pips_assert("ec is consistent fourth", control_consistent_p(ec));
813  nel += clean_up_control_test(ec);
814  }
815  , tl);
816 
817  gen_free_list(el);
818  gen_free_list(tl);
819 
820  pips_debug(2, "End: %d useless successors destroyed\n", nel);
821 }
822 ␌
824 {
825  fprintf(stderr, "%s\n", message);
826  HASH_MAP(c1, c2,
827  {
828  fprintf(stderr, "%p -> %p\n", c1, c2);
829  }
830  , map);
831  fprintf(stderr, "\n");
832 }
833 
834 ␌
835 /* Functions to deal with the ancestor_map */
836 
837 static bool ancestor_control_p(hash_table ancestor_map, control c)
838 {
839  bool ancestor_p = false;
840 
841  HASH_MAP(c_new, c_old,
842  {
843  if(c_old==(void *) c) {
844  ancestor_p = true;
845  break;
846  }
847  }
848  , ancestor_map);
849  return ancestor_p;
850 }
851 
852 /* If vertex is an ancestor control node from the input control graph,
853  return vertex, else return its ancestor. Is used in other libraries. */
855 {
856  control ancestor = control_undefined;
857 
858  if((ancestor = hash_get(ancestor_map, vertex))==HASH_UNDEFINED_VALUE)
859  ancestor = vertex;
860  ifdebug(2) {
861  /* This assert may be too strong, but all control nodes are copied
862  when just after entering bourdoncle_partition which should make it
863  correct. */
864  /* Theoretically only useful when HASH_UNDEFINED_VALUE has been
865  returned, i.e. when ancestor==vertex */
866  if(!ancestor_control_p(ancestor_map, ancestor)) {
868  statement as = control_statement(ancestor);
869 
870  pips_debug(2, "vertex=%p with statement %s\n",
872  pips_debug(2, "ancestor=%p with statement %s\n",
873  ancestor, statement_identification(as));
874  pips_assert("ancestor really is an ancestor",
875  ancestor_control_p(ancestor_map, ancestor));
876  }
877  }
878 
879  return ancestor;
880 }
881 
882 static bool registered_controls_p(hash_table ancestor_map, list l)
883 {
884  bool registered_p = true;
885 
886  MAP(CONTROL, c,
887  {
888  if(!meaningless_control_p(c)) {
889  if(hash_get(ancestor_map, c)==HASH_UNDEFINED_VALUE) {
890  if(!ancestor_control_p(ancestor_map, c)) {
891  ifdebug(2) {
892  pips_debug(2, "Control %p is not registered:\n", c);
894  }
895  registered_p = false;
896  }
897  }
898  }
899  }
900  , l);
901  return registered_p;
902 }
903 
904 /* No control can be an ancestor and a child */
905 static bool ancestor_map_consistent_p(hash_table ancestor_map)
906 {
907  bool check_p = true;
908  list ancestors = NIL;
909  list children = NIL;
910 
911  HASH_MAP(c_new, c_old,
912  {
913  ancestors = gen_once((control) c_old, ancestors);
914  children = CONS(CONTROL, (control) c_new, children);
915  }
916  , ancestor_map);
917  gen_list_and(&ancestors, children);
918  check_p = ENDP(ancestors);
919 
920  ifdebug(2) {
921  if(!ENDP(ancestors)) {
922  pips_debug(2, "Bug some control are children and ancestors:\n");
923  print_control_nodes(ancestors);
924  print_control_to_control_mapping("Child to ancestor map:", ancestor_map);
925  }
926  }
927 
928  gen_free_list(ancestors);
929  gen_free_list(children);
930 
931  return check_p;
932 }
933 
934 static void add_child_parent_pair(hash_table ancestor_map,
935  control child,
936  control parent)
937 {
938  control ancestor = control_undefined;
939 
940  /* The child will inherit the parent's ancestor, if any */
941  if((ancestor = (control) hash_get(ancestor_map, parent))==(control) HASH_UNDEFINED_VALUE) {
942  ancestor = parent;
943  }
944  ifdebug(2) {
945  /* The child should not already be in ancestor_map */
946  pips_assert("The child should not already be in ancestor_map",
947  hash_get(ancestor_map, child)==HASH_UNDEFINED_VALUE);
948  /* The child cannot be an ancestor */
949  pips_assert("The child is not an ancestor", !ancestor_control_p(ancestor_map, child));
950  }
951  hash_put(ancestor_map, (void *) child, (void *) ancestor);
952 }
953 ␌
954 /* Replication of unstructured (i.e. CFG) and control nodes
955  (i.e. vertices) */
956 
958 
959 static void print_replicate_map()
960 {
961  fprintf(stderr,"\nDump of replicate_map:\n");
962  HASH_MAP(old_c, new_c,
963  {
964  fprintf(stderr, "Old %p -> New %p\n", old_c, new_c);
965  }
966  , replicate_map);
967  fprintf(stderr,"\n");
968 }
969 
971 {
972  control new_c = control_undefined;
973 
974  new_c = make_control(control_statement(c),
977  hash_put(replicate_map, (void *) c, (void *) new_c);
978 
979  return new_c;
980 }
981 
983 {
984  MAPL(c_c,
985  {
986  control old_c = CONTROL(CAR(c_c));
987  control new_c = hash_get(replicate_map, (void *) old_c);
988  pips_assert("new_c is defined", new_c!=(control) HASH_UNDEFINED_VALUE);
989  CONTROL_(CAR(c_c)) = new_c;
990  }
991  , control_predecessors(c));
992  MAPL(c_c,
993  {
994  control old_c = CONTROL(CAR(c_c));
995  control new_c = hash_get(replicate_map, (void *) old_c);
996  pips_assert("new_c is defined", new_c!=(control) HASH_UNDEFINED_VALUE);
997  CONTROL_(CAR(c_c)) = new_c;
998  }
999  , control_successors(c));
1000 }
1001 
1003 {
1004  control new_c = hash_get(replicate_map, (void *) old_c);
1005  pips_assert("new_c is defined", new_c!=(control) HASH_UNDEFINED_VALUE);
1006  return new_c;
1007 }
1008 
1010 {
1012 
1013  pips_assert("replicate_map is undefined",
1015 
1017 
1018  /* Do not go down into nested unstructured and replicate all control
1019  nodes */
1022 
1024 
1025  /* Update predecessor and successor arcs of the new control nodes to
1026  point to the new control nodes using the global conversion mapping
1027  replicate_map */
1028  HASH_MAP(old_c, new_c,
1029  {
1031  }
1032  , replicate_map);
1033 
1034  /* Generate new unstructured with relevant entry and exit nodes */
1037 
1038  /* Generate ancestor_map as the inverse function of replicate_map */
1039  HASH_MAP(old_n, new_n,{
1040  add_child_parent_pair(ancestor_map, (control) new_n, (control) old_n);
1041  }, replicate_map);
1042 
1045 
1046  return new_u;
1047 }
1048 
1049 ␌
1050 /* Build a new unstructured (CFG) for partition. vertex is the entry and
1051  exit point. New nodes must be allocated because the parent graph is
1052  untouched. vertex is supposed to be included into partition. */
1054 {
1056  list useless = NIL;
1057 
1059 
1060  /* Create the translation table replicate_map */
1061  MAP(CONTROL, c,
1062  {
1063  (void) control_shallow_copy(c);
1064  }
1065  , partition);
1066 
1067  /* Generate new unstructured with relevant entry and exit node vertex */
1070 
1071  /* Update arcs */
1072 
1073  /* Update predecessors */
1074  MAP(CONTROL, c,
1075  {
1076  control c_new = control_to_replicate(c);
1077 
1078  /* The only predecessors that are useful are in partition */
1079  MAPL(c_c,
1080  {
1081  control old_c = CONTROL(CAR(c_c));
1082  control new_c = control_undefined;
1083 
1084  if(gen_in_list_p(old_c, partition)) {
1085  new_c = hash_get(replicate_map, (void *) old_c);
1086  pips_assert("new_c is defined", new_c!=(control) HASH_UNDEFINED_VALUE);
1087  CONTROL_(CAR(c_c)) = new_c;
1088  }
1089  else {
1090  /* This predecessor is irrelevant */
1091  useless = CONS(CONTROL, old_c, useless);
1092  }
1093  }
1094  , control_predecessors(c_new));
1095 
1098  useless = NIL;
1099 
1100  /* Handle successors */
1101  MAPL(c_c,
1102  {
1103  control old_c = CONTROL(CAR(c_c));
1104  control new_c = control_undefined;
1105 
1106  if(gen_in_list_p(old_c, partition)) {
1107  new_c = hash_get(replicate_map, (void *) old_c);
1108  pips_assert("new_c is defined",
1109  new_c!=(control) HASH_UNDEFINED_VALUE);
1110  }
1111  else {
1112  pips_assert("If a successor is not in partition,"
1113  " then there must be two successors",
1114  gen_length(control_successors(c_new))==2);
1115  /* This successor is irrelevant but irrelevant true branches must
1116  be preserved */
1117  if(c_c==control_successors(c_new)) {
1118  pips_assert("The second successor is in partition and has been copied",
1119  gen_in_list_p(CONTROL(CAR(CDR(c_c))),partition) );
1120  new_c = make_meaningless_control(CONS(CONTROL, c_new, NIL), NIL);
1121  }
1122  else {
1123  /* A second irrelevant successor is not needed. But are we ready
1124  to have IF nodes with only one successor? */
1125  /* useless = CONS(CONTROL, old_c, useless); */
1126  new_c = make_meaningless_control(CONS(CONTROL, c_new, NIL), NIL);
1127  }
1128  }
1129 
1130  if(!control_undefined_p(new_c))
1131  CONTROL_(CAR(c_c)) = new_c;
1132  }
1133  , control_successors(c_new));
1134  }
1135  , partition);
1136 
1137  /*
1138  control_predecessors(c_new) = gen_list_and_not(control_predecessors(c_new), useless);
1139  gen_free_list(useless);
1140  useless = NIL;
1141  */
1142 
1145 
1146  return new_u;
1147 }
1148 ␌
1150  list partition,
1151  bool check_entry)
1152 {
1153  bool is_entry_or_exit_p = false;
1154  list nodes = check_entry?control_predecessors(c):control_successors(c);
1155 
1156  MAP(CONTROL, pred,
1157  {
1158  if(!meaningless_control_p(pred) && !gen_in_list_p(pred, partition)) {
1159  is_entry_or_exit_p = true;
1160  break;
1161  }
1162  }
1163  , nodes);
1164 
1165  return is_entry_or_exit_p;
1166 }
1167 static bool entry_control_p(control c, list partition)
1168 {
1169  return entry_or_exit_control_p(c, partition, true);
1170 }
1171 
1172 static bool exit_control_p(control c, list partition)
1173 {
1174  return entry_or_exit_control_p(c, partition, false);
1175 }
1176 
1177 ␌
1178 /* If complement_p, preserve successors in the complement of
1179  partition. Else preserve successors in partition. Take care of test
1180  nodes wich must keep two successors no matter what. */
1182  list partition,
1183  bool complement_p)
1184 {
1185  list * succs = &control_successors(c);
1186 
1187  pips_assert("We have at least one successor", gen_length(*succs)>0);
1188 
1189  if(gen_length(*succs)==1) {
1190  control c = CONTROL(CAR(*succs));
1191  /* The CFG may not be connexe or the exit path may be hidden by a STOP statement */
1192  /*
1193  pips_assert("The unique successor must be in partition",
1194  gen_in_list_p(c, partition)==!complement_p);
1195  */
1196  if(gen_in_list_p(c, partition)==complement_p) {
1197  if(complement_p)
1198  gen_list_and_not(succs, partition);
1199  else
1200  gen_list_and(succs, partition);
1201  }
1202  }
1203  else if (control_test_p(c)) {
1204  /* Let's boldy assume the parent control node is used as a CFG test
1205  statement and preserve the number of successors. */
1206  MAPL(succ_c, {
1207  control succ = CONTROL(CAR(succ_c));
1208 
1209  if(gen_in_list_p(succ, partition)==complement_p)
1211  } , *succs);
1212  }
1213  else {
1214  /* It is not a CFG test node: get rid of useless */
1215  if(complement_p)
1216  gen_list_and_not(succs, partition);
1217  else
1218  gen_list_and(succs, partition);
1219  }
1220  /* Not true: you may have non-connexe CFG and loops with no exit. */
1221  /* pips_assert("We still have at least one successor", gen_length(*succs)>0); */
1222 }
1223 
1225  list partition)
1226 {
1228 }
1229 
1231  list partition)
1232 {
1234 }
1235 ␌
1236 static void __attribute__ ((unused))
1237 insert_non_deterministic_control_node(list succs,
1238  control pred,
1239  control new_c,
1240  control old_c)
1241 {
1242  if(false) {
1243  /* Can we do without a extra-node? */
1244  }
1245  else{
1246  /* */
1248  control cnop = make_control(nop,
1249  CONS(CONTROL, pred, NIL),
1250  CONS(CONTROL, new_c, CONS(CONTROL, old_c, NIL)));
1251  pips_assert("succs points to old_c", CONTROL(CAR(succs))==old_c);
1252  pips_debug(2, "Allocate new control node %p as CONTINUE for test control node %p"
1253  " with two successors, a new one, %p, and an old one %p\n",
1254  cnop, pred, new_c, old_c);
1255  CONTROL_(CAR(succs)) = cnop;
1256  gen_list_patch(control_predecessors(old_c), pred, cnop);
1257  gen_list_patch(control_predecessors(new_c), pred, cnop);
1258  }
1259 }
1260 ␌
1261 /* new_c is not consistent on entry and might not be on exit because it is
1262  called from within a loop */
1264 {
1265  pips_assert("old_c is already a successor of pred",
1266  gen_in_list_p(old_c, control_successors(pred)));
1267  pips_assert("new_c is not yet a successor of pred",
1268  !gen_in_list_p(new_c, control_successors(pred)));
1269 
1270  if(control_test_p(pred)) {
1271  int r = 0;
1272  bool insert_p = false; /* A meaningless control node must be inserted? */
1273 
1274  if((r=gen_position(old_c, control_successors(pred)))==0) {
1275  pips_internal_error("old_c %p must be a successor of pred %p", old_c, pred);
1276  }
1277  else if(r%2==1) {
1278  /* true successor */
1279  insert_p = (gen_length(control_successors(pred))%2==1);
1280  }
1281  else /* r%2==0 */ {
1282  insert_p = (gen_length(control_successors(pred))%2==0);
1283  }
1284  if(insert_p) {
1286 
1288  CONS(CONTROL, mlc, NIL));
1289  }
1291  CONS(CONTROL, new_c, NIL));
1292 
1293  ifdebug(8) {
1294  pips_debug(8, "%s meaningless control node added. New successor list of pred %p:\n",
1295  insert_p? "A" : "No", pred);
1297  }
1298  }
1299  else {
1300  /* No problem this kind of node may have several successors */
1301  control_successors(pred) = CONS(CONTROL, new_c, control_successors(pred));
1302  }
1303 }
1304 
1306 {
1307  /* These asserts might become wrong when intermediate CONTINUE nodes are
1308  used to carry many true and/or false successors */
1309  pips_assert("old_c is already a predecessor of succ",
1310  gen_in_list_p(old_c, control_predecessors(succ)));
1311  pips_assert("new_c is not yet a predecessor of succ",
1312  !gen_in_list_p(new_c, control_predecessors(succ)));
1313 
1314  control_predecessors(succ) = CONS(CONTROL, new_c, control_predecessors(succ));
1315 }
1316 ␌
1317 /* Called from within a loop where neither t nor new_s are consistent */
1318 static void add_test_successor(control t, control new_s, bool is_true_successor)
1319 {
1320  bool slot_found = false;
1321  size_t rank = 0;
1322  size_t pos = 0;
1323 
1324  pips_debug(8, "Begin with t=%p, new_s=%p and is_true_successor=%d",
1325  t, new_s, is_true_successor);
1326 
1327  pips_assert("t is a control with a test", control_test_p(t));
1328  pips_assert("is_true_successor is 0 or 1",
1329  is_true_successor==0 || is_true_successor==1);
1330 
1331  MAPL(s_c, {
1332  control s = CONTROL(CAR(s_c));
1333 
1334  rank = 1 - rank;
1335  pos++;
1336 
1337  if(rank==(size_t) is_true_successor && meaningless_control_p(s)) {
1338  pips_debug(2, "Free meaningless control node %p\n", s);
1340  CONTROL_(CAR(s_c)) = new_s;
1341  slot_found = true;
1342  break;
1343  }
1344  } , control_successors(t));
1345 
1346  if(!slot_found) {
1347  if( ((bool)gen_length(control_successors(t))%2) == is_true_successor) {
1348  /* Allocate a meaningless control */
1350 
1352  CONS(CONTROL,
1353  mlc,
1354  CONS(CONTROL, new_s, NIL)));
1355  pos += 2;
1356  }
1357  else {
1359  CONS(CONTROL, new_s, NIL));
1360  pos++;
1361  }
1362  }
1363 
1364  pips_debug(8, "End with slot_found=%s, rank=%zd and pos=%zd\n",
1365  bool_to_string(slot_found), rank, pos);
1366 
1367  pips_assert("The position is consistent with is_true_successor",
1368  (bool)pos%2 == is_true_successor);
1369 
1370  /* t may not be consistent because of the caller
1371  ifdebug(1) {
1372  pips_assert("t is consistent", check_control_statement(t));
1373  }
1374  */
1375 }
1376 ␌
1377 /* Make new_c a successor of new_pred, the same way c is a successor of
1378  pred. new_c is not consistent on entry: it points towards pred but is
1379  not a successor of pred. Also, this function is called from within a
1380  loop and new_c is onsistent only on loop exit.*/
1381 static void update_successor_in_copy(control new_pred,
1382  control pred,
1383  control c,
1384  control new_c)
1385 {
1386  ifdebug(3) {
1387  pips_debug(3, "Begin for new_pred=%p, pred=%p, c=%p, new_c=%p\n",
1388  new_pred, pred, c, new_c);
1389  print_control_node_b(new_pred);
1390  print_control_node_b(pred);
1392  /* new_c is not consistent, this is why this function is called! */
1394 
1395  pips_assert("pred is predecessor of new_c",
1396  gen_in_list_p(pred, control_predecessors(new_c)));
1397  }
1398 
1399  /* c and new_c have pred as predecessor */
1400  /* new_c must have new_pred as predecessor instead */
1401  pips_assert("c is a successor of pred",
1402  gen_in_list_p(c, control_successors(pred)));
1403  pips_assert("pred is a predecessor of new_c",
1404  gen_in_list_p(pred, control_predecessors(new_c)));
1405  pips_assert("pred and new_pred share the same statement",
1406  control_statement(pred) ==control_statement(new_pred));
1407  pips_assert("c and new_c share the same statement",
1408  control_statement(c) ==control_statement(new_c));
1409 
1410  if(control_test_p(pred)) {
1411  int pos = gen_position(c, control_successors(pred));
1412  bool is_true_succ = pos%2;
1413 
1414  pips_assert("The position is not zero since c is among the sucessors of pred"
1415  " (see previous assert)", pos!=0);
1416 
1417  pips_debug(4, "position=%d, is_true_succ=%d \n", pos, is_true_succ);
1418  pips_assert("pred is a test, new_pred is a test too", control_test_p(new_pred));
1419  pips_assert("pred is still a predecessor of new_c",
1420  gen_in_list_p(pred, control_predecessors(new_c)));
1421  add_test_successor(new_pred, new_c, is_true_succ);
1422  }
1423  else {
1424  control_successors(new_pred) = gen_nconc(control_successors(new_pred),
1425  CONS(CONTROL, new_c, NIL));
1426  }
1427 
1428  gen_list_patch(control_predecessors(new_c), pred, new_pred);
1429 
1430  ifdebug(3) {
1431  pips_debug(3, "End with new_pred=%p\n", new_pred);
1432  print_control_node_b(new_pred);
1433  pips_debug(3, "and pred=%p\n", pred);
1434  print_control_node_b(pred);
1435  pips_debug(3, "and c=%p\n", c);
1437  pips_debug(3, "and new_c=%p\n", new_c);
1439  }
1440 }
1441 ␌
1442 /* The nodes in scc partition but root must be removed. Replicated nodes
1443  must be added to build all input and output paths to and from root
1444  using control nodes in partition.
1445 
1446  This is the key change to Bourdoncle's algorithm. When moving back up
1447  in the recursion, the CFG is altered to obtain pure cycles with only
1448  one entry and one exit nodes.
1449 
1450  The cycle head must be a non-deterministic control node. Hence it
1451  cannot be a test since tests stay deterministics (thanks to Pierre's
1452  data structure). Another node in the partition could be chosen as new
1453  cycle head, but the partition may only contain tests with side
1454  effects. Instead we choose to insert a non-deterministic node in front
1455  in case it is needed. The initial CFG is less disturbed.
1456 
1457  Well, I changed my mind again because inserting new control nodes does
1458  not make it easy to stay compatible with Bourdoncle's algorithm and
1459  previous assumptions made about the cycle head. Instead, tests can
1460  become non-deterministic. The number of successors must be even or odd. Odd
1461  successors are true successors. Even successors are false successors.
1462 
1463  */
1464 
1465 static unstructured scc_to_dag(control root, list partition, hash_table ancestor_map)
1466 {
1468  control new_root = control_undefined;
1469  hash_table replicated_input_controls = hash_table_make(hash_pointer, 0);
1470  hash_table replicated_output_controls = hash_table_make(hash_pointer, 0);
1471  bool stable_graph_p = false;
1472  int number_in = 0;
1473  int number_out = 0;
1474  int iteration = 0;
1475  char msg[200];
1476 
1477  ifdebug(3) {
1478  pips_debug(3, "Begin for vertex %p and partition:\n", root);
1479  print_control_nodes(partition);
1480  }
1481 
1482  while(!stable_graph_p) {
1483  int number = 0;
1484  list c_c = list_undefined;
1485 
1486  stable_graph_p = true;
1487  iteration++;
1488 
1489  /* Look for new input nodes in partition */
1490  for(c_c = partition; !ENDP(c_c); POP(c_c)) {
1491  control c = CONTROL(CAR(c_c));
1492 
1493  if(c!=root && hash_get(replicated_input_controls, c)==HASH_UNDEFINED_VALUE) {
1494  if(entry_control_p(c, partition)) {
1495  control new_c1 = control_undefined;
1496 
1497  /* c cannot have been replicated earlier or it would not be in partition */
1498  pips_assert("c has not yet been input replicated",
1499  hash_get(replicated_input_controls, c)==HASH_UNDEFINED_VALUE);
1500  new_c1 = make_control(control_statement(c),
1503  add_child_parent_pair(ancestor_map, new_c1, c);
1504  copy_dfn(new_c1, c);
1505 
1506  pips_debug(4, "Allocate new input control node new_c1=%p"
1507  " as a copy of node %p with depth %td\n",
1508  new_c1, c, get_dfn(c));
1509 
1510  /* Keep only predecessors out of partition for new_c1. */
1511  gen_list_and_not(&control_predecessors(new_c1), partition);
1512  /* Make sure that new_c1 is a successor of its
1513  predecessors... Oupss, there is a problem with test to encode
1514  the fact that is is a true or a false successor? */
1515  MAP(CONTROL, pred_c1, {
1516  gen_list_patch(control_successors(pred_c1), c, new_c1);
1517  },control_predecessors(new_c1) );
1518 
1519 
1520  /* Keep only c's predecessors in partition.
1521 
1522  That's probably wrong and damaging because we need them for
1523  paths crossing directly the SCC and useless because these
1524  nodes will be destroyed in parent graph. */
1525  gen_list_and(&control_predecessors(c), partition);
1526  /* Update the successor of its predecessors */
1527 
1528  /* Update successors of new_c1 which have already been replicated */
1529  MAPL(succ_new_c1_c,
1530  {
1531  control succ_new_c1 = CONTROL(CAR(succ_new_c1_c));
1532  control new_succ_new_c1 = hash_get(replicated_input_controls, succ_new_c1);
1533  if(new_succ_new_c1!=HASH_UNDEFINED_VALUE) {
1534  CONTROL_(CAR(succ_new_c1_c)) = new_succ_new_c1;
1535  /* Add new_c1 as predecessor of new_succ */
1536  control_predecessors(new_succ_new_c1) =
1537  gen_once(new_c1, control_predecessors(new_succ_new_c1));
1538  }
1539  else {
1540  update_predecessors_of_successor(succ_new_c1, new_c1, c);
1541  }
1542  }
1543  , control_successors(new_c1));
1544 
1545  stable_graph_p = false;
1546  number++;
1547  number_in++;
1548  hash_put(replicated_input_controls, c, new_c1);
1549  }
1550  }
1551  }
1552 
1553  ifdebug(9) {
1554  if(number>0) {
1555  pips_debug(4, "Embedding graph after replication"
1556  " of %d input control node(s) at iteration %d:\n",
1557  number, iteration);
1558  sprintf(msg, "Embedding graph after replication"
1559  " of %d input control node(s) at iteration %d:",
1560  number, iteration);
1561  print_embedding_graph(root, msg);
1562  pips_debug(4, "End of embedding graph after replication"
1563  " of input control nodes\n");
1564  }
1565  else {
1566  pips_debug(4, "No new input control node at iteration %d\n",
1567  iteration);
1568  }
1569  }
1570 
1571  /* Look for new output nodes */
1572  number = 0;
1573  for(c_c = partition; !ENDP(c_c); POP(c_c)) {
1574  control c = CONTROL(CAR(c_c));
1575 
1576  if(c!=root && hash_get(replicated_output_controls, c)==HASH_UNDEFINED_VALUE) {
1577  if(exit_control_p(c, partition)) {
1578  control new_c2 = control_undefined;
1579  list pred_new_c2_c = list_undefined;
1580 
1581  /* c cannot have been replicated earlier or it would not be in partition */
1582  pips_assert("c has not yet been output replicated",
1583  hash_get(replicated_output_controls, c)==HASH_UNDEFINED_VALUE);
1584  new_c2 = make_control(control_statement(c),
1587  add_child_parent_pair(ancestor_map, new_c2, c);
1588  copy_dfn(new_c2, c);
1589 
1590  pips_debug(4, "Allocate new output control node new_c2=%p as a copy of node %p\n",
1591  new_c2, c);
1592 
1593  /* Keep only successors out of partition for new_c2. */
1594  /* A true branch successor may have to be replaced to preserve
1595  the position of the false branch successor. */
1596  /* gen_list_and_not(&control_successors(new_c2), partition); */
1598 
1599  /* Update reverse arcs */
1600  MAP(CONTROL, succ_c2, {
1601  gen_list_patch(control_predecessors(succ_c2), c, new_c2);
1602  }, control_successors(new_c2) );
1603 
1604  /* Keep only c's succcessors in partition. Wise or not? See above... */
1605  /* gen_list_and(&control_successors(c), partition); */
1607  /* Reverse arcs have already been correctly updated */
1608 
1609  /* Update predecessors of new_c2 which have already been replicated */
1610  for(pred_new_c2_c=control_predecessors(new_c2);
1611  !ENDP(pred_new_c2_c); POP(pred_new_c2_c)) {
1612  control pred_new_c2 = CONTROL(CAR(pred_new_c2_c));
1613  control new_pred_new_c2 = hash_get(replicated_output_controls, pred_new_c2);
1614  if(new_pred_new_c2!=HASH_UNDEFINED_VALUE) {
1615  /* gen_list_patch() has no effect because new_pred's
1616  successors have already been updated and c does not
1617  appear there. */
1618  /* gen_list_patch(control_successors(new_pred), c, new_c2); */
1619  update_successor_in_copy(new_pred_new_c2, pred_new_c2, c, new_c2);
1620  /*
1621  CONTROL(CAR(pred_new_c2_c)) = new_pred_new_c2;
1622  pips_assert("new_c2 is now consistent", check_control_statement(new_c2));
1623  */
1624  }
1625  else{
1626  /* Update reverse edges of predecessors... which may be
1627  tough if you end up with more than one true and/or more
1628  than one false successor. */
1629  update_successors_of_predecessor(pred_new_c2, new_c2, c);
1630  }
1631  }
1632  pips_assert("new_c2 is now consistent", check_control_statement(new_c2));
1633 
1634  stable_graph_p = false;
1635  number++;
1636  number_out++;
1637  hash_put(replicated_output_controls, c, new_c2);
1638  }
1639  }
1640  }
1641 
1642  ifdebug(9) {
1643  if(number>0) {
1644  pips_debug(3, "Embedding graph after replication"
1645  " of output control nodes at iteration %d:\n", iteration);
1646  sprintf(msg, "Embedding graph after replication"
1647  " of output control nodes at iteration %d:", iteration);
1648  print_embedding_graph(root, msg);
1649  pips_debug(3, "End of embedding graph after replication"
1650  " of output control nodes\n");
1651  }
1652  else {
1653  pips_debug(3, "No new output control node at iteration %d\n", iteration);
1654  }
1655  }
1656  }
1657 
1659 
1660  ifdebug(3) {
1661  if(number_out>0 || number_in>0) {
1662  pips_assert("At least two iterations", iteration>1);
1663  pips_debug(3, "Final embedding graph after replication of all input and output paths (%d iterations)\n", iteration);
1664  sprintf(msg, "Final embedding graph after replication of all input and output paths (%d iterations)", iteration);
1665  print_embedding_graph(root, msg);
1666  pips_debug(3, "End of final embedding graph after replication of all input and output paths\n");
1667  }
1668  else {
1669  pips_assert("Only one iteration", iteration==1);
1670  pips_debug(3, "No new output paths\n");
1671  }
1672  }
1673 
1674  /* Remove cycle partition but its head */
1675  /* We do not want to free these nodes because they are part of
1676  partition. We should not have duplicated partition earlier to make a
1677  new unstructured. We only need a copy of the head, root. This should
1678  be dealt with after scc_to_dag is called and not before.
1679 
1680  MAP(CONTROL, c,
1681  {
1682  if(c!=root) {
1683  shallow_free_control(c);
1684  }
1685  }
1686  , partition); */
1687  /* Unlink partition from its head.
1688  We could keep an arc from root to root... */
1689  /* This unlinking would better be performed at the caller level, even
1690  though this would make the name scc_to_dag() imprecise. */
1691 
1692  /* replicate root and unlink the cycles defined by partition from the
1693  embedding graph */
1694  new_root = make_control(control_statement(root),
1697 
1698  add_child_parent_pair(ancestor_map, new_root, root);
1699  u = make_unstructured(new_root, new_root);
1700  gen_list_and_not(&control_predecessors(root), partition);
1702  gen_list_and(&control_predecessors(new_root), partition);
1703  intersect_successors_with_partition(new_root, partition);
1704  MAP(CONTROL, c,
1705  {
1706  gen_list_patch(control_successors(c), root, new_root);
1707  gen_list_patch(control_predecessors(c), root, new_root);
1708  }
1709  , partition);
1710  /* new_root does not belong to partition and must be processed too. */
1711  gen_list_patch(control_successors(new_root), root, new_root);
1712  gen_list_patch(control_predecessors(new_root), root, new_root);
1713 
1714  ifdebug(3) {
1715  list l_root = node_to_linked_nodes(root);
1716  list l_new_root = node_to_linked_nodes(new_root);
1717 
1718  pips_debug(3, "Nodes linked to root=%p\n", root);
1719  print_control_node_b(root);
1720  print_control_nodes(l_root);
1721  pips_debug(3, "Nodes linked to new_root=%p\n", new_root);
1722  print_control_node_b(new_root);
1723  print_control_nodes(l_new_root);
1724 
1725  gen_list_and(&l_root, l_new_root);
1726 
1727  if(!ENDP(l_root)) {
1728  fprintf(stderr,
1729  "Bug: l_root and l_new_root share at least one node and must be equal\n");
1730  fprintf(stderr, "Common control nodes:\n");
1731  print_control_nodes(l_root);
1732  pips_assert("l_root and l_new_root have an empty intersection",
1733  ENDP(l_root));
1734  }
1735 
1736  gen_free_list(l_root);
1737  gen_free_list(l_new_root);
1738  }
1739 
1741 
1742  clean_up_embedding_graph(new_root);
1743 
1744  ifdebug(3) {
1745  list l_root = node_to_linked_nodes(root);
1746  list l_new_root = node_to_linked_nodes(new_root);
1747 
1748  pips_debug(3, "Final embedding graph after replication and cycle removal\n");
1749  sprintf(msg, "Final embedding graph after replication and cycle removal");
1750  print_embedding_graph(root, msg);
1751  pips_debug(3, "End of final embedding graph after replication and cycle removal\n");
1752  pips_debug(3, "Final cycle graph\n");
1753  sprintf(msg, "Final cycle graph");
1754  print_embedding_graph(new_root, msg);
1755  pips_debug(3, "End of final cycle graph\n");
1756 
1757  /* All nodes are linked to an ancestor node or are an ancestor or are
1758  meaningless */
1759  pips_assert("All nodes in l_root are registered",
1760  registered_controls_p(ancestor_map, l_root));
1761  pips_assert("All nodes in l_new_root are registered",
1762  registered_controls_p(ancestor_map, l_new_root));
1763  pips_assert("Children and ancestors have an empty intersection",
1764  ancestor_map_consistent_p(ancestor_map));
1765 
1766  gen_free_list(l_root);
1767  gen_free_list(l_new_root);
1768  }
1769 
1770  ifdebug(3) {
1771  pips_debug(3, "End with %d replicated control nodes:\n\n", number_in+number_out);
1772  if(number_in>0){
1773  print_control_to_control_mapping("replicated_input_controls",
1774  replicated_input_controls);
1775  }
1776 
1777  if(number_out>0) {
1778  print_control_to_control_mapping("replicated_output_controls",
1779  replicated_output_controls);
1780  }
1781 
1782  }
1783 
1784  hash_table_free(replicated_input_controls);
1785  hash_table_free(replicated_output_controls);
1786 
1787  ifdebug(3) {
1788  pips_debug(3, "End for vertex %p and partition:\n", root);
1789  print_control_nodes(partition);
1790  }
1791 
1792  return u;
1793 }
1794 ␌
1795 /*
1796  *
1797  * To deal with transformers computed in context, each call sites of a
1798  * cycle is attached a private copy of the cycle.
1799  *
1800  * This is not part of Bourdoncle's algorithm.
1801  *
1802  */
1803 static void replicate_cycles(unstructured u_main, hash_table scc_map, hash_table ancestor_map)
1804 {
1805  list scc_to_process = CONS(UNSTRUCTURED, u_main, NIL);
1806  list scc_to_process_next = NIL;
1807  list live_scc = NIL;
1808  int replication_number = 0;
1809  int deletion_number = 0;
1810 
1811  pips_debug(1, "Start with %d cycles for unstructured %p with entry %p\n",
1812  hash_table_entry_count(scc_map), u_main, unstructured_control(u_main));
1813 
1814  while(!ENDP(scc_to_process)) {
1815  MAP(UNSTRUCTURED, u, {
1816  list nl = NIL;
1817  control root = unstructured_control(u);
1818  FORWARD_CONTROL_MAP(c, {
1819  /* Only the head of the main unstructured can be a pointer to a
1820  cycle. The head of a scc cannot point to another scc as they
1821  would be the same by construction: all their cycles would be
1822  cut off by the head of each scc. */
1823  if((c!=root || c==unstructured_control(u_main))
1824  && !meaningless_control_p(c)
1825  && cycle_head_p((control)c, ancestor_map, scc_map)) {
1826  control a = control_to_ancestor(c, ancestor_map);
1827  unstructured scc_u = ancestor_cycle_head_to_scc((control)a, scc_map);
1828 
1829  unstructured scc_u_c = unstructured_shallow_copy(scc_u, ancestor_map);
1830  scc_to_process_next = CONS(UNSTRUCTURED, scc_u_c, scc_to_process_next);
1831  hash_put(scc_map, c, (void *) scc_u_c);
1832  replication_number++;
1833 
1834  ifdebug(1) {
1835  control e = unstructured_control(scc_u_c);
1836  pips_debug(1, "New scc %p with entry %p for node %p copy of ancestor node %p\n",
1837  scc_u_c, e, c, control_to_ancestor(c, ancestor_map));
1838  pips_assert("e is a proper cycle head", proper_cycle_head_p(e, scc_map));
1839  pips_assert("e is a cycle head", cycle_head_p(e, ancestor_map, scc_map));
1840  pips_assert("e is a not a direct cycle head",
1841  !direct_cycle_head_p(e, scc_map));
1842  }
1843 
1844  }
1845  }, root, nl);
1846  gen_free_list(nl);
1847  }, scc_to_process);
1848  live_scc = gen_nconc(live_scc, scc_to_process);
1849  scc_to_process = scc_to_process_next;
1850  scc_to_process_next = NIL;
1851  }
1852 
1853  /* Only live scc are useful */
1854  HASH_MAP(c, scc, {
1855  if(!gen_in_list_p(scc, live_scc)) {
1856  void * key = NULL;
1858  u_u = (unstructured) hash_delget(scc_map, (void *) c, (void **) &key);
1859  if(unstructured_undefined_p(u_u)) {
1860  pips_internal_error("No scc for control %p", c);
1861  }
1862  else {
1863  /* we really need a shallow_free_unstructured() */
1864  /* free_unstructured(u_u); */
1865  pips_debug(1, "scc %p unlinked from node %p\n", u_u, c);
1866  deletion_number++;
1867  }
1868  }
1869  }, scc_map);
1870 
1871  pips_assert("The number of scc's has increased",
1872  replication_number >= deletion_number);
1873 
1874  gen_free_list(live_scc);
1875 
1876  pips_debug(1, "End replication process with %d copies and %d deletions\n",
1877  replication_number, deletion_number);
1878 
1879  pips_debug(1, "Start with %d cycles\n", hash_table_entry_count(scc_map));
1880 }
1881 ␌
1882 /*
1883  MAIN ENTRY: BOURDONCLE_PARTITION
1884 
1885  Decomposition of control flow graph u into a non-deterministic DAG CFG
1886  *p_ndu and two mappings.
1887 
1888  Mapping scc_map maps nodes of u used to break cycles to the
1889  unstructured representing these cycles if cycles are not
1890  replicated. Else, scc_map maps nodes of ndu and of the scc's to their
1891  own copies of the relevant scc's.
1892 
1893  Mapping ancestor_map maps nodes used in DAG new_u or in unstructured
1894  refered to by scc_map to nodes in u. The initial control nodes in u are
1895  called "ancestors".
1896 
1897  The partition list returned should be compatible with a
1898  top-down partial order.
1899 
1900  */
1901 
1903  unstructured * p_ndu,
1904  hash_table *p_ancestor_map,
1905  hash_table * p_scc_map)
1906 {
1907  list partition = NIL;
1908  control root = control_undefined;
1909  /* DAG derived from u by elimination all cycles */
1911  /* mapping from nodes in the new unstructured to the node in the input unstructured */
1912  hash_table ancestor_map = hash_table_make(hash_pointer, 0);
1913  /* mapping from nodes in u, used as cycle breakers, to the corresponding
1914  scc represented as an unstructured */
1915  hash_table scc_map = hash_table_make(hash_pointer, 0);
1916 
1918 
1919  ifdebug(2) {
1920  pips_debug(2, "Begin with unstructured:\n");
1921  print_unstructured(u);
1922  davinci_print_non_deterministic_unstructured(u, "Initial unstructured", scc_map, ancestor_map);
1923  }
1924 
1925  make_vertex_stack();
1926  num = 0;
1928 
1929  new_u = unstructured_shallow_copy(u, ancestor_map);
1930 
1931  ifdebug(8) {
1932  pips_debug(3, "Copied unstructured new_u:\n");
1933  print_unstructured(new_u);
1934  davinci_print_non_deterministic_unstructured(u, "Copy of initial unstructured",
1935  scc_map, ancestor_map);
1936  }
1937 
1938 
1939  /* Initialize dfn to 0 */
1942 
1943  /* Start from the entry point */
1944  root = unstructured_control(new_u);
1945  (void) bourdoncle_visit(root, &partition, ancestor_map, scc_map);
1946 
1947  /* Should the partition be translated? */
1948 
1949  free_vertex_stack();
1951 
1952  /* Should the cycles be replicated to refine the analyses? If yes, replicate them. */
1953  if(get_bool_property("SEMANTICS_COMPUTE_TRANSFORMERS_IN_CONTEXT")) {
1954  replicate_cycles(new_u, scc_map, ancestor_map);
1955  }
1956 
1957  ifdebug(2) {
1958  int number_of_fix_points = 0;
1959 
1960  pips_debug(2, "End with partition:");
1961  print_control_nodes(partition);
1962 
1963  pips_debug(2, "End with scc_map:\n");
1964  HASH_MAP(h, uc, {
1965  number_of_fix_points++;
1966  fprintf(stderr, "head=%p with unstructured=%p\n", h, uc);
1967  } , scc_map);
1968  pips_debug(2, "End with %d fix points\n", number_of_fix_points);
1969 
1970  /* Control nodes associated to a fix point */
1971  pips_debug(2, "\nControl nodes associated to a fix point via a sub-unstructured:\n");
1972  HASH_MAP(c_n, c_o, {
1973  void * su;
1974  if((su=hash_get(scc_map, c_o))!=HASH_UNDEFINED_VALUE) {
1975  fprintf(stderr, "head=%p copy of %p with unstructured=%p\n", c_n, c_o, su);
1976  }
1977  }, ancestor_map);
1978 
1979  /* The hierarchy of fix points is/seems to be lost by scc_map... */
1980  pips_debug(2, "End with %d sets of cycles:\n", number_of_fix_points);
1981  number_of_fix_points = 0;
1982  HASH_MAP(h, uc, {
1983  char msg[256];
1984 
1985  number_of_fix_points++;
1986  fprintf(stderr, "Cycles associated to head=%p with unstructured=%p (fix point %d)\n",
1987  h, uc, number_of_fix_points);
1988  sprintf(msg, "Cycles associated to head=%p\\n with unstructured=%p (fix point %d)",
1989  h, uc, number_of_fix_points);
1991  davinci_print_non_deterministic_unstructured((unstructured) uc, msg, scc_map, ancestor_map);
1992  fprintf(stderr, "\n");
1993  } , scc_map);
1994 
1995  /* We also need the final embedding graph... which might be lost? Or
1996  hanging from the first node in partition, the entry point, and hence from new_u. */
1997  pips_debug(2, "Final embedding graph %p:\n", new_u);
1998  print_unstructured(new_u);
1999  davinci_print_non_deterministic_unstructured(new_u, "Final embedding graph", scc_map, ancestor_map);
2000 
2001  pips_debug(2, "End. \n");
2002  }
2003 
2004  *p_ndu = new_u;
2005  *p_ancestor_map = ancestor_map;
2006  *p_scc_map = scc_map;
2007 
2008  return partition;
2009 }
2010 ␌
2011 /* FUNCTIONS FOR BOURDONCLE_COMPONENT()
2012  */
2013 
2014 /* Check the existence of a path from b to e with all its element in partition */
2015 static bool partition_successor_p(control b, control e, list partition)
2016 {
2017  bool path_p = false;
2018  list preds = CONS(CONTROL, b, NIL);
2019  list succs = NIL;
2020  list not_seen = gen_copy_seq(partition);
2021  int length = 0;
2022 
2023  pips_debug(3, "Begin with b=%p abd e=%p\n", b, e);
2024 
2025  ifdebug(3) {
2026  pips_assert("b is in partition", gen_in_list_p(b, partition));
2027  pips_assert("e is in partition", gen_in_list_p(e, partition));
2028  /* You might be interested in the existence of a cycle from b to b */
2029  /* pips_assert("b is not e", b!=e); */
2030  }
2031 
2032  gen_remove(&not_seen, b);
2033 
2034  while(!ENDP(preds)){
2035  length++;
2036  pips_debug(3, "Iteration %d\n", length);
2037  MAP(CONTROL, pred, {
2038  pips_debug(3, "\tpred=%p\n", pred);
2039  MAP(CONTROL, succ, {
2040  pips_debug(3, "\t\tsucc=%p\n", succ);
2041  if(succ==e) {
2042  path_p = true;
2043  goto end;
2044  }
2045  else if(gen_in_list_p(succ, not_seen)) {
2046  gen_remove(&not_seen, succ);
2047  succs = CONS(CONTROL, succ, succs);
2048  }
2049  } , control_successors(pred));
2050  } , preds);
2051  gen_free_list(preds);
2052  preds = succs;
2053  succs = NIL;
2054  }
2055 
2056  end:
2057  gen_free_list(not_seen);
2058  gen_free_list(preds);
2059  gen_free_list(succs);
2060 
2061  pips_debug(3, "End: path_p=%s\n", bool_to_string(path_p));
2062 
2063  return path_p;
2064 }
2065 ␌
2066 static void update_partition(control root,
2067  list partition,
2068  hash_table ancestor_map)
2069 {
2070  /* Controls in partition may have been copied and may not appear anymore
2071  in the graph embedding root. The input graph is modified to
2072  separate clean cycles with one entry-exit node, but Bourdoncle's
2073  algorithm is not aware of this. */
2074  list embedding_nodes = node_to_linked_nodes(root);
2075  int changes = 0;
2076  size_t eliminations = 0;
2077  list eliminated = NIL;
2078 
2079  pips_assert("The head is in the partition", gen_in_list_p(root, partition));
2080 
2081  ifdebug(2) {
2082  pips_debug(2, "Begin for partition:\n");
2083  print_control_nodes(partition);
2084  }
2085 
2086  MAPL(c_c,
2087  {
2088  control c = CONTROL(CAR(c_c));
2089  pips_assert("c is not a meaningless control node",
2090  !meaningless_control_p(c));
2091 
2092  if(!gen_in_list_p(c, embedding_nodes)) {
2093  /* Find a replacement for c, and hope to find only one! */
2094  control a = control_to_ancestor(c, ancestor_map);
2095  list replacement_list = NIL;
2096 
2097  changes++;
2098 
2099  MAP(CONTROL, c_new,
2100  {
2101  if(!meaningless_control_p(c_new)) {
2102  control a_new = control_to_ancestor(c_new, ancestor_map);
2103  if(a==a_new) {
2104  replacement_list = CONS(CONTROL, c_new, replacement_list);
2105  }
2106  }
2107 
2108  }
2109  , embedding_nodes);
2110 
2111  switch(gen_length(replacement_list)) {
2112  case 0:
2113  /* pips_internal_error("No replacement for node %p", c); */
2114  /* This node must be eliminated since it is no longer part of the
2115  partition, probably because it has disappeared in an inner
2116  cycle. */
2117  eliminated = CONS(CONTROL, CONTROL(CAR(c_c)), eliminated);
2118  break;
2119  case 1:
2120  CONTROL_(CAR(c_c)) = CONTROL(CAR(replacement_list));
2121  gen_free_list(replacement_list);
2122  break;
2123  default:
2124  /* Many possible replacements... Keep them all. */
2125  CONTROL_(CAR(c_c)) = CONTROL(CAR(replacement_list));
2126  replacement_list = gen_nconc(replacement_list, CDR(c_c));
2127  CDR(c_c) = CDR(replacement_list);
2128  CDR(replacement_list) = NIL;
2129  gen_free_list(replacement_list);
2130 
2131  /*
2132  fprintf(stderr,
2133  "Too many replacement nodes (%d) in embedding nodes for node %p\n",
2134  gen_length(replacement_list), c);
2135  print_control_node_b(c);
2136  print_control_nodes(replacement_list);
2137  pips_internal_error("Too many replacement nodes");
2138  */
2139  break;
2140  }
2141 
2142  }
2143  }
2144  , partition);
2145 
2146  ifdebug(2) {
2147  if(changes==0) {
2148  pips_debug(2, "No renaming\n");
2149  }
2150  else{
2151  pips_debug(2, "After %d renamings, new partition:\n", changes);
2152  print_control_nodes(partition);
2153  }
2154  }
2155 
2156  eliminations = gen_length(eliminated);
2157 
2158  ifdebug(2) {
2159  if(eliminations>0) {
2160  pips_debug(2, "Untranslatable control(s) to be eliminated from partition:\n");
2161  print_control_nodes(eliminated);
2162  }
2163  }
2164 
2165  /* Some nodes may no longer be part of the partition because they only
2166  belonged to an inner cycle which has already been eliminated. */
2167 
2168  MAP(CONTROL, c, {
2169  if(c==root) {
2170  pips_assert("There is a path from root to root in partition",
2171  partition_successor_p(c, root, partition));
2172  }
2173  else {
2174  if(!partition_successor_p(c, root, partition)){
2175  eliminated = gen_once(c, eliminated);
2176  }
2177  }
2178  } , partition);
2179 
2180  ifdebug(2) {
2181  if(eliminations < gen_length(eliminated)) {
2182  /* Unreachable controls have been found */
2183  pips_debug(2, "Untranslatable and unreachable control(s) to be eliminated from partition:\n");
2184  print_control_nodes(eliminated);
2185  }
2186  }
2187 
2188  /* It does not make sense to use &partition but... */
2189  pips_assert("root is the fist element in partition",
2190  CONTROL(CAR(partition))==root);
2191  pips_assert("root is not eliminated",
2192  !gen_in_list_p(root, eliminated));
2193  gen_list_and_not(&partition, eliminated);
2194 
2195  gen_free_list(eliminated);
2196 
2197  ifdebug(2) {
2198  if(eliminations==0 && changes==0) {
2199  pips_debug(2, "End with same partition (no renaming, no elimination)\n");
2200  }
2201  else if(eliminations==0){
2202  pips_debug(2, "End with renamed partition (%d renamings, no elimination)\n",
2203  changes);
2204  }
2205  else if(changes==0){
2206  pips_debug(2, "End with reduced partition (no renamings, %zd eliminations)\n",
2207  eliminations);
2208  }
2209  else{
2210  pips_debug(2, "End with new partition (%d renamings, %zd eliminations):\n",
2211  changes, eliminations);
2212  print_control_nodes(partition);
2213  }
2214  }
2215 }
2216 
2217 ␌
2219  hash_table ancestor_map,
2220  hash_table scc_map)
2221 {
2222  list partition = NIL;
2223  list b_partition = list_undefined; /* bourdoncle partition */
2225  control vertex_ancestor = control_undefined;
2226 
2227  ifdebug(2) {
2228  pips_debug(2, "Begin for node: \n");
2230  }
2231 
2232  MAP(CONTROL, c,
2233  {
2234  if(get_dfn(c)==0) {
2235  (void) bourdoncle_visit(c, &partition, ancestor_map, scc_map);
2236  }
2237  }
2239 
2240  partition = CONS(CONTROL, vertex, partition);
2241 
2242  /* Build sub-unstructured associated to vertex and partition */
2243  /* Re-use copying performed in scc_to_dag() instead
2244  u = partition_to_unstructured(vertex, partition);
2245  vertex_ancestor = control_to_ancestor(vertex, ancestor_map);
2246  hash_put(scc_map, vertex_ancestor, u);
2247  */
2248 
2249  /* The partition may have to be refreshed because of the previous
2250  recursive descent and its graph restructuring action. It is assumed
2251  that vertex is still good because heads are not: however vertex is
2252  not an ancestor because the input unstructured is copied right away
2253  by bourdoncle_partition().
2254 
2255  It might be better to recompute the smallest scc including
2256  vertex... */
2257  b_partition = gen_copy_seq(partition);
2258 
2259  update_partition(vertex, partition, ancestor_map);
2260 
2261  /* Update parent unstructured containing vertex and partition, remove
2262  partition and return a new unstructured with the partition inside,
2263  except for the initial vertex node which is left in the embedding
2264  graph */
2265  u = scc_to_dag(vertex, partition, ancestor_map);
2266 
2267  /* Build sub-unstructured associated to vertex and partition */
2268  /* u = unlink_partition_and build_unstructured(vertex, partition); */
2269  vertex_ancestor = control_to_ancestor(vertex, ancestor_map);
2270  hash_put(scc_map, vertex_ancestor, u);
2271 
2272  ifdebug(2) {
2273  pips_debug(2, "End with internal partition: ");
2274  print_control_nodes(partition);
2275  pips_debug(2, "End with Bourdoncle partition: ");
2276  print_control_nodes(b_partition);
2277  pips_debug(2, "End with new nodes:\n");
2278  /* Do not go down into nested unstructured */
2281  pips_debug(2, "With entry nodes\n");
2283  pips_debug(2, "And exit node\n");
2285  pips_debug(2, "End.\n");
2286  }
2287 
2288  gen_free_list(partition);
2289  return b_partition;
2290 }
2291 
2292 ␌
2294  list * ppartition,
2295  hash_table ancestor_map,
2296  hash_table scc_map)
2297 {
2298  _int min = 0;
2299  _int head = 0;
2300  bool loop = false;
2301 
2302  vertex_push(vertex);
2303  num = num+1;
2304  head = num;
2305  update_dfn(vertex, num);
2306 
2307  MAP(CONTROL, succ,
2308  {
2309  if(get_dfn(succ)==0) {
2310  min = bourdoncle_visit(succ, ppartition, ancestor_map, scc_map);
2311  }
2312  else {
2313  min = get_dfn(succ);
2314  }
2315  if(min<=head) {
2316  head = min;
2317  loop = true;
2318  }
2319  }
2321 
2322  if (head==get_dfn(vertex)) {
2323  control e = vertex_pop();
2324 
2325  update_dfn(vertex, LONG_MAX);
2326 
2327  if(loop) {
2328  while(e!=vertex) {
2329  update_dfn(e, 0);
2330  e = vertex_pop();
2331  }
2332  *ppartition = gen_nconc(bourdoncle_component(vertex, ancestor_map, scc_map),
2333  *ppartition);
2334  }
2335  else {
2336  *ppartition = CONS(CONTROL, vertex, *ppartition);
2337  }
2338 
2339  }
2340 
2341  return head;
2342 }
2343 ␌
2344 /*
2345  * OBSERVERS TO USE THE DATASTRUCTURES BUILT BY BOURDONCLE_PARTITION()
2346  *
2347  *
2348  */
2349 
2350 /* scc_map maps either the ancestor node to its scc if the transformers
2351  are computed without context, or the call site node to its scc if
2352  cycles are replicated to compute transformers within their context. */
2353 
2354 /* In spite of the name, this function can be call with ANY control,
2355  ancestor or not. An ancestor or a call site are mapped to a defined value. */
2357 {
2359 
2360  if((scc_u = (unstructured) hash_get(scc_map, (void *) a))
2362  scc_u = unstructured_undefined;
2363 
2364  return scc_u;
2365 }
2366 
2367 /* Retrieve a scc_u from its head */
2369 {
2371 
2372  HASH_MAP(k_c, v_scc_u, {
2373  unstructured scc_u = (unstructured) v_scc_u;
2374 
2375  if(h==unstructured_control(scc_u)) {
2376  r_scc_u = scc_u;
2377  break;
2378  }
2379  }, scc_map);
2380 
2381  return r_scc_u;
2382 }
2383 
2384 /* This node is a cycle call site. */
2385 bool cycle_head_p(control c, hash_table ancestor_map, hash_table scc_map)
2386 {
2387  /* This is correct whether the actual scc associated to c is scc_u or
2388  not. If a copy of a control is associated to a scc, its ancestors and
2389  all the others copies also are associated to a scc. */
2390  bool is_cycle = false;
2391  control a = control_to_ancestor(c, ancestor_map);
2392  unstructured scc_u = ancestor_cycle_head_to_scc(a, scc_map);
2393 
2394  if(unstructured_undefined_p(scc_u)) {
2395  /* we may be in the context sensitive transformer mode */
2396  scc_u = ancestor_cycle_head_to_scc(c, scc_map);
2397  }
2398 
2399  is_cycle = !unstructured_undefined_p(scc_u);
2400 
2401  return is_cycle;
2402 }
2403 
2404 /* This node is really the head of a cycle (red on daVinci pictures). */
2406 {
2407  bool is_proper_cycle_head_p = false;
2408 
2409  HASH_MAP(n, u,
2410  {
2411  unstructured scc_u = (unstructured) u;
2412 
2413  if((control)c==unstructured_control(scc_u)) {
2414  is_proper_cycle_head_p = true;
2415  break;
2416  }
2417  }, scc_map);
2418  return is_proper_cycle_head_p;
2419 }
2420 
2421 /* This node is directly associated to a specific cycle. */
2423 {
2424  bool is_cycle = false;
2425  unstructured scc_u = ancestor_cycle_head_to_scc(c, scc_map);
2426 
2427  is_cycle = !unstructured_undefined_p(scc_u);
2428 
2429  return is_cycle;
2430 }
2431 
2432 /* The ancestor of this node is associated to a specific cycle. */
2434 {
2435  /* either we have a direct connection, or we need to go thru the
2436  ancestor node */
2437 
2438  unstructured scc_u = ancestor_cycle_head_to_scc(c, scc_map);
2439 
2440  if(unstructured_undefined_p(scc_u)) {
2441  control a = control_to_ancestor(c, ancestor_map);
2442  scc_u = ancestor_cycle_head_to_scc(a, scc_map);
2443  }
2444  return scc_u;
2445 }
2446 ␌
2447 /* useful for non-deterministic control flow graph only */
2448 
2449 /* There exists at least one real successor of the requested kind and only
2450  meaningless successors of the other kind. */
2452 {
2453  bool one_kind_only_p = false;
2454  bool is_true_successor_p = true;
2455  bool real_successor_found_p = false;
2456  list succs = control_successors(c);
2457 
2458  pips_assert("c is a test", control_test_p(c));
2459 
2460  MAP(CONTROL, s, {
2461  if(is_true_successor_p && true_p) {
2462  real_successor_found_p |= !meaningless_control_p(s);
2463  }
2464  else {
2465  one_kind_only_p &= meaningless_control_p(s);
2466  }
2467  }, succs);
2468 
2469  one_kind_only_p &= real_successor_found_p;
2470 
2471  return one_kind_only_p;
2472 }
2473 
2475 {
2476  bool true_only_p = false;
2477  true_only_p = one_successor_kind_only_p(c, true);
2478  return true_only_p;
2479 }
2480 
2482 {
2483  bool true_only_p = false;
2484  true_only_p = one_successor_kind_only_p(c, true);
2485  return true_only_p;
2486 }
2487 ␌
2489 {
2490  /* The statements were not replicated and are still pointed to by the
2491  initial unstructured. */
2493 }
2494 
2496 {
2497  /* Suppress references to statements, but do not go down recursively
2498  into nested unstructured. */
2502 
2503  free_unstructured(u);
2504 }
2505 
2507  hash_table ancestor_map,
2508  hash_table scc_map)
2509 {
2510  /* Do not free the statements pointed by the nodes in ndu or in the scc */
2512  HASH_MAP(k, v, {
2514  }, scc_map);
2515  hash_table_free(ancestor_map);
2516  hash_table_free(scc_map);
2517 }
unstructured make_unstructured(control a1, control a2)
Definition: ri.c:2778
void free_control(control p)
Definition: ri.c:490
void free_unstructured(unstructured p)
Definition: ri.c:2745
bool control_consistent_p(control p)
Definition: ri.c:496
control make_control(statement a1, list a2, list a3)
Definition: ri.c:523
static void reset_dfn(control c)
Definition: bourdoncle.c:106
static control make_meaningless_control(list preds, list succs)
With non-deterministic graph, the outcome of a test may be known in advance.
Definition: bourdoncle.c:146
static int clean_up_control_test(control c)
Take care of two problems: meaningless control nodes may end up shared by effective nodes and they ma...
Definition: bourdoncle.c:655
static control control_to_replicate(control old_c)
Definition: bourdoncle.c:1002
static hash_table dfn
bourdoncle.c
Definition: bourdoncle.c:104
static _int get_dfn(control c)
Definition: bourdoncle.c:121
bool meaningless_control_p(control c)
Is exported to exploit non-deterministic control flow graphs.
Definition: bourdoncle.c:154
static int control_cons_compare(list l1, list l2)
Definition: bourdoncle.c:402
void intersect_successors_with_partition_complement(control c, list partition)
Definition: bourdoncle.c:1230
static void add_test_successor(control t, control new_s, bool is_true_successor)
Called from within a loop where neither t nor new_s are consistent.
Definition: bourdoncle.c:1318
static void print_and_check_control_node(control c, bool check_p)
Definition: bourdoncle.c:276
static list node_to_linked_nodes(control c)
Allocate a list of control nodes transitively linked to control c.
Definition: bourdoncle.c:626
static void replicate_cycles(unstructured u_main, hash_table scc_map, hash_table ancestor_map)
Definition: bourdoncle.c:1803
bool control_test_p(control c)
Check that a node is used as a test in a CFG.
Definition: bourdoncle.c:175
static bool ancestor_map_consistent_p(hash_table ancestor_map)
No control can be an ancestor and a child.
Definition: bourdoncle.c:905
static bool exit_control_p(control c, list partition)
Definition: bourdoncle.c:1172
unstructured partition_to_unstructured(control vertex, list partition)
Build a new unstructured (CFG) for partition.
Definition: bourdoncle.c:1053
static void print_control_node_without_check(control)
Code partly replicated from semantics/unstructured.c to be able to taylor it to the exact needs.
Definition: bourdoncle.c:304
static void __attribute__((unused))
Definition: bourdoncle.c:1236
control control_to_ancestor(control vertex, hash_table ancestor_map)
If vertex is an ancestor control node from the input control graph, return vertex,...
Definition: bourdoncle.c:854
void bourdoncle_free(unstructured ndu, hash_table ancestor_map, hash_table scc_map)
Definition: bourdoncle.c:2506
static void update_dfn(control c, _int d)
Definition: bourdoncle.c:131
static void update_predecessors_of_successor(control succ, control new_c, control old_c)
Definition: bourdoncle.c:1305
list bourdoncle_partition(unstructured u, unstructured *p_ndu, hash_table *p_ancestor_map, hash_table *p_scc_map)
Definition: bourdoncle.c:1902
static void print_embedding_graph(control c, string msg)
Definition: bourdoncle.c:540
static void bourdoncle_unstructured_free(unstructured u)
Definition: bourdoncle.c:2495
static void update_partition(control root, list partition, hash_table ancestor_map)
Definition: bourdoncle.c:2066
static bool registered_controls_p(hash_table ancestor_map, list l)
Definition: bourdoncle.c:882
static void davinci_print_control_nodes(list l, string msg)
Definition: bourdoncle.c:433
unstructured unstructured_shallow_copy(unstructured u, hash_table ancestor_map)
Definition: bourdoncle.c:1009
static int num
Definition: bourdoncle.c:137
static int davinci_count
This counter is pre-incremented each time a new graph is stored to generate a new file name.
Definition: bourdoncle.c:425
bool direct_cycle_head_p(control c, hash_table scc_map)
This node is directly associated to a specific cycle.
Definition: bourdoncle.c:2422
static void davinci_print_non_deterministic_unstructured(unstructured u, string msg, hash_table scc_map, hash_table ancestor_map)
Definition: bourdoncle.c:473
static void print_control_nodes_without_check(list l)
Was moved into ri-util/control, minus the check_control_statement()
Definition: bourdoncle.c:322
bool proper_cycle_head_p(control c, hash_table scc_map)
This node is really the head of a cycle (red on daVinci pictures).
Definition: bourdoncle.c:2405
bool cycle_head_p(control c, hash_table ancestor_map, hash_table scc_map)
This node is a cycle call site.
Definition: bourdoncle.c:2385
unstructured ancestor_cycle_head_to_scc(control a, hash_table scc_map)
scc_map maps either the ancestor node to its scc if the transformers are computed without context,...
Definition: bourdoncle.c:2356
static unstructured scc_to_dag(control root, list partition, hash_table ancestor_map)
The nodes in scc partition but root must be removed.
Definition: bourdoncle.c:1465
static bool entry_control_p(control c, list partition)
Definition: bourdoncle.c:1167
static void davinci_print_control_node(control c, FILE *f, bool entry_p, bool exit_p, bool fix_point_p)
Output CFG in daVinci format.
Definition: bourdoncle.c:344
static bool ancestor_control_p(hash_table ancestor_map, control c)
Functions to deal with the ancestor_map.
Definition: bourdoncle.c:837
list bourdoncle_component(control vertex, hash_table ancestor_map, hash_table scc_map)
Definition: bourdoncle.c:2218
unstructured proper_cycle_head_to_scc(control h, hash_table scc_map)
Retrieve a scc_u from its head.
Definition: bourdoncle.c:2368
static void free_meaningless_control(control c)
Definition: bourdoncle.c:159
static void clean_up_embedding_graph(control c)
Definition: bourdoncle.c:747
static void copy_dfn(control new_c, control old_c)
Definition: bourdoncle.c:111
static void print_control_node_b(control c)
Definition: bourdoncle.c:299
static bool partition_successor_p(control b, control e, list partition)
FUNCTIONS FOR BOURDONCLE_COMPONENT()
Definition: bourdoncle.c:2015
static void set_davinci_count()
Definition: bourdoncle.c:427
void intersect_successors_with_partition_or_complement(control c, list partition, bool complement_p)
If complement_p, preserve successors in the complement of partition.
Definition: bourdoncle.c:1181
static void print_replicate_map()
Definition: bourdoncle.c:959
bool false_successors_only_p(control c)
Definition: bourdoncle.c:2481
bool true_successors_only_p(control c)
Definition: bourdoncle.c:2474
static bool entry_or_exit_control_p(control c, list partition, bool check_entry)
Definition: bourdoncle.c:1149
static void suppress_statement_reference(control c)
Definition: bourdoncle.c:2488
static void control_translate_arcs(control c)
Definition: bourdoncle.c:982
static void print_unstructured(unstructured u)
Definition: bourdoncle.c:331
static list embedding_control_list
Definition: bourdoncle.c:533
static void update_successors_of_predecessor(control pred, control new_c, control old_c)
new_c is not consistent on entry and might not be on exit because it is called from within a loop
Definition: bourdoncle.c:1263
static void add_child_parent_pair(hash_table ancestor_map, control child, control parent)
Definition: bourdoncle.c:934
void intersect_successors_with_partition(control c, list partition)
Definition: bourdoncle.c:1224
static void update_successor_in_copy(control new_pred, control pred, control c, control new_c)
Make new_c a successor of new_pred, the same way c is a successor of pred.
Definition: bourdoncle.c:1381
int bourdoncle_visit(control vertex, list *ppartition, hash_table ancestor_map, hash_table scc_map)
Definition: bourdoncle.c:2293
void add_control_to_embedding_control_list(control c)
Definition: bourdoncle.c:535
control control_shallow_copy(control c)
Definition: bourdoncle.c:970
unstructured cycle_head_to_scc(control c, hash_table ancestor_map, hash_table scc_map)
The ancestor of this node is associated to a specific cycle.
Definition: bourdoncle.c:2433
static hash_table replicate_map
Replication of unstructured (i.e.
Definition: bourdoncle.c:957
bool one_successor_kind_only_p(control c, bool true_p)
useful for non-deterministic control flow graph only
Definition: bourdoncle.c:2451
static void print_control_to_control_mapping(string message, hash_table map)
Definition: bourdoncle.c:823
static bool check_control_statement(control c)
Definition: bourdoncle.c:198
static hash_table nts
Definition: prettyprint.c:63
struct _newgen_struct_vertex_ * vertex
Definition: dg.h:28
#define min(a, b)
FILE * safe_fopen(const char *filename, const char *what)
Definition: file.c:67
int safe_fclose(FILE *stream, const char *filename)
Definition: file.c:77
bool get_bool_property(const string)
FC 2015-07-20: yuk, moved out to prevent an include cycle dependency include "properties....
void free(void *)
void print_control_nodes(list l)
Display identification of a list of control nodes.
Definition: control.c:589
void forward_control_map_get_blocs(c, l)
Build recursively the list of all controls forward-reachable from a control of an unstructured.
Definition: control.c:208
#define FORWARD_CONTROL_MAP(ctl, code, c, list)
Walk through all the controls forward-reachable from a given control node of an unstructured.
const char * get_current_module_name(void)
Get the name of the current module.
Definition: static.c:121
void gen_multi_recurse(void *o,...)
Multi recursion visitor function.
Definition: genClib.c:3428
bool gen_false(__attribute__((unused)) gen_chunk *unused)
Return false and ignore the argument.
Definition: genClib.c:2796
void gen_null(__attribute__((unused)) void *unused)
Ignore the argument.
Definition: genClib.c:2752
bool gen_true(__attribute__((unused)) gen_chunk *unused)
Return true and ignore the argument.
Definition: genClib.c:2780
#define ENDP(l)
Test if a list is empty.
Definition: newgen_list.h:66
void gen_remove(list *cpp, const void *o)
remove all occurences of item o from list *cpp, which is thus modified.
Definition: list.c:685
void gen_list_and(list *a, const list b)
Compute A = A inter B: complexity in O(n2)
Definition: list.c:941
int gen_position(const void *item, const list l)
Element ranks are strictly positive as for first, second, and so on.
Definition: list.c:995
#define POP(l)
Modify a list pointer to point on the next element of the list.
Definition: newgen_list.h:59
#define NIL
The empty list (nil in Lisp)
Definition: newgen_list.h:47
list gen_once(const void *vo, list l)
Prepend an item to a list only if it is not already in the list.
Definition: list.c:722
list gen_copy_seq(list l)
Copy a list structure.
Definition: list.c:501
size_t gen_length(const list l)
Definition: list.c:150
void gen_list_and_not(list *a, const list b)
Compute A = A inter non B:
Definition: list.c:963
#define CONS(_t_, _i_, _l_)
List element cell constructor (insert an element at the beginning of a list)
Definition: newgen_list.h:150
list gen_nconc(list cp1, list cp2)
physically concatenates CP1 and CP2 but do not duplicates the elements
Definition: list.c:344
#define CAR(pcons)
Get the value of the first element of a list.
Definition: newgen_list.h:92
void gen_free_list(list l)
free the spine of the list
Definition: list.c:327
bool gen_in_list_p(const void *vo, const list lx)
tell whether vo belongs to lx
Definition: list.c:734
#define CDR(pcons)
Get the list less its first element.
Definition: newgen_list.h:111
#define MAPL(_map_list_cp, _code, _l)
Apply some code on the addresses of all the elements of a list.
Definition: newgen_list.h:203
void gen_list_patch(list l, const void *x, const void *y)
Replace all the reference to x in list l by a reference to y:
Definition: list.c:985
#define list_undefined
Undefined list definition :-)
Definition: newgen_list.h:69
#define MAP(_map_CASTER, _map_item, _map_code, _map_list)
Apply/map an instruction block on all the elements of a list (old fashioned)
Definition: newgen_list.h:226
void gen_sort_list(list l, gen_cmp_func_t compare)
Sorts a list of gen_chunks in place, to avoid allocations...
Definition: list.c:796
test statement_test(statement)
Get the test of a statement.
Definition: statement.c:1348
bool empty_statement_or_labelless_continue_p(statement)
Return true if the statement is an empty instruction block without label or a continue without label ...
Definition: statement.c:446
bool statement_test_p(statement)
Definition: statement.c:343
string statement_identification(statement)
Like external_statement_identification(), but with internal information, the hexadecimal address of t...
Definition: statement.c:1700
string safe_statement_identification(statement)
Definition: statement.c:1726
statement make_continue_statement(entity)
Definition: statement.c:953
char end
Definition: gtk_status.c:82
hash_table hash_table_make(hash_key_type key_type, size_t size)
Definition: hash.c:294
void * hash_get(const hash_table htp, const void *key)
this function retrieves in the hash table pointed to by htp the couple whose key is equal to key.
Definition: hash.c:449
void hash_put(hash_table htp, const void *key, const void *val)
This functions stores a couple (key,val) in the hash table pointed to by htp.
Definition: hash.c:364
void hash_update(hash_table htp, const void *key, const void *val)
update key->val in htp, that MUST be pre-existent.
Definition: hash.c:491
void hash_table_free(hash_table htp)
this function deletes a hash table that is no longer useful.
Definition: hash.c:327
void * hash_delget(hash_table htp, const void *key, void **pkey)
deletes key from the hash table.
Definition: hash.c:398
int hash_table_entry_count(hash_table htp)
now we define observers in order to hide the hash_table type...
Definition: hash.c:818
static int useless(Pproblem XX, int i)
Definition: isolve.c:521
#define pips_debug
these macros use the GNU extensions that allow variadic macros, including with an empty list.
Definition: misc-local.h:145
#define pips_assert(what, predicate)
common macros, two flavors depending on NDEBUG
Definition: misc-local.h:172
#define pips_internal_error
Definition: misc-local.h:149
static entity rank
char * i2a(int)
I2A (Integer TO Ascii) yields a string for a given Integer.
Definition: string.c:121
string bool_to_string(bool)
Definition: string.c:243
string concatenate(const char *,...)
Return the concatenation of the given strings.
Definition: string.c:183
#define DEFINE_LOCAL_STACK(name, type)
#define HASH_MAP(k, v, code, ht)
Definition: newgen_hash.h:60
@ hash_pointer
Definition: newgen_hash.h:32
#define HASH_UNDEFINED_VALUE
value returned by hash_get() when the key is not found; could also be called HASH_KEY_NOT_FOUND,...
Definition: newgen_hash.h:56
#define hash_table_undefined_p(h)
Definition: newgen_hash.h:50
#define hash_table_undefined
Value of an undefined hash_table.
Definition: newgen_hash.h:49
intptr_t _int
_INT
Definition: newgen_types.h:53
int(* gen_cmp_func_t)(const void *, const void *)
Definition: newgen_types.h:114
int f(int off1, int off2, int n, float r[n], float a[n], float b[n])
Definition: offsets.c:15
string db_get_current_workspace_directory(void)
Definition: workspace.c:96
#define ORDERING_NUMBER(o)
#define ORDERING_STATEMENT(o)
#define unstructured_control
After the modification in Newgen: unstructured = entry:control x exit:control we have create a macro ...
entity entity_empty_label(void)
Definition: entity.c:1105
#define control_undefined
Definition: ri.h:916
#define CONTROL_(x)
Definition: ri.h:913
#define control_predecessors(x)
Definition: ri.h:943
struct _newgen_struct_unstructured_ * unstructured
Definition: ri.h:447
#define statement_ordering(x)
Definition: ri.h:2454
#define test_false(x)
Definition: ri.h:2837
#define unstructured_undefined
Definition: ri.h:2980
#define statement_domain
newgen_sizeofexpression_domain_defined
Definition: ri.h:362
#define control_domain
newgen_controlmap_domain_defined
Definition: ri.h:98
#define CONTROL(x)
CONTROL.
Definition: ri.h:910
#define test_true(x)
Definition: ri.h:2835
#define control_successors(x)
Definition: ri.h:945
#define control_undefined_p(x)
Definition: ri.h:917
#define unstructured_exit(x)
Definition: ri.h:3006
#define unstructured_undefined_p(x)
Definition: ri.h:2981
#define control_statement(x)
Definition: ri.h:941
#define statement_undefined_p(x)
Definition: ri.h:2420
#define UNSTRUCTURED(x)
UNSTRUCTURED.
Definition: ri.h:2974
#define statement_undefined
Definition: ri.h:2419
int fprintf()
test sc_min : ce test s'appelle par : programme fichier1.data fichier2.data ...
s1
Definition: set.c:247
#define ifdebug(n)
Definition: sg.c:47
The structure used to build lists in NewGen.
Definition: newgen_list.h:41