1 /* 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 1565 2004-11-23 15:56:37Z twisti $
57 #include "exceptions.h"
60 #include "statistics.h"
61 #include "jit/codegen.inc.h"
62 #include "toolbox/memory.h"
63 #include "toolbox/logging.h"
64 #include "toolbox/avl.h"
65 #include "threads/thread.h"
66 #ifndef STATIC_CLASSPATH
70 /* in this tree we store all method addresses */
72 #if defined(__I386__) || defined(__X86_64__)
73 static struct avl_table *methodtree = NULL;
74 static int methodtree_comparator(const void *pc, const void *element,
79 /* codegen_init ****************************************************************
83 *******************************************************************************/
87 #if defined(__I386__) || defined(__X86_64__)
88 /* this tree is global, not method specific */
90 methodtree_element *mte;
92 methodtree = avl_create(methodtree_comparator, NULL, NULL);
94 mte = NEW(methodtree_element);
96 mte->startpc = (functionptr) asm_calljavafunction;
97 mte->endpc = (functionptr) ((long) asm_calljavafunction2 - 1);
99 avl_insert(methodtree, mte);
101 mte = NEW(methodtree_element);
103 mte->startpc = (functionptr) asm_calljavafunction2;
104 mte->endpc = (functionptr) ((long) asm_call_jit_compiler - 1);
106 avl_insert(methodtree, mte);
112 /* codegen_setup **************************************************************
114 allocates and initialises code area, data area and references
116 *******************************************************************************/
118 void codegen_setup(methodinfo *m, codegendata *cd, t_inlining_globals *id)
120 cd->mcodebase = MNEW(u1, MCODEINITSIZE);
121 cd->mcodesize = MCODEINITSIZE;
123 cd->dsegtop = MNEW(u1, DSEGINITSIZE);
124 cd->dsegsize = DSEGINITSIZE;
125 cd->dsegtop += cd->dsegsize;
128 cd->jumpreferences = NULL;
129 cd->datareferences = NULL;
130 cd->xboundrefs = NULL;
131 cd->xcheckarefs = NULL;
132 cd->xnullrefs = NULL;
133 cd->xcastrefs = NULL;
135 cd->xexceptionrefs = NULL;
136 cd->clinitrefs = NULL;
138 cd->linenumberreferences = NULL;
139 cd->linenumbertablesizepos = 0;
140 cd->linenumbertablestartpos = 0;
141 cd->linenumbertab = 0;
144 cd->exceptiontable = 0;
145 cd->exceptiontablelength = 0;
147 if (useinlining && id) {
148 if (id->cumextablelength > 0) {
149 cd->exceptiontablelength = id->cumextablelength;
150 cd->exceptiontable = DMNEW(exceptiontable, id->cumextablelength + 1);
153 } else if (id && (id->method->exceptiontablelength >0)) {
154 cd->exceptiontablelength = m->exceptiontablelength;
155 cd->exceptiontable = DMNEW(exceptiontable, m->exceptiontablelength + 1);
159 cd->maxstack = id->cummaxstack;
160 cd->maxlocals = id->cumlocals;
162 cd->maxstack = m->maxstack;
163 cd->maxlocals = m->maxlocals;
166 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
167 cd->threadcritcurrent.next = NULL;
168 cd->threadcritcount = 0;
173 /* codegen_free ****************************************************************
175 releases temporary code and data area
177 *******************************************************************************/
179 void codegen_free(methodinfo *m, codegendata *cd)
183 if (cd->exceptiontablelength) {
184 cd->exceptiontablelength = m->exceptiontablelength;
185 MFREE(cd->exceptiontable, exceptiontable, cd->exceptiontablelength + 1);
186 cd->exceptiontable = 0;
187 cd->exceptiontablelength = 0;
192 MFREE(cd->mcodebase, u1, cd->mcodesize);
193 cd->mcodebase = NULL;
197 MFREE(cd->dsegtop - cd->dsegsize, u1, cd->dsegsize);
204 /* codegen_close ***************************************************************
208 *******************************************************************************/
212 /* TODO: release avl tree on i386 and x86_64 */
216 /* codegen_increase doubles code area */
218 static s4 *codegen_increase(codegendata *cd, u1 *codeptr)
222 len = codeptr - cd->mcodebase;
223 cd->mcodebase = MREALLOC(cd->mcodebase,
228 cd->mcodeend = (s4 *) (cd->mcodebase + cd->mcodesize);
230 return (s4 *) (cd->mcodebase + len);
234 /* desg_increase doubles data area */
236 static void dseg_increase(codegendata *cd)
240 newstorage = MNEW(u1, cd->dsegsize * 2);
242 memcpy(newstorage + cd->dsegsize, cd->dsegtop - cd->dsegsize, cd->dsegsize);
243 MFREE(cd->dsegtop - cd->dsegsize, u1, cd->dsegsize);
245 cd->dsegtop = newstorage;
247 cd->dsegtop += cd->dsegsize;
251 static s4 dseg_adds4_increase(codegendata *cd, s4 value)
255 *((s4 *) (cd->dsegtop - cd->dseglen)) = value;
257 return -(cd->dseglen);
261 static s4 dseg_adds4(codegendata *cd, s4 value)
266 dataptr = (s4 *) (cd->dsegtop - cd->dseglen);
268 if (cd->dseglen > cd->dsegsize)
269 return dseg_adds4_increase(cd, value);
273 return -(cd->dseglen);
277 #if !defined(__I386__)
278 static s4 dseg_adds8_increase(codegendata *cd, s8 value)
282 *((s8 *) (cd->dsegtop - cd->dseglen)) = value;
284 return -(cd->dseglen);
288 static s4 dseg_adds8(codegendata *cd, s8 value)
292 cd->dseglen = ALIGN(cd->dseglen + 8, 8);
293 dataptr = (s8 *) (cd->dsegtop - cd->dseglen);
295 if (cd->dseglen > cd->dsegsize)
296 return dseg_adds8_increase(cd, value);
300 return -(cd->dseglen);
305 static s4 dseg_addfloat_increase(codegendata *cd, float value)
309 *((float *) (cd->dsegtop - cd->dseglen)) = value;
311 return -(cd->dseglen);
315 static s4 dseg_addfloat(codegendata *cd, float value)
320 dataptr = (float *) (cd->dsegtop - cd->dseglen);
322 if (cd->dseglen > cd->dsegsize)
323 return dseg_addfloat_increase(cd, value);
327 return -(cd->dseglen);
331 static s4 dseg_adddouble_increase(codegendata *cd, double value)
335 *((double *) (cd->dsegtop - cd->dseglen)) = value;
337 return -(cd->dseglen);
341 static s4 dseg_adddouble(codegendata *cd, double value)
345 cd->dseglen = ALIGN(cd->dseglen + 8, 8);
346 dataptr = (double *) (cd->dsegtop - cd->dseglen);
348 if (cd->dseglen > cd->dsegsize)
349 return dseg_adddouble_increase(cd, value);
353 return -(cd->dseglen);
357 static void dseg_addtarget(codegendata *cd, basicblock *target)
362 jr->tablepos = dseg_addaddress(cd, NULL);
364 jr->next = cd->jumpreferences;
365 cd->jumpreferences = jr;
369 static void dseg_adddata(codegendata *cd, u1 *ptr)
374 dr->pos = (u1 *) (ptr - cd->mcodebase);
375 dr->next = cd->datareferences;
376 cd->datareferences = dr;
380 static void dseg_addlinenumbertablesize(codegendata *cd)
383 dseg_adds4(cd, 0); /*PADDING*/
385 cd->linenumbertablesizepos = dseg_addaddress(cd, NULL); /*it could be considered to use adds4 here, to avoid 1 double word padding on ALPHA */
387 cd->linenumbertablestartpos = dseg_addaddress(cd, NULL);
389 dseg_adds4(cd, 0); /*PADDING*/
394 static void dseg_addlinenumber(codegendata *cd, u2 linenumber, u1 *ptr)
398 lr = DNEW(linenumberref);
399 lr->linenumber = linenumber;
401 lr->targetmpc = (ptr - cd->mcodebase);
402 lr->next = cd->linenumberreferences;
403 cd->linenumberreferences = lr;
407 /* we need this function externally on i386 and x86_64, but keep the call fast
410 #if defined(__I386__) || defined(__X86_64__)
411 void codegen_addreference(codegendata *cd, basicblock *target, void *branchptr)
413 static void codegen_addreference(codegendata *cd, basicblock *target, void *branchptr)
418 branchpos = (u1 *) branchptr - cd->mcodebase;
420 if (target->mpc >= 0) {
421 gen_resolvebranch((u1 *) cd->mcodebase + branchpos,
426 branchref *br = DNEW(branchref);
428 br->branchpos = branchpos;
429 br->next = target->branchrefs;
430 target->branchrefs = br;
435 static void codegen_addxboundrefs(codegendata *cd, void *branchptr, s4 reg)
440 branchpos = (u1 *) branchptr - cd->mcodebase;
442 br = DNEW(branchref);
443 br->branchpos = branchpos;
445 br->next = cd->xboundrefs;
450 static void codegen_addxcheckarefs(codegendata *cd, void *branchptr)
455 branchpos = (u1 *) branchptr - cd->mcodebase;
457 br = DNEW(branchref);
458 br->branchpos = branchpos;
459 br->next = cd->xcheckarefs;
460 cd->xcheckarefs = br;
464 static void codegen_addxnullrefs(codegendata *cd, void *branchptr)
469 branchpos = (u1 *) branchptr - cd->mcodebase;
471 br = DNEW(branchref);
472 br->branchpos = branchpos;
473 br->next = cd->xnullrefs;
478 static void codegen_addxcastrefs(codegendata *cd, void *branchptr)
483 branchpos = (u1 *) branchptr - cd->mcodebase;
485 br = DNEW(branchref);
486 br->branchpos = branchpos;
487 br->next = cd->xcastrefs;
492 static void codegen_addxexceptionrefs(codegendata *cd, void *branchptr)
497 branchpos = (u1 *) branchptr - cd->mcodebase;
499 br = DNEW(branchref);
500 br->branchpos = branchpos;
501 br->next = cd->xexceptionrefs;
502 cd->xexceptionrefs = br;
506 #if defined(__I386__) || defined(__X86_64__)
507 static void codegen_addxdivrefs(codegendata *cd, void *branchptr)
512 branchpos = (u1 *) branchptr - cd->mcodebase;
514 br = DNEW(branchref);
515 br->branchpos = branchpos;
516 br->next = cd->xdivrefs;
522 static void codegen_addclinitref(codegendata *cd,
529 branchpos = (u1 *) branchptr - cd->mcodebase;
531 cr = DNEW(clinitref);
532 cr->branchpos = branchpos;
534 cr->next = cd->clinitrefs;
539 static void codegen_createlinenumbertable(codegendata *cd)
544 for (lr = cd->linenumberreferences; lr != NULL; lr = lr->next) {
545 lr->tablepos = dseg_addaddress(cd, NULL);
547 if (cd->linenumbertab == 0)
548 cd->linenumbertab = lr->tablepos;
550 dseg_addaddress(cd, lr->linenumber);
556 #if defined(__I386__) || defined(__X86_64__)
557 static int methodtree_comparator(const void *pc, const void *element, void *param)
559 methodtree_element *mte;
560 methodtree_element *mtepc;
562 mte = (methodtree_element *) element;
563 mtepc = (methodtree_element *) pc;
565 /* compare both startpc and endpc of pc, even if they have the same value,
566 otherwise the avl_probe sometimes thinks the element is already in the
568 if ((long) mte->startpc <= (long) mtepc->startpc &&
569 (long) mtepc->startpc <= (long) mte->endpc &&
570 (long) mte->startpc <= (long) mtepc->endpc &&
571 (long) mtepc->endpc <= (long) mte->endpc) {
574 } else if ((long) mtepc->startpc < (long) mte->startpc) {
584 void *codegen_findmethod1(void *pc)
586 void * retVal=findmethod(pc);
587 methodinfo **ma=(methodinfo**)retVal;
588 methodinfo *m=ma[-1];
591 utf_display(m->name);
594 else log_text("No methodinfo");
600 void codegen_insertmethod(functionptr startpc, functionptr endpc)
602 methodtree_element *mte;
604 #if defined(USE_THREADS)
605 #if defined(NATIVE_THREADS)
610 mte = NEW(methodtree_element);
611 mte->startpc = startpc;
614 if (avl_insert(methodtree, mte)) {
615 #if defined(USE_THREADS)
616 #if defined(NATIVE_THREADS)
620 throw_cacao_exception_exit(string_java_lang_InternalError,
624 #if defined(USE_THREADS)
625 #if defined(NATIVE_THREADS)
632 functionptr codegen_findmethod(functionptr pc)
634 methodtree_element *mtepc;
635 methodtree_element *mte;
637 #if defined(USE_THREADS)
638 #if defined(NATIVE_THREADS)
643 mtepc = NEW(methodtree_element);
647 mte = avl_find(methodtree, mtepc);
649 FREE(mtepc, methodtree_element);
652 #if defined(USE_THREADS)
653 #if defined(NATIVE_THREADS)
657 throw_cacao_exception_exit(string_java_lang_InternalError,
658 "cannot find function");
661 #if defined(USE_THREADS)
662 #if defined(NATIVE_THREADS)
672 static void codegen_finish(methodinfo *m, codegendata *cd, s4 mcodelen)
679 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
680 extralen = sizeof(threadcritnode) * cd->threadcritcount;
685 #if defined(STATISTICS)
687 count_code_len += mcodelen;
688 count_data_len += cd->dseglen;
692 cd->dseglen = ALIGN(cd->dseglen, MAX_ALIGN);
693 alignedlen = ALIGN(mcodelen, MAX_ALIGN) + cd->dseglen;
695 m->mcodelength = mcodelen + cd->dseglen;
696 m->mcode = (functionptr) (long) CNEW(u1, alignedlen + extralen);
698 memcpy((void *) (long) m->mcode, cd->dsegtop - cd->dseglen, cd->dseglen);
699 memcpy((void *) ((long) m->mcode + cd->dseglen), cd->mcodebase, mcodelen);
701 m->entrypoint = epoint = (functionptr) ((long) m->mcode + cd->dseglen);
703 /* jump table resolving */
704 jr = cd->jumpreferences;
706 *((functionptr *) ((long) epoint + jr->tablepos)) =
707 (functionptr) ((long) epoint + (long) jr->target->mpc);
712 /* line number table resolving */
721 for (lr = cd->linenumberreferences; lr != NULL; lr = lr->next) {
723 *((functionptr *) ((long) epoint + (long) lr->tablepos)) =
724 (functionptr) ((long) epoint + (long) lr->targetmpc);
727 *((functionptr *) ((long) epoint + cd->linenumbertablestartpos)) =
728 (functionptr) ((long) epoint + cd->linenumbertab);
730 *((s8 *) ((s8) epoint + cd->linenumbertablesizepos)) = lrtlen;
732 *((s4 *) ((s4) epoint + cd->linenumbertablesizepos)) = lrtlen;
737 #if defined(__I386__) || defined(__X86_64__)
741 /* add method into methodtree to find the entrypoint */
742 codegen_insertmethod(m->entrypoint,
743 (functionptr) ((long) m->entrypoint + mcodelen));
745 /* data segment references resolving */
746 dr = cd->datareferences;
748 *((functionptr *) ((long) epoint + (long) dr->pos - POINTERSIZE)) =
755 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
757 threadcritnode *n = (threadcritnode *) ((long) m->mcode + alignedlen);
759 threadcritnodetemp *nt = cd->threadcrit;
761 for (i = 0; i < cd->threadcritcount; i++) {
762 n->mcodebegin = (u1 *) (long) m->mcode + nt->mcodebegin;
763 n->mcodeend = (u1 *) (long) m->mcode + nt->mcodeend;
764 n->mcoderestart = (u1 *) (long) m->mcode + nt->mcoderestart;
765 thread_registercritical(n);
774 void dseg_display(methodinfo *m, codegendata *cd)
779 s4ptr = (s4 *) (long) m->mcode;
781 printf(" --- dump of datasegment\n");
782 for (i = cd->dseglen; i > 0 ; i -= 4) {
783 printf("-%6x: %8x\n", i, (s4) (*s4ptr++));
785 printf(" --- begin of data segment: %p\n", (void *) s4ptr);
790 This function determines a register, to which the result of an operation
791 should go, when it is ultimatively intended to store the result in
793 If v is assigned to an actual register, this register will be returned.
794 Otherwise (when v is spilled) this function returns tempregnum.
795 If not already done, regoff and flags are set in the stack location.
798 static int reg_of_var(registerdata *rd, stackptr v, int tempregnum)
802 switch (v->varkind) {
804 if (!(v->flags & INMEMORY))
808 var = &(rd->interfaces[v->varnum][v->type]);
809 v->regoff = var->regoff;
810 if (!(var->flags & INMEMORY))
814 var = &(rd->locals[v->varnum][v->type]);
815 v->regoff = var->regoff;
816 if (!(var->flags & INMEMORY))
820 v->regoff = v->varnum;
821 if (IS_FLT_DBL_TYPE(v->type)) {
822 if (v->varnum < rd->fltreg_argnum) {
823 v->regoff = rd->argfltregs[v->varnum];
824 return(rd->argfltregs[v->varnum]);
828 #if defined(__POWERPC__)
829 if (v->varnum < rd->intreg_argnum - (IS_2_WORD_TYPE(v->type) != 0)) {
831 if (v->varnum < rd->intreg_argnum) {
833 v->regoff = rd->argintregs[v->varnum];
834 return (rd->argintregs[v->varnum]);
837 #if defined(__POWERPC__)
840 v->regoff -= rd->intreg_argnum;
844 v->flags |= INMEMORY;
849 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
850 static void codegen_threadcritrestart(codegendata *cd, int offset)
852 cd->threadcritcurrent.mcoderestart = offset;
856 static void codegen_threadcritstart(codegendata *cd, int offset)
858 cd->threadcritcurrent.mcodebegin = offset;
862 static void codegen_threadcritstop(codegendata *cd, int offset)
864 cd->threadcritcurrent.next = cd->threadcrit;
865 cd->threadcritcurrent.mcodeend = offset;
866 cd->threadcrit = DNEW(threadcritnodetemp);
867 *(cd->threadcrit) = cd->threadcritcurrent;
868 cd->threadcritcount++;
873 #ifndef STATIC_CLASSPATH
874 static size_t codegen_overloadPartLen(utf *desc) {
875 char *utf_ptr=desc->text;
878 while ((c=utf_nextu2(&utf_ptr))!=')') {
895 while ( (c=utf_nextu2(&utf_ptr)) != ';')
901 default: panic ("invalid method descriptor");
908 static void codegen_fillInOverloadPart(char *target,utf *desc) {
909 char *utf_ptr=desc->text;
911 char* insertpos=&target[strlen(target)];
914 while ((c=utf_nextu2(&utf_ptr))!=')') {
932 while ( (c=utf_nextu2(&utf_ptr)) != ';')
933 if ( ((c>='a') && (c<='z')) ||
934 ((c>='A') && (c<='Z')) ||
935 ((c>='0') && (c<='9')) )
937 else *(insertpos++)='_';
943 default: panic ("invalid method descriptor");
950 static void codegen_resolve_native(methodinfo *m,void **insertionPoint,void *jmpTarget,void **jmpPatchTarget) {
951 char *nativeName, *nativeNameEscape;
957 builtin_monitorenter((java_objectheader*) m);
958 if ((*jmpPatchTarget)==jmpTarget) {
959 builtin_monitorexit((java_objectheader*) m);
962 log_text("trying to resolve a native method");
963 utf_display(m->class->name);
964 utf_display(m->name);
966 lib=dlopen(0,RTLD_NOW | RTLD_GLOBAL);
969 /*generate the name of the native function in the form Java_package1_package2...._classname_methodname*/
970 nativeLen=/*Java_*/5+strlen(m->class->name->text)+/*_*/1+strlen(m->name->text)+/*\0*/1;
971 nativeName=MNEW(char,nativeLen);
972 sprintf(nativeName,"Java_%s/%s",m->class->name->text,m->name->text);
974 while (i<nativeLen) {
975 if (nativeName[i]=='_') { /* escape underscore */
976 nativeNameEscape = MNEW(char,nativeLen+1);
977 memcpy(nativeNameEscape,nativeName,i+1);
978 nativeNameEscape[i+1] = '1'; /* escape sequence for _ is _1 */
979 memcpy(&nativeNameEscape[i+2],&nativeName[i+1],nativeLen-i-1);
980 MFREE(nativeName,char,nativeLen);
983 nativeNameEscape[nativeLen]=0;
984 nativeName=nativeNameEscape;
986 if (nativeName[i]=='/') nativeName[i]='_';
990 /* printf("nativename: %s\n",nativeName); */
992 sym=dlsym(lib,nativeName);
995 log_text("resolved");
996 MFREE(nativeName,char,nativeLen);
998 size_t overloadedNativeLen=nativeLen+codegen_overloadPartLen(m->descriptor);
999 char *overloadedNative=MNEW(char,overloadedNativeLen);
1000 sprintf(overloadedNative,"%s",nativeName);
1001 MFREE(nativeName,char,nativeLen);
1002 codegen_fillInOverloadPart(overloadedNative,m->descriptor);
1003 log_text("symbol not found,trying harder (overloaded member ?)");
1004 sym=dlsym(lib,overloadedNative);
1006 MFREE(overloadedNative,char,overloadedNativeLen);
1008 log_text("resolved");
1010 MFREE(overloadedNative,char,overloadedNativeLen);
1011 log_text("It was not possible to find the native function implementation. Not even in overloading case");
1015 (*insertionPoint)=sym;
1016 (*jmpPatchTarget)=jmpTarget;
1017 builtin_monitorexit((java_objectheader *) m );
1021 } else log_text("library not found");
1027 slen=2+strlen(m->class->name->text)+strlen(m->name->text)+strlen(m->descriptor->text);
1028 info=(char*)MNEW(char,slen);
1029 sprintf(info,"%s.%s%s",m->class->name->text,m->name->text,m->descriptor->text);
1031 builtin_monitorexit((java_objectheader *) m );
1032 throw_cacao_exception_exit(string_java_lang_LinkageError,
1041 * These are local overrides for various environment variables in Emacs.
1042 * Please do not remove this and leave it at the end of the file, where
1043 * Emacs will automagically detect them.
1044 * ---------------------------------------------------------------------
1047 * indent-tabs-mode: t