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 3021 2005-07-12 23:47:22Z twisti $
63 #include "mm/memory.h"
64 #include "toolbox/avl.h"
65 #include "toolbox/logging.h"
66 #include "native/native.h"
68 #if defined(USE_THREADS)
69 # if defined(NATIVE_THREADS)
70 # include "threads/native/threads.h"
72 # include "threads/green/threads.h"
76 #include "vm/exceptions.h"
77 #include "vm/method.h"
78 #include "vm/options.h"
79 #include "vm/statistics.h"
80 #include "vm/jit/codegen.inc.h"
81 #include "vm/jit/jit.h"
89 /* in this tree we store all method addresses *********************************/
91 #if defined(__I386__) || defined(__X86_64__)
92 static struct avl_table *methodtree = NULL;
93 static int methodtree_comparator(const void *pc, const void *element,
98 /* codegen_init ****************************************************************
102 *******************************************************************************/
104 void codegen_init(void)
106 #if defined(__I386__) || defined(__X86_64__)
107 /* this tree is global, not method specific */
109 methodtree_element *mte;
111 methodtree = avl_create(methodtree_comparator, NULL, NULL);
113 /* insert asm_calljavafunction */
115 mte = NEW(methodtree_element);
117 mte->startpc = (functionptr) asm_calljavafunction;
118 mte->endpc = (functionptr) ((ptrint) asm_calljavafunction2 - 1);
120 avl_insert(methodtree, mte);
122 /* insert asm_calljavafunction2 */
124 mte = NEW(methodtree_element);
126 mte->startpc = (functionptr) asm_calljavafunction2;
127 mte->endpc = (functionptr) ((ptrint) asm_call_jit_compiler - 1);
129 avl_insert(methodtree, mte);
135 /* codegen_setup **************************************************************
137 allocates and initialises code area, data area and references
139 *******************************************************************************/
141 void codegen_setup(methodinfo *m, codegendata *cd, t_inlining_globals *id)
143 cd->mcodebase = DMNEW(u1, MCODEINITSIZE);
144 cd->mcodesize = MCODEINITSIZE;
146 cd->dsegtop = DMNEW(u1, DSEGINITSIZE);
147 cd->dsegsize = DSEGINITSIZE;
148 cd->dsegtop += cd->dsegsize;
151 cd->jumpreferences = NULL;
152 cd->datareferences = NULL;
153 cd->xboundrefs = NULL;
154 cd->xcheckarefs = NULL;
155 cd->xnullrefs = NULL;
156 cd->xcastrefs = NULL;
157 cd->xstorerefs = NULL;
159 cd->xexceptionrefs = NULL;
160 cd->patchrefs = NULL;
162 cd->linenumberreferences = NULL;
163 cd->linenumbertablesizepos = 0;
164 cd->linenumbertablestartpos = 0;
165 cd->linenumbertab = 0;
168 cd->exceptiontable = 0;
169 cd->exceptiontablelength = 0;
171 if (useinlining && id) {
172 if (id->cumextablelength > 0) {
173 cd->exceptiontablelength = id->cumextablelength;
175 DMNEW(exceptiontable, id->cumextablelength + 1);
178 } else if (id && (id->method->exceptiontablelength > 0)) {
179 cd->exceptiontablelength = m->exceptiontablelength;
180 cd->exceptiontable = DMNEW(exceptiontable, m->exceptiontablelength + 1);
184 cd->maxstack = id->cummaxstack;
185 cd->maxlocals = id->cumlocals;
187 cd->maxstack = m->maxstack;
188 cd->maxlocals = m->maxlocals;
191 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
192 cd->threadcritcurrent.next = NULL;
193 cd->threadcritcount = 0;
198 /* codegen_free ****************************************************************
200 Releases temporary code and data area.
202 *******************************************************************************/
204 void codegen_free(methodinfo *m, codegendata *cd)
209 MFREE(cd->mcodebase, u1, cd->mcodesize);
210 cd->mcodebase = NULL;
214 MFREE(cd->dsegtop - cd->dsegsize, u1, cd->dsegsize);
222 /* codegen_close ***************************************************************
226 *******************************************************************************/
228 void codegen_close(void)
230 /* TODO: release avl tree on i386 and x86_64 */
234 /* codegen_increase doubles code area */
236 static s4 *codegen_increase(codegendata *cd, u1 *codeptr)
240 len = codeptr - cd->mcodebase;
241 cd->mcodebase = DMREALLOC(cd->mcodebase,
246 cd->mcodeend = (s4 *) (cd->mcodebase + cd->mcodesize);
248 return (s4 *) (cd->mcodebase + len);
252 /* desg_increase doubles data area */
254 static void dseg_increase(codegendata *cd)
258 newstorage = DMNEW(u1, cd->dsegsize * 2);
260 MCOPY(newstorage + cd->dsegsize, cd->dsegtop - cd->dsegsize, u1,
263 cd->dsegtop = newstorage;
265 cd->dsegtop += cd->dsegsize;
269 static s4 dseg_adds4_increase(codegendata *cd, s4 value)
273 *((s4 *) (cd->dsegtop - cd->dseglen)) = value;
275 return -(cd->dseglen);
279 static s4 dseg_adds4(codegendata *cd, s4 value)
284 dataptr = (s4 *) (cd->dsegtop - cd->dseglen);
286 if (cd->dseglen > cd->dsegsize)
287 return dseg_adds4_increase(cd, value);
291 return -(cd->dseglen);
295 #if !defined(__I386__) && !defined(__POWERPC__)
296 static s4 dseg_adds8_increase(codegendata *cd, s8 value)
300 *((s8 *) (cd->dsegtop - cd->dseglen)) = value;
302 return -(cd->dseglen);
306 static s4 dseg_adds8(codegendata *cd, s8 value)
310 cd->dseglen = ALIGN(cd->dseglen + 8, 8);
311 dataptr = (s8 *) (cd->dsegtop - cd->dseglen);
313 if (cd->dseglen > cd->dsegsize)
314 return dseg_adds8_increase(cd, value);
318 return -(cd->dseglen);
323 #if !defined(__XDSPCORE__)
324 static s4 dseg_addfloat_increase(codegendata *cd, float value)
328 *((float *) (cd->dsegtop - cd->dseglen)) = value;
330 return -(cd->dseglen);
334 static s4 dseg_addfloat(codegendata *cd, float value)
339 dataptr = (float *) (cd->dsegtop - cd->dseglen);
341 if (cd->dseglen > cd->dsegsize)
342 return dseg_addfloat_increase(cd, value);
346 return -(cd->dseglen);
350 static s4 dseg_adddouble_increase(codegendata *cd, double value)
354 *((double *) (cd->dsegtop - cd->dseglen)) = value;
356 return -(cd->dseglen);
360 static s4 dseg_adddouble(codegendata *cd, double value)
364 cd->dseglen = ALIGN(cd->dseglen + 8, 8);
365 dataptr = (double *) (cd->dsegtop - cd->dseglen);
367 if (cd->dseglen > cd->dsegsize)
368 return dseg_adddouble_increase(cd, value);
372 return -(cd->dseglen);
374 #endif /* !defined(__XDSPCORE__) */
377 static void dseg_addtarget(codegendata *cd, basicblock *target)
382 jr->tablepos = dseg_addaddress(cd, NULL);
384 jr->next = cd->jumpreferences;
385 cd->jumpreferences = jr;
389 #if defined(__I386__) || defined(__X86_64__)
390 static void dseg_adddata(codegendata *cd, u1 *ptr)
395 dr->pos = (u1 *) (ptr - cd->mcodebase);
396 dr->next = cd->datareferences;
397 cd->datareferences = dr;
402 static void dseg_addlinenumbertablesize(codegendata *cd)
404 #if defined (__ALPHA__) || defined(__MIPS__) || defined (__X86_64__)
405 dseg_adds4(cd, 0); /* ALIGNMENT PADDING */
408 /* it could be considered to use adds4 here, to avoid 1 double word */
409 /* padding on ALPHA */
411 cd->linenumbertablesizepos = dseg_addaddress(cd, NULL);
412 cd->linenumbertablestartpos = dseg_addaddress(cd, NULL);
414 #if defined(__ALPHA__) || defined(__MIPS__) || defined (__X86_64__)
415 dseg_adds4(cd, 0); /* ALIGNMENT PADDING */
420 static void dseg_addlinenumber(codegendata *cd, u2 linenumber, u1 *ptr)
424 lr = DNEW(linenumberref);
425 lr->linenumber = linenumber;
427 lr->targetmpc = (ptr - cd->mcodebase);
428 lr->next = cd->linenumberreferences;
429 cd->linenumberreferences = lr;
433 /* we need this function externally on i386 and x86_64, but keep the call fast
436 #if defined(__I386__) || defined(__X86_64__)
437 void codegen_addreference(codegendata *cd, basicblock *target, void *branchptr)
439 static void codegen_addreference(codegendata *cd, basicblock *target, void *branchptr)
444 branchpos = (u1 *) branchptr - cd->mcodebase;
446 /* Check if the target basicblock has already a start pc, so the jump is */
447 /* backward and we can resolve it immediately. */
449 if (target->mpc >= 0) {
450 gen_resolvebranch((u1 *) cd->mcodebase + branchpos,
455 branchref *br = DNEW(branchref);
457 br->branchpos = branchpos;
458 br->next = target->branchrefs;
459 target->branchrefs = br;
464 static void codegen_addxboundrefs(codegendata *cd, void *branchptr, s4 reg)
469 branchpos = (u1 *) branchptr - cd->mcodebase;
471 br = DNEW(branchref);
472 br->branchpos = branchpos;
474 br->next = cd->xboundrefs;
479 static void codegen_addxcheckarefs(codegendata *cd, void *branchptr)
484 branchpos = (u1 *) branchptr - cd->mcodebase;
486 br = DNEW(branchref);
487 br->branchpos = branchpos;
488 br->next = cd->xcheckarefs;
489 cd->xcheckarefs = br;
493 static void codegen_addxnullrefs(codegendata *cd, void *branchptr)
498 branchpos = (u1 *) branchptr - cd->mcodebase;
500 br = DNEW(branchref);
501 br->branchpos = branchpos;
502 br->next = cd->xnullrefs;
507 static void codegen_addxcastrefs(codegendata *cd, void *branchptr)
512 branchpos = (u1 *) branchptr - cd->mcodebase;
514 br = DNEW(branchref);
515 br->branchpos = branchpos;
516 br->next = cd->xcastrefs;
521 /* codegen_addxstorerefs *******************************************************
523 Adds an ArrayStoreException branch to the list.
525 *******************************************************************************/
527 static void codegen_addxstorerefs(codegendata *cd, void *branchptr)
532 branchpos = (u1 *) branchptr - cd->mcodebase;
534 br = DNEW(branchref);
535 br->branchpos = branchpos;
536 br->next = cd->xstorerefs;
541 /* codegen_addxdivrefs *********************************************************
543 Adds an ArithmeticException branch to the list.
545 *******************************************************************************/
547 static void codegen_addxdivrefs(codegendata *cd, void *branchptr)
552 branchpos = (u1 *) branchptr - cd->mcodebase;
554 br = DNEW(branchref);
555 br->branchpos = branchpos;
556 br->next = cd->xdivrefs;
561 static void codegen_addxexceptionrefs(codegendata *cd, void *branchptr)
566 branchpos = (u1 *) branchptr - cd->mcodebase;
568 br = DNEW(branchref);
569 br->branchpos = branchpos;
570 br->next = cd->xexceptionrefs;
571 cd->xexceptionrefs = br;
575 static void codegen_addpatchref(codegendata *cd,
583 branchpos = (u1 *) branchptr - cd->mcodebase;
586 pr->branchpos = branchpos;
587 pr->patcher = patcher;
589 pr->next = cd->patchrefs;
594 static void codegen_createlinenumbertable(codegendata *cd)
598 for (lr = cd->linenumberreferences; lr != NULL; lr = lr->next) {
599 lr->tablepos = dseg_addaddress(cd, NULL);
601 if (cd->linenumbertab == 0)
602 cd->linenumbertab = lr->tablepos;
604 dseg_addaddress(cd, lr->linenumber);
609 #if defined(__I386__) || defined(__X86_64__)
610 /* methodtree_comparator *******************************************************
614 *******************************************************************************/
616 static int methodtree_comparator(const void *pc, const void *element, void *param)
618 methodtree_element *mte;
619 methodtree_element *mtepc;
621 mte = (methodtree_element *) element;
622 mtepc = (methodtree_element *) pc;
624 /* compare both startpc and endpc of pc, even if they have the same value,
625 otherwise the avl_probe sometimes thinks the element is already in the
628 if ((long) mte->startpc <= (long) mtepc->startpc &&
629 (long) mtepc->startpc <= (long) mte->endpc &&
630 (long) mte->startpc <= (long) mtepc->endpc &&
631 (long) mtepc->endpc <= (long) mte->endpc) {
634 } else if ((long) mtepc->startpc < (long) mte->startpc) {
643 /* codegen_insertmethod ********************************************************
647 *******************************************************************************/
649 void codegen_insertmethod(functionptr startpc, functionptr endpc)
651 methodtree_element *mte;
653 #if defined(USE_THREADS)
654 #if defined(NATIVE_THREADS)
659 mte = NEW(methodtree_element);
660 mte->startpc = startpc;
663 if (avl_insert(methodtree, mte)) {
664 #if defined(USE_THREADS)
665 #if defined(NATIVE_THREADS)
670 throw_cacao_exception_exit(string_java_lang_InternalError,
674 #if defined(USE_THREADS)
675 #if defined(NATIVE_THREADS)
682 /* codegen_findmethod **********************************************************
686 *******************************************************************************/
688 functionptr codegen_findmethod(functionptr pc)
690 methodtree_element *mtepc;
691 methodtree_element *mte;
693 #if defined(USE_THREADS)
694 #if defined(NATIVE_THREADS)
699 mtepc = NEW(methodtree_element);
703 mte = avl_find(methodtree, mtepc);
705 FREE(mtepc, methodtree_element);
708 #if defined(USE_THREADS)
709 #if defined(NATIVE_THREADS)
713 printf("Cannot find Java function at %p\n", (void *) (ptrint) pc);
715 throw_cacao_exception_exit(string_java_lang_InternalError,
716 "Cannot find Java function at %p", pc);
719 #if defined(USE_THREADS)
720 #if defined(NATIVE_THREADS)
727 #endif /* defined(__I386__) || defined(__X86_64__) */
730 /* codegen_finish **************************************************************
732 Finishes the code generation. A new memory, large enough for both
733 data and code, is allocated and data and code are copied together
734 to their final layout, unresolved jumps are resolved, ...
736 *******************************************************************************/
738 static void codegen_finish(methodinfo *m, codegendata *cd, s4 mcodelen)
745 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
746 extralen = sizeof(threadcritnode) * cd->threadcritcount;
751 #if defined(STATISTICS)
753 count_code_len += mcodelen;
754 count_data_len += cd->dseglen;
758 cd->dseglen = ALIGN(cd->dseglen, MAX_ALIGN);
759 alignedlen = ALIGN(mcodelen, MAX_ALIGN) + cd->dseglen;
761 /* allocate new memory */
763 m->mcodelength = mcodelen + cd->dseglen;
764 m->mcode = (functionptr) (ptrint) CNEW(u1, alignedlen + extralen);
766 /* copy data and code to their new location */
768 MCOPY((void *) (ptrint) m->mcode, cd->dsegtop - cd->dseglen, u1,
770 MCOPY((void *) ((ptrint) m->mcode + cd->dseglen), cd->mcodebase, u1,
773 m->entrypoint = epoint = (functionptr) ((ptrint) m->mcode + cd->dseglen);
775 /* jump table resolving */
777 jr = cd->jumpreferences;
779 *((functionptr *) ((ptrint) epoint + jr->tablepos)) =
780 (functionptr) ((ptrint) epoint + (ptrint) jr->target->mpc);
784 /* line number table resolving */
789 for (lr = cd->linenumberreferences; lr != NULL; lr = lr->next) {
791 *((functionptr *) ((ptrint) epoint + (ptrint) lr->tablepos)) =
792 (functionptr) ((ptrint) epoint + (ptrint) lr->targetmpc);
795 *((functionptr *) ((ptrint) epoint + cd->linenumbertablestartpos)) =
796 (functionptr) ((ptrint) epoint + cd->linenumbertab);
798 *((ptrint *) ((ptrint) epoint + cd->linenumbertablesizepos)) = lrtlen;
801 #if defined(__I386__) || defined(__X86_64__)
802 /* add method into methodtree to find the entrypoint */
804 codegen_insertmethod(m->entrypoint,
805 (functionptr) ((ptrint) m->entrypoint + mcodelen));
808 #if defined(__I386__) || defined(__X86_64__) || defined(__XDSPCORE__)
812 /* data segment references resolving */
814 dr = cd->datareferences;
816 *((functionptr *) ((ptrint) epoint + (ptrint) dr->pos -
817 SIZEOF_VOID_P)) = epoint;
823 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
825 threadcritnode *n = (threadcritnode *) ((ptrint) m->mcode + alignedlen);
827 threadcritnodetemp *nt = cd->threadcrit;
829 for (i = 0; i < cd->threadcritcount; i++) {
830 n->mcodebegin = (u1 *) (ptrint) m->mcode + nt->mcodebegin;
831 n->mcodeend = (u1 *) (ptrint) m->mcode + nt->mcodeend;
832 n->mcoderestart = (u1 *) (ptrint) m->mcode + nt->mcoderestart;
833 thread_registercritical(n);
842 void dseg_display(methodinfo *m, codegendata *cd)
847 s4ptr = (s4 *) (ptrint) m->mcode;
849 printf(" --- dump of datasegment\n");
850 for (i = cd->dseglen; i > 0 ; i -= 4) {
851 #if SIZEOF_VOID_P == 8
852 printf("0x%016lx: -%6x (%6d): %8x\n",
853 (ptrint) s4ptr, i, i, (s4) *s4ptr);
855 printf("0x%08x: -%6x (%6d): %8x\n",
856 (ptrint) s4ptr, i, i, (s4) *s4ptr);
861 printf(" --- begin of data segment: %p\n", (void *) s4ptr);
866 /* codegen_createnativestub ****************************************************
868 Wrapper for createnativestub.
870 *******************************************************************************/
872 functionptr codegen_createnativestub(functionptr f, methodinfo *m)
876 t_inlining_globals *id;
882 /* mark dump memory */
884 dumpsize = dump_size();
886 cd = DNEW(codegendata);
887 rd = DNEW(registerdata);
888 id = DNEW(t_inlining_globals);
890 /* setup code generation stuff */
892 inlining_setup(m, id);
893 reg_setup(m, rd, id);
894 codegen_setup(m, cd, id);
896 /* create new method descriptor with additional native parameters */
899 nativeparams = (m->flags & ACC_STATIC) ? 2 : 1;
901 nmd = (methoddesc *) DMNEW(u1, sizeof(methoddesc) - sizeof(typedesc) +
902 md->paramcount * sizeof(typedesc) +
903 nativeparams * sizeof(typedesc));
905 nmd->paramcount = md->paramcount + nativeparams;
907 nmd->params = DMNEW(paramdesc, nmd->paramcount);
909 nmd->paramtypes[0].type = TYPE_ADR; /* add environment pointer */
911 if (m->flags & ACC_STATIC)
912 nmd->paramtypes[1].type = TYPE_ADR; /* add class pointer */
914 MCOPY(nmd->paramtypes + nativeparams, md->paramtypes, typedesc,
919 /* generate the code */
921 m->stubroutine = createnativestub(f, m, cd, rd, nmd);
923 /* entrypoint was set in codegen_finish, clear it */
925 m->entrypoint = NULL;
927 #if defined(STATISTICS)
929 count_nstub_len += m->mcodelength;
932 /* disassemble native stub */
934 if (opt_shownativestub) {
935 codegen_disassemble_nativestub(m, (s4 *) (ptrint) m->stubroutine,
936 m->mcodelength - cd->dseglen);
938 /* show data segment */
940 if (opt_showddatasegment)
946 dump_release(dumpsize);
948 /* return stubroutine entry point */
950 return m->stubroutine;
954 /* codegen_disassemble_nativestub **********************************************
956 Disassembles the generated native stub.
958 *******************************************************************************/
960 void codegen_disassemble_nativestub(methodinfo *m, s4 *code, s4 len)
962 printf("Native stub: ");
963 utf_fprint_classname(stdout, m->class->name);
965 utf_fprint(stdout, m->name);
966 utf_fprint(stdout, m->descriptor);
967 printf("\n\nLength: %d\n\n", len);
969 #if defined(__I386__) || defined(__X86_64__)
970 disassemble((u1 *) code, len);
972 disassemble(code, len);
977 /* removecompilerstub **********************************************************
979 Deletes a compilerstub from memory (simply by freeing it).
981 *******************************************************************************/
983 void removecompilerstub(functionptr stub)
989 /* removenativestub ************************************************************
991 Removes a previously created native-stub from memory.
993 *******************************************************************************/
995 void removenativestub(functionptr stub)
1002 This function determines a register, to which the result of an operation
1003 should go, when it is ultimatively intended to store the result in
1005 If v is assigned to an actual register, this register will be returned.
1006 Otherwise (when v is spilled) this function returns tempregnum.
1007 If not already done, regoff and flags are set in the stack location.
1010 static int reg_of_var(registerdata *rd, stackptr v, int tempregnum)
1014 switch (v->varkind) {
1016 if (!(v->flags & INMEMORY))
1021 var = &(rd->interfaces[v->varnum][v->type]);
1022 v->regoff = var->regoff;
1023 if (!(var->flags & INMEMORY))
1024 return(var->regoff);
1028 var = &(rd->locals[v->varnum][v->type]);
1029 v->regoff = var->regoff;
1030 if (!(var->flags & INMEMORY))
1031 return(var->regoff);
1035 if (!(v->flags & INMEMORY))
1041 count_spills_read++;
1043 v->flags |= INMEMORY;
1048 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
1049 static void codegen_threadcritrestart(codegendata *cd, int offset)
1051 cd->threadcritcurrent.mcoderestart = offset;
1055 static void codegen_threadcritstart(codegendata *cd, int offset)
1057 cd->threadcritcurrent.mcodebegin = offset;
1061 static void codegen_threadcritstop(codegendata *cd, int offset)
1063 cd->threadcritcurrent.next = cd->threadcrit;
1064 cd->threadcritcurrent.mcodeend = offset;
1065 cd->threadcrit = DNEW(threadcritnodetemp);
1066 *(cd->threadcrit) = cd->threadcritcurrent;
1067 cd->threadcritcount++;
1073 * These are local overrides for various environment variables in Emacs.
1074 * Please do not remove this and leave it at the end of the file, where
1075 * Emacs will automagically detect them.
1076 * ---------------------------------------------------------------------
1079 * indent-tabs-mode: t