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