Use avl tree instead of a simple linked list for x86*'s methodtable.
[cacao.git] / src / vm / jit / codegen.inc
1 /* jit/codegen.inc - architecture independent code generator
2
3    Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
4    Institut f. Computersprachen, TU Wien
5    R. Grafl, A. Krall, C. Kruegel, C. Oates, R. Obermaisser, M. Probst,
6    S. Ring, E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich,
7    J. Wenninger
8
9    This file is part of CACAO.
10
11    This program is free software; you can redistribute it and/or
12    modify it under the terms of the GNU General Public License as
13    published by the Free Software Foundation; either version 2, or (at
14    your option) any later version.
15
16    This program is distributed in the hope that it will be useful, but
17    WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19    General Public License for more details.
20
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
24    02111-1307, USA.
25
26    Contact: cacao@complang.tuwien.ac.at
27
28    Authors: Reinhard Grafl
29             Andreas  Krall
30
31    Changes: Michael Gschwind
32             Christian Thalinger
33
34    All functions assume the following code area / data area layout:
35
36    +-----------+
37    |           |
38    | code area | code area grows to higher addresses
39    |           |
40    +-----------+ <-- start of procedure
41    |           |
42    | data area | data area grows to lower addresses
43    |           |
44    +-----------+
45
46    The functions first write into a temporary code/data area allocated by
47    "codegen_init". "codegen_finish" copies the code and data area into permanent
48    memory. All functions writing values into the data area return the offset
49    relative the begin of the code area (start of procedure).    
50
51    $Id: codegen.inc 1121 2004-06-03 20:40:39Z twisti $
52
53 */
54
55
56 #include <string.h>
57 #include "toolbox/memory.h"
58 #include "toolbox/logging.h"
59 #include "toolbox/avl.h"
60 #include "threads/thread.h"
61
62
63 /************************* critical sections  *********************************/
64
65 struct threadcritnodetemp {
66         struct threadcritnodetemp *next;
67         int mcodebegin, mcodeend, mcoderestart;
68 };
69
70 #define MCODEINITSIZE (1<<15)       /* 32 Kbyte code area initialization size */
71 #define DSEGINITSIZE  (1<<12)       /*  4 Kbyte data area initialization size */
72
73 static u1* mcodebase = NULL;        /* base pointer of code area              */
74 static s4* mcodeend  = NULL;        /* pointer to end of code area            */
75 static int mcodesize;               /* complete size of code area (bytes)     */
76
77 static u1* dsegtop = NULL;          /* pointer to top (end) of data area      */
78 static int dsegsize;                /* complete size of data area (bytes)     */
79 int dseglen;                        /* used size of data area (bytes)         */
80                                     /* data area grows from top to bottom     */
81
82 static jumpref *jumpreferences;     /* list of jumptable target addresses     */
83 static dataref *datareferences;     /* list of data segment references        */
84 static branchref *xboundrefs;       /* list of bound check branches           */
85 static branchref *xcheckarefs;      /* list of array size check branches      */
86 static branchref *xnullrefs;        /* list of null check branches            */
87 static branchref *xcastrefs;        /* list of cast check branches            */
88 static branchref *xdivrefs;         /* list of divide by zero branches        */
89 static branchref *xexceptionrefs;   /* list of exception branches             */
90 static linenumberref *linenumberreferences; /*list of line numbers and the program counters of their first instruction*/
91 static s4 linenumbertablesizepos;
92 static s4 linenumbertablestartpos;
93 static s4 linenumbertab;
94
95 static struct threadcritnodetemp *threadcrit;
96                                     /* List of critical code regions          */
97 static struct threadcritnodetemp threadcritcurrent;
98 static int threadcritcount;         /* Number of critical regions             */
99
100 int parentargs_base; /* offset in stackframe for the parameter from the caller*/
101
102 void codegen_init();                /* allocates code and data area           */
103 void codegen_close();               /* releases temporary storage             */
104 static void codegen_finish();       /* makes code and data area permanent and */
105                                     /* updates branch references to code/data */
106
107 static s4 dseg_adds4(s4 value);         /* adds an int to data area           */
108
109 #if !defined(__I386__)
110 static s4 dseg_adds8(s8 value);         /* adds an long to data area          */
111 #endif
112
113 static s4 dseg_addfloat (float value);  /* adds an float to data area         */
114 static s4 dseg_adddouble(double value); /* adds an double to data area        */
115
116 #if POINTERSIZE == 8
117 #define dseg_addaddress(value)      dseg_adds8((s8)(value))
118 #else
119 #define dseg_addaddress(value)      dseg_adds4((s4)(value))
120 #endif
121
122 static void dseg_addtarget(basicblock *target);
123 static void dseg_adddata(u1 *ptr);
124 static void codegen_addreference(basicblock *target, void *branchptr);
125 static void codegen_addxboundrefs(void *branchptr, s4 reg);
126 static void codegen_addxnullrefs(void *branchptr);
127 static void codegen_addxcastrefs(void *branchptr);
128 static void codegen_addxdivrefs(void *branchptr);
129 static void codegen_addxexceptionrefs(void *branchptr);
130 static void codegen_threadcritrestart(int offset);
131 static void codegen_threadcritstart(int offset);
132 static void codegen_threadcritstop(int offset);
133
134 #if defined(__I386__) || defined(__X86_64__)
135 typedef struct _methodtree_element methodtree_element;
136
137 struct _methodtree_element {
138         void *startpc;
139         void *endpc;
140 };
141
142 static struct avl_table *methodtree;
143 static int methodtree_comparator(const void *pc, const void *element,
144                                                                  void *param);
145 #endif
146
147 void dseg_display(s4 *s4ptr);
148
149
150
151 /* codegen_init allocates and initialises code area, data area and references */
152
153 void codegen_init()
154 {
155         if (!mcodebase) {
156                 mcodebase = MNEW(u1, MCODEINITSIZE);
157                 mcodesize = MCODEINITSIZE;
158         }
159
160         if (!dsegtop) {
161                 dsegtop = MNEW(u1, DSEGINITSIZE);
162                 dsegsize = DSEGINITSIZE;
163                 dsegtop += dsegsize;
164         }
165
166         dseglen = 0;
167
168         linenumberreferences = NULL;
169         linenumbertablesizepos = 0;
170         linenumbertablestartpos = 0;
171         linenumbertab = 0;
172         jumpreferences = NULL;
173         datareferences = NULL;
174         xboundrefs = NULL;
175         xcheckarefs = NULL;
176         xnullrefs = NULL;
177         xcastrefs = NULL;
178         xdivrefs = NULL;
179         xexceptionrefs = NULL;
180
181 #if defined(__I386__) || defined(__X86_64__)
182         if (!methodtree) {
183                 methodtree_element *mte;
184
185                 methodtree = avl_create(methodtree_comparator, NULL, NULL);
186
187                 mte = NEW(methodtree_element);
188                 mte->startpc = asm_calljavafunction;
189                 mte->endpc = asm_calljavafunction2;
190                 avl_insert(methodtree, mte);
191
192                 mte = NEW(methodtree_element);
193                 mte->startpc = asm_calljavafunction2;
194                 mte->endpc = asm_call_jit_compiler;
195                 avl_insert(methodtree, mte);
196         }
197 #endif
198
199 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
200         threadcritcurrent.next = NULL;
201         threadcritcount = 0;
202 #endif
203 }
204
205
206
207 /* codegen_close releases temporary code and data area                        */
208
209 void codegen_close()
210 {
211         if (mcodebase) {
212                 MFREE(mcodebase, u1, mcodesize);
213                 mcodebase = NULL;
214         }
215
216         if (dsegtop) {
217                 MFREE(dsegtop - dsegsize, u1, dsegsize);
218                 dsegtop = NULL;
219         }
220 }
221
222
223
224 /* codegen_increase doubles code area                                         */
225
226 static s4 *codegen_increase(u1 *codeptr)
227 {
228         long len;
229
230         len = codeptr - mcodebase;
231         mcodebase = MREALLOC(mcodebase, u1, mcodesize, mcodesize * 2);
232         mcodesize *= 2;
233         mcodeend = (s4*) (mcodebase + mcodesize);
234         return (s4*) (mcodebase + len);
235 }
236
237
238
239 /* desg_increase doubles data area                                            */
240
241 static void dseg_increase()
242 {
243         u1 *newstorage = MNEW(u1, dsegsize * 2);
244         memcpy(newstorage + dsegsize, dsegtop - dsegsize, dsegsize);
245         MFREE(dsegtop - dsegsize, u1, dsegsize);
246         dsegtop = newstorage;
247         dsegsize *= 2;
248         dsegtop += dsegsize;
249 }
250
251
252
253 static s4 dseg_adds4_increase(s4 value)
254 {
255         dseg_increase();
256         *((s4 *) (dsegtop - dseglen)) = value;
257         return -dseglen;
258 }
259
260
261
262 static s4 dseg_adds4(s4 value)
263 {
264         s4 *dataptr;
265
266         dseglen += 4;
267         dataptr = (s4 *) (dsegtop - dseglen);
268         if (dseglen > dsegsize)
269                 return dseg_adds4_increase(value);
270         *dataptr = value;
271         return -dseglen;
272 }
273
274
275
276 #if !defined(__I386__)
277 static s4 dseg_adds8_increase(s8 value)
278 {
279         dseg_increase();
280         *((s8 *) (dsegtop - dseglen)) = value;
281         return -dseglen;
282 }
283
284
285 static s4 dseg_adds8(s8 value)
286 {
287         s8 *dataptr;
288
289         dseglen = ALIGN (dseglen + 8, 8);
290         dataptr = (s8 *) (dsegtop - dseglen);
291         if (dseglen > dsegsize)
292                 return dseg_adds8_increase(value);
293         *dataptr = value;
294         return -dseglen;
295 }
296 #endif
297
298
299 static s4 dseg_addfloat_increase(float value)
300 {
301         dseg_increase();
302         *((float *) (dsegtop - dseglen)) = value;
303         return -dseglen;
304 }
305
306
307
308 static s4 dseg_addfloat(float value)
309 {
310         float *dataptr;
311
312         dseglen += 4;
313         dataptr = (float *) (dsegtop - dseglen);
314         if (dseglen > dsegsize)
315                 return dseg_addfloat_increase(value);
316         *dataptr = value;
317         return -dseglen;
318 }
319
320
321
322 static s4 dseg_adddouble_increase(double value)
323 {
324         dseg_increase();
325         *((double *) (dsegtop - dseglen)) = value;
326         return -dseglen;
327 }
328
329
330
331 static s4 dseg_adddouble(double value)
332 {
333         double *dataptr;
334
335         dseglen = ALIGN (dseglen + 8, 8);
336         dataptr = (double *) (dsegtop - dseglen);
337         if (dseglen > dsegsize)
338                 return dseg_adddouble_increase(value);
339         *dataptr = value;
340         return -dseglen;
341 }
342
343
344
345 static void dseg_addtarget(basicblock *target)
346 {
347         jumpref *jr = DNEW(jumpref);
348         
349         jr->tablepos = dseg_addaddress(NULL);
350         jr->target = target;
351         jr->next = jumpreferences;
352         jumpreferences = jr;
353 }
354
355
356
357 static void dseg_adddata(u1 *ptr)
358 {
359         dataref *dr = DNEW(dataref);
360
361         dr->pos = (u1 *) (ptr - mcodebase);
362         dr->next = datareferences;
363         datareferences = dr;
364 }
365
366 static void dseg_addlinenumbertablesize() {
367 #ifdef __ALPHA__
368         dseg_adds4(0); /*PADDING*/
369 #endif
370         linenumbertablesizepos=dseg_addaddress(NULL); /*it could be considered to use adds4 here, to avoid 1 double word padding on ALPHA */
371
372         linenumbertablestartpos=dseg_addaddress(NULL);
373 #ifdef __ALPHA__
374         dseg_adds4(0); /*PADDING*/
375 #endif
376 }
377
378 static void dseg_addlinenumber(u2 linenumber,u1 *ptr) {
379         linenumberref *lr=DNEW(linenumberref);
380         lr->linenumber=linenumber;
381         lr->tablepos=0;
382         lr->targetmpc=(ptr-mcodebase);
383         lr->next=linenumberreferences;
384         linenumberreferences=lr;
385 }
386
387 static void codegen_addreference(basicblock *target, void *branchptr)
388 {
389         s4 branchpos = (u1*) branchptr - mcodebase;
390
391         if (target->mpc >= 0) {
392                 gen_resolvebranch((u1*) mcodebase + branchpos, branchpos, target->mpc);
393         }
394         else {
395                 branchref *br = DNEW(branchref);
396
397                 br->branchpos = branchpos;
398                 br->next = target->branchrefs;
399                 target->branchrefs= br;
400         }
401 }
402
403
404
405 static void codegen_addxboundrefs(void *branchptr, s4 reg)
406 {
407         s4 branchpos = (u1*) branchptr - mcodebase;
408
409         branchref *br = DNEW(branchref);
410
411         br->branchpos = branchpos;
412         br->reg = reg;
413         br->next = xboundrefs;
414         xboundrefs = br;
415 }
416
417
418
419 static void codegen_addxcheckarefs(void *branchptr)
420 {
421         s4 branchpos = (u1*) branchptr - mcodebase;
422
423         branchref *br = DNEW(branchref);
424
425         br->branchpos = branchpos;
426         br->next = xcheckarefs;
427         xcheckarefs = br;
428 }
429
430
431
432 static void codegen_addxnullrefs(void *branchptr)
433 {
434         s4 branchpos = (u1*) branchptr - mcodebase;
435
436         branchref *br = DNEW(branchref);
437
438         br->branchpos = branchpos;
439         br->next = xnullrefs;
440         xnullrefs = br;
441 }
442
443
444
445 static void codegen_addxcastrefs(void *branchptr)
446 {
447         s4 branchpos = (u1*) branchptr - mcodebase;
448
449         branchref *br = DNEW(branchref);
450
451         br->branchpos = branchpos;
452         br->next = xcastrefs;
453         xcastrefs = br;
454 }
455
456
457 static void codegen_addxexceptionrefs(void *branchptr)
458 {
459         s4 branchpos = (u1*) branchptr - mcodebase;
460
461         branchref *br = DNEW(branchref);
462
463         br->branchpos = branchpos;
464         br->next = xexceptionrefs;
465         xexceptionrefs = br;
466 }
467
468
469 static void codegen_addxdivrefs(void *branchptr)
470 {
471         s4 branchpos = (u1*) branchptr - mcodebase;
472
473         branchref *br = DNEW(branchref);
474
475         br->branchpos = branchpos;
476         br->next = xdivrefs;
477         xdivrefs = br;
478 }
479
480
481 static void codegen_createlinenumbertable() {
482 #ifdef __I386__
483         /*log_text("codegen_createlinnumbertable");*/
484         {
485                 linenumberref *lr;
486                 for (lr=linenumberreferences;lr!=NULL;lr=lr->next) {
487                         /*log_text("Adding line number entry");*/
488                         lr->tablepos=dseg_addaddress(NULL);
489                         if (linenumbertab==0) linenumbertab=lr->tablepos;
490                         dseg_addaddress(lr->linenumber);
491                 }
492         }
493 #endif
494 }
495
496
497 #if defined(__I386__) || defined(__X86_64__)
498 static int methodtree_comparator(const void *pc, const void *element,
499                                                                  void *param)
500 {
501         methodtree_element *mtepc;
502         methodtree_element *mte;
503
504         mtepc = (methodtree_element *) pc;
505         mte = (methodtree_element *) element;
506
507         if (mte->startpc <= mtepc->startpc && mtepc->startpc <= mte->endpc) {
508                 return 0;
509
510         } else if (mtepc->startpc < mte->startpc) {
511                 return -1;
512
513         } else {
514                 return 1;
515         }
516 }
517
518
519 #if 0
520 void *codegen_findmethod1(void *pc)
521 {
522         void * retVal=findmethod(pc);
523         methodinfo **ma=(methodinfo**)retVal;
524         methodinfo *m=ma[-1];
525         if (m)
526                 if (m->name)
527                         utf_display(m->name);
528                 else 
529                         log_text("No Name");
530         else log_text("No methodinfo");
531         return retVal;
532 }
533 #endif
534
535 void *codegen_findmethod(void *pc)
536 {
537         methodtree_element *mtepc;
538         methodtree_element *mte;
539
540         mtepc = NEW(methodtree_element);
541         mtepc->startpc = pc;
542
543         mte = avl_find(methodtree, mtepc);
544
545         FREE(mtepc, methodtree_element);
546
547         if (!mte)
548                 throw_cacao_exception_exit(string_java_lang_InternalError, "cannot find function");
549
550         return mte->startpc;
551 }
552 #endif
553
554
555 static void codegen_finish(int mcodelen)
556 {
557         jumpref *jr;
558         u1 *epoint;
559
560         int extralen = 0;
561
562 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
563         extralen += sizeof(threadcritnode) * threadcritcount;
564 #endif
565
566         count_code_len += mcodelen;
567         count_data_len += dseglen;
568
569         dseglen = ALIGN(dseglen, MAX_ALIGN);
570
571         method->mcodelength = mcodelen + dseglen;
572         method->mcode = CNEW(u1, mcodelen + dseglen + extralen);
573
574         memcpy(method->mcode, dsegtop - dseglen, dseglen);
575         memcpy(method->mcode + dseglen, mcodebase, mcodelen);
576
577         method->entrypoint = epoint = (u1 *) (method->mcode + dseglen);
578
579         /* jump table resolving */
580         jr = jumpreferences;
581         while (jr != NULL) {
582             *((void**) (epoint + jr->tablepos)) = epoint + jr->target->mpc;
583             jr = jr->next;
584         }
585
586 #ifdef __I386__
587         /* line number table resolving */
588         {
589                 linenumberref *lr;
590                 #if POINTERSIZE == 8
591                         s8  lrtlen=0;
592                 #else
593                         s4  lrtlen=0;
594                 #endif
595
596                 for (lr=linenumberreferences;lr!=NULL;lr=lr->next) {
597                         lrtlen++;
598                         *((void**)(epoint+lr->tablepos))=epoint+lr->targetmpc;
599                         /*log_text("resolving line number information");*/
600                 }
601                 
602                 *((void**)(epoint+linenumbertablestartpos))=epoint+linenumbertab;
603 #if POINTERSIZE == 8
604                 *((s8*)(epoint+linenumbertablesizepos))=lrtlen;
605 #else
606                 *((s4*)(epoint+linenumbertablesizepos))=lrtlen;
607 #endif
608
609         }
610 #endif
611 #if defined(__I386__) || defined(__X86_64__)
612         {
613                 dataref *dr;
614
615                 /* add method into methodtree to find the entrypoint */
616                 methodtree_element *mte;
617
618                 mte = NEW(methodtree_element);
619                 mte->startpc = method->entrypoint;
620                 mte->endpc = method->entrypoint + mcodelen;
621
622                 if (avl_insert(methodtree, mte))
623                         panic("duplicate entry");
624
625                 /* data segment references resolving */
626                 dr = datareferences;
627                 while (dr != NULL) {
628                         *((void**) ((long) epoint + (long) dr->pos - POINTERSIZE)) = epoint;
629                         dr = dr->next;
630                 }
631         }
632 #endif
633
634
635 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
636         {
637                 threadcritnode *n = (threadcritnode*) (method->mcode + mcodelen + dseglen);
638                 int i;
639                 struct threadcritnodetemp *nt = threadcrit;
640
641                 for (i=0; i<threadcritcount; i++)
642                 {
643                         n->mcodebegin = method->mcode + nt->mcodebegin;
644                         n->mcodeend = method->mcode + nt->mcodeend;
645                         n->mcoderestart = method->mcode + nt->mcoderestart;
646                         thread_registercritical(n);
647                         n++;
648                         nt = nt->next;
649                 }
650         }
651 #endif
652 }
653
654
655
656 void dseg_display(s4 *s4ptr)
657 {
658         int i;
659         
660         printf("  --- dump of datasegment\n");
661         for (i = dseglen; i > 0 ; i -= 4) {
662                 printf("-%6x: %8x\n", i, (int)(*s4ptr++));
663         }
664         printf("  --- begin of data segment: %p\n", s4ptr);
665 }
666
667 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
668 void codegen_threadcritrestart(int offset)
669 {
670         threadcritcurrent.mcoderestart = offset;
671 }
672
673 void codegen_threadcritstart(int offset)
674 {
675         threadcritcurrent.mcodebegin = offset;
676 }
677
678 void codegen_threadcritstop(int offset)
679 {
680         threadcritcurrent.next = threadcrit;
681         threadcritcurrent.mcodeend = offset;
682         threadcrit = DNEW(struct threadcritnodetemp);
683         *threadcrit = threadcritcurrent;
684         threadcritcount++;
685 }
686 #endif
687
688 /*
689  * These are local overrides for various environment variables in Emacs.
690  * Please do not remove this and leave it at the end of the file, where
691  * Emacs will automagically detect them.
692  * ---------------------------------------------------------------------
693  * Local variables:
694  * mode: c
695  * indent-tabs-mode: t
696  * c-basic-offset: 4
697  * tab-width: 4
698  * End:
699  */