CACAO
schedule.c
Go to the documentation of this file.
1 /* src/vm/jit/schedule/schedule.c - architecture independent instruction
2  scheduler
3 
4  Copyright (C) 1996-2013
5  CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
6 
7  This file is part of CACAO.
8 
9  This program is free software; you can redistribute it and/or
10  modify it under the terms of the GNU General Public License as
11  published by the Free Software Foundation; either version 2, or (at
12  your option) any later version.
13 
14  This program is distributed in the hope that it will be useful, but
15  WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  General Public License for more details.
18 
19  You should have received a copy of the GNU General Public License
20  along with this program; if not, write to the Free Software
21  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22  02110-1301, USA.
23 
24  Contact: cacao@cacaojvm.org
25 
26  Authors: Christian Thalinger
27 
28  Changes:
29 
30 */
31 
32 
33 #include "config.h"
34 
35 #include <stdio.h>
36 
37 #include "vm/types.hpp"
38 
39 #include "disass.hpp"
40 
41 #include "mm/memory.hpp"
42 #include "vm/options.hpp"
43 #include "vm/statistics.hpp"
45 
46 #error port to C++ first!
47 STAT_REGISTER_GROUP(sched_stat,"inst. sched.","Instruction scheduler statistics:")
48 STAT_REGISTER_GROUP_VAR(s4,count_schedule_basic_blocks,0,"num basicblocs","Number of basic blocks")
49 STAT_REGISTER_GROUP_VAR(s4,count_schedule_nodes,0,"num nodes","Number of nodes")
50 STAT_REGISTER_GROUP_VAR(s4,count_schedule_leaders,0,"num leader nodes","Number of leaders nodes")
51 STAT_REGISTER_GROUP_VAR(s4,count_schedule_critical_path,0,"critical path","Length of critical path")
52 STAT_REGISTER_GROUP_VAR(s4,count_schedule_max_leaders,"max leader nodes","Number of max. leaders nodes")
53 
54 /* XXX quick hack */
56 
58 {
59  scheduledata *sd;
60 
61  sd = DNEW(scheduledata);
62 
63  stackrange =
64  (rd->savintregcnt - rd->maxsavintreguse) +
65  (rd->savfltregcnt - rd->maxsavfltreguse) +
66  rd->maxmemuse +
67  m->parseddesc->paramcount +
68  1; /* index 0 are all other memory accesses */
69 
70  /* XXX quick hack: just use a fixed number of instructions */
71  sd->mi = DMNEW(minstruction, 20000);
72 
73  sd->intregs_define_dep = DMNEW(edgenode*, rd->intregsnum);
74  sd->fltregs_define_dep = DMNEW(edgenode*, rd->fltregsnum);
75 
76  sd->intregs_use_dep = DMNEW(edgenode*, rd->intregsnum);
77  sd->fltregs_use_dep = DMNEW(edgenode*, rd->fltregsnum);
78 
79  sd->memory_define_dep = DMNEW(edgenode*, stackrange);
80  sd->memory_use_dep = DMNEW(edgenode*, stackrange);
81 
82 
83  /* open graphviz file */
84 
85  if (opt_verbose) {
86  FILE *file;
87 
88  file = fopen("cacao.dot", "w+");
89  fprintf(file, "digraph G {\n");
90 
91  sd->file = file;
92  }
93 
94  return sd;
95 }
96 
97 
99 {
100  sd->micount = 0;
101  sd->leaders = NULL;
102 
103  /* set define array to -1, because 0 is a valid node number */
104 
105  MSET(sd->intregs_define_dep, 0, edgenode*, rd->intregsnum);
106  MSET(sd->fltregs_define_dep, 0, edgenode*, rd->fltregsnum);
107 
108  /* clear all use pointers */
109 
110  MSET(sd->intregs_use_dep, 0, edgenode*, rd->intregsnum);
111  MSET(sd->fltregs_use_dep, 0, edgenode*, rd->fltregsnum);
112 
115 }
116 
117 
119 {
120  if (opt_verbose) {
121  FILE *file;
122 
123  file = sd->file;
124 
125  fprintf(file, "}\n");
126  fclose(file);
127  }
128 }
129 
130 
131 
132 /* schedule_add_define_dep *****************************************************
133 
134  XXX
135 
136 *******************************************************************************/
137 
138 void schedule_add_define_dep(scheduledata *sd, s1 opnum, edgenode **define_dep, edgenode **use_dep)
139 {
140  minstruction *mi;
141  minstruction *defmi;
142  minstruction *usemi;
143  edgenode *en;
144  edgenode *useen;
145  edgenode *defen;
146  edgenode *tmpen;
147  s4 minum;
148 
149  /* get current machine instruction */
150 
151  minum = sd->micount - 1;
152  mi = &sd->mi[minum];
153 
154  /* get current use dependency nodes, if non-null use them... */
155 
156  if ((useen = *use_dep)) {
157  while (useen) {
158  /* Save next pointer so we can link the current node to the */
159  /* machine instructions' dependency list. */
160 
161  tmpen = useen->next;
162 
163  /* don't add the current machine instruction (e.g. add A0,A0,A0) */
164 
165  if (useen->minum != minum) {
166  /* some edges to current machine instruction -> no leader */
167 
168  mi->flags &= ~SCHEDULE_LEADER;
169 
170  /* get use machine instruction */
171 
172  usemi = &sd->mi[useen->minum];
173 
174  /* link current use node into dependency list */
175 
176  useen->minum = minum;
177  useen->opnum2 = opnum;
178 
179  /* calculate latency, for define add 1 cycle */
180  useen->latency = (usemi->op[useen->opnum].lastcycle -
181  mi->op[opnum].firstcycle) + 1;
182 
183  useen->next = usemi->deps;
184  usemi->deps = useen;
185  }
186 
187  useen = tmpen;
188  }
189 
190  /* ...otherwise use last define dependency, if non-null */
191  } else if ((defen = *define_dep)) {
192  /* some edges to current machine instruction -> no leader */
193 
194  mi->flags &= ~SCHEDULE_LEADER;
195 
196  /* get last define machine instruction */
197 
198  defmi = &sd->mi[defen->minum];
199 
200  /* link current define node into dependency list */
201 
202  defen->minum = minum;
203  defen->opnum2 = opnum;
204 
205  /* calculate latency, for define add 1 cycle */
206  defen->latency = (defmi->op[defen->opnum].lastcycle -
207  mi->op[opnum].firstcycle) + 1;
208 
209  defen->next = defmi->deps;
210  defmi->deps = defen;
211  }
212 
213  /* Set current instruction as new define dependency and clear use */
214  /* dependencies. */
215 
216  en = DNEW(edgenode);
217  en->minum = minum;
218  en->opnum = opnum;
219 
220  *define_dep = en;
221  *use_dep = NULL;
222 }
223 
224 
225 /* schedule_add_use_dep ********************************************************
226 
227  XXX
228 
229 *******************************************************************************/
230 
231 void schedule_add_use_dep(scheduledata *sd, s1 opnum, edgenode **define_dep, edgenode **use_dep)
232 {
233  minstruction *mi;
234  minstruction *defmi;
235  edgenode *en;
236  edgenode *defen;
237  s4 minum;
238 
239  /* get current machine instruction */
240 
241  minum = sd->micount - 1;
242  mi = &sd->mi[minum];
243 
244  /* get current define dependency instruction */
245 
246  if ((defen = *define_dep)) {
247  /* some edges to current machine instruction -> no leader */
248 
249  mi->flags &= ~SCHEDULE_LEADER;
250 
251  /* add node to dependency list of current define node */
252 
253  defmi = &sd->mi[defen->minum];
254 
255  en = DNEW(edgenode);
256  en->minum = minum;
257  en->opnum = defen->opnum;
258  en->opnum2 = opnum;
259 
260  /* calculate latency */
261  en->latency = (defmi->op[defen->opnum].lastcycle -
262  mi->op[opnum].firstcycle);
263 
264  en->next = defmi->deps;
265  defmi->deps = en;
266  }
267 
268  /* add node to list of current use nodes */
269 
270  en = DNEW(edgenode);
271  en->minum = minum;
272  en->opnum = opnum;
273  en->next = *use_dep;
274  *use_dep = en;
275 }
276 
277 
278 /* schedule_calc_priorities ****************************************************
279 
280  Calculates the current node's priority by getting highest priority
281  of the dependency nodes, adding this nodes latency plus 1 (for the
282  instruction itself).
283 
284 *******************************************************************************/
285 
287 {
288  minstruction *mi;
289  minstruction *lastmi;
290  edgenode *en;
291  s4 lastnode;
292  s4 i;
293  s4 j;
294  s4 criticalpath;
295  s4 currentpath;
296  s1 lastcycle;
297 
298 
299  /* last node MUST always be the last instruction scheduled */
300 
301  lastnode = sd->micount - 1;
302 
303  /* if last instruction is the first instruction, skip everything */
304 
305  if (lastnode > 0) {
306  lastmi = &sd->mi[lastnode];
307 
308  /* last instruction is no leader */
309 
310  lastmi->flags &= ~SCHEDULE_LEADER;
311 
312  /* last instruction has no dependencies, use virtual sink node */
313 
314  lastcycle = 0;
315 
316  for (j = 0; j < 4; j++) {
317  if (lastmi->op[j].lastcycle > lastcycle)
318  lastcycle = lastmi->op[j].lastcycle;
319  }
320 
321 #define EARLIEST_USE_CYCLE 3
322  lastmi->priority = (lastcycle - EARLIEST_USE_CYCLE) + 1;
323 
324 
325  /* walk through all remaining machine instructions backwards */
326 
327  for (i = lastnode - 1, mi = &sd->mi[lastnode - 1]; i >= 0; i--, mi--) {
328  en = mi->deps;
329 
330  if (en) {
331  s4 priority;
332 
333  criticalpath = 0;
334 
335  /* walk through depedencies and calculate highest latency */
336 
337  while (en) {
338  priority = sd->mi[en->minum].priority;
339 
340  currentpath = en->latency + priority;
341 
342  if (currentpath > criticalpath)
343  criticalpath = currentpath;
344 
345  en = en->next;
346  }
347 
348  /* set priority to critical path */
349 
350  mi->priority = criticalpath;
351 
352  } else {
353  /* no dependencies, use virtual sink node */
354 
355  lastcycle = 0;
356 
357  for (j = 0; j < 4; j++) {
358  if (mi->op[j].lastcycle > lastcycle)
359  lastcycle = mi->op[j].lastcycle;
360  }
361 
362  mi->priority = (lastcycle - EARLIEST_USE_CYCLE);
363  }
364 
365  /* add current machine instruction to leader list, if one */
366 
367  if (mi->flags & SCHEDULE_LEADER) {
368  en = DNEW(edgenode);
369  en->minum = i;
370  en->next = sd->leaders;
371  sd->leaders = en;
372  }
373  }
374 
375  } else {
376  /* last node is first instruction, add to leader list */
377 
378  en = DNEW(edgenode);
379  en->minum = lastnode;
380  en->next = sd->leaders;
381  sd->leaders = en;
382  }
383 }
384 
385 
386 static void schedule_create_graph(scheduledata *sd, s4 criticalpath)
387 {
388  minstruction *mi;
389  edgenode *en;
390  s4 i;
391 
392  FILE *file;
393  static int bb = 1;
394 
395  file = sd->file;
396 
397  fprintf(file, "subgraph cluster_%d {\n", bb);
398  fprintf(file, "label = \"BB%d (nodes: %d, critical path: %d)\"\n", bb, sd->micount, criticalpath);
399 
400  for (i = 0, mi = sd->mi; i < sd->micount; i++, mi++) {
401 
402  en = mi->deps;
403 
404  if (en) {
405  while (en) {
406  fprintf(file, "\"#%d.%d ", bb, i);
407  disassinstr(file, &mi->instr);
408  fprintf(file, "\\np=%d\"", mi->priority);
409 
410  fprintf(file, " -> ");
411 
412  fprintf(file, "\"#%d.%d ", bb, en->minum);
413  disassinstr(file, &sd->mi[en->minum].instr);
414  fprintf(file, "\\np=%d\"", sd->mi[en->minum].priority);
415 
416  fprintf(file, " [ label = \"%d\" ]\n", en->latency);
417  en = en->next;
418  }
419 
420  } else {
421  fprintf(file, "\"#%d.%d ", bb, i);
422  disassinstr(file, &mi->instr);
423  fprintf(file, "\\np=%d\"", mi->priority);
424 
425  fprintf(file, " -> ");
426 
427  fprintf(file, "\"end %d\"", bb);
428 
429  fprintf(file, " [ label = \"%d\" ]\n", mi->priority);
430 
431  fprintf(file, "\n");
432  }
433  }
434 
435  fprintf(file, "}\n");
436 
437  bb++;
438 }
439 
440 
441 /* schedule_add_deps_to_leaders ************************************************
442 
443  Walk through all dependencies, change the starttime and add the
444  node to the leaders list.
445 
446 *******************************************************************************/
447 
449 {
450  edgenode *depen;
451  edgenode *preven;
452 
453  if ((depen = deps)) {
454  while (depen) {
455  /* set starttime of instruction */
456 
457  sd->mi[depen->minum].starttime = time + depen->latency;
458 
459  /* save last entry */
460 
461  preven = depen;
462 
463  depen = depen->next;
464  }
465 
466  /* add dependencies to front of the list */
467 
468  preven->next = sd->leaders;
469  sd->leaders = deps;
470  }
471 }
472 
473 
474 /* schedule_do_schedule ********************************************************
475 
476  XXX
477 
478 *******************************************************************************/
479 
481 {
482  minstruction *mi;
483  edgenode *en;
484  s4 i;
485  s4 j;
486  s4 leaders;
487  s4 criticalpath;
488  s4 time;
489  s4 schedulecount;
490 
491  minstruction *alumi;
492  minstruction *memmi;
493  minstruction *brmi;
494 
495  edgenode *preven;
496  edgenode *depen;
497 
498  edgenode *aluen;
499  edgenode *memen;
500  edgenode *bren;
501 
502  /* It may be possible that no instructions are in the current basic block */
503  /* because after an branch instruction the instructions are scheduled. */
504 
505  if (sd->micount > 0) {
506  /* calculate priorities and critical path */
507 
509 
510  if (opt_verbose) {
511  printf("bb start ---\n");
512  printf("nodes: %d\n", sd->micount);
513  printf("leaders: ");
514  }
515 
516  leaders = 0;
517  criticalpath = 0;
518 
519  en = sd->leaders;
520  while (en) {
521  if (opt_verbose) {
522  printf("#%d ", en->minum);
523  }
524 
525  leaders++;
526  if (sd->mi[en->minum].priority > criticalpath)
527  criticalpath = sd->mi[en->minum].priority;
528  en = en->next;
529  }
530 
531  /* check last node for critical path (e.g. ret) */
532 
533  if (sd->mi[sd->micount - 1].priority > criticalpath)
534  criticalpath = sd->mi[sd->micount - 1].priority;
535 
536  if (opt_verbose) {
537  printf("\n");
538  printf("critical path: %d\n", criticalpath);
539 
540  for (i = 0, mi = sd->mi; i < sd->micount; i++, mi++) {
541  disassinstr(stdout, &mi->instr);
542 
543  printf("\t--> #%d, prio=%d", i, mi->priority);
544 
545  printf(", mem=%d:%d", mi->op[0].firstcycle, mi->op[0].lastcycle);
546 
547  for (j = 1; j <= 3; j++) {
548  printf(", op%d=%d:%d", j, mi->op[j].firstcycle, mi->op[j].lastcycle);
549  }
550 
551  printf(", deps= ");
552  en = mi->deps;
553  while (en) {
554  printf("#%d (op%d->op%d: %d) ", en->minum, en->opnum, en->opnum2, en->latency);
555  en = en->next;
556  }
557  printf("\n");
558  }
559  printf("bb end ---\n\n");
560 
561  schedule_create_graph(sd, criticalpath);
562  }
563 
564 
565  /* set start time to zero */
566 
567  printf("\n\nschedule start ---\n");
568  time = 0;
569  schedulecount = 0;
570 
571  while (sd->leaders) {
572  /* XXX argh! how to make this portable? */
573  for (j = 0; j < 2; j++ ) {
574  en = sd->leaders;
575  preven = NULL;
576 
577  alumi = NULL;
578  memmi = NULL;
579  brmi = NULL;
580 
581  aluen = NULL;
582  memen = NULL;
583  bren = NULL;
584 
585  printf("\n\nleaders: ");
586  while (en) {
587  /* get current machine instruction from leader list */
588 
589  mi = &sd->mi[en->minum];
590  disassinstr(stdout, &mi->instr);
591  printf(", ");
592 
593  /* starttime reached */
594 
595  if (mi->starttime <= time) {
596 
597  /* check for a suitable ALU instruction */
598 
599  if ((mi->flags & SCHEDULE_UNIT_ALU) &&
600  (!alumi || (mi->priority > alumi->priority))) {
601  /* remove/replace current node from leaders list */
602 
603  if (preven)
604  if (alumi) {
605  preven->next = aluen;
606  aluen->next = en->next;
607  } else {
608  preven->next = en->next;
609  }
610  else
611  if (alumi) {
612  sd->leaders = aluen;
613  aluen->next = en->next;
614  } else {
615  sd->leaders = en->next;
616  }
617 
618  /* set current ALU instruction and node */
619 
620  alumi = mi;
621  aluen = en;
622 
623  /* check for a suitable MEM instruction */
624 
625  } else if ((mi->flags & SCHEDULE_UNIT_MEM) &&
626  (!memmi || (mi->priority > memmi->priority))) {
627  if (preven)
628  if (memmi) {
629  preven->next = memen;
630  memen->next = en->next;
631  } else {
632  preven->next = en->next;
633  }
634  else
635  if (memmi) {
636  sd->leaders = memen;
637  memen->next = en->next;
638  } else {
639  sd->leaders = en->next;
640  }
641 
642  memmi = mi;
643  memen = en;
644 
645  /* check for a suitable BRANCH instruction */
646 
647  } else if ((mi->flags & SCHEDULE_UNIT_BRANCH) &&
648  (!brmi || (mi->priority > brmi->priority))) {
649  if (preven)
650  preven->next = en->next;
651  else
652  sd->leaders = en->next;
653 
654  memmi = mi;
655  memen = en;
656 
657  } else
658  preven = en;
659 
660  } else {
661  /* not leader removed, save next previous node */
662 
663  preven = en;
664  }
665 
666  en = en->next;
667  }
668  printf("\n");
669 
670  /* schedule ALU instruction, if one was found */
671 
672  if (aluen) {
673  mi = &sd->mi[aluen->minum];
674 
675  disassinstr(stdout, &mi->instr);
676  printf(" || ");
677 
678  schedulecount++;
679  schedule_add_deps_to_leaders(sd, mi->deps, time);
680  }
681 
682  /* schedule MEM instruction, if one was found */
683 
684  if (memen) {
685  mi = &sd->mi[memen->minum];
686 
687  disassinstr(stdout, &mi->instr);
688  printf(" || ");
689 
690  schedulecount++;
691  schedule_add_deps_to_leaders(sd, mi->deps, time);
692  }
693 
694  /* schedule BRANCH instruction, if one was found */
695 
696  if (bren) {
697  mi = &sd->mi[bren->minum];
698 
699  disassinstr(stdout, &brmi->instr);
700  printf(" || ");
701 
702  schedulecount++;
703  schedule_add_deps_to_leaders(sd, mi->deps, time);
704  }
705 
706  if (!aluen && !memen && !bren) {
707  printf("nop");
708  printf(" || ");
709  }
710  }
711  printf("\n");
712 
713  /* bundle finished, increase execution time */
714 
715  time++;
716  }
717  printf("schedule end ---\n\n");
718 
719  STATISTICS(count_schedule_basic_blocks++);
720  STATISTICS(count_schedule_nodes += sd->micount);
721  STATISTICS(count_schedule_leaders += leaders);
722  STATISTICS(count_schedule_max_leaders.max(leaders));
723  STATISTICS(count_schedule_critical_path += criticalpath);
724  }
725 }
726 
727 
728 /*
729  * These are local overrides for various environment variables in Emacs.
730  * Please do not remove this and leave it at the end of the file, where
731  * Emacs will automagically detect them.
732  * ---------------------------------------------------------------------
733  * Local variables:
734  * mode: c
735  * indent-tabs-mode: t
736  * c-basic-offset: 4
737  * tab-width: 4
738  * End:
739  * vim:noexpandtab:sw=4:ts=4:
740  */
void schedule_reset(scheduledata *sd, registerdata *rd)
Definition: schedule.c:98
#define SCHEDULE_UNIT_ALU
Definition: schedule.h:52
#define STATISTICS(x)
Wrapper for statistics only code.
Definition: statistics.hpp:975
minstruction * mi
Definition: schedule.h:73
s1 opnum
Definition: schedule.h:120
opcycles op[4]
Definition: schedule.h:103
u1 * disassinstr(u1 *code)
Definition: disass.cpp:48
edgenode ** fltregs_use_dep
Definition: schedule.h:82
#define max(a, b)
Definition: lsra.hpp:80
#define MSET(ptr, byte, type, num)
Definition: memory.hpp:104
edgenode ** fltregs_define_dep
Definition: schedule.h:78
edgenode ** memory_use_dep
Definition: schedule.h:83
s1 latency
Definition: schedule.h:122
void schedule_close(scheduledata *sd)
Definition: schedule.c:118
void schedule_add_define_dep(scheduledata *sd, s1 opnum, edgenode **define_dep, edgenode **use_dep)
Definition: schedule.c:138
edgenode ** memory_define_dep
Definition: schedule.h:79
void schedule_add_use_dep(scheduledata *sd, s1 opnum, edgenode **define_dep, edgenode **use_dep)
Definition: schedule.c:231
#define DNEW(type)
Definition: dumpmemory.hpp:370
FILE * file
Definition: schedule.h:85
#define SCHEDULE_UNIT_BRANCH
Definition: schedule.h:54
#define SCHEDULE_LEADER
Definition: schedule.h:49
This file contains the statistics framework.
edgenode ** intregs_use_dep
Definition: schedule.h:81
#define STAT_REGISTER_GROUP_VAR(type, var, init, name, description, group)
Register an statistics variable and add it to a group.
Definition: statistics.hpp:967
s1 lastcycle
Definition: schedule.h:62
#define STAT_REGISTER_GROUP(var, name, description)
Register a statistics group.
Definition: statistics.hpp:971
MIIterator i
int32_t s4
Definition: types.hpp:45
edgenode * leaders
Definition: schedule.h:75
static void schedule_create_graph(scheduledata *sd, s4 criticalpath)
Definition: schedule.c:386
u4 instr[2]
Definition: schedule.h:97
edgenode ** intregs_define_dep
Definition: schedule.h:77
edgenode * next
Definition: schedule.h:123
void schedule_calc_priorities(scheduledata *sd)
Definition: schedule.c:286
s4 minum
Definition: schedule.h:119
void schedule_do_schedule(scheduledata *sd)
Definition: schedule.c:480
#define EARLIEST_USE_CYCLE
edgenode * deps
Definition: schedule.h:106
s4 stackrange
Definition: schedule.c:55
s1 firstcycle
Definition: schedule.h:61
int8_t s1
Definition: types.hpp:39
#define DMNEW(type, num)
Definition: dumpmemory.hpp:371
scheduledata * schedule_init(methodinfo *m, registerdata *rd)
Definition: schedule.c:57
block_count * blocks[HASH_SIZE]
Definition: profile.c:48
static void schedule_add_deps_to_leaders(scheduledata *sd, edgenode *deps, s4 time)
Definition: schedule.c:448
s1 opnum2
Definition: schedule.h:121
#define printf(...)
Definition: ssa2.cpp:40
#define SCHEDULE_UNIT_MEM
Definition: schedule.h:53
bool opt_verbose
Definition: options.cpp:67