Merged revisions 8245-8298 via svnmerge from
[cacao.git] / src / vm / jit / s390 / md.c
1 /* src/vm/jit/s390/md.c - machine dependent s390 Linux functions
2
3    Copyright (C) 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 8298 2007-08-12 18:49:16Z pm $
26
27 */
28
29
30 #define _GNU_SOURCE
31
32 #include "config.h"
33
34 #include <assert.h>
35 #include <stdint.h>
36 #include <stdlib.h>
37 #include <ucontext.h>
38
39 #include "vm/jit/s390/md-abi.h"
40
41 #if defined(ENABLE_THREADS)
42 # include "threads/threads-common.h"
43 # include "threads/native/threads.h"
44 #endif
45
46 #include "vm/exceptions.h"
47 #include "vm/signallocal.h"
48 #include "vm/jit/asmpart.h"
49 #include "vm/jit/abi.h"
50 #include "vm/jit/methodheader.h"
51 #include "vm/jit/stacktrace.h"
52
53 #if !defined(NDEBUG) && defined(ENABLE_DISASSEMBLER)
54 #include "vmcore/options.h" /* XXX debug */
55 #include "vm/jit/disass.h" /* XXX debug */
56 #endif
57
58 #include "vm/jit/codegen-common.h"
59 #include "vm/jit/s390/codegen.h"
60
61 #include <assert.h>
62 #define OOPS() assert(0);
63
64 /* prototypes *****************************************************************/
65
66 void md_signal_handler_sigill(int sig, siginfo_t *siginfo, void *_p);
67
68 void md_dump_context(u1 *pc, mcontext_t *mc);
69
70 /* md_init *********************************************************************
71
72    Do some machine dependent initialization.
73
74 *******************************************************************************/
75
76 void md_init(void)
77 {
78 }
79
80 /* md_dump_context ************************************************************
81  
82    Logs the machine context
83   
84 *******************************************************************************/
85
86 void md_dump_context(u1 *pc, mcontext_t *mc) {
87         int i;
88         u1 *pv;
89         methodinfo *m;
90
91         union {
92                 u8 l;
93                 fpreg_t fr;
94         } freg;
95
96         log_println("Dumping context.");
97
98         log_println("Program counter: 0x%08X", pc);
99
100         pv = codegen_get_pv_from_pc_nocheck(pc);
101         if (pv == NULL) {
102                 log_println("No java method found at location.");
103         } else {
104                 m = (*(codeinfo **)(pv + CodeinfoPointer))->m;
105                 log_println(
106                         "Java method: class %s, method %s, descriptor %s.",
107                         utf_bytes(m->class->name), utf_bytes(m->name), utf_bytes(m->descriptor)
108                 );
109         }
110
111 #if defined(ENABLE_DISASSEMBLER)
112         log_println("Printing instruction at program counter:");
113         disassinstr(pc);
114 #endif
115
116         log_println("General purpose registers:");
117
118         for (i = 0; i < 16; i++) {
119                 log_println("\tr%d:\t0x%08X\t%d", i, mc->gregs[i], mc->gregs[i]);
120         }
121
122         log_println("Floating point registers:");
123
124         for (i = 0; i < 16; i++) {
125                 freg.fr.d = mc->fpregs.fprs[i].d;
126                 log_println("\tf%d\t0x%016llX\t(double)%e\t(float)%f", i, freg.l, freg.fr.d, freg.fr.f);
127         }
128
129 #if defined(ENABLE_THREADS)
130         log_println("Dumping the current stacktrace:");
131         threads_print_stacktrace();
132 #endif
133
134 }
135
136 /* md_signal_handler_sigsegv ***************************************************
137
138    NullPointerException signal handler for hardware null pointer
139    check.
140
141 *******************************************************************************/
142
143 void md_signal_handler_sigsegv(int sig, siginfo_t *siginfo, void *_p)
144 {
145         stackframeinfo  sfi;
146         ucontext_t     *_uc;
147         mcontext_t     *_mc;
148         u1             *pv;
149         u1             *sp;
150         u1             *ra;
151         u1             *xpc;
152         int             type;
153         intptr_t        val;
154         void           *p;
155         s4              base;
156         s4              is_null;
157
158         _uc = (ucontext_t *) _p;
159         _mc = &_uc->uc_mcontext;
160
161         xpc = (u1 *)_mc->psw.addr;
162
163         /* Check opcodes and verify it is a null pointer exception */
164
165         switch (xpc[0]) {
166                 case 0x58: /* L */
167                 case 0x50: /* ST */
168                 case 0x55: /* CL (array size check on NULL array) */
169                         base = (xpc[2] >> 4) & 0xF;
170                         if (base == 0) {
171                                 is_null = 1;
172                         } else if (_mc->gregs[base] == 0) {
173                                 is_null = 1;
174                         } else {
175                                 is_null = 0;
176                         }
177                         break;
178         }
179
180         if (! is_null) {
181 #if !defined(NDEBUG)
182                 md_dump_context(xpc, _mc);
183 #endif
184                 vm_abort("%s: segmentation fault at %p, aborting.", __FUNCTION__, xpc);
185         }
186
187         pv = (u1 *)_mc->gregs[REG_PV] - N_PV_OFFSET;
188         sp = (u1 *)_mc->gregs[REG_SP];
189         ra = xpc;
190         type = EXCEPTION_HARDWARE_NULLPOINTER;
191         val = 0;
192
193         /* create stackframeinfo */
194
195         stacktrace_create_extern_stackframeinfo(&sfi, pv, sp, ra, xpc);
196
197         /* Handle the type. */
198
199         p = signal_handle(xpc, type, val);
200
201         /* remove stackframeinfo */
202
203         stacktrace_remove_stackframeinfo(&sfi);
204
205         if (p != NULL) {
206                 _mc->gregs[REG_ITMP1_XPTR] = (intptr_t) p;
207                 _mc->gregs[REG_ITMP2_XPC]  = (intptr_t) xpc;
208                 _mc->psw.addr              = (intptr_t) asm_handle_exception;
209         }
210         else {
211                 _mc->psw.addr              = (intptr_t) xpc;
212         }
213 }
214
215 void md_signal_handler_sigill(int sig, siginfo_t *siginfo, void *_p)
216 {
217         stackframeinfo  sfi;
218         ucontext_t     *_uc;
219         mcontext_t     *_mc;
220         u1             *xpc;
221         u1             *ra;
222         u1             *pv;
223         u1             *sp;
224         int             type;
225         intptr_t        val;
226         void           *p;
227         s4              reg;
228
229         _uc = (ucontext_t *) _p;
230         _mc = &_uc->uc_mcontext;
231         xpc = ra = siginfo->si_addr;
232
233         /* Our trap instruction has the format: { 0x02, one_byte_of_data }. */
234
235         if ((siginfo->si_code == ILL_ILLOPC) && (xpc[0] == 0x02)) {
236
237                 /* bits 7-4 contain a register holding a value */
238                 reg = (xpc[1] >> 4) & 0xF;
239
240                 /* bits 3-0 designate the exception type */
241                 type = xpc[1] & 0xF;  
242
243                 pv = (u1 *)_mc->gregs[REG_PV] - N_PV_OFFSET;
244                 sp = (u1 *)_mc->gregs[REG_SP];
245                 val = (ptrint)_mc->gregs[reg];
246
247                 /* create stackframeinfo */
248
249                 stacktrace_create_extern_stackframeinfo(&sfi, pv, sp, ra, xpc);
250
251                 /* Handle the type. */
252
253                 p = signal_handle(xpc, type, val);
254
255                 /* remove stackframeinfo */
256
257                 stacktrace_remove_stackframeinfo(&sfi);
258
259                 if (p != NULL) {
260                         _mc->gregs[REG_ITMP1_XPTR] = (intptr_t) p;
261                         _mc->gregs[REG_ITMP2_XPC]  = (intptr_t) xpc;
262                         _mc->psw.addr              = (intptr_t) asm_handle_exception;
263                 }
264                 else {
265                         _mc->psw.addr              = (intptr_t) xpc;
266                 }
267         } else {
268 #if !defined(NDEBUG)
269                 md_dump_context(xpc, _mc);
270 #endif
271                 vm_abort("%s: illegal instruction at %p, aborting.", __FUNCTION__, xpc);
272         }
273 }
274
275 /* md_signal_handler_sigfpe ****************************************************
276
277    ArithmeticException signal handler for hardware divide by zero
278    check.
279
280 *******************************************************************************/
281
282 void md_signal_handler_sigfpe(int sig, siginfo_t *siginfo, void *_p)
283 {
284         stackframeinfo  sfi;
285         ucontext_t     *_uc;
286         mcontext_t     *_mc;
287         u1             *pv;
288         u1             *sp;
289         u1             *ra;
290         u1             *xpc;
291         u1             *pc;
292         int             r1, r2;
293         int             type;
294         intptr_t        val;
295         void           *p;
296
297         _uc = (ucontext_t *) _p;
298         _mc = &_uc->uc_mcontext;
299
300         /* Instruction that raised signal */
301         xpc = siginfo->si_addr;
302
303         /* Check opcodes */
304
305         if (xpc[0] == 0x1D) { /* DR */
306
307                 r1 = (xpc[1] >> 4) & 0xF;
308                 r2 = xpc[1] & 0xF;
309
310                 if (
311                         (_mc->gregs[r1] == 0xFFFFFFFF) &&
312                         (_mc->gregs[r1 + 1] == 0x80000000) && 
313                         (_mc->gregs[r2] == 0xFFFFFFFF)
314                 ) {
315                         /* handle special case 0x80000000 / 0xFFFFFFFF that fails on hardware */
316                         /* next instruction */
317                         pc = (u1 *)_mc->psw.addr;
318                         /* reminder */
319                         _mc->gregs[r1] = 0;
320                         /* quotient */
321                         _mc->gregs[r1 + 1] = 0x80000000;
322                         /* continue at next instruction */
323                         _mc->psw.addr = (ptrint) pc;
324
325                         return;
326                 }
327                 else if (_mc->gregs[r2] == 0) {
328                         /* division by 0 */
329
330                         pv = (u1 *)_mc->gregs[REG_PV] - N_PV_OFFSET;
331                         sp = (u1 *)_mc->gregs[REG_SP];
332                         ra = xpc;
333
334                         type = EXCEPTION_HARDWARE_ARITHMETIC;
335                         val = 0;
336
337                         /* create stackframeinfo */
338
339                         stacktrace_create_extern_stackframeinfo(&sfi, pv, sp, ra, xpc);
340
341                         /* Handle the type. */
342
343                         p = signal_handle(xpc, type, val);
344
345                         /* remove stackframeinfo */
346
347                         stacktrace_remove_stackframeinfo(&sfi);
348
349                         _mc->gregs[REG_ITMP1_XPTR] = (intptr_t) p;
350                         _mc->gregs[REG_ITMP2_XPC]  = (intptr_t) xpc;
351                         _mc->psw.addr              = (intptr_t) asm_handle_exception;
352
353                         return;
354                 }
355         }
356
357         /* Could not handle signal */
358
359 #if !defined(NDEBUG)
360         md_dump_context(xpc, _mc);
361 #endif
362         vm_abort("%s: floating point exception at %p, aborting.", __FUNCTION__, xpc);
363 }
364
365
366 /* md_signal_handler_sigusr2 ***************************************************
367
368    Signal handler for profiling sampling.
369
370 *******************************************************************************/
371
372 #if defined(ENABLE_THREADS)
373 void md_signal_handler_sigusr2(int sig, siginfo_t *siginfo, void *_p)
374 {
375         threadobject *t;
376         ucontext_t   *_uc;
377         mcontext_t   *_mc;
378         u1           *pc;
379
380         t = THREADOBJECT;
381
382         _uc = (ucontext_t *) _p;
383         _mc = &_uc->uc_mcontext;
384
385         /* ATTENTION: Don't use CACAO's internal REG_* defines as they are
386            different to the ones in <ucontext.h>. */
387
388         pc = (u1 *) _mc->psw.addr;
389
390         t->pc = pc;
391 }
392 #endif
393
394
395 #if defined(ENABLE_THREADS)
396 void md_critical_section_restart(ucontext_t *_uc)
397 {
398         mcontext_t *_mc;
399         u1         *pc;
400         void       *npc;
401
402         _mc = &_uc->uc_mcontext;
403
404         pc = (u1 *)_mc->psw.addr;
405
406         npc = critical_find_restart_point(pc);
407
408         if (npc != NULL) {
409                 log_println("%s: pc=%p, npc=%p", __FUNCTION__, pc, npc);
410                 _mc->psw.addr = (ptrint) npc;
411         }
412 }
413 #endif
414
415
416 /* md_codegen_patch_branch *****************************************************
417
418    Back-patches a branch instruction.
419
420 *******************************************************************************/
421
422 void md_codegen_patch_branch(codegendata *cd, s4 branchmpc, s4 targetmpc)
423 {
424
425         s4 *mcodeptr;
426         s4  disp;                           /* branch displacement                */
427
428         /* calculate the patch position */
429
430         mcodeptr = (s4 *) (cd->mcodebase + branchmpc);
431
432         /* Calculate the branch displacement. */
433
434         disp = targetmpc - branchmpc;
435         disp += 4; /* size of branch */
436         disp /= 2; /* specified in halfwords */
437
438         ASSERT_VALID_BRANCH(disp);      
439
440         /* patch the branch instruction before the mcodeptr */
441
442         mcodeptr[-1] |= (disp & 0xFFFF);
443 }
444
445
446 /* md_stacktrace_get_returnaddress *********************************************
447
448    Returns the return address of the current stackframe, specified by
449    the passed stack pointer and the stack frame size.
450
451 *******************************************************************************/
452
453 u1 *md_stacktrace_get_returnaddress(u1 *sp, u4 framesize)
454 {
455         u1 *ra;
456
457         /* on S390 the return address is located on the top of the stackframe */
458
459         ra = *((u1 **) (sp + framesize - 8));
460
461         return ra;
462 }
463
464
465 /* md_get_method_patch_address *************************************************
466
467    Gets the patch address of the currently compiled method. The offset
468    is extracted from the load instruction(s) before the jump and added
469    to the right base address (PV or REG_METHODPTR).
470
471    INVOKESTATIC/SPECIAL:
472
473 0x7748d7b2:   a7 18 ff d4                      lhi      %r1,-44  
474 (load dseg offset)
475 0x7748d7b6:   58 d1 d0 00                      l        %r13,0(%r1,%r13)
476 (load pv)
477 0x7748d7ba:   0d ed                            basr     %r14,%r13
478 (jump to pv)
479
480    INVOKEVIRTUAL:
481
482 0x7748d82a:   58 c0 20 00                      l        %r12,0(%r2)
483 (load mptr)
484 0x7748d82e:   58 d0 c0 00                      l        %r13,0(%r12)
485 (load pv from mptr)
486 0x7748d832:   0d ed                            basr     %r14,%r13
487 (jump to pv)
488
489
490    INVOKEINTERFACE:
491
492 last 2 instructions the same as in invokevirtual
493
494 *******************************************************************************/
495
496 u1 *md_get_method_patch_address(u1 *ra, stackframeinfo *sfi, u1 *mptr)
497 {
498         u1  base, index;
499         s4  offset;
500         u1 *pa;                             /* patch address                      */
501
502         /* go back to the load before the call instruction */
503
504         ra = ra - 2 /* sizeof bcr */ - 4 /* sizeof l */;
505
506         /* get the base register of the load */
507
508         base = ra[2] >> 4;
509         index = ra[1] & 0xF;
510
511         /* check for the different calls */
512
513         switch (base) {
514                 case 0xd:
515                         /* INVOKESTATIC/SPECIAL */
516
517                 
518                         switch (index) {
519                                 case 0x0:
520                                         /* the offset is in the load instruction */
521                                         offset = ((*(u2 *)(ra + 2)) & 0xFFF) + N_PV_OFFSET;
522                                         break;
523                                 case 0x1:
524                                         /* the offset is in the immediate load before the load */
525                                         offset = *((s2 *) (ra - 2));
526                                         break;
527                                 default:
528                                         assert(0);
529                         }
530
531                         /* add the offset to the procedure vector */
532
533                         pa = sfi->pv + offset;
534
535                         break;
536
537                 case 0xc:
538                         /* mptr relative */
539                         /* INVOKEVIRTUAL/INTERFACE */
540
541                         offset = *((u2 *)(ra + 2)) & 0xFFF;
542
543                         /* return NULL if no mptr was specified (used for replacement) */
544
545                         if (mptr == NULL)
546                                 return NULL;
547
548                         /* add offset to method pointer */
549                         
550                         pa = mptr + offset;
551                         break;
552                 default:
553                         /* catch any problems */
554                         assert(0); 
555                         break;
556         }
557
558         return pa;
559 }
560
561
562 /* md_codegen_get_pv_from_pc ***************************************************
563
564    On this architecture just a wrapper function to
565    codegen_get_pv_from_pc.
566
567 *******************************************************************************/
568
569 u1 *md_codegen_get_pv_from_pc(u1 *ra)
570 {
571         u1 *pv;
572
573         /* Get the start address of the function which contains this
574        address from the method table. */
575
576         pv = codegen_get_pv_from_pc(ra);
577
578         return pv;
579 }
580
581
582 /* md_cacheflush ***************************************************************
583
584    Calls the system's function to flush the instruction and data
585    cache.
586
587 *******************************************************************************/
588
589 void md_cacheflush(u1 *addr, s4 nbytes)
590 {
591         /* do nothing */
592 }
593
594
595 /* md_icacheflush **************************************************************
596
597    Calls the system's function to flush the instruction cache.
598
599 *******************************************************************************/
600
601 void md_icacheflush(u1 *addr, s4 nbytes)
602 {
603         /* do nothing */
604 }
605
606
607 /* md_dcacheflush **************************************************************
608
609    Calls the system's function to flush the data cache.
610
611 *******************************************************************************/
612
613 void md_dcacheflush(u1 *addr, s4 nbytes)
614 {
615         /* do nothing */
616 }
617
618
619 /* md_patch_replacement_point **************************************************
620
621    Patch the given replacement point.
622
623 *******************************************************************************/
624 #if defined(ENABLE_REPLACEMENT)
625 void md_patch_replacement_point(codeinfo *code, s4 index, rplpoint *rp, u1 *savedmcode)
626 {
627         assert(0);
628 }
629 #endif
630
631 void md_handle_exception(int32_t *regs, int64_t *fregs, int32_t *out) {
632
633         uint8_t *xptr;
634         uint8_t *xpc;
635         uint8_t *sp;
636         uint8_t *pv;
637         uint8_t *ra;
638         uint8_t *handler;
639         int32_t framesize;
640         int32_t intsave;
641         int32_t fltsave;
642         int64_t *savearea;
643         int i;
644         int reg;
645         int loops = 0;
646
647         /* get registers */
648
649         xptr = *(uint8_t **)(regs + REG_ITMP1_XPTR);
650         xpc = *(uint8_t **)(regs + REG_ITMP2_XPC);
651         sp = *(uint8_t **)(regs + REG_SP);
652
653
654         /* initialize number of calle saved int regs to restore to 0 */
655         out[0] = 0;
656
657         /* initialize number of calle saved flt regs to restore to 0 */
658         out[1] = 0;
659
660         do {
661
662                 ++loops;
663
664                 pv = codegen_get_pv_from_pc(xpc);
665
666                 handler = exceptions_handle_exception(xptr, xpc, pv, sp);
667
668                 if (handler == NULL) {
669
670                         /* exception was not handled
671                          * get values of calee saved registers and remove stack frame 
672                          */
673
674                         /* read stuff from data segment */
675
676                         framesize = *(int32_t *)(pv + FrameSize);
677
678                         intsave = *(int32_t *)(pv + IntSave);
679                         if (intsave > out[0]) {
680                                 out[0] = intsave;
681                         }
682
683                         fltsave = *(int32_t *)(pv + FltSave);
684                         if (fltsave > out[1]) {
685                                 out[1] = fltsave;
686                         }
687
688                         /* pointer to register save area */
689
690                         savearea = (int64_t *)(sp + framesize - 8);
691
692                         /* return address */
693
694                         ra = *(uint8_t **)(sp + framesize - 8);
695
696                         /* restore saved registers */
697
698                         for (i = 0; i < intsave; ++i) {
699                                 --savearea;
700                                 reg = abi_registers_integer_saved[INT_SAV_CNT - 1 - i];
701                                 regs[reg] = *(int32_t *)(savearea);
702                         }
703
704                         for (i = 0; i < fltsave; ++i) {
705                                 --savearea;
706                                 reg = abi_registers_float_saved[FLT_SAV_CNT - 1 - i];
707                                 fregs[reg] = *savearea;
708                         }
709
710                         /* remove stack frame */
711
712                         sp += framesize;
713
714                         /* new xpc is call before return address */
715
716                         xpc = ra;
717
718                 } else {
719                         xpc = handler;
720                 }
721         } while (handler == NULL);
722
723         /* write new values for registers */
724
725         *(uint8_t **)(regs + REG_ITMP1_XPTR) = xptr;
726         *(uint8_t **)(regs + REG_ITMP2_XPC) = xpc;
727         *(uint8_t **)(regs + REG_SP) = sp;
728         *(uint8_t **)(regs + REG_PV) = pv - 0XFFC;
729
730         /* maybe leaf flag */
731
732         out[2] = (loops == 1);
733 }
734
735 /*
736  * These are local overrides for various environment variables in Emacs.
737  * Please do not remove this and leave it at the end of the file, where
738  * Emacs will automagically detect them.
739  * ---------------------------------------------------------------------
740  * Local variables:
741  * mode: c
742  * indent-tabs-mode: t
743  * c-basic-offset: 4
744  * tab-width: 4
745  * End:
746  * vim:noexpandtab:sw=4:ts=4:
747  */