1 /* vm/jit/codegen.inc - architecture independent code generator
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,
9 This file is part of CACAO.
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.
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.
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
26 Contact: cacao@complang.tuwien.ac.at
28 Authors: Reinhard Grafl
31 Changes: Michael Gschwind
34 All functions assume the following code area / data area layout:
38 | code area | code area grows to higher addresses
40 +-----------+ <-- start of procedure
42 | data area | data area grows to lower addresses
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).
51 $Id: codegen.inc 1621 2004-11-30 13:06:55Z twisti $
58 #if !defined(STATIC_CLASSPATH)
62 #include "mm/memory.h"
63 #include "toolbox/avl.h"
64 #include "toolbox/logging.h"
65 #include "native/native.h"
67 #if defined(USE_THREADS)
68 # if defined(NATIVE_THREADS)
69 # include "threads/native/threads.h"
71 # include "threads/green/threads.h"
75 #include "vm/exceptions.h"
76 #include "vm/options.h"
77 #include "vm/statistics.h"
78 #include "vm/jit/codegen.inc.h"
81 /* in this tree we store all method addresses *********************************/
83 #if defined(__I386__) || defined(__X86_64__)
84 static struct avl_table *methodtree = NULL;
85 static int methodtree_comparator(const void *pc, const void *element,
90 /* codegen_init ****************************************************************
94 *******************************************************************************/
98 #if defined(__I386__) || defined(__X86_64__)
99 /* this tree is global, not method specific */
101 methodtree_element *mte;
103 methodtree = avl_create(methodtree_comparator, NULL, NULL);
105 mte = NEW(methodtree_element);
107 mte->startpc = (functionptr) asm_calljavafunction;
108 mte->endpc = (functionptr) ((long) asm_calljavafunction2 - 1);
110 avl_insert(methodtree, mte);
112 mte = NEW(methodtree_element);
114 mte->startpc = (functionptr) asm_calljavafunction2;
115 mte->endpc = (functionptr) ((long) asm_call_jit_compiler - 1);
117 avl_insert(methodtree, mte);
123 /* codegen_setup **************************************************************
125 allocates and initialises code area, data area and references
127 *******************************************************************************/
129 void codegen_setup(methodinfo *m, codegendata *cd, t_inlining_globals *id)
131 cd->mcodebase = MNEW(u1, MCODEINITSIZE);
132 cd->mcodesize = MCODEINITSIZE;
134 cd->dsegtop = MNEW(u1, DSEGINITSIZE);
135 cd->dsegsize = DSEGINITSIZE;
136 cd->dsegtop += cd->dsegsize;
139 cd->jumpreferences = NULL;
140 cd->datareferences = NULL;
141 cd->xboundrefs = NULL;
142 cd->xcheckarefs = NULL;
143 cd->xnullrefs = NULL;
144 cd->xcastrefs = NULL;
146 cd->xexceptionrefs = NULL;
147 cd->clinitrefs = NULL;
149 cd->linenumberreferences = NULL;
150 cd->linenumbertablesizepos = 0;
151 cd->linenumbertablestartpos = 0;
152 cd->linenumbertab = 0;
155 cd->exceptiontable = 0;
156 cd->exceptiontablelength = 0;
158 if (useinlining && id) {
159 if (id->cumextablelength > 0) {
160 cd->exceptiontablelength = id->cumextablelength;
161 cd->exceptiontable = DMNEW(exceptiontable, id->cumextablelength + 1);
164 } else if (id && (id->method->exceptiontablelength >0)) {
165 cd->exceptiontablelength = m->exceptiontablelength;
166 cd->exceptiontable = DMNEW(exceptiontable, m->exceptiontablelength + 1);
170 cd->maxstack = id->cummaxstack;
171 cd->maxlocals = id->cumlocals;
173 cd->maxstack = m->maxstack;
174 cd->maxlocals = m->maxlocals;
177 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
178 cd->threadcritcurrent.next = NULL;
179 cd->threadcritcount = 0;
184 /* codegen_free ****************************************************************
186 releases temporary code and data area
188 *******************************************************************************/
190 void codegen_free(methodinfo *m, codegendata *cd)
194 if (cd->exceptiontablelength) {
195 cd->exceptiontablelength = m->exceptiontablelength;
196 MFREE(cd->exceptiontable, exceptiontable, cd->exceptiontablelength + 1);
197 cd->exceptiontable = 0;
198 cd->exceptiontablelength = 0;
203 MFREE(cd->mcodebase, u1, cd->mcodesize);
204 cd->mcodebase = NULL;
208 MFREE(cd->dsegtop - cd->dsegsize, u1, cd->dsegsize);
215 /* codegen_close ***************************************************************
219 *******************************************************************************/
223 /* TODO: release avl tree on i386 and x86_64 */
227 /* codegen_increase doubles code area */
229 static s4 *codegen_increase(codegendata *cd, u1 *codeptr)
233 len = codeptr - cd->mcodebase;
234 cd->mcodebase = MREALLOC(cd->mcodebase,
239 cd->mcodeend = (s4 *) (cd->mcodebase + cd->mcodesize);
241 return (s4 *) (cd->mcodebase + len);
245 /* desg_increase doubles data area */
247 static void dseg_increase(codegendata *cd)
251 newstorage = MNEW(u1, cd->dsegsize * 2);
253 memcpy(newstorage + cd->dsegsize, cd->dsegtop - cd->dsegsize, cd->dsegsize);
254 MFREE(cd->dsegtop - cd->dsegsize, u1, cd->dsegsize);
256 cd->dsegtop = newstorage;
258 cd->dsegtop += cd->dsegsize;
262 static s4 dseg_adds4_increase(codegendata *cd, s4 value)
266 *((s4 *) (cd->dsegtop - cd->dseglen)) = value;
268 return -(cd->dseglen);
272 static s4 dseg_adds4(codegendata *cd, s4 value)
277 dataptr = (s4 *) (cd->dsegtop - cd->dseglen);
279 if (cd->dseglen > cd->dsegsize)
280 return dseg_adds4_increase(cd, value);
284 return -(cd->dseglen);
288 #if !defined(__I386__)
289 static s4 dseg_adds8_increase(codegendata *cd, s8 value)
293 *((s8 *) (cd->dsegtop - cd->dseglen)) = value;
295 return -(cd->dseglen);
299 static s4 dseg_adds8(codegendata *cd, s8 value)
303 cd->dseglen = ALIGN(cd->dseglen + 8, 8);
304 dataptr = (s8 *) (cd->dsegtop - cd->dseglen);
306 if (cd->dseglen > cd->dsegsize)
307 return dseg_adds8_increase(cd, value);
311 return -(cd->dseglen);
316 static s4 dseg_addfloat_increase(codegendata *cd, float value)
320 *((float *) (cd->dsegtop - cd->dseglen)) = value;
322 return -(cd->dseglen);
326 static s4 dseg_addfloat(codegendata *cd, float value)
331 dataptr = (float *) (cd->dsegtop - cd->dseglen);
333 if (cd->dseglen > cd->dsegsize)
334 return dseg_addfloat_increase(cd, value);
338 return -(cd->dseglen);
342 static s4 dseg_adddouble_increase(codegendata *cd, double value)
346 *((double *) (cd->dsegtop - cd->dseglen)) = value;
348 return -(cd->dseglen);
352 static s4 dseg_adddouble(codegendata *cd, double value)
356 cd->dseglen = ALIGN(cd->dseglen + 8, 8);
357 dataptr = (double *) (cd->dsegtop - cd->dseglen);
359 if (cd->dseglen > cd->dsegsize)
360 return dseg_adddouble_increase(cd, value);
364 return -(cd->dseglen);
368 static void dseg_addtarget(codegendata *cd, basicblock *target)
373 jr->tablepos = dseg_addaddress(cd, NULL);
375 jr->next = cd->jumpreferences;
376 cd->jumpreferences = jr;
380 static void dseg_adddata(codegendata *cd, u1 *ptr)
385 dr->pos = (u1 *) (ptr - cd->mcodebase);
386 dr->next = cd->datareferences;
387 cd->datareferences = dr;
391 static void dseg_addlinenumbertablesize(codegendata *cd)
394 dseg_adds4(cd, 0); /*PADDING*/
396 cd->linenumbertablesizepos = dseg_addaddress(cd, NULL); /*it could be considered to use adds4 here, to avoid 1 double word padding on ALPHA */
398 cd->linenumbertablestartpos = dseg_addaddress(cd, NULL);
400 dseg_adds4(cd, 0); /*PADDING*/
405 static void dseg_addlinenumber(codegendata *cd, u2 linenumber, u1 *ptr)
409 lr = DNEW(linenumberref);
410 lr->linenumber = linenumber;
412 lr->targetmpc = (ptr - cd->mcodebase);
413 lr->next = cd->linenumberreferences;
414 cd->linenumberreferences = lr;
418 /* we need this function externally on i386 and x86_64, but keep the call fast
421 #if defined(__I386__) || defined(__X86_64__)
422 void codegen_addreference(codegendata *cd, basicblock *target, void *branchptr)
424 static void codegen_addreference(codegendata *cd, basicblock *target, void *branchptr)
429 branchpos = (u1 *) branchptr - cd->mcodebase;
431 if (target->mpc >= 0) {
432 gen_resolvebranch((u1 *) cd->mcodebase + branchpos,
437 branchref *br = DNEW(branchref);
439 br->branchpos = branchpos;
440 br->next = target->branchrefs;
441 target->branchrefs = br;
446 static void codegen_addxboundrefs(codegendata *cd, void *branchptr, s4 reg)
451 branchpos = (u1 *) branchptr - cd->mcodebase;
453 br = DNEW(branchref);
454 br->branchpos = branchpos;
456 br->next = cd->xboundrefs;
461 static void codegen_addxcheckarefs(codegendata *cd, void *branchptr)
466 branchpos = (u1 *) branchptr - cd->mcodebase;
468 br = DNEW(branchref);
469 br->branchpos = branchpos;
470 br->next = cd->xcheckarefs;
471 cd->xcheckarefs = br;
475 static void codegen_addxnullrefs(codegendata *cd, void *branchptr)
480 branchpos = (u1 *) branchptr - cd->mcodebase;
482 br = DNEW(branchref);
483 br->branchpos = branchpos;
484 br->next = cd->xnullrefs;
489 static void codegen_addxcastrefs(codegendata *cd, void *branchptr)
494 branchpos = (u1 *) branchptr - cd->mcodebase;
496 br = DNEW(branchref);
497 br->branchpos = branchpos;
498 br->next = cd->xcastrefs;
503 static void codegen_addxexceptionrefs(codegendata *cd, void *branchptr)
508 branchpos = (u1 *) branchptr - cd->mcodebase;
510 br = DNEW(branchref);
511 br->branchpos = branchpos;
512 br->next = cd->xexceptionrefs;
513 cd->xexceptionrefs = br;
517 #if defined(__I386__) || defined(__X86_64__)
518 static void codegen_addxdivrefs(codegendata *cd, void *branchptr)
523 branchpos = (u1 *) branchptr - cd->mcodebase;
525 br = DNEW(branchref);
526 br->branchpos = branchpos;
527 br->next = cd->xdivrefs;
533 static void codegen_addclinitref(codegendata *cd,
540 branchpos = (u1 *) branchptr - cd->mcodebase;
542 cr = DNEW(clinitref);
543 cr->branchpos = branchpos;
545 cr->next = cd->clinitrefs;
550 static void codegen_createlinenumbertable(codegendata *cd)
555 for (lr = cd->linenumberreferences; lr != NULL; lr = lr->next) {
556 lr->tablepos = dseg_addaddress(cd, NULL);
558 if (cd->linenumbertab == 0)
559 cd->linenumbertab = lr->tablepos;
561 dseg_addaddress(cd, lr->linenumber);
567 #if defined(__I386__) || defined(__X86_64__)
568 static int methodtree_comparator(const void *pc, const void *element, void *param)
570 methodtree_element *mte;
571 methodtree_element *mtepc;
573 mte = (methodtree_element *) element;
574 mtepc = (methodtree_element *) pc;
576 /* compare both startpc and endpc of pc, even if they have the same value,
577 otherwise the avl_probe sometimes thinks the element is already in the
579 if ((long) mte->startpc <= (long) mtepc->startpc &&
580 (long) mtepc->startpc <= (long) mte->endpc &&
581 (long) mte->startpc <= (long) mtepc->endpc &&
582 (long) mtepc->endpc <= (long) mte->endpc) {
585 } else if ((long) mtepc->startpc < (long) mte->startpc) {
595 void *codegen_findmethod1(void *pc)
597 void * retVal=findmethod(pc);
598 methodinfo **ma=(methodinfo**)retVal;
599 methodinfo *m=ma[-1];
602 utf_display(m->name);
605 else log_text("No methodinfo");
611 void codegen_insertmethod(functionptr startpc, functionptr endpc)
613 methodtree_element *mte;
615 #if defined(USE_THREADS)
616 #if defined(NATIVE_THREADS)
621 mte = NEW(methodtree_element);
622 mte->startpc = startpc;
625 if (avl_insert(methodtree, mte)) {
626 #if defined(USE_THREADS)
627 #if defined(NATIVE_THREADS)
631 throw_cacao_exception_exit(string_java_lang_InternalError,
635 #if defined(USE_THREADS)
636 #if defined(NATIVE_THREADS)
643 functionptr codegen_findmethod(functionptr pc)
645 methodtree_element *mtepc;
646 methodtree_element *mte;
648 #if defined(USE_THREADS)
649 #if defined(NATIVE_THREADS)
654 mtepc = NEW(methodtree_element);
658 mte = avl_find(methodtree, mtepc);
660 FREE(mtepc, methodtree_element);
663 #if defined(USE_THREADS)
664 #if defined(NATIVE_THREADS)
668 throw_cacao_exception_exit(string_java_lang_InternalError,
669 "cannot find function");
672 #if defined(USE_THREADS)
673 #if defined(NATIVE_THREADS)
683 static void codegen_finish(methodinfo *m, codegendata *cd, s4 mcodelen)
690 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
691 extralen = sizeof(threadcritnode) * cd->threadcritcount;
696 #if defined(STATISTICS)
698 count_code_len += mcodelen;
699 count_data_len += cd->dseglen;
703 cd->dseglen = ALIGN(cd->dseglen, MAX_ALIGN);
704 alignedlen = ALIGN(mcodelen, MAX_ALIGN) + cd->dseglen;
706 m->mcodelength = mcodelen + cd->dseglen;
707 m->mcode = (functionptr) (long) CNEW(u1, alignedlen + extralen);
709 memcpy((void *) (long) m->mcode, cd->dsegtop - cd->dseglen, cd->dseglen);
710 memcpy((void *) ((long) m->mcode + cd->dseglen), cd->mcodebase, mcodelen);
712 m->entrypoint = epoint = (functionptr) ((long) m->mcode + cd->dseglen);
714 /* jump table resolving */
715 jr = cd->jumpreferences;
717 *((functionptr *) ((long) epoint + jr->tablepos)) =
718 (functionptr) ((long) epoint + (long) jr->target->mpc);
723 /* line number table resolving */
732 for (lr = cd->linenumberreferences; lr != NULL; lr = lr->next) {
734 *((functionptr *) ((long) epoint + (long) lr->tablepos)) =
735 (functionptr) ((long) epoint + (long) lr->targetmpc);
738 *((functionptr *) ((long) epoint + cd->linenumbertablestartpos)) =
739 (functionptr) ((long) epoint + cd->linenumbertab);
741 *((s8 *) ((s8) epoint + cd->linenumbertablesizepos)) = lrtlen;
743 *((s4 *) ((s4) epoint + cd->linenumbertablesizepos)) = lrtlen;
748 #if defined(__I386__) || defined(__X86_64__)
752 /* add method into methodtree to find the entrypoint */
753 codegen_insertmethod(m->entrypoint,
754 (functionptr) ((long) m->entrypoint + mcodelen));
756 /* data segment references resolving */
757 dr = cd->datareferences;
759 *((functionptr *) ((long) epoint + (long) dr->pos - POINTERSIZE)) =
766 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
768 threadcritnode *n = (threadcritnode *) ((long) m->mcode + alignedlen);
770 threadcritnodetemp *nt = cd->threadcrit;
772 for (i = 0; i < cd->threadcritcount; i++) {
773 n->mcodebegin = (u1 *) (long) m->mcode + nt->mcodebegin;
774 n->mcodeend = (u1 *) (long) m->mcode + nt->mcodeend;
775 n->mcoderestart = (u1 *) (long) m->mcode + nt->mcoderestart;
776 thread_registercritical(n);
785 void dseg_display(methodinfo *m, codegendata *cd)
790 s4ptr = (s4 *) (long) m->mcode;
792 printf(" --- dump of datasegment\n");
793 for (i = cd->dseglen; i > 0 ; i -= 4) {
794 printf("-%6x: %8x\n", i, (s4) (*s4ptr++));
796 printf(" --- begin of data segment: %p\n", (void *) s4ptr);
801 This function determines a register, to which the result of an operation
802 should go, when it is ultimatively intended to store the result in
804 If v is assigned to an actual register, this register will be returned.
805 Otherwise (when v is spilled) this function returns tempregnum.
806 If not already done, regoff and flags are set in the stack location.
809 static int reg_of_var(registerdata *rd, stackptr v, int tempregnum)
813 switch (v->varkind) {
815 if (!(v->flags & INMEMORY))
819 var = &(rd->interfaces[v->varnum][v->type]);
820 v->regoff = var->regoff;
821 if (!(var->flags & INMEMORY))
825 var = &(rd->locals[v->varnum][v->type]);
826 v->regoff = var->regoff;
827 if (!(var->flags & INMEMORY))
831 v->regoff = v->varnum;
832 if (IS_FLT_DBL_TYPE(v->type)) {
833 if (v->varnum < rd->fltreg_argnum) {
834 v->regoff = rd->argfltregs[v->varnum];
835 return(rd->argfltregs[v->varnum]);
839 #if defined(__POWERPC__)
840 if (v->varnum < rd->intreg_argnum - (IS_2_WORD_TYPE(v->type) != 0)) {
842 if (v->varnum < rd->intreg_argnum) {
844 v->regoff = rd->argintregs[v->varnum];
845 return (rd->argintregs[v->varnum]);
848 #if defined(__POWERPC__)
851 v->regoff -= rd->intreg_argnum;
855 v->flags |= INMEMORY;
860 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
861 static void codegen_threadcritrestart(codegendata *cd, int offset)
863 cd->threadcritcurrent.mcoderestart = offset;
867 static void codegen_threadcritstart(codegendata *cd, int offset)
869 cd->threadcritcurrent.mcodebegin = offset;
873 static void codegen_threadcritstop(codegendata *cd, int offset)
875 cd->threadcritcurrent.next = cd->threadcrit;
876 cd->threadcritcurrent.mcodeend = offset;
877 cd->threadcrit = DNEW(threadcritnodetemp);
878 *(cd->threadcrit) = cd->threadcritcurrent;
879 cd->threadcritcount++;
884 #ifndef STATIC_CLASSPATH
885 static size_t codegen_overloadPartLen(utf *desc) {
886 char *utf_ptr=desc->text;
889 while ((c=utf_nextu2(&utf_ptr))!=')') {
906 while ( (c=utf_nextu2(&utf_ptr)) != ';')
912 default: panic ("invalid method descriptor");
919 static void codegen_fillInOverloadPart(char *target,utf *desc) {
920 char *utf_ptr=desc->text;
922 char* insertpos=&target[strlen(target)];
925 while ((c=utf_nextu2(&utf_ptr))!=')') {
943 while ( (c=utf_nextu2(&utf_ptr)) != ';')
944 if ( ((c>='a') && (c<='z')) ||
945 ((c>='A') && (c<='Z')) ||
946 ((c>='0') && (c<='9')) )
948 else *(insertpos++)='_';
954 default: panic ("invalid method descriptor");
961 static void codegen_resolve_native(methodinfo *m,void **insertionPoint,void *jmpTarget,void **jmpPatchTarget) {
962 char *nativeName, *nativeNameEscape;
968 builtin_monitorenter((java_objectheader*) m);
969 if ((*jmpPatchTarget)==jmpTarget) {
970 builtin_monitorexit((java_objectheader*) m);
973 log_text("trying to resolve a native method");
974 utf_display(m->class->name);
975 utf_display(m->name);
977 lib=dlopen(0,RTLD_NOW | RTLD_GLOBAL);
980 /*generate the name of the native function in the form Java_package1_package2...._classname_methodname*/
981 nativeLen=/*Java_*/5+strlen(m->class->name->text)+/*_*/1+strlen(m->name->text)+/*\0*/1;
982 nativeName=MNEW(char,nativeLen);
983 sprintf(nativeName,"Java_%s/%s",m->class->name->text,m->name->text);
985 while (i<nativeLen) {
986 if (nativeName[i]=='_') { /* escape underscore */
987 nativeNameEscape = MNEW(char,nativeLen+1);
988 memcpy(nativeNameEscape,nativeName,i+1);
989 nativeNameEscape[i+1] = '1'; /* escape sequence for _ is _1 */
990 memcpy(&nativeNameEscape[i+2],&nativeName[i+1],nativeLen-i-1);
991 MFREE(nativeName,char,nativeLen);
994 nativeNameEscape[nativeLen]=0;
995 nativeName=nativeNameEscape;
997 if (nativeName[i]=='/') nativeName[i]='_';
1001 /* printf("nativename: %s\n",nativeName); */
1003 sym=dlsym(lib,nativeName);
1006 log_text("resolved");
1007 MFREE(nativeName,char,nativeLen);
1009 size_t overloadedNativeLen=nativeLen+codegen_overloadPartLen(m->descriptor);
1010 char *overloadedNative=MNEW(char,overloadedNativeLen);
1011 sprintf(overloadedNative,"%s",nativeName);
1012 MFREE(nativeName,char,nativeLen);
1013 codegen_fillInOverloadPart(overloadedNative,m->descriptor);
1014 log_text("symbol not found,trying harder (overloaded member ?)");
1015 sym=dlsym(lib,overloadedNative);
1017 MFREE(overloadedNative,char,overloadedNativeLen);
1019 log_text("resolved");
1021 MFREE(overloadedNative,char,overloadedNativeLen);
1022 log_text("It was not possible to find the native function implementation. Not even in overloading case");
1026 (*insertionPoint)=sym;
1027 (*jmpPatchTarget)=jmpTarget;
1028 builtin_monitorexit((java_objectheader *) m );
1032 } else log_text("library not found");
1038 slen=2+strlen(m->class->name->text)+strlen(m->name->text)+strlen(m->descriptor->text);
1039 info=(char*)MNEW(char,slen);
1040 sprintf(info,"%s.%s%s",m->class->name->text,m->name->text,m->descriptor->text);
1042 builtin_monitorexit((java_objectheader *) m );
1043 throw_cacao_exception_exit(string_java_lang_LinkageError,
1052 * These are local overrides for various environment variables in Emacs.
1053 * Please do not remove this and leave it at the end of the file, where
1054 * Emacs will automagically detect them.
1055 * ---------------------------------------------------------------------
1058 * indent-tabs-mode: t