1 /* src/vm/jit/codegen.inc - architecture independent code generator
3 Copyright (C) 1996-2005 R. Grafl, A. Krall, C. Kruegel, C. Oates,
4 R. Obermaisser, M. Platter, M. Probst, S. Ring, E. Steiner,
5 C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich, J. Wenninger,
6 Institut f. Computersprachen - TU Wien
8 This file is part of CACAO.
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License as
12 published by the Free Software Foundation; either version 2, or (at
13 your option) any later version.
15 This program is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
25 Contact: cacao@complang.tuwien.ac.at
27 Authors: Reinhard Grafl
30 Changes: Christian Thalinger
33 All functions assume the following code area / data area layout:
37 | code area | code area grows to higher addresses
39 +-----------+ <-- start of procedure
41 | data area | data area grows to lower addresses
45 The functions first write into a temporary code/data area allocated by
46 "codegen_init". "codegen_finish" copies the code and data area into permanent
47 memory. All functions writing values into the data area return the offset
48 relative the begin of the code area (start of procedure).
50 $Id: codegen.inc 2661 2005-06-13 14:17:37Z twisti $
60 #include "mm/memory.h"
61 #include "toolbox/avl.h"
62 #include "toolbox/logging.h"
63 #include "native/native.h"
65 #if defined(USE_THREADS)
66 # if defined(NATIVE_THREADS)
67 # include "threads/native/threads.h"
69 # include "threads/green/threads.h"
73 #include "vm/exceptions.h"
74 #include "vm/options.h"
75 #include "vm/statistics.h"
76 #include "vm/jit/codegen.inc.h"
84 /* in this tree we store all method addresses *********************************/
86 #if defined(__I386__) || defined(__X86_64__)
87 static struct avl_table *methodtree = NULL;
88 static int methodtree_comparator(const void *pc, const void *element,
93 /* codegen_init ****************************************************************
97 *******************************************************************************/
101 #if defined(__I386__) || defined(__X86_64__)
102 /* this tree is global, not method specific */
104 methodtree_element *mte;
106 methodtree = avl_create(methodtree_comparator, NULL, NULL);
108 /* insert asm_calljavafunction */
110 mte = NEW(methodtree_element);
112 mte->startpc = (functionptr) asm_calljavafunction;
113 mte->endpc = (functionptr) ((ptrint) asm_calljavafunction2 - 1);
115 avl_insert(methodtree, mte);
117 /* insert asm_calljavafunction2 */
119 mte = NEW(methodtree_element);
121 mte->startpc = (functionptr) asm_calljavafunction2;
122 mte->endpc = (functionptr) ((ptrint) asm_call_jit_compiler - 1);
124 avl_insert(methodtree, mte);
130 /* codegen_setup **************************************************************
132 allocates and initialises code area, data area and references
134 *******************************************************************************/
136 void codegen_setup(methodinfo *m, codegendata *cd, t_inlining_globals *id)
138 cd->mcodebase = DMNEW(u1, MCODEINITSIZE);
139 cd->mcodesize = MCODEINITSIZE;
141 cd->dsegtop = DMNEW(u1, DSEGINITSIZE);
142 cd->dsegsize = DSEGINITSIZE;
143 cd->dsegtop += cd->dsegsize;
146 cd->jumpreferences = NULL;
147 cd->datareferences = NULL;
148 cd->xboundrefs = NULL;
149 cd->xcheckarefs = NULL;
150 cd->xnullrefs = NULL;
151 cd->xcastrefs = NULL;
153 cd->xexceptionrefs = NULL;
154 cd->patchrefs = NULL;
156 cd->linenumberreferences = NULL;
157 cd->linenumbertablesizepos = 0;
158 cd->linenumbertablestartpos = 0;
159 cd->linenumbertab = 0;
162 cd->exceptiontable = 0;
163 cd->exceptiontablelength = 0;
165 if (useinlining && id) {
166 if (id->cumextablelength > 0) {
167 cd->exceptiontablelength = id->cumextablelength;
169 DMNEW(exceptiontable, id->cumextablelength + 1);
172 } else if (id && (id->method->exceptiontablelength > 0)) {
173 cd->exceptiontablelength = m->exceptiontablelength;
174 cd->exceptiontable = DMNEW(exceptiontable, m->exceptiontablelength + 1);
178 cd->maxstack = id->cummaxstack;
179 cd->maxlocals = id->cumlocals;
181 cd->maxstack = m->maxstack;
182 cd->maxlocals = m->maxlocals;
185 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
186 cd->threadcritcurrent.next = NULL;
187 cd->threadcritcount = 0;
192 /* codegen_free ****************************************************************
194 Releases temporary code and data area.
196 *******************************************************************************/
198 void codegen_free(methodinfo *m, codegendata *cd)
203 MFREE(cd->mcodebase, u1, cd->mcodesize);
204 cd->mcodebase = NULL;
208 MFREE(cd->dsegtop - cd->dsegsize, u1, cd->dsegsize);
216 /* codegen_close ***************************************************************
220 *******************************************************************************/
224 /* TODO: release avl tree on i386 and x86_64 */
228 /* codegen_increase doubles code area */
230 static s4 *codegen_increase(codegendata *cd, u1 *codeptr)
234 len = codeptr - cd->mcodebase;
235 cd->mcodebase = DMREALLOC(cd->mcodebase,
240 cd->mcodeend = (s4 *) (cd->mcodebase + cd->mcodesize);
242 return (s4 *) (cd->mcodebase + len);
246 /* desg_increase doubles data area */
248 static void dseg_increase(codegendata *cd)
252 newstorage = DMNEW(u1, cd->dsegsize * 2);
254 memcpy(newstorage + cd->dsegsize, cd->dsegtop - cd->dsegsize, cd->dsegsize);
256 MFREE(cd->dsegtop - cd->dsegsize, u1, cd->dsegsize);
259 cd->dsegtop = newstorage;
261 cd->dsegtop += cd->dsegsize;
265 static s4 dseg_adds4_increase(codegendata *cd, s4 value)
269 *((s4 *) (cd->dsegtop - cd->dseglen)) = value;
271 return -(cd->dseglen);
275 static s4 dseg_adds4(codegendata *cd, s4 value)
280 dataptr = (s4 *) (cd->dsegtop - cd->dseglen);
282 if (cd->dseglen > cd->dsegsize)
283 return dseg_adds4_increase(cd, value);
287 return -(cd->dseglen);
291 #if !defined(__I386__)
292 static s4 dseg_adds8_increase(codegendata *cd, s8 value)
296 *((s8 *) (cd->dsegtop - cd->dseglen)) = value;
298 return -(cd->dseglen);
302 static s4 dseg_adds8(codegendata *cd, s8 value)
306 cd->dseglen = ALIGN(cd->dseglen + 8, 8);
307 dataptr = (s8 *) (cd->dsegtop - cd->dseglen);
309 if (cd->dseglen > cd->dsegsize)
310 return dseg_adds8_increase(cd, value);
314 return -(cd->dseglen);
319 #if !defined(__XDSPCORE__)
320 static s4 dseg_addfloat_increase(codegendata *cd, float value)
324 *((float *) (cd->dsegtop - cd->dseglen)) = value;
326 return -(cd->dseglen);
330 static s4 dseg_addfloat(codegendata *cd, float value)
335 dataptr = (float *) (cd->dsegtop - cd->dseglen);
337 if (cd->dseglen > cd->dsegsize)
338 return dseg_addfloat_increase(cd, value);
342 return -(cd->dseglen);
346 static s4 dseg_adddouble_increase(codegendata *cd, double value)
350 *((double *) (cd->dsegtop - cd->dseglen)) = value;
352 return -(cd->dseglen);
356 static s4 dseg_adddouble(codegendata *cd, double value)
360 cd->dseglen = ALIGN(cd->dseglen + 8, 8);
361 dataptr = (double *) (cd->dsegtop - cd->dseglen);
363 if (cd->dseglen > cd->dsegsize)
364 return dseg_adddouble_increase(cd, value);
368 return -(cd->dseglen);
370 #endif /* !defined(__XDSPCORE__) */
373 static void dseg_addtarget(codegendata *cd, basicblock *target)
378 jr->tablepos = dseg_addaddress(cd, NULL);
380 jr->next = cd->jumpreferences;
381 cd->jumpreferences = jr;
385 static void dseg_adddata(codegendata *cd, u1 *ptr)
390 dr->pos = (u1 *) (ptr - cd->mcodebase);
391 dr->next = cd->datareferences;
392 cd->datareferences = dr;
396 static void dseg_addlinenumbertablesize(codegendata *cd)
398 #if defined (__ALPHA__) || defined (__X86_64__)
399 dseg_adds4(cd, 0); /*PADDING*/
401 cd->linenumbertablesizepos = dseg_addaddress(cd, NULL); /*it could be considered to use adds4 here, to avoid 1 double word padding on ALPHA */
403 cd->linenumbertablestartpos = dseg_addaddress(cd, NULL);
404 #if defined(__ALPHA__) || defined (__X86_64__)
405 dseg_adds4(cd, 0); /*PADDING*/
410 static void dseg_addlinenumber(codegendata *cd, u2 linenumber, u1 *ptr)
414 lr = DNEW(linenumberref);
415 lr->linenumber = linenumber;
417 lr->targetmpc = (ptr - cd->mcodebase);
418 lr->next = cd->linenumberreferences;
419 cd->linenumberreferences = lr;
423 /* we need this function externally on i386 and x86_64, but keep the call fast
426 #if defined(__I386__) || defined(__X86_64__)
427 void codegen_addreference(codegendata *cd, basicblock *target, void *branchptr)
429 static void codegen_addreference(codegendata *cd, basicblock *target, void *branchptr)
434 branchpos = (u1 *) branchptr - cd->mcodebase;
436 /* Check if the target basicblock has already a start pc, so the jump is */
437 /* backward and we can resolve it immediately. */
439 if (target->mpc >= 0) {
440 gen_resolvebranch((u1 *) cd->mcodebase + branchpos,
445 branchref *br = DNEW(branchref);
447 br->branchpos = branchpos;
448 br->next = target->branchrefs;
449 target->branchrefs = br;
454 static void codegen_addxboundrefs(codegendata *cd, void *branchptr, s4 reg)
459 branchpos = (u1 *) branchptr - cd->mcodebase;
461 br = DNEW(branchref);
462 br->branchpos = branchpos;
464 br->next = cd->xboundrefs;
469 static void codegen_addxcheckarefs(codegendata *cd, void *branchptr)
474 branchpos = (u1 *) branchptr - cd->mcodebase;
476 br = DNEW(branchref);
477 br->branchpos = branchpos;
478 br->next = cd->xcheckarefs;
479 cd->xcheckarefs = br;
483 static void codegen_addxnullrefs(codegendata *cd, void *branchptr)
488 branchpos = (u1 *) branchptr - cd->mcodebase;
490 br = DNEW(branchref);
491 br->branchpos = branchpos;
492 br->next = cd->xnullrefs;
497 static void codegen_addxcastrefs(codegendata *cd, void *branchptr)
502 branchpos = (u1 *) branchptr - cd->mcodebase;
504 br = DNEW(branchref);
505 br->branchpos = branchpos;
506 br->next = cd->xcastrefs;
511 static void codegen_addxexceptionrefs(codegendata *cd, void *branchptr)
516 branchpos = (u1 *) branchptr - cd->mcodebase;
518 br = DNEW(branchref);
519 br->branchpos = branchpos;
520 br->next = cd->xexceptionrefs;
521 cd->xexceptionrefs = br;
525 #if defined(__I386__) || defined(__X86_64__)
526 static void codegen_addxdivrefs(codegendata *cd, void *branchptr)
531 branchpos = (u1 *) branchptr - cd->mcodebase;
533 br = DNEW(branchref);
534 br->branchpos = branchpos;
535 br->next = cd->xdivrefs;
541 static void codegen_addpatchref(codegendata *cd,
549 branchpos = (u1 *) branchptr - cd->mcodebase;
552 pr->branchpos = branchpos;
553 pr->patcher = patcher;
555 pr->next = cd->patchrefs;
560 static void codegen_createlinenumbertable(codegendata *cd)
562 #if defined(__I386__) || defined(__ALPHA__) || defined(__X86_64__)
565 for (lr = cd->linenumberreferences; lr != NULL; lr = lr->next) {
566 lr->tablepos = dseg_addaddress(cd, NULL);
568 if (cd->linenumbertab == 0)
569 cd->linenumbertab = lr->tablepos;
571 dseg_addaddress(cd, lr->linenumber);
577 #if defined(__I386__) || defined(__X86_64__)
578 static int methodtree_comparator(const void *pc, const void *element, void *param)
580 methodtree_element *mte;
581 methodtree_element *mtepc;
583 mte = (methodtree_element *) element;
584 mtepc = (methodtree_element *) pc;
586 /* compare both startpc and endpc of pc, even if they have the same value,
587 otherwise the avl_probe sometimes thinks the element is already in the
589 if ((long) mte->startpc <= (long) mtepc->startpc &&
590 (long) mtepc->startpc <= (long) mte->endpc &&
591 (long) mte->startpc <= (long) mtepc->endpc &&
592 (long) mtepc->endpc <= (long) mte->endpc) {
595 } else if ((long) mtepc->startpc < (long) mte->startpc) {
604 void codegen_insertmethod(functionptr startpc, functionptr endpc)
606 methodtree_element *mte;
608 #if defined(USE_THREADS)
609 #if defined(NATIVE_THREADS)
614 mte = NEW(methodtree_element);
615 mte->startpc = startpc;
618 if (avl_insert(methodtree, mte)) {
619 #if defined(USE_THREADS)
620 #if defined(NATIVE_THREADS)
625 throw_cacao_exception_exit(string_java_lang_InternalError,
629 #if defined(USE_THREADS)
630 #if defined(NATIVE_THREADS)
637 functionptr codegen_findmethod(functionptr pc)
639 methodtree_element *mtepc;
640 methodtree_element *mte;
642 #if defined(USE_THREADS)
643 #if defined(NATIVE_THREADS)
648 mtepc = NEW(methodtree_element);
652 mte = avl_find(methodtree, mtepc);
654 FREE(mtepc, methodtree_element);
657 #if defined(USE_THREADS)
658 #if defined(NATIVE_THREADS)
665 fprintf(stderr,"backtrace:");
666 backtrace_symbols_fd(bt,5,2);
670 throw_cacao_exception_exit(string_java_lang_InternalError,
671 "Cannot find Java function at %p", pc);
674 #if defined(USE_THREADS)
675 #if defined(NATIVE_THREADS)
684 #if defined(__ALPHA__)
685 /*perhaps in the end I'll have to go for the tree version on alpha too, since this is insecure for eg segfault signals in native code,
686 since it will still return an adress of a datasegment, but which will not be a valid one of a java method. This version is faster though (jowenn)*/
687 void *codegen_findmethod(void *returnAdress)
692 d=*((s4*)returnAdress);
695 dataseg=returnAdress+d;
696 d=*(((s4*)returnAdress)+1);
700 d=*(((s4*)returnAdress)+1);
706 ldl t0,0(ra) /* load instruction LDA PV,xxx(RA) */
708 sra t0,48,t0 /* isolate offset */
709 addq t0,ra,pv /* compute update address */
710 ldl t0,4(ra) /* load instruction LDAH PV,xxx(PV) */
711 srl t0,16,t0 /* isolate instruction code */
712 lda t0,-0x177b(t0) /* test for LDAH */
714 ldl t0,4(ra) /* load instruction LDAH PV,xxx(RA) */
715 sll t0,16,t0 /* compute high offset */
716 addl t0,0,t0 /* sign extend high offset */
717 addq t0,pv,pv /* compute update address */
725 /* codegen_finish **************************************************************
727 Finishes the code generation. A new memory, large enough for both
728 data and code, is allocated and data and code are copied together
729 to their final layout, unresolved jumps are resolved, ...
731 *******************************************************************************/
733 static void codegen_finish(methodinfo *m, codegendata *cd, s4 mcodelen)
740 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
741 extralen = sizeof(threadcritnode) * cd->threadcritcount;
746 #if defined(STATISTICS)
748 count_code_len += mcodelen;
749 count_data_len += cd->dseglen;
753 cd->dseglen = ALIGN(cd->dseglen, MAX_ALIGN);
754 alignedlen = ALIGN(mcodelen, MAX_ALIGN) + cd->dseglen;
756 /* allocate new memory */
758 m->mcodelength = mcodelen + cd->dseglen;
759 m->mcode = (functionptr) (ptrint) CNEW(u1, alignedlen + extralen);
761 /* copy data and code to their new location */
763 MCOPY((void *) (ptrint) m->mcode, cd->dsegtop - cd->dseglen, u1,
765 MCOPY((void *) ((ptrint) m->mcode + cd->dseglen), cd->mcodebase, u1,
768 m->entrypoint = epoint = (functionptr) ((ptrint) m->mcode + cd->dseglen);
770 /* jump table resolving */
772 jr = cd->jumpreferences;
774 *((functionptr *) ((ptrint) epoint + jr->tablepos)) =
775 (functionptr) ((ptrint) epoint + (ptrint) jr->target->mpc);
779 #if defined(__I386__) || defined(__ALPHA__) || defined(__X86_64__)
780 /* line number table resolving */
785 for (lr = cd->linenumberreferences; lr != NULL; lr = lr->next) {
787 *((functionptr *) ((ptrint) epoint + (ptrint) lr->tablepos)) =
788 (functionptr) ((ptrint) epoint + (ptrint) lr->targetmpc);
791 *((functionptr *) ((ptrint) epoint + cd->linenumbertablestartpos)) =
792 (functionptr) ((ptrint) epoint + cd->linenumbertab);
794 *((ptrint *) ((ptrint) epoint + cd->linenumbertablesizepos)) = lrtlen;
798 #if defined(__I386__) || defined(__X86_64__)
799 /* add method into methodtree to find the entrypoint */
801 codegen_insertmethod(m->entrypoint,
802 (functionptr) ((ptrint) m->entrypoint + mcodelen));
805 #if defined(__I386__) || defined(__X86_64__) || defined(__XDSPCORE__)
809 /* data segment references resolving */
811 dr = cd->datareferences;
813 *((functionptr *) ((ptrint) epoint + (ptrint) dr->pos -
814 SIZEOF_VOID_P)) = epoint;
820 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
822 threadcritnode *n = (threadcritnode *) ((ptrint) m->mcode + alignedlen);
824 threadcritnodetemp *nt = cd->threadcrit;
826 for (i = 0; i < cd->threadcritcount; i++) {
827 n->mcodebegin = (u1 *) (ptrint) m->mcode + nt->mcodebegin;
828 n->mcodeend = (u1 *) (ptrint) m->mcode + nt->mcodeend;
829 n->mcoderestart = (u1 *) (ptrint) m->mcode + nt->mcoderestart;
830 thread_registercritical(n);
839 void dseg_display(methodinfo *m, codegendata *cd)
844 s4ptr = (s4 *) (ptrint) m->mcode;
846 printf(" --- dump of datasegment\n");
847 for (i = cd->dseglen; i > 0 ; i -= 4) {
848 #if SIZEOF_VOID_P == 8
849 printf("0x%016lx: -%6x (%6d): %8x\n",
850 (ptrint) s4ptr, i, i, (s4) *s4ptr);
852 printf("0x%08x: -%6x (%6d): %8x\n",
853 (ptrint) s4ptr, i, i, (s4) *s4ptr);
858 printf(" --- begin of data segment: %p\n", (void *) s4ptr);
863 /* codegen_createnativestub ****************************************************
865 Wrapper for createnativestub.
867 *******************************************************************************/
869 functionptr codegen_createnativestub(functionptr f, methodinfo *m)
873 t_inlining_globals *id;
876 /* mark dump memory */
878 dumpsize = dump_size();
880 cd = DNEW(codegendata);
881 rd = DNEW(registerdata);
882 id = DNEW(t_inlining_globals);
884 /* setup code generation stuff */
886 inlining_setup(m, id);
887 reg_setup(m, rd, id);
888 codegen_setup(m, cd, id);
890 m->stubroutine = createnativestub(f, m, cd, rd);
892 /* entrypoint was set in codegen_finish, clear it */
894 m->entrypoint = NULL;
896 #if defined(STATISTICS)
898 count_nstub_len += m->mcodelength + cd->dseglen;
903 dump_release(dumpsize);
906 /* disassemble native stub */
909 codegen_disassemble_nativestub(m, (s4 *) (ptrint) m->stubroutine,
912 /* return stubroutine entry point */
914 return m->stubroutine;
918 /* codegen_disassemble_nativestub **********************************************
920 Disassembles the generated native stub.
922 *******************************************************************************/
924 void codegen_disassemble_nativestub(methodinfo *m, s4 *code, s4 len)
926 printf("Native stub: ");
927 utf_fprint_classname(stdout, m->class->name);
929 utf_fprint(stdout, m->name);
930 utf_fprint(stdout, m->descriptor);
931 printf("\n\nLength: %d\n\n", len);
933 #if defined(__I386__) || defined(__X86_64__)
934 disassemble((u1 *) code, len);
936 disassemble(code, len);
941 /* removecompilerstub **********************************************************
943 Deletes a compilerstub from memory (simply by freeing it).
945 *******************************************************************************/
947 void removecompilerstub(functionptr stub)
953 /* removenativestub ************************************************************
955 Removes a previously created native-stub from memory.
957 *******************************************************************************/
959 void removenativestub(functionptr stub)
966 This function determines a register, to which the result of an operation
967 should go, when it is ultimatively intended to store the result in
969 If v is assigned to an actual register, this register will be returned.
970 Otherwise (when v is spilled) this function returns tempregnum.
971 If not already done, regoff and flags are set in the stack location.
974 static int reg_of_var(registerdata *rd, stackptr v, int tempregnum)
978 switch (v->varkind) {
980 if (!(v->flags & INMEMORY))
985 var = &(rd->interfaces[v->varnum][v->type]);
986 v->regoff = var->regoff;
987 if (!(var->flags & INMEMORY))
992 var = &(rd->locals[v->varnum][v->type]);
993 v->regoff = var->regoff;
994 if (!(var->flags & INMEMORY))
999 if (!(v->flags & INMEMORY))
1005 count_spills_read++;
1007 v->flags |= INMEMORY;
1012 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
1013 static void codegen_threadcritrestart(codegendata *cd, int offset)
1015 cd->threadcritcurrent.mcoderestart = offset;
1019 static void codegen_threadcritstart(codegendata *cd, int offset)
1021 cd->threadcritcurrent.mcodebegin = offset;
1025 static void codegen_threadcritstop(codegendata *cd, int offset)
1027 cd->threadcritcurrent.next = cd->threadcrit;
1028 cd->threadcritcurrent.mcodeend = offset;
1029 cd->threadcrit = DNEW(threadcritnodetemp);
1030 *(cd->threadcrit) = cd->threadcritcurrent;
1031 cd->threadcritcount++;
1037 * These are local overrides for various environment variables in Emacs.
1038 * Please do not remove this and leave it at the end of the file, where
1039 * Emacs will automagically detect them.
1040 * ---------------------------------------------------------------------
1043 * indent-tabs-mode: t