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