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 2694 2005-06-14 18:35:52Z 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__) && !defined(__POWERPC__)
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 #if defined(__I386__) || defined(__X86_64__)
386 static void dseg_adddata(codegendata *cd, u1 *ptr)
391 dr->pos = (u1 *) (ptr - cd->mcodebase);
392 dr->next = cd->datareferences;
393 cd->datareferences = dr;
398 static void dseg_addlinenumbertablesize(codegendata *cd)
400 #if defined (__ALPHA__) || defined (__X86_64__)
401 dseg_adds4(cd, 0); /*PADDING*/
403 cd->linenumbertablesizepos = dseg_addaddress(cd, NULL); /*it could be considered to use adds4 here, to avoid 1 double word padding on ALPHA */
405 cd->linenumbertablestartpos = dseg_addaddress(cd, NULL);
406 #if defined(__ALPHA__) || defined (__X86_64__)
407 dseg_adds4(cd, 0); /*PADDING*/
412 static void dseg_addlinenumber(codegendata *cd, u2 linenumber, u1 *ptr)
416 lr = DNEW(linenumberref);
417 lr->linenumber = linenumber;
419 lr->targetmpc = (ptr - cd->mcodebase);
420 lr->next = cd->linenumberreferences;
421 cd->linenumberreferences = lr;
425 /* we need this function externally on i386 and x86_64, but keep the call fast
428 #if defined(__I386__) || defined(__X86_64__)
429 void codegen_addreference(codegendata *cd, basicblock *target, void *branchptr)
431 static void codegen_addreference(codegendata *cd, basicblock *target, void *branchptr)
436 branchpos = (u1 *) branchptr - cd->mcodebase;
438 /* Check if the target basicblock has already a start pc, so the jump is */
439 /* backward and we can resolve it immediately. */
441 if (target->mpc >= 0) {
442 gen_resolvebranch((u1 *) cd->mcodebase + branchpos,
447 branchref *br = DNEW(branchref);
449 br->branchpos = branchpos;
450 br->next = target->branchrefs;
451 target->branchrefs = br;
456 static void codegen_addxboundrefs(codegendata *cd, void *branchptr, s4 reg)
461 branchpos = (u1 *) branchptr - cd->mcodebase;
463 br = DNEW(branchref);
464 br->branchpos = branchpos;
466 br->next = cd->xboundrefs;
471 static void codegen_addxcheckarefs(codegendata *cd, void *branchptr)
476 branchpos = (u1 *) branchptr - cd->mcodebase;
478 br = DNEW(branchref);
479 br->branchpos = branchpos;
480 br->next = cd->xcheckarefs;
481 cd->xcheckarefs = br;
485 static void codegen_addxnullrefs(codegendata *cd, void *branchptr)
490 branchpos = (u1 *) branchptr - cd->mcodebase;
492 br = DNEW(branchref);
493 br->branchpos = branchpos;
494 br->next = cd->xnullrefs;
499 static void codegen_addxcastrefs(codegendata *cd, void *branchptr)
504 branchpos = (u1 *) branchptr - cd->mcodebase;
506 br = DNEW(branchref);
507 br->branchpos = branchpos;
508 br->next = cd->xcastrefs;
513 static void codegen_addxexceptionrefs(codegendata *cd, void *branchptr)
518 branchpos = (u1 *) branchptr - cd->mcodebase;
520 br = DNEW(branchref);
521 br->branchpos = branchpos;
522 br->next = cd->xexceptionrefs;
523 cd->xexceptionrefs = br;
527 #if defined(__I386__) || defined(__X86_64__)
528 static void codegen_addxdivrefs(codegendata *cd, void *branchptr)
533 branchpos = (u1 *) branchptr - cd->mcodebase;
535 br = DNEW(branchref);
536 br->branchpos = branchpos;
537 br->next = cd->xdivrefs;
543 static void codegen_addpatchref(codegendata *cd,
551 branchpos = (u1 *) branchptr - cd->mcodebase;
554 pr->branchpos = branchpos;
555 pr->patcher = patcher;
557 pr->next = cd->patchrefs;
562 static void codegen_createlinenumbertable(codegendata *cd)
564 #if defined(__I386__) || defined(__ALPHA__) || defined(__X86_64__)
567 for (lr = cd->linenumberreferences; lr != NULL; lr = lr->next) {
568 lr->tablepos = dseg_addaddress(cd, NULL);
570 if (cd->linenumbertab == 0)
571 cd->linenumbertab = lr->tablepos;
573 dseg_addaddress(cd, lr->linenumber);
579 #if defined(__I386__) || defined(__X86_64__)
580 static int methodtree_comparator(const void *pc, const void *element, void *param)
582 methodtree_element *mte;
583 methodtree_element *mtepc;
585 mte = (methodtree_element *) element;
586 mtepc = (methodtree_element *) pc;
588 /* compare both startpc and endpc of pc, even if they have the same value,
589 otherwise the avl_probe sometimes thinks the element is already in the
591 if ((long) mte->startpc <= (long) mtepc->startpc &&
592 (long) mtepc->startpc <= (long) mte->endpc &&
593 (long) mte->startpc <= (long) mtepc->endpc &&
594 (long) mtepc->endpc <= (long) mte->endpc) {
597 } else if ((long) mtepc->startpc < (long) mte->startpc) {
606 void codegen_insertmethod(functionptr startpc, functionptr endpc)
608 methodtree_element *mte;
610 #if defined(USE_THREADS)
611 #if defined(NATIVE_THREADS)
616 mte = NEW(methodtree_element);
617 mte->startpc = startpc;
620 if (avl_insert(methodtree, mte)) {
621 #if defined(USE_THREADS)
622 #if defined(NATIVE_THREADS)
627 throw_cacao_exception_exit(string_java_lang_InternalError,
631 #if defined(USE_THREADS)
632 #if defined(NATIVE_THREADS)
639 functionptr codegen_findmethod(functionptr pc)
641 methodtree_element *mtepc;
642 methodtree_element *mte;
644 #if defined(USE_THREADS)
645 #if defined(NATIVE_THREADS)
650 mtepc = NEW(methodtree_element);
654 mte = avl_find(methodtree, mtepc);
656 FREE(mtepc, methodtree_element);
659 #if defined(USE_THREADS)
660 #if defined(NATIVE_THREADS)
667 fprintf(stderr,"backtrace:");
668 backtrace_symbols_fd(bt,5,2);
672 throw_cacao_exception_exit(string_java_lang_InternalError,
673 "Cannot find Java function at %p", pc);
676 #if defined(USE_THREADS)
677 #if defined(NATIVE_THREADS)
686 #if defined(__ALPHA__)
687 /*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,
688 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)*/
689 void *codegen_findmethod(void *returnAdress)
694 d=*((s4*)returnAdress);
697 dataseg=returnAdress+d;
698 d=*(((s4*)returnAdress)+1);
702 d=*(((s4*)returnAdress)+1);
708 ldl t0,0(ra) /* load instruction LDA PV,xxx(RA) */
710 sra t0,48,t0 /* isolate offset */
711 addq t0,ra,pv /* compute update address */
712 ldl t0,4(ra) /* load instruction LDAH PV,xxx(PV) */
713 srl t0,16,t0 /* isolate instruction code */
714 lda t0,-0x177b(t0) /* test for LDAH */
716 ldl t0,4(ra) /* load instruction LDAH PV,xxx(RA) */
717 sll t0,16,t0 /* compute high offset */
718 addl t0,0,t0 /* sign extend high offset */
719 addq t0,pv,pv /* compute update address */
727 /* codegen_finish **************************************************************
729 Finishes the code generation. A new memory, large enough for both
730 data and code, is allocated and data and code are copied together
731 to their final layout, unresolved jumps are resolved, ...
733 *******************************************************************************/
735 static void codegen_finish(methodinfo *m, codegendata *cd, s4 mcodelen)
742 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
743 extralen = sizeof(threadcritnode) * cd->threadcritcount;
748 #if defined(STATISTICS)
750 count_code_len += mcodelen;
751 count_data_len += cd->dseglen;
755 cd->dseglen = ALIGN(cd->dseglen, MAX_ALIGN);
756 alignedlen = ALIGN(mcodelen, MAX_ALIGN) + cd->dseglen;
758 /* allocate new memory */
760 m->mcodelength = mcodelen + cd->dseglen;
761 m->mcode = (functionptr) (ptrint) CNEW(u1, alignedlen + extralen);
763 /* copy data and code to their new location */
765 MCOPY((void *) (ptrint) m->mcode, cd->dsegtop - cd->dseglen, u1,
767 MCOPY((void *) ((ptrint) m->mcode + cd->dseglen), cd->mcodebase, u1,
770 m->entrypoint = epoint = (functionptr) ((ptrint) m->mcode + cd->dseglen);
772 /* jump table resolving */
774 jr = cd->jumpreferences;
776 *((functionptr *) ((ptrint) epoint + jr->tablepos)) =
777 (functionptr) ((ptrint) epoint + (ptrint) jr->target->mpc);
781 #if defined(__I386__) || defined(__ALPHA__) || defined(__X86_64__)
782 /* line number table resolving */
787 for (lr = cd->linenumberreferences; lr != NULL; lr = lr->next) {
789 *((functionptr *) ((ptrint) epoint + (ptrint) lr->tablepos)) =
790 (functionptr) ((ptrint) epoint + (ptrint) lr->targetmpc);
793 *((functionptr *) ((ptrint) epoint + cd->linenumbertablestartpos)) =
794 (functionptr) ((ptrint) epoint + cd->linenumbertab);
796 *((ptrint *) ((ptrint) epoint + cd->linenumbertablesizepos)) = lrtlen;
800 #if defined(__I386__) || defined(__X86_64__)
801 /* add method into methodtree to find the entrypoint */
803 codegen_insertmethod(m->entrypoint,
804 (functionptr) ((ptrint) m->entrypoint + mcodelen));
807 #if defined(__I386__) || defined(__X86_64__) || defined(__XDSPCORE__)
811 /* data segment references resolving */
813 dr = cd->datareferences;
815 *((functionptr *) ((ptrint) epoint + (ptrint) dr->pos -
816 SIZEOF_VOID_P)) = epoint;
822 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
824 threadcritnode *n = (threadcritnode *) ((ptrint) m->mcode + alignedlen);
826 threadcritnodetemp *nt = cd->threadcrit;
828 for (i = 0; i < cd->threadcritcount; i++) {
829 n->mcodebegin = (u1 *) (ptrint) m->mcode + nt->mcodebegin;
830 n->mcodeend = (u1 *) (ptrint) m->mcode + nt->mcodeend;
831 n->mcoderestart = (u1 *) (ptrint) m->mcode + nt->mcoderestart;
832 thread_registercritical(n);
841 void dseg_display(methodinfo *m, codegendata *cd)
846 s4ptr = (s4 *) (ptrint) m->mcode;
848 printf(" --- dump of datasegment\n");
849 for (i = cd->dseglen; i > 0 ; i -= 4) {
850 #if SIZEOF_VOID_P == 8
851 printf("0x%016lx: -%6x (%6d): %8x\n",
852 (ptrint) s4ptr, i, i, (s4) *s4ptr);
854 printf("0x%08x: -%6x (%6d): %8x\n",
855 (ptrint) s4ptr, i, i, (s4) *s4ptr);
860 printf(" --- begin of data segment: %p\n", (void *) s4ptr);
865 /* codegen_createnativestub ****************************************************
867 Wrapper for createnativestub.
869 *******************************************************************************/
871 functionptr codegen_createnativestub(functionptr f, methodinfo *m)
875 t_inlining_globals *id;
878 /* mark dump memory */
880 dumpsize = dump_size();
882 cd = DNEW(codegendata);
883 rd = DNEW(registerdata);
884 id = DNEW(t_inlining_globals);
886 /* setup code generation stuff */
888 inlining_setup(m, id);
889 reg_setup(m, rd, id);
890 codegen_setup(m, cd, id);
892 m->stubroutine = createnativestub(f, m, cd, rd);
894 /* entrypoint was set in codegen_finish, clear it */
896 m->entrypoint = NULL;
898 #if defined(STATISTICS)
900 count_nstub_len += m->mcodelength + cd->dseglen;
905 dump_release(dumpsize);
908 /* disassemble native stub */
911 codegen_disassemble_nativestub(m, (s4 *) (ptrint) m->stubroutine,
914 /* return stubroutine entry point */
916 return m->stubroutine;
920 /* codegen_disassemble_nativestub **********************************************
922 Disassembles the generated native stub.
924 *******************************************************************************/
926 void codegen_disassemble_nativestub(methodinfo *m, s4 *code, s4 len)
928 printf("Native stub: ");
929 utf_fprint_classname(stdout, m->class->name);
931 utf_fprint(stdout, m->name);
932 utf_fprint(stdout, m->descriptor);
933 printf("\n\nLength: %d\n\n", len);
935 #if defined(__I386__) || defined(__X86_64__)
936 disassemble((u1 *) code, len);
938 disassemble(code, len);
943 /* removecompilerstub **********************************************************
945 Deletes a compilerstub from memory (simply by freeing it).
947 *******************************************************************************/
949 void removecompilerstub(functionptr stub)
955 /* removenativestub ************************************************************
957 Removes a previously created native-stub from memory.
959 *******************************************************************************/
961 void removenativestub(functionptr stub)
968 This function determines a register, to which the result of an operation
969 should go, when it is ultimatively intended to store the result in
971 If v is assigned to an actual register, this register will be returned.
972 Otherwise (when v is spilled) this function returns tempregnum.
973 If not already done, regoff and flags are set in the stack location.
976 static int reg_of_var(registerdata *rd, stackptr v, int tempregnum)
980 switch (v->varkind) {
982 if (!(v->flags & INMEMORY))
987 var = &(rd->interfaces[v->varnum][v->type]);
988 v->regoff = var->regoff;
989 if (!(var->flags & INMEMORY))
994 var = &(rd->locals[v->varnum][v->type]);
995 v->regoff = var->regoff;
996 if (!(var->flags & INMEMORY))
1001 if (!(v->flags & INMEMORY))
1007 count_spills_read++;
1009 v->flags |= INMEMORY;
1014 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
1015 static void codegen_threadcritrestart(codegendata *cd, int offset)
1017 cd->threadcritcurrent.mcoderestart = offset;
1021 static void codegen_threadcritstart(codegendata *cd, int offset)
1023 cd->threadcritcurrent.mcodebegin = offset;
1027 static void codegen_threadcritstop(codegendata *cd, int offset)
1029 cd->threadcritcurrent.next = cd->threadcrit;
1030 cd->threadcritcurrent.mcodeend = offset;
1031 cd->threadcrit = DNEW(threadcritnodetemp);
1032 *(cd->threadcrit) = cd->threadcritcurrent;
1033 cd->threadcritcount++;
1039 * These are local overrides for various environment variables in Emacs.
1040 * Please do not remove this and leave it at the end of the file, where
1041 * Emacs will automagically detect them.
1042 * ---------------------------------------------------------------------
1045 * indent-tabs-mode: t