e9eed202b16e4c7dce53574fd7f61013ba7f46ef
[cacao.git] / src / vm / jit / mips / md.c
1 /* src/vm/jit/mips/md.c - machine dependent MIPS functions
2
3    Copyright (C) 1996-2005, 2006 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    Contact: cacao@cacaojvm.org
26
27    Authors: Christian Thalinger
28             Edwin Steiner
29
30    $Id: md.c 6180 2006-12-11 23:29:26Z twisti $
31
32 */
33
34
35 #include "config.h"
36
37 #include <assert.h>
38 #include <unistd.h>
39 #include <sys/cachectl.h>
40
41 #include "vm/types.h"
42
43 #include "toolbox/logging.h"
44 #include "vm/exceptions.h"
45 #include "vm/global.h"
46 #include "vm/vm.h"
47 #include "vm/jit/stacktrace.h"
48
49 #if !defined(NDEBUG) && defined(ENABLE_DISASSEMBLER)
50 #include "vm/options.h" /* XXX debug */
51 #include "vm/jit/disass.h" /* XXX debug */
52 #endif
53
54
55 /* md_codegen_patch_branch *****************************************************
56
57    Back-patches a branch instruction.
58
59 *******************************************************************************/
60
61 void md_codegen_patch_branch(codegendata *cd, s4 branchmpc, s4 targetmpc)
62 {
63         s4 *mcodeptr;
64         s4  mcode;
65         s4  disp;                           /* branch displacement                */
66         s4  lo;
67         s4  hi;
68
69         /* calculate the patch position */
70
71         mcodeptr = (s4 *) (cd->mcodebase + branchmpc);
72
73         /* get the instruction before the exception point */
74
75         mcode = mcodeptr[-1];
76
77         /* check for: ori t9,t9,0 */
78
79         if ((mcode >> 16) == 0x3739) {
80                 /* Calculate the branch displacement.  For jumps we need a
81                    displacement relative to PV. */
82
83                 disp = targetmpc;
84
85         lo = (short) disp;
86         hi = (short) ((disp - lo) >> 16);
87
88                 /* patch the two instructions before the mcodeptr */
89
90                 mcodeptr[-2] |= (hi & 0x0000ffff);
91                 mcodeptr[-1] |= (lo & 0x0000ffff);
92         }
93         else {
94                 /* Calculate the branch displacement.  For branches we need a
95                    displacement relative and shifted to the branch PC. */
96
97                 disp = (targetmpc - branchmpc) >> 2;
98
99                 /* On the MIPS we can only branch signed 16-bit instruction words
100                    (signed 18-bit = 32KB = +/- 16KB). Check this! */
101
102                 if ((disp < (s4) 0xffff8000) || (disp > (s4) 0x00007fff))
103                         vm_abort("jump displacement is out of range: %d > +/-%d", disp, 0x00007fff);
104
105                 /* patch the branch instruction before the mcodeptr */
106
107                 mcodeptr[-1] |= (disp & 0x0000ffff);
108         }
109 }
110
111
112 /* md_stacktrace_get_returnaddress *********************************************
113
114    Returns the return address of the current stackframe, specified by
115    the passed stack pointer and the stack frame size.
116
117 *******************************************************************************/
118
119 u1 *md_stacktrace_get_returnaddress(u1 *sp, u4 framesize)
120 {
121         u1 *ra;
122
123         /* on MIPS the return address is located on the top of the stackframe */
124
125         /* XXX change this if we ever want to use 4-byte stackslots */
126         /* ra = *((u1 **) (sp + framesize - SIZEOF_VOID_P)); */
127         ra = *((u1 **) (sp + framesize - 8));
128
129         return ra;
130 }
131
132
133 /* md_get_method_patch_address *************************************************
134
135    Gets the patch address of the currently compiled method. The offset
136    is extracted from the load instruction(s) before the jump and added
137    to the right base address (PV or REG_METHODPTR).
138
139    INVOKESTATIC/SPECIAL:
140
141    dfdeffb8    ld       s8,-72(s8)
142    03c0f809    jalr     s8
143    00000000    nop
144
145    INVOKEVIRTUAL:
146
147    dc990000    ld       t9,0(a0)
148    df3e0000    ld       s8,0(t9)
149    03c0f809    jalr     s8
150    00000000    nop
151
152    INVOKEINTERFACE:
153
154    dc990000    ld       t9,0(a0)
155    df39ff90    ld       t9,-112(t9)
156    df3e0018    ld       s8,24(t9)
157    03c0f809    jalr     s8
158    00000000    nop
159
160 *******************************************************************************/
161
162 u1 *md_get_method_patch_address(u1 *ra, stackframeinfo *sfi, u1 *mptr)
163 {
164         u4  mcode;
165         s4  offset;
166         u1 *pa;
167
168         /* go back to the actual load instruction (3 instructions on MIPS) */
169
170         ra -= 3 * 4;
171
172         /* get first instruction word on current PC */
173
174         mcode = *((u4 *) ra);
175
176         /* check if we have 2 instructions (lui) */
177
178         if ((mcode >> 16) == 0x3c19) {
179                 /* XXX write a regression for this */
180                 assert(0);
181
182                 /* get displacement of first instruction (lui) */
183
184                 offset = (s4) (mcode << 16);
185
186                 /* get displacement of second instruction (daddiu) */
187
188                 mcode = *((u4 *) (ra + 1 * 4));
189
190                 assert((mcode >> 16) != 0x6739);
191
192                 offset += (s2) (mcode & 0x0000ffff);
193         }
194         else {
195                 /* get first instruction (ld) */
196
197                 mcode = *((u4 *) ra);
198
199                 /* get the offset from the instruction */
200
201                 offset = (s2) (mcode & 0x0000ffff);
202
203                 /* check for call with REG_METHODPTR: ld s8,x(t9) */
204
205 #if SIZEOF_VOID_P == 8
206                 if ((mcode >> 16) == 0xdf3e) {
207 #else
208                 if ((mcode >> 16) == 0x8f3e) {
209 #endif
210                         /* in this case we use the passed method pointer */
211
212                         /* return NULL if no mptr was specified (used for replacement) */
213
214                         if (mptr == NULL)
215                                 return NULL;
216
217                         pa = mptr + offset;
218                 }
219                 else {
220                         /* in the normal case we check for a `ld s8,x(s8)' instruction */
221
222 #if SIZEOF_VOID_P == 8
223                         assert((mcode >> 16) == 0xdfde);
224 #else
225                         assert((mcode >> 16) == 0x8fde);
226 #endif
227
228                         /* and get the final data segment address */
229
230                         pa = sfi->pv + offset;
231                 }
232         }
233
234         return pa;
235 }
236
237
238 /* md_codegen_get_pv_from_pc ***************************************************
239
240    Machine code:
241
242    03c0f809    jalr     s8
243    00000000    nop
244    27feff9c    addiu    s8,ra,-100
245
246 *******************************************************************************/
247
248 u1 *md_codegen_get_pv_from_pc(u1 *ra)
249 {
250         u1 *pv;
251         u4  mcode;
252         s4  offset;
253
254         /* get the offset of the instructions */
255
256         /* get first instruction word after jump */
257
258         mcode = *((u4 *) ra);
259
260         /* check if we have 2 instructions (lui, daddiu) */
261
262         if ((mcode >> 16) == 0x3c19) {
263                 /* get displacement of first instruction (lui) */
264
265                 offset = (s4) (mcode << 16);
266
267                 /* get displacement of second instruction (daddiu) */
268
269                 mcode = *((u4 *) (ra + 1 * 4));
270
271 #if SIZEOF_VOID_P == 8
272                 assert((mcode >> 16) == 0x6739);
273 #else
274                 assert((mcode >> 16) == 0x2739);
275 #endif
276
277                 offset += (s2) (mcode & 0x0000ffff);
278         }
279         else {
280                 /* get offset of first instruction (daddiu) */
281
282                 mcode = *((u4 *) ra);
283
284 #if SIZEOF_VOID_P == 8
285                 assert((mcode >> 16) == 0x67fe);
286 #else
287                 assert((mcode >> 16) == 0x27fe);
288 #endif
289
290                 offset = (s2) (mcode & 0x0000ffff);
291         }
292
293         /* calculate PV via RA + offset */
294
295         pv = ra + offset;
296
297         return pv;
298 }
299
300
301 /* md_cacheflush ***************************************************************
302
303    Calls the system's function to flush the instruction and data
304    cache.
305
306 *******************************************************************************/
307
308 void md_cacheflush(u1 *addr, s4 nbytes)
309 {
310         cacheflush(addr, nbytes, BCACHE);
311 }
312
313
314 /* md_icacheflush **************************************************************
315
316    Calls the system's function to flush the instruction cache.
317
318 *******************************************************************************/
319
320 void md_icacheflush(u1 *addr, s4 nbytes)
321 {
322         cacheflush(addr, nbytes, ICACHE);
323 }
324
325
326 /* md_dcacheflush **************************************************************
327
328    Calls the system's function to flush the data cache.
329
330 *******************************************************************************/
331
332 void md_dcacheflush(u1 *addr, s4 nbytes)
333 {
334         cacheflush(addr, nbytes, DCACHE);
335 }
336
337
338 /* md_patch_replacement_point **************************************************
339
340    Patch the given replacement point.
341
342 *******************************************************************************/
343
344 void md_patch_replacement_point(codeinfo *code, s4 index, rplpoint *rp,
345                                                                 u1 *savedmcode)
346 {
347         s4 disp;
348         union {
349                 u8 both;
350                 u4 words[2];
351         } mcode;
352
353         if (index < 0) {
354                 /* restore the patched-over instruction */
355                 *(u8*)(rp->pc) = *(u8*)(savedmcode);
356         }
357         else {
358                 /* save the current machine code */
359                 *(u8*)(savedmcode) = *(u8*)(rp->pc);
360
361                 /* make machine code for patching */
362
363                 disp = ((u4*)code->replacementstubs - (u4*)rp->pc)
364                            + index * REPLACEMENT_STUB_SIZE
365                            - 1;
366
367                 if ((disp < (s4) 0xffff8000) || (disp > (s4) 0x00007fff)) {
368                         *exceptionptr =
369                                 new_internalerror("Jump offset is out of range: %d > +/-%d",
370                                                                   disp, 0x00007fff);
371                         return;
372                 }
373
374                 /* BR */
375         mcode.words[0] = (((0x04) << 26) | ((0) << 21) | ((0) << 16) | ((disp) & 0xffff));
376                 mcode.words[1] = 0; /* NOP in delay slot */ 
377
378                 /* write the new machine code */
379                 *(u8*)(rp->pc) = mcode.both;
380         }
381
382 #if !defined(NDEBUG) && defined(ENABLE_DISASSEMBLER)
383         {
384                 u1* u1ptr = rp->pc;
385                 DISASSINSTR(u1ptr);
386                 DISASSINSTR(u1ptr);
387                 fflush(stdout);
388         }
389 #endif
390
391         /* flush instruction cache */
392     md_icacheflush(rp->pc,2*4);
393 }
394
395 /*
396  * These are local overrides for various environment variables in Emacs.
397  * Please do not remove this and leave it at the end of the file, where
398  * Emacs will automagically detect them.
399  * ---------------------------------------------------------------------
400  * Local variables:
401  * mode: c
402  * indent-tabs-mode: t
403  * c-basic-offset: 4
404  * tab-width: 4
405  * End:
406  * vim:noexpandtab:sw=4:ts=4:
407  */