* src/vm/jit/arm/emit.c (mm/memory.h): Added.
[cacao.git] / src / vm / jit / arm / emit.c
1 /* src/vm/jit/arm/emit.c - Arm code emitter functions
2
3    Copyright (C) 1996-2005, 2006, 2007 R. Grafl, A. Krall, C. Kruegel,
4    C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring,
5    E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich,
6    J. Wenninger, Institut f. Computersprachen - TU Wien
7
8    This file is part of CACAO.
9
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.
14
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.
19
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., 51 Franklin Street, Fifth Floor, Boston, MA
23    02110-1301, USA.
24
25    $Id: emit.c 4398 2006-01-31 23:43:08Z twisti $
26
27 */
28
29
30 #include "config.h"
31
32 #include <assert.h>
33
34 #include "vm/types.h"
35
36 #include "md-abi.h"
37
38 #include "vm/jit/arm/codegen.h"
39
40 #include "mm/memory.h"
41
42 #if defined(ENABLE_THREADS)
43 # include "threads/native/lock.h"
44 #endif
45
46 #include "vm/builtin.h"
47 #include "vm/global.h"
48
49 #include "vm/jit/asmpart.h"
50 #include "vm/jit/emit-common.h"
51 #include "vm/jit/jit.h"
52 #include "vm/jit/replace.h"
53
54 #include "toolbox/logging.h" /* XXX for debugging only */
55
56
57 /* emit_load *******************************************************************
58
59    Emits a possible load of an operand.
60
61 *******************************************************************************/
62
63 s4 emit_load(jitdata *jd, instruction *iptr, varinfo *src, s4 tempreg)
64 {
65         codegendata  *cd;
66         s4            disp;
67         s4            reg;
68
69         /* get required compiler data */
70
71         cd = jd->cd;
72
73         if (src->flags & INMEMORY) {
74                 COUNT_SPILLS;
75
76                 disp = src->vv.regoff * 4;
77
78                 if (IS_FLT_DBL_TYPE(src->type)) {
79 #if defined(ENABLE_SOFTFLOAT)
80                         if (IS_2_WORD_TYPE(src->type))
81                                 M_LLD(tempreg, REG_SP, disp);
82                         else
83                                 M_ILD(tempreg, REG_SP, disp);
84 #else
85                         if (IS_2_WORD_TYPE(src->type))
86                                 M_DLD(tempreg, REG_SP, disp);
87                         else
88                                 M_FLD(tempreg, REG_SP, disp);
89 #endif
90                 }
91                 else {
92                         if (IS_2_WORD_TYPE(src->type))
93                                 M_LLD(tempreg, REG_SP, disp);
94                         else
95                                 M_ILD(tempreg, REG_SP, disp);
96                 }
97
98                 reg = tempreg;
99         }
100         else
101                 reg = src->vv.regoff;
102
103         return reg;
104 }
105
106
107 /* emit_load_low ***************************************************************
108
109    Emits a possible load of the low 32-bits of a long source operand.
110
111 *******************************************************************************/
112
113 s4 emit_load_low(jitdata *jd, instruction *iptr, varinfo *src, s4 tempreg)
114 {
115         codegendata  *cd;
116         s4            disp;
117         s4            reg;
118
119         assert(src->type == TYPE_LNG);
120
121         /* get required compiler data */
122
123         cd = jd->cd;
124
125         if (src->flags & INMEMORY) {
126                 COUNT_SPILLS;
127
128                 disp = src->vv.regoff * 4;
129
130 #if defined(__ARMEL__)
131                 M_ILD(tempreg, REG_SP, disp);
132 #else
133                 M_ILD(tempreg, REG_SP, disp + 4);
134 #endif
135
136                 reg = tempreg;
137         }
138         else
139                 reg = GET_LOW_REG(src->vv.regoff);
140
141         return reg;
142 }
143
144
145 /* emit_load_high **************************************************************
146
147    Emits a possible load of the high 32-bits of a long source operand.
148
149 *******************************************************************************/
150
151 s4 emit_load_high(jitdata *jd, instruction *iptr, varinfo *src, s4 tempreg)
152 {
153         codegendata  *cd;
154         s4            disp;
155         s4            reg;
156
157         assert(src->type == TYPE_LNG);
158
159         /* get required compiler data */
160
161         cd = jd->cd;
162
163         if (src->flags & INMEMORY) {
164                 COUNT_SPILLS;
165
166                 disp = src->vv.regoff * 4;
167
168 #if defined(__ARMEL__)
169                 M_ILD(tempreg, REG_SP, disp + 4);
170 #else
171                 M_ILD(tempreg, REG_SP, disp);
172 #endif
173
174                 reg = tempreg;
175         }
176         else
177                 reg = GET_HIGH_REG(src->vv.regoff);
178
179         return reg;
180 }
181
182
183 /* emit_store ******************************************************************
184
185    Emits a possible store to a variable.
186
187 *******************************************************************************/
188
189 void emit_store(jitdata *jd, instruction *iptr, varinfo *dst, s4 d)
190 {
191         codegendata  *cd;
192         s4            disp;
193
194         /* get required compiler data */
195
196         cd = jd->cd;
197
198         if (dst->flags & INMEMORY) {
199                 COUNT_SPILLS;
200
201                 disp = dst->vv.regoff * 4;
202
203                 if (IS_FLT_DBL_TYPE(dst->type)) {
204 #if defined(ENABLE_SOFTFLOAT)
205                         if (IS_2_WORD_TYPE(dst->type))
206                                 M_LST(d, REG_SP, disp);
207                         else
208                                 M_IST(d, REG_SP, disp);
209 #else
210                         if (IS_2_WORD_TYPE(dst->type))
211                                 M_DST(d, REG_SP, disp);
212                         else
213                                 M_FST(d, REG_SP, disp);
214 #endif
215                 }
216                 else {
217                         if (IS_2_WORD_TYPE(dst->type))
218                                 M_LST(d, REG_SP, disp);
219                         else
220                                 M_IST(d, REG_SP, disp);
221                 }
222         }
223         else if (IS_LNG_TYPE(dst->type)) {
224 #if defined(__ARMEL__)
225                 if (GET_HIGH_REG(dst->vv.regoff) == REG_SPLIT)
226                         M_IST_INTERN(GET_HIGH_REG(d), REG_SP, 0 * 4);
227 #else
228                 if (GET_LOW_REG(dst->vv.regoff) == REG_SPLIT)
229                         M_IST_INTERN(GET_LOW_REG(d), REG_SP, 0 * 4);
230 #endif
231         }
232 }
233
234
235 /* emit_copy *******************************************************************
236
237    XXX
238
239 *******************************************************************************/
240
241 void emit_copy(jitdata *jd, instruction *iptr, varinfo *src, varinfo *dst)
242 {
243         codegendata  *cd;
244         registerdata *rd;
245         s4            s1, d;
246
247         /* get required compiler data */
248
249         cd = jd->cd;
250         rd = jd->rd;
251
252         /* XXX dummy call, removed me!!! */
253         d = codegen_reg_of_var(iptr->opc, dst, REG_ITMP1);
254
255         if ((src->vv.regoff != dst->vv.regoff) ||
256                 ((src->flags ^ dst->flags) & INMEMORY)) {
257
258                 /* If one of the variables resides in memory, we can eliminate
259                    the register move from/to the temporary register with the
260                    order of getting the destination register and the load. */
261
262                 if (IS_INMEMORY(src->flags)) {
263 #if !defined(ENABLE_SOFTFLOAT)
264                         if (IS_FLT_DBL_TYPE(src->type))
265                                 d = codegen_reg_of_var(iptr->opc, dst, REG_FTMP1);
266                         else
267 #endif
268                         {
269                                 if (IS_2_WORD_TYPE(src->type))
270                                         d = codegen_reg_of_var(iptr->opc, dst, REG_ITMP12_PACKED);
271                                 else
272                                         d = codegen_reg_of_var(iptr->opc, dst, REG_ITMP1);
273                         }
274
275                         s1 = emit_load(jd, iptr, src, d);
276                 }
277                 else {
278 #if !defined(ENABLE_SOFTFLOAT)
279                         if (IS_FLT_DBL_TYPE(src->type))
280                                 s1 = emit_load(jd, iptr, src, REG_FTMP1);
281                         else
282 #endif
283                         {
284                                 if (IS_2_WORD_TYPE(src->type))
285                                         s1 = emit_load(jd, iptr, src, REG_ITMP12_PACKED);
286                                 else
287                                         s1 = emit_load(jd, iptr, src, REG_ITMP1);
288                         }
289
290                         d = codegen_reg_of_var(iptr->opc, dst, s1);
291                 }
292
293                 if (s1 != d) {
294                         if (IS_FLT_DBL_TYPE(src->type)) {
295 #if defined(ENABLE_SOFTFLOAT)
296                                 if (IS_2_WORD_TYPE(src->type))
297                                         M_LNGMOVE(s1, d);
298                                 else
299                                         /* XXX grrrr, wrong direction! */
300                                         M_MOV(d, s1);
301 #else
302                                 if (IS_2_WORD_TYPE(src->type))
303                                         M_DMOV(s1, d);
304                                 else
305                                         M_FMOV(s1, d);
306 #endif
307                         }
308                         else {
309                                 if (IS_2_WORD_TYPE(src->type))
310                                         M_LNGMOVE(s1, d);
311                                 else
312                                         /* XXX grrrr, wrong direction! */
313                                         M_MOV(d, s1);
314                         }
315                 }
316
317                 emit_store(jd, iptr, dst, d);
318         }
319 }
320
321
322 /* emit_iconst *****************************************************************
323
324    XXX
325
326 *******************************************************************************/
327
328 void emit_iconst(codegendata *cd, s4 d, s4 value)
329 {
330         s4 disp;
331
332         if (IS_IMM(value))
333                 M_MOV_IMM(d, value);
334         else {
335                 disp = dseg_add_s4(cd, value);
336                 M_DSEG_LOAD(d, disp);
337         }
338 }
339
340
341 /* emit_nullpointer_check ******************************************************
342
343    Emit a NullPointerException check.
344
345 *******************************************************************************/
346
347 void emit_nullpointer_check(codegendata *cd, instruction *iptr, s4 reg)
348 {
349         if (INSTRUCTION_MUST_CHECK(iptr)) {
350                 M_TST(reg, reg);
351                 M_BEQ(0);
352                 codegen_add_nullpointerexception_ref(cd);
353         }
354 }
355
356
357 /* emit_arrayindexoutofbounds_check ********************************************
358
359    Emit a ArrayIndexOutOfBoundsException check.
360
361 *******************************************************************************/
362
363 void emit_arrayindexoutofbounds_check(codegendata *cd, instruction *iptr, s4 s1, s4 s2)
364 {
365         if (INSTRUCTION_MUST_CHECK(iptr)) {
366                 M_ILD_INTERN(REG_ITMP3, s1, OFFSET(java_arrayheader, size));
367                 M_CMP(s2, REG_ITMP3);
368                 M_BHS(0);
369                 codegen_add_arrayindexoutofboundsexception_ref(cd, s2);
370         }
371 }
372
373
374 /* emit_exception_stubs ********************************************************
375
376    Generates the code for the exception stubs.
377
378 *******************************************************************************/
379
380 void emit_exception_stubs(jitdata *jd)
381 {
382         codegendata  *cd;
383         registerdata *rd;
384         exceptionref *er;
385         s4            branchmpc;
386         s4            targetmpc;
387         s4            targetdisp;
388         s4            disp;
389
390         /* get required compiler data */
391
392         cd = jd->cd;
393         rd = jd->rd;
394
395         /* generate exception stubs */
396
397         targetdisp = 0;
398
399         for (er = cd->exceptionrefs; er != NULL; er = er->next) {
400                 /* back-patch the branch to this exception code */
401
402                 branchmpc = er->branchpos;
403                 targetmpc = cd->mcodeptr - cd->mcodebase;
404
405                 md_codegen_patch_branch(cd, branchmpc, targetmpc);
406
407                 MCODECHECK(100);
408
409                 /* Check if the exception is an
410                    ArrayIndexOutOfBoundsException.  If so, move index register
411                    into REG_ITMP1. */
412
413                 if (er->reg != -1)
414                         M_MOV(REG_ITMP1, er->reg);
415
416                 /* calcuate exception address */
417
418                 assert((er->branchpos - 4) % 4 == 0);
419                 M_ADD_IMM_EXT_MUL4(REG_ITMP2_XPC, REG_IP, (er->branchpos - 4) / 4);
420
421                 /* move function to call into REG_ITMP3 */
422
423                 disp = dseg_add_functionptr(cd, er->function);
424                 M_DSEG_LOAD(REG_ITMP3, disp);
425
426                 if (targetdisp == 0) {
427                         targetdisp = ((u4 *) cd->mcodeptr) - ((u4 *) cd->mcodebase);
428
429                         M_MOV(rd->argintregs[0], REG_IP);
430                         M_MOV(rd->argintregs[1], REG_SP);
431
432                         if (jd->isleafmethod)
433                                 M_MOV(rd->argintregs[2], REG_LR);
434                         else
435                                 M_LDR(rd->argintregs[2], REG_SP,
436                                           cd->stackframesize * 4 - SIZEOF_VOID_P);
437
438                         M_MOV(rd->argintregs[3], REG_ITMP2_XPC);
439
440                         /* save registers */
441                         /* TODO: we only need to save LR in leaf methods */
442
443                         M_STMFD(BITMASK_ARGS | 1<<REG_IP | 1<<REG_LR, REG_SP);
444
445                         /* move a3 to stack */
446
447                         M_STR_UPDATE(REG_ITMP1, REG_SP, -4);
448
449                         /* do the exception call */
450
451                         M_MOV(REG_LR, REG_PC);
452                         M_MOV(REG_PC, REG_ITMP3);
453
454                         M_ADD_IMM(REG_SP, REG_SP, 4);
455
456                         /* result of stacktrace is our XPTR */
457
458                         M_MOV(REG_ITMP1_XPTR, REG_RESULT);
459
460                         /* restore registers */
461
462                         M_LDMFD(BITMASK_ARGS | 1<<REG_IP | 1<<REG_LR, REG_SP);
463
464                         disp = dseg_add_functionptr(cd, asm_handle_exception);
465                         M_DSEG_LOAD(REG_ITMP3, disp);
466                         M_MOV(REG_PC, REG_ITMP3);
467                 }
468                 else {
469                         disp = (((u4 *) cd->mcodebase) + targetdisp) -
470                                 (((u4 *) cd->mcodeptr) + 2);
471
472                         M_B(disp);
473                 }
474         }
475 }
476
477
478 /* emit_patcher_stubs **********************************************************
479
480    Generates the code for the patcher stubs.
481
482 *******************************************************************************/
483
484 void emit_patcher_stubs(jitdata *jd)
485 {
486         codegendata *cd;
487         patchref    *pref;
488         u4           mcode;
489         u1          *savedmcodeptr;
490         u1          *tmpmcodeptr;
491         s4           targetdisp;
492         s4           disp;
493
494         /* get required compiler data */
495
496         cd = jd->cd;
497
498         /* generate patcher stub call code */
499
500         targetdisp = 0;
501
502         for (pref = cd->patchrefs; pref != NULL; pref = pref->next) {
503                 /* check code segment size */
504
505                 MCODECHECK(100);
506
507                 /* Get machine code which is patched back in later. The
508                    call is 1 instruction word long. */
509
510                 tmpmcodeptr = (u1 *) (cd->mcodebase + pref->branchpos);
511
512                 mcode = *((u4 *) tmpmcodeptr);
513
514                 /* Patch in the call to call the following code (done at
515                    compile time). */
516
517                 savedmcodeptr = cd->mcodeptr;   /* save current mcodeptr              */
518                 cd->mcodeptr  = tmpmcodeptr;    /* set mcodeptr to patch position     */
519
520                 disp = ((u4 *) savedmcodeptr) - (((u4 *) tmpmcodeptr) + 2);
521                 M_B(disp);
522
523                 cd->mcodeptr = savedmcodeptr;   /* restore the current mcodeptr       */
524
525                 /* create stack frame (align stack to 8-byte) */
526
527                 M_SUB_IMM(REG_SP, REG_SP, 8 * 4);
528
529                 /* save itmp3 onto stack */
530
531                 M_STR_INTERN(REG_ITMP3, REG_SP, 6 * 4);
532
533                 /* calculate return address and move it onto stack */
534                 /* ATTENTION: we can not use BL to branch to patcher stub,        */
535                 /* ATTENTION: because we need to preserve LR for leaf methods     */
536
537                 disp = (s4) (((u4 *) cd->mcodeptr) - (((u4 *) tmpmcodeptr) + 1) + 2);
538
539                 M_SUB_IMM_EXT_MUL4(REG_ITMP3, REG_PC, disp);
540                 M_STR_INTERN(REG_ITMP3, REG_SP, 4 * 4);
541
542                 /* move pointer to java_objectheader onto stack */
543
544 #if defined(ENABLE_THREADS)
545                 /* order reversed because of data segment layout */
546
547                 (void) dseg_add_unique_address(cd, NULL);           /* flcword    */
548                 (void) dseg_add_unique_address(cd, lock_get_initial_lock_word());
549                 disp = dseg_add_unique_address(cd, NULL);           /* vftbl      */
550
551                 M_SUB_IMM_EXT_MUL4(REG_ITMP3, REG_IP, -disp / 4);
552                 M_STR_INTERN(REG_ITMP3, REG_SP, 3 * 4);
553 #else
554                 M_EOR(REG_ITMP3, REG_ITMP3, REG_ITMP3);
555                 M_STR_INTERN(REG_ITMP3, REG_SP, 3 * 4);
556 #endif
557
558                 /* move machine code onto stack */
559
560                 disp = dseg_add_unique_s4(cd, mcode);
561                 M_DSEG_LOAD(REG_ITMP3, disp);
562                 M_STR_INTERN(REG_ITMP3, REG_SP, 2 * 4);
563
564                 /* move class/method/field reference onto stack */
565
566                 disp = dseg_add_unique_address(cd, pref->ref);
567                 M_DSEG_LOAD(REG_ITMP3, disp);
568                 M_STR_INTERN(REG_ITMP3, REG_SP, 1 * 4);
569
570                 /* move data segment displacement onto stack */
571
572                 disp = dseg_add_unique_s4(cd, pref->disp);
573                 M_DSEG_LOAD(REG_ITMP3, disp);
574                 M_STR_INTERN(REG_ITMP3, REG_SP, 5 * 4);
575
576                 /* move patcher function pointer onto stack */
577
578                 disp = dseg_add_functionptr(cd, pref->patcher);
579                 M_DSEG_LOAD(REG_ITMP3, disp);
580                 M_STR_INTERN(REG_ITMP3, REG_SP, 0 * 4);
581
582                 /* finally call the patcher via asm_patcher_wrapper */
583                 /* ATTENTION: don't use REG_IP here, because some patchers need it */
584
585                 if (targetdisp == 0) {
586                         targetdisp = ((u4 *) cd->mcodeptr) - ((u4 *) cd->mcodebase);
587
588                         disp = dseg_add_functionptr(cd, asm_patcher_wrapper);
589                         /*M_DSEG_BRANCH_NOLINK(REG_PC, REG_IP, a);*/
590                         /* TODO: this is only a hack */
591                         M_DSEG_LOAD(REG_ITMP3, disp);
592                         M_MOV(REG_PC, REG_ITMP3);
593                 }
594                 else {
595                         disp = (((u4 *) cd->mcodebase) + targetdisp) -
596                                 (((u4 *) cd->mcodeptr) + 2);
597
598                         M_B(disp);
599                 }
600         }
601 }
602
603
604 /* emit_replacement_stubs ******************************************************
605
606    Generates the code for the replacement stubs.
607
608 *******************************************************************************/
609
610 #if defined(ENABLE_REPLACEMENT)
611 void emit_replacement_stubs(jitdata *jd)
612 {
613         codegendata *cd;
614         codeinfo    *code;
615         rplpoint    *rplp;
616         u1          *savedmcodeptr;
617         s4           disp;
618         s4           i;
619
620         /* get required compiler data */
621
622         cd   = jd->cd;
623         code = jd->code;
624 }
625 #endif /* defined(ENABLE_REPLACEMENT) */
626
627
628 /* emit_verbosecall_enter ******************************************************
629
630    Generates the code for the call trace.
631
632 *******************************************************************************/
633
634 #if !defined(NDEBUG)
635 void emit_verbosecall_enter(jitdata *jd)
636 {
637         methodinfo   *m;
638         codegendata  *cd;
639         registerdata *rd;
640         methoddesc   *md;
641         s4            stackframesize;
642         s4            disp;
643         s4            i, t, s1, s2;
644
645         /* get required compiler data */
646
647         m  = jd->m;
648         cd = jd->cd;
649         rd = jd->rd;
650
651         md = m->parseddesc;
652
653         /* stackframesize is changed below */
654
655         stackframesize = cd->stackframesize;
656
657         /* mark trace code */
658
659         M_NOP;
660
661         /* save argument registers to stack (including LR and IP) */
662         M_STMFD(BITMASK_ARGS | (1<<REG_LR) | (1<<REG_IP), REG_SP);
663         M_SUB_IMM(REG_SP, REG_SP, (2 + 2 + 1) * 4);     /* space for a3, a4 and m */
664
665         stackframesize += 6 + 2 + 2 + 1;
666
667         /* prepare args for tracer */
668
669         i = md->paramcount - 1;
670
671         if (i > 3)
672                 i = 3;
673
674         for (; i >= 0; i--) {
675                 t = md->paramtypes[i].type;
676
677                 /* load argument into register (s1) and make it of TYPE_LNG */
678
679                 if (!md->params[i].inmemory) {
680                         s1 = md->params[i].regoff;
681
682                         if (!IS_2_WORD_TYPE(t)) {
683                                 M_MOV_IMM(REG_ITMP1, 0);
684                                 s1 = PACK_REGS(s1, REG_ITMP1);
685                         }
686                         else {
687                                 SPLIT_OPEN(t, s1, REG_ITMP1);
688                                 SPLIT_LOAD(t, s1, stackframesize);
689                         }
690                 }
691                 else {
692                         s1 = md->params[i].regoff + stackframesize;
693
694                         if (IS_2_WORD_TYPE(t))
695                                 M_LLD(REG_ITMP12_PACKED, REG_SP, s1 * 4);
696                         else
697                                 M_ILD(REG_ITMP1, REG_SP, s1 * 4);
698                 }
699
700                 /* place argument for tracer */
701
702                 if (i < 2) {
703 #if defined(__ARMEL__)
704                         s2 = PACK_REGS(rd->argintregs[i * 2], rd->argintregs[i * 2 + 1]);
705 #else /* defined(__ARMEB__) */
706                         s2 = PACK_REGS(rd->argintregs[i * 2 + 1], rd->argintregs[i * 2]);
707 #endif          
708                         M_LNGMOVE(s1, s2);
709                 }
710                 else {
711                         s2 = (i - 2) * 2;
712                         M_LST(s1, REG_SP, s2 * 4);
713                 }
714         }
715
716         /* prepare methodinfo pointer for tracer */
717
718         disp = dseg_add_address(cd, m);
719         M_DSEG_LOAD(REG_ITMP1, disp);
720         M_STR_INTERN(REG_ITMP1, REG_SP, 16);
721
722         /* call tracer here (we use a long branch) */
723
724         M_LONGBRANCH(builtin_trace_args);
725
726         /* restore argument registers from stack */
727
728         M_ADD_IMM(REG_SP, REG_SP, (2 + 2 + 1) * 4);        /* free argument stack */
729         M_LDMFD(BITMASK_ARGS | (1<<REG_LR) | (1<<REG_IP), REG_SP);
730
731         /* mark trace code */
732
733         M_NOP;
734 }
735 #endif /* !defined(NDEBUG) */
736
737
738 /* emit_verbosecall_exit *******************************************************
739
740    Generates the code for the call trace.
741
742 *******************************************************************************/
743
744 #if !defined(NDEBUG)
745 void emit_verbosecall_exit(jitdata *jd)
746 {
747         methodinfo   *m;
748         codegendata  *cd;
749         registerdata *rd;
750         methoddesc   *md;
751         s4            disp;
752
753         /* get required compiler data */
754
755         m  = jd->m;
756         cd = jd->cd;
757         rd = jd->rd;
758
759         md = m->parseddesc;
760
761         /* mark trace code */
762
763         M_NOP;
764
765         M_STMFD(BITMASK_RESULT | (1<<REG_LR) | (1<<REG_IP), REG_SP);
766         M_SUB_IMM(REG_SP, REG_SP, (1 + 1) * 4);    /* space for d[high reg] and f */
767
768         switch (md->returntype.type) {
769         case TYPE_ADR:
770         case TYPE_INT:
771                 M_INTMOVE(REG_RESULT, GET_LOW_REG(REG_A1_A2_PACKED));
772                 M_MOV_IMM(GET_HIGH_REG(REG_A1_A2_PACKED), 0);
773                 break;
774
775         case TYPE_LNG:
776                 M_LNGMOVE(REG_RESULT_PACKED, REG_A1_A2_PACKED);
777                 break;
778
779         case TYPE_FLT:
780                 M_IST(REG_RESULT, REG_SP, 1 * 4);
781                 break;
782
783         case TYPE_DBL:
784                 M_INTMOVE(REG_RESULT, REG_A3);
785                 M_IST(REG_RESULT2, REG_SP, 0 * 4);
786                 break;
787         }
788
789         disp = dseg_add_address(cd, m);
790         M_DSEG_LOAD(REG_A0, disp);
791         M_LONGBRANCH(builtin_displaymethodstop);
792
793         M_ADD_IMM(REG_SP, REG_SP, (1 + 1) * 4);            /* free argument stack */
794         M_LDMFD(BITMASK_RESULT | (1<<REG_LR) | (1<<REG_IP), REG_SP);
795
796         /* mark trace code */
797
798         M_NOP;
799 }
800 #endif /* !defined(NDEBUG) */
801
802
803 /*
804  * These are local overrides for various environment variables in Emacs.
805  * Please do not remove this and leave it at the end of the file, where
806  * Emacs will automagically detect them.
807  * ---------------------------------------------------------------------
808  * Local variables:
809  * mode: c
810  * indent-tabs-mode: t
811  * c-basic-offset: 4
812  * tab-width: 4
813  * End:
814  * vim:noexpandtab:sw=4:ts=4:
815  */