6129ff20a1db309d808130c084cb6b6cd47a9d6b
[cacao.git] / src / vm / jit / s390 / md.c
1 /* src/vm/jit/x86_64/md.c - machine dependent x86_64 Linux 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
29    Changes: Edwin Steiner
30
31    $Id: md.c 7848 2007-05-01 21:40:26Z pm $
32
33 */
34
35 #define _GNU_SOURCE
36
37 #include "config.h"
38
39 #include <assert.h>
40 #include <stdlib.h>
41 #include <ucontext.h>
42
43 #include "vm/jit/s390/md-abi.h"
44
45 #if defined(ENABLE_THREADS)
46 # include "threads/threads-common.h"
47 # include "threads/native/threads.h"
48 #endif
49
50 #include "vm/exceptions.h"
51 #include "vm/signallocal.h"
52 #include "vm/jit/asmpart.h"
53 #include "vm/jit/stacktrace.h"
54
55 #if !defined(NDEBUG) && defined(ENABLE_DISASSEMBLER)
56 #include "vmcore/options.h" /* XXX debug */
57 #include "vm/jit/disass.h" /* XXX debug */
58 #endif
59
60 #include "vm/jit/codegen-common.h"
61 #include "vm/jit/s390/codegen.h"
62
63 #include <assert.h>
64 #define OOPS() assert(0);
65
66 /* prototypes *****************************************************************/
67
68 void md_signal_handler_sigill(int sig, siginfo_t *siginfo, void *_p);
69
70 void md_dump_context(u1 *pc, mcontext_t *mc);
71
72 /* md_init *********************************************************************
73
74    Do some machine dependent initialization.
75
76 *******************************************************************************/
77
78 void md_init(void)
79 {
80         struct sigaction act;
81         
82         act.sa_sigaction = md_signal_handler_sigill;
83         act.sa_flags     = SA_NODEFER | SA_SIGINFO;
84
85         if (sigaction(SIGILL, &act, NULL) == -1)        {
86                 vm_abort("%s: error registering SIGILL signal handler.", __FUNCTION__);
87         }
88 }
89
90 /* md_dump_context ************************************************************
91  
92    Logs the machine context
93   
94 *******************************************************************************/
95
96 void md_dump_context(u1 *pc, mcontext_t *mc) {
97         int i;
98         
99         union {
100                 u8 l;
101                 fpreg_t fr;
102         } freg;
103
104         log_println("Dumping context.");
105
106         log_println("Program counter: 0x%08X", pc);
107
108 #if defined(ENABLE_DISASSEMBLER)
109         log_println("Printing instruction at program counter:");
110         disassinstr(pc);
111 #endif
112
113         log_println("General purpose registers:");
114
115         for (i = 0; i < 16; i++) {
116                 log_println("\tr%d:\t0x%08X\t%d", i, mc->gregs[i], mc->gregs[i]);
117         }
118
119         log_println("Floating point registers:");
120
121         for (i = 0; i < 16; i++) {
122                 freg.fr.d = mc->fpregs.fprs[i].d;
123                 log_println("\tf%d\t0x%016llX\t(double)%e\t(float)%f", i, freg.l, freg.fr.d, freg.fr.f);
124         }
125
126 #if defined(ENABLE_THREADS)
127         log_println("Dumping the current stacktrace:");
128         threads_print_stacktrace();
129 #endif
130
131 }
132
133 /* md_signal_handler_sigsegv ***************************************************
134
135    NullPointerException signal handler for hardware null pointer
136    check.
137
138 *******************************************************************************/
139
140 void md_signal_handler_sigsegv(int sig, siginfo_t *siginfo, void *_p)
141 {
142         ucontext_t        *_uc;
143         mcontext_t        *_mc;
144         u1                *pv;
145         u1                *sp;
146         u1                *ra;
147         u1                *xpc;
148         s4                 type;
149         ptrint             val;
150         java_objectheader *e;
151         s4                 base;
152         s4                 is_null;
153
154         _uc = (ucontext_t *) _p;
155         _mc = &_uc->uc_mcontext;
156
157         xpc = (u1 *)_mc->psw.addr;
158
159         /* Check opcodes and verify it is a null pointer exception */
160
161         switch (xpc[0]) {
162                 case 0x58: /* L */
163                 case 0x50: /* ST */
164                         base = (xpc[2] >> 4) & 0xF;
165                         if (base == 0) {
166                                 is_null = 1;
167                         } else if (_mc->gregs[base] == 0) {
168                                 is_null = 1;
169                         } else {
170                                 is_null = 0;
171                         }
172                         break;
173         }
174
175         if (! is_null) {
176 #if !defined(NDEBUG)
177                 md_dump_context(xpc, _mc);
178 #endif
179                 vm_abort("%s: segmentation fault at %p, aborting.", __FUNCTION__, xpc);
180         }
181
182         pv = (u1 *)_mc->gregs[REG_PV];
183         sp = (u1 *)_mc->gregs[REG_SP];
184         ra = xpc;
185         type = EXCEPTION_HARDWARE_NULLPOINTER;
186         val = 0;
187
188         e = exceptions_new_hardware_exception(pv, sp, ra, xpc, type, val);
189
190         _mc->gregs[REG_ITMP2_XPC] = (ptrint) xpc;
191         _mc->gregs[REG_ITMP1_XPTR] = (ptrint) e;
192         _mc->psw.addr = (ptrint) asm_handle_exception;
193 }
194
195 void md_signal_handler_sigill(int sig, siginfo_t *siginfo, void *_p) {
196         ucontext_t        *_uc;
197         mcontext_t        *_mc;
198         u1                *xpc;
199         u1                *ra;
200         u1                *pv;
201         u1                *sp;
202         s4                 type;
203         ptrint             val;
204         java_objectheader *e;
205         s4                 reg;
206
207         _uc = (ucontext_t *) _p;
208         _mc = &_uc->uc_mcontext;
209         xpc = ra = siginfo->si_addr;
210
211         /* Our trap instruction has the format: { 0x02, one_byte_of_data }. */
212
213         if ((siginfo->si_code == ILL_ILLOPC) && (xpc[0] == 0x02)) {
214
215                 /* bits 7-4 contain a register holding a value */
216                 reg = (xpc[1] >> 4) & 0xF;
217
218                 /* bits 3-0 designate the exception type */
219                 type = xpc[1] & 0xF;  
220
221                 pv = (u1 *)_mc->gregs[REG_PV];
222                 sp = (u1 *)_mc->gregs[REG_SP];
223                 val = (ptrint)_mc->gregs[reg];
224
225                 e = exceptions_new_hardware_exception(pv, sp, ra, xpc, type, val);
226
227                 _mc->gregs[REG_ITMP1_XPTR] = (ptrint)e;
228                 _mc->gregs[REG_ITMP2_XPC] = (ptrint)xpc;
229                 _mc->psw.addr = (ptrint) asm_handle_exception;
230
231         } else {
232 #if !defined(NDEBUG)
233                 md_dump_context(xpc, _mc);
234 #endif
235                 vm_abort("%s: illegal instruction at %p, aborting.", __FUNCTION__, xpc);
236         }
237 }
238
239 /* md_signal_handler_sigfpe ****************************************************
240
241    ArithmeticException signal handler for hardware divide by zero
242    check.
243
244 *******************************************************************************/
245
246 void md_signal_handler_sigfpe(int sig, siginfo_t *siginfo, void *_p)
247 {
248         ucontext_t         *_uc;
249         mcontext_t         *_mc;
250         u1                 *pv;
251         u1                 *sp;
252         u1                 *ra;
253         u1                 *xpc;
254         u1                 *pc;
255         s4                  r1, r2;
256         s4                  type;
257         ptrint              val;
258         java_objectheader  *e;
259
260         _uc = (ucontext_t *) _p;
261         _mc = &_uc->uc_mcontext;
262
263         /* Instruction that raised signal */
264         xpc = siginfo->si_addr;
265
266         /* Check opcodes */
267
268         if (xpc[0] == 0x1D) { /* DR */
269
270                 r1 = (xpc[1] >> 4) & 0xF;
271                 r2 = xpc[1] & 0xF;
272
273                 if (
274                         (_mc->gregs[r1] == 0xFFFFFFFF) &&
275                         (_mc->gregs[r1 + 1] == 0x80000000) && 
276                         (_mc->gregs[r2] == 0xFFFFFFFF)
277                 ) {
278                         /* handle special case */
279                         /* next instruction */
280                         pc = (u1 *)_mc->psw.addr;
281                         /* reminder */
282                         _mc->gregs[r1] = 0;
283                         /* quotient */
284                         _mc->gregs[r1 + 1] = 0x80000000;
285                         /* continue at next instruction */
286                         _mc->psw.addr = (ptrint) pc;
287
288                         return;
289                 } else if (_mc->gregs[r2] == 0) {
290                         /* division by 0 */
291
292                         pv = (u1 *)_mc->gregs[REG_PV];
293                         sp = (u1 *)_mc->gregs[REG_SP];
294                         ra = xpc;
295
296                         type = EXCEPTION_HARDWARE_ARITHMETIC;
297                         val = 0;
298
299                         e = exceptions_new_hardware_exception(pv, sp, ra, xpc, type, val);
300
301                         _mc->gregs[REG_ITMP1_XPTR] = (ptrint)e;
302                         _mc->gregs[REG_ITMP2_XPC] = (ptrint)xpc;
303                         _mc->psw.addr = (ptrint) asm_handle_exception;
304
305                         return;
306                 }
307         }
308
309         /* Could not handle signal */
310
311 #if !defined(NDEBUG)
312         md_dump_context(xpc, _mc);
313 #endif
314         vm_abort("%s: floating point exception at %p, aborting.", __FUNCTION__, xpc);
315 }
316
317
318 /* md_signal_handler_sigusr2 ***************************************************
319
320    Signal handler for profiling sampling.
321
322 *******************************************************************************/
323
324 #if defined(ENABLE_THREADS)
325 void md_signal_handler_sigusr2(int sig, siginfo_t *siginfo, void *_p)
326 {
327         threadobject *t;
328         ucontext_t   *_uc;
329         mcontext_t   *_mc;
330         u1           *pc;
331
332         t = THREADOBJECT;
333
334         _uc = (ucontext_t *) _p;
335         _mc = &_uc->uc_mcontext;
336
337         /* ATTENTION: Don't use CACAO's internal REG_* defines as they are
338            different to the ones in <ucontext.h>. */
339
340         pc = (u1 *) _mc->psw.addr;
341
342         t->pc = pc;
343 }
344 #endif
345
346
347 #if defined(ENABLE_THREADS)
348 void thread_restartcriticalsection(ucontext_t *_uc)
349 {
350         mcontext_t *_mc;
351         void       *pc;
352
353         _mc = &_uc->uc_mcontext;
354
355         pc = critical_find_restart_point((void *) _mc->psw.addr);
356
357         if (pc != NULL)
358                 _mc->psw.addr = (ptrint) pc;
359 }
360 #endif
361
362
363 /* md_codegen_patch_branch *****************************************************
364
365    Back-patches a branch instruction.
366
367 *******************************************************************************/
368
369 void md_codegen_patch_branch(codegendata *cd, s4 branchmpc, s4 targetmpc)
370 {
371
372         s4 *mcodeptr;
373         s4  disp;                           /* branch displacement                */
374
375         /* calculate the patch position */
376
377         mcodeptr = (s4 *) (cd->mcodebase + branchmpc);
378
379         /* Calculate the branch displacement. */
380
381         disp = targetmpc - branchmpc;
382         disp += 4; /* size of branch */
383         disp /= 2; /* specified in halfwords */
384
385         ASSERT_VALID_BRANCH(disp);      
386
387         /* patch the branch instruction before the mcodeptr */
388
389         mcodeptr[-1] |= (disp & 0xFFFF);
390 }
391
392
393 /* md_stacktrace_get_returnaddress *********************************************
394
395    Returns the return address of the current stackframe, specified by
396    the passed stack pointer and the stack frame size.
397
398 *******************************************************************************/
399
400 u1 *md_stacktrace_get_returnaddress(u1 *sp, u4 framesize)
401 {
402         u1 *ra;
403
404         /* on S390 the return address is located on the top of the stackframe */
405
406         ra = *((u1 **) (sp + framesize - SIZEOF_VOID_P));
407
408         return ra;
409 }
410
411
412 /* md_get_method_patch_address *************************************************
413
414    Gets the patch address of the currently compiled method. The offset
415    is extracted from the load instruction(s) before the jump and added
416    to the right base address (PV or REG_METHODPTR).
417
418    INVOKESTATIC/SPECIAL:
419
420 0x7748d7b2:   a7 18 ff d4                      lhi      %r1,-44  
421 (load dseg offset)
422 0x7748d7b6:   58 d1 d0 00                      l        %r13,0(%r1,%r13)
423 (load pv)
424 0x7748d7ba:   0d ed                            basr     %r14,%r13
425 (jump to pv)
426
427    INVOKEVIRTUAL:
428
429 0x7748d82a:   58 c0 20 00                      l        %r12,0(%r2)
430 (load mptr)
431 0x7748d82e:   58 d0 c0 00                      l        %r13,0(%r12)
432 (load pv from mptr)
433 0x7748d832:   0d ed                            basr     %r14,%r13
434 (jump to pv)
435
436
437    INVOKEINTERFACE:
438
439 last 2 instructions the same as in invokevirtual
440
441 *******************************************************************************/
442
443 u1 *md_get_method_patch_address(u1 *ra, stackframeinfo *sfi, u1 *mptr)
444 {
445         u1  base;
446         s4  offset;
447         u1 *pa;                             /* patch address                      */
448
449         /* go back to the load before the call instruction */
450
451         ra = ra - 2 /* sizeof bcr */ - 4 /* sizeof l */;
452
453         /* get the base register of the load */
454
455         base = ra[2] >> 4;
456
457         /* check for the different calls */
458
459         if (base == 0xd) { /* pv relative */
460                 /* INVOKESTATIC/SPECIAL */
461
462                 /* the offset is in the load before the load */
463
464                 offset = *((s2 *) (ra - 2));
465
466                 /* add the offset to the procedure vector */
467
468                 pa = sfi->pv + offset;
469         }
470         else if (base == 0xc) { /* mptr relative */
471                 /* INVOKEVIRTUAL/INTERFACE */
472
473                 offset = *((u2 *)(ra + 2)) & 0xFFF;
474
475                 /* add offset to method pointer */
476                 
477                 pa = mptr + offset;
478         }
479         else {
480                 /* catch any problems */
481                 assert(0); 
482         }
483
484         return pa;
485 }
486
487
488 /* md_codegen_get_pv_from_pc ***************************************************
489
490    On this architecture just a wrapper function to
491    codegen_get_pv_from_pc.
492
493 *******************************************************************************/
494
495 u1 *md_codegen_get_pv_from_pc(u1 *ra)
496 {
497         u1 *pv;
498
499         /* Get the start address of the function which contains this
500        address from the method table. */
501
502         pv = codegen_get_pv_from_pc(ra);
503
504         return pv;
505 }
506
507
508 /* md_cacheflush ***************************************************************
509
510    Calls the system's function to flush the instruction and data
511    cache.
512
513 *******************************************************************************/
514
515 void md_cacheflush(u1 *addr, s4 nbytes)
516 {
517         /* do nothing */
518 }
519
520
521 /* md_icacheflush **************************************************************
522
523    Calls the system's function to flush the instruction cache.
524
525 *******************************************************************************/
526
527 void md_icacheflush(u1 *addr, s4 nbytes)
528 {
529         /* do nothing */
530 }
531
532
533 /* md_dcacheflush **************************************************************
534
535    Calls the system's function to flush the data cache.
536
537 *******************************************************************************/
538
539 void md_dcacheflush(u1 *addr, s4 nbytes)
540 {
541         /* do nothing */
542 }
543
544
545 /* md_patch_replacement_point **************************************************
546
547    Patch the given replacement point.
548
549 *******************************************************************************/
550 #if 0
551 void md_patch_replacement_point(rplpoint *rp)
552 {
553     u8 mcode;
554
555         /* XXX this is probably unsafe! */
556
557         /* save the current machine code */
558         mcode = *(u8*)rp->pc;
559
560         /* write spinning instruction */
561         *(u2*)(rp->pc) = 0xebfe;
562
563         /* write 5th byte */
564         rp->pc[4] = (rp->mcode >> 32);
565
566         /* write first word */
567     *(u4*)(rp->pc) = (u4) rp->mcode;
568
569         /* store saved mcode */
570         rp->mcode = mcode;
571         
572 #if !defined(NDEBUG) && defined(ENABLE_DISASSEMBLER)
573         {
574                 u1* u1ptr = rp->pc;
575                 DISASSINSTR(u1ptr);
576                 fflush(stdout);
577         }
578 #endif
579                         
580     /* XXX if required asm_cacheflush(rp->pc,8); */
581 }
582 #endif
583 /*
584  * These are local overrides for various environment variables in Emacs.
585  * Please do not remove this and leave it at the end of the file, where
586  * Emacs will automagically detect them.
587  * ---------------------------------------------------------------------
588  * Local variables:
589  * mode: c
590  * indent-tabs-mode: t
591  * c-basic-offset: 4
592  * tab-width: 4
593  * End:
594  * vim:noexpandtab:sw=4:ts=4:
595  */