Merged with tip.
[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, 2008
4    CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
5
6    This file is part of CACAO.
7
8    This program is free software; you can redistribute it and/or
9    modify it under the terms of the GNU General Public License as
10    published by the Free Software Foundation; either version 2, or (at
11    your option) any later version.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23 */
24
25
26 #define _GNU_SOURCE
27
28 #include "config.h"
29
30 #include <assert.h>
31 #include <stdint.h>
32 #include <stdlib.h>
33 #include <ucontext.h>
34
35 #include "vm/jit/s390/md-abi.h"
36
37 #include "threads/thread.h"
38
39 #include "vm/exceptions.h"
40 #include "vm/signallocal.h"
41 #include "vm/jit/asmpart.h"
42 #include "vm/jit/abi.h"
43 #include "vm/jit/methodheader.h"
44 #include "vm/jit/methodtree.h"
45 #include "vm/jit/stacktrace.h"
46 #include "vm/jit/trap.h"
47
48 #if !defined(NDEBUG) && defined(ENABLE_DISASSEMBLER)
49 #include "vmcore/options.h" /* XXX debug */
50 #include "vm/jit/disass.h" /* XXX debug */
51 #endif
52
53 #include "vm/jit/codegen-common.h"
54 #include "vm/jit/s390/codegen.h"
55 #include "vm/jit/s390/md.h"
56
57
58 /* prototypes *****************************************************************/
59
60 u1 *exceptions_handle_exception(java_object_t *xptro, u1 *xpc, u1 *pv, u1 *sp);
61
62 void md_signal_handler_sigill(int sig, siginfo_t *siginfo, void *_p);
63
64 void md_dump_context(u1 *pc, mcontext_t *mc);
65
66 /* md_init *********************************************************************
67
68    Do some machine dependent initialization.
69
70 *******************************************************************************/
71
72 void md_init(void)
73 {
74 }
75
76 /* md_dump_context ************************************************************
77  
78    Logs the machine context
79   
80 *******************************************************************************/
81
82 void md_dump_context(u1 *pc, mcontext_t *mc) {
83         int i;
84         u1 *pv;
85         methodinfo *m;
86
87         union {
88                 u8 l;
89                 fpreg_t fr;
90         } freg;
91
92         log_println("Dumping context.");
93
94         log_println("Program counter: 0x%08X", pc);
95
96         pv = methodtree_find_nocheck(pc);
97
98         if (pv == NULL) {
99                 log_println("No java method found at location.");
100         } else {
101                 m = (*(codeinfo **)(pv + CodeinfoPointer))->m;
102                 log_println(
103                         "Java method: class %s, method %s, descriptor %s.",
104                         m->clazz->name->text, m->name->text, m->descriptor->text
105                 );
106         }
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         log_println("Dumping the current stacktrace:");
127         stacktrace_print_current();
128 }
129
130 /* md_signal_handler_sigsegv ***************************************************
131
132    NullPointerException signal handler for hardware null pointer
133    check.
134
135 *******************************************************************************/
136
137 void md_signal_handler_sigsegv(int sig, siginfo_t *siginfo, void *_p)
138 {
139         ucontext_t     *_uc;
140         mcontext_t     *_mc;
141         u1             *pv;
142         u1             *sp;
143         u1             *ra;
144         u1             *xpc;
145         int             type;
146         intptr_t        val;
147         void           *p;
148         s4              base;
149         s4              is_null;
150
151         _uc = (ucontext_t *) _p;
152         _mc = &_uc->uc_mcontext;
153
154         xpc = (u1 *)_mc->psw.addr;
155
156         /* Check opcodes and verify it is a null pointer exception */
157
158         switch (N_RX_GET_OPC(xpc)) {
159                 case OPC_L:
160                 case OPC_ST:
161                 case OPC_CL: /* array size check on NULL array */
162                         base = N_RX_GET_BASE(xpc);
163                         if (base == 0) {
164                                 is_null = 1;
165                         } else if (_mc->gregs[base] == 0) {
166                                 is_null = 1;
167                         } else {
168                                 is_null = 0;
169                         }
170                         break;
171                 default:
172                         is_null = 0;
173                         break;
174         }
175
176         if (! is_null) {
177 #if !defined(NDEBUG)
178                 md_dump_context(xpc, _mc);
179 #endif
180                 vm_abort("%s: segmentation fault at %p, aborting.", __FUNCTION__, xpc);
181         }
182
183         pv = (u1 *)_mc->gregs[REG_PV] - N_PV_OFFSET;
184         sp = (u1 *)_mc->gregs[REG_SP];
185         ra = xpc;
186         type = TRAP_NullPointerException;
187         val = 0;
188
189         /* Handle the trap. */
190
191         p = trap_handle(type, val, pv, sp, ra, xpc, _p);
192
193         if (p != NULL) {
194                 _mc->gregs[REG_ITMP3_XPTR] = (uintptr_t) p;
195                 _mc->gregs[REG_ITMP1_XPC]  = (uintptr_t) xpc;
196                 _mc->psw.addr              = (uintptr_t) asm_handle_exception;
197         }
198         else {
199                 _mc->psw.addr              = (uintptr_t) xpc;
200         }
201 }
202
203 void md_signal_handler_sigill(int sig, siginfo_t *siginfo, void *_p)
204 {
205         ucontext_t     *_uc;
206         mcontext_t     *_mc;
207         u1             *xpc;
208         u1             *ra;
209         u1             *pv;
210         u1             *sp;
211         int             type;
212         intptr_t        val;
213         void           *p;
214         s4              reg;
215
216         _uc = (ucontext_t *) _p;
217         _mc = &_uc->uc_mcontext;
218         xpc = ra = siginfo->si_addr;
219
220         /* Our trap instruction has the format: { 0x02, one_byte_of_data }. */
221
222         if ((siginfo->si_code == ILL_ILLOPC) && (N_RR_GET_OPC(xpc) == OPC_ILL)) {
223
224                 /* bits 7-4 contain a register holding a value */
225                 reg = N_ILL_GET_REG(xpc);
226
227                 /* bits 3-0 designate the exception type */
228                 type = N_ILL_GET_TYPE(xpc);
229
230                 pv = (u1 *)_mc->gregs[REG_PV] - N_PV_OFFSET;
231                 sp = (u1 *)_mc->gregs[REG_SP];
232                 val = (ptrint)_mc->gregs[reg];
233
234                 if (TRAP_COMPILER == type) {
235                         /* The PV from the compiler stub is equal to the XPC. */
236
237                         pv = xpc;
238
239                         /* The return address in is REG_RA */
240
241                         ra = (u1 *)_mc->gregs[REG_RA];
242
243                         xpc = ra - 2;
244                 }
245
246                 /* Handle the trap. */
247
248                 p = trap_handle(type, val, pv, sp, ra, xpc, _p);
249
250                 if (TRAP_COMPILER == type) {
251                         if (NULL == p) {
252                                 _mc->gregs[REG_ITMP3_XPTR] = (uintptr_t) builtin_retrieve_exception();
253                                 _mc->gregs[REG_ITMP1_XPC]  = (uintptr_t) ra - 2;
254                                 _mc->gregs[REG_PV]         = (uintptr_t) md_codegen_get_pv_from_pc(ra);
255                                 _mc->psw.addr              = (uintptr_t) asm_handle_exception;
256                         } else {
257                                 _mc->gregs[REG_PV]         = (uintptr_t) p;
258                                 _mc->psw.addr              = (uintptr_t) p;
259                         }
260                 } else {
261                         if (p != NULL) {
262                                 _mc->gregs[REG_ITMP3_XPTR] = (uintptr_t) p;
263                                 _mc->gregs[REG_ITMP1_XPC]  = (uintptr_t) xpc;
264                                 _mc->psw.addr              = (uintptr_t) asm_handle_exception;
265                         }
266                         else {
267                                 _mc->psw.addr              = (uintptr_t) xpc;
268                         }
269                 }
270         } else {
271 #if !defined(NDEBUG)
272                 md_dump_context(xpc, _mc);
273 #endif
274                 vm_abort("%s: illegal instruction at %p, aborting.", __FUNCTION__, xpc);
275         }
276 }
277
278 /* md_signal_handler_sigfpe ****************************************************
279
280    ArithmeticException signal handler for hardware divide by zero
281    check.
282
283 *******************************************************************************/
284
285 void md_signal_handler_sigfpe(int sig, siginfo_t *siginfo, void *_p)
286 {
287         ucontext_t     *_uc;
288         mcontext_t     *_mc;
289         u1             *pv;
290         u1             *sp;
291         u1             *ra;
292         u1             *xpc;
293         u1             *pc;
294         int             r1, r2;
295         int             type;
296         intptr_t        val;
297         void           *p;
298
299         _uc = (ucontext_t *) _p;
300         _mc = &_uc->uc_mcontext;
301
302         /* Instruction that raised signal */
303         xpc = siginfo->si_addr;
304
305         /* Check opcodes */
306
307         if (N_RR_GET_OPC(xpc) == OPC_DR) { /* DR */
308
309                 r1 = N_RR_GET_REG1(xpc);
310                 r2 = N_RR_GET_REG2(xpc);
311
312                 if (
313                         (_mc->gregs[r1] == 0xFFFFFFFF) &&
314                         (_mc->gregs[r1 + 1] == 0x80000000) && 
315                         (_mc->gregs[r2] == 0xFFFFFFFF)
316                 ) {
317                         /* handle special case 0x80000000 / 0xFFFFFFFF that fails on hardware */
318                         /* next instruction */
319                         pc = (u1 *)_mc->psw.addr;
320                         /* reminder */
321                         _mc->gregs[r1] = 0;
322                         /* quotient */
323                         _mc->gregs[r1 + 1] = 0x80000000;
324                         /* continue at next instruction */
325                         _mc->psw.addr = (ptrint) pc;
326
327                         return;
328                 }
329                 else if (_mc->gregs[r2] == 0) {
330                         /* division by 0 */
331
332                         pv = (u1 *)_mc->gregs[REG_PV] - N_PV_OFFSET;
333                         sp = (u1 *)_mc->gregs[REG_SP];
334                         ra = xpc;
335
336                         type = TRAP_ArithmeticException;
337                         val = 0;
338
339                         /* Handle the trap. */
340
341                         p = trap_handle(type, val, pv, sp, ra, xpc, _p);
342
343                         _mc->gregs[REG_ITMP3_XPTR] = (uintptr_t) p;
344                         _mc->gregs[REG_ITMP1_XPC]  = (uintptr_t) xpc;
345                         _mc->psw.addr              = (uintptr_t) asm_handle_exception;
346
347                         return;
348                 }
349         }
350
351         /* Could not handle signal */
352
353 #if !defined(NDEBUG)
354         md_dump_context(xpc, _mc);
355 #endif
356         vm_abort("%s: floating point exception at %p, aborting.", __FUNCTION__, xpc);
357 }
358
359
360 /* md_signal_handler_sigusr2 ***************************************************
361
362    Signal handler for profiling sampling.
363
364 *******************************************************************************/
365
366 #if defined(ENABLE_THREADS)
367 void md_signal_handler_sigusr2(int sig, siginfo_t *siginfo, void *_p)
368 {
369         threadobject *t;
370         ucontext_t   *_uc;
371         mcontext_t   *_mc;
372         u1           *pc;
373
374         t = THREADOBJECT;
375
376         _uc = (ucontext_t *) _p;
377         _mc = &_uc->uc_mcontext;
378
379         /* ATTENTION: Don't use CACAO's internal REG_* defines as they are
380            different to the ones in <ucontext.h>. */
381
382         pc = (u1 *) _mc->psw.addr;
383
384         t->pc = pc;
385 }
386 #endif
387
388
389 #if defined(ENABLE_THREADS)
390 void md_critical_section_restart(ucontext_t *_uc)
391 {
392         mcontext_t *_mc;
393         u1         *pc;
394         void       *npc;
395
396         _mc = &_uc->uc_mcontext;
397
398         pc = (u1 *)_mc->psw.addr;
399
400         npc = critical_find_restart_point(pc);
401
402         if (npc != NULL) {
403                 log_println("%s: pc=%p, npc=%p", __FUNCTION__, pc, npc);
404                 _mc->psw.addr = (ptrint) npc;
405         }
406 }
407 #endif
408
409
410 /* md_jit_method_patch_address *************************************************
411
412    Gets the patch address of the currently compiled method. The offset
413    is extracted from the load instruction(s) before the jump and added
414    to the right base address (PV or REG_METHODPTR).
415
416    INVOKESTATIC/SPECIAL:
417
418 0x7748d7b2:   a7 18 ff d4                      lhi      %r1,-44  
419 (load dseg offset)
420 0x7748d7b6:   58 d1 d0 00                      l        %r13,0(%r1,%r13)
421 (load pv)
422 0x7748d7ba:   0d ed                            basr     %r14,%r13
423 (jump to pv)
424
425    INVOKEVIRTUAL:
426
427 0x7748d82a:   58 c0 20 00                      l        %r12,0(%r2)
428 (load mptr)
429 0x7748d82e:   58 d0 c0 00                      l        %r13,0(%r12)
430 (load pv from mptr)
431 0x7748d832:   0d ed                            basr     %r14,%r13
432 (jump to pv)
433
434
435    INVOKEINTERFACE:
436
437 last 2 instructions the same as in invokevirtual
438
439 *******************************************************************************/
440
441 void *md_jit_method_patch_address(void* pv, void *ra, void *mptr)
442 {
443         uint8_t *pc;
444         uint8_t  base, index;
445         int32_t  offset;
446         void    *pa;                        /* patch address                      */
447
448         /* go back to the load before the call instruction */
449
450         pc = ((uint8_t *) ra) - SZ_BCR - SZ_L;
451
452         /* get the base register of the load */
453
454         base  = N_RX_GET_BASE(pc);
455         index = N_RX_GET_INDEX(pc);
456
457         /* check for the different calls */
458
459         switch (base) {
460                 case REG_PV:
461                         /* INVOKESTATIC/SPECIAL */
462
463                         switch (index) {
464                                 case R0:
465                                         /* the offset is in the load instruction */
466                                         offset = N_RX_GET_DISP(pc) + N_PV_OFFSET;
467                                         break;
468                                 case REG_ITMP1:
469                                         /* the offset is in the immediate load before the load */
470                                         offset = N_RI_GET_IMM(pc - SZ_L);
471                                         break;
472                                 default:
473                                         assert(0);
474                         }
475
476                         /* add the offset to the procedure vector */
477
478                         pa = ((uint8_t *) pv) + offset;
479                         break;
480
481                 case REG_METHODPTR:
482                         /* mptr relative */
483                         /* INVOKEVIRTUAL/INTERFACE */
484
485                         offset = N_RX_GET_DISP(pc);
486
487                         /* return NULL if no mptr was specified (used for replacement) */
488
489                         if (mptr == NULL)
490                                 return NULL;
491
492                         /* add offset to method pointer */
493                         
494                         pa = (uint8_t *)mptr + offset;
495                         break;
496
497                 default:
498                         /* catch any problems */
499                         vm_abort("md_jit_method_patch_address");
500                         break;
501         }
502
503         return pa;
504 }
505
506
507 /* md_patch_replacement_point **************************************************
508
509    Patch the given replacement point.
510
511 *******************************************************************************/
512 #if defined(ENABLE_REPLACEMENT)
513 void md_patch_replacement_point(u1 *pc, u1 *savedmcode, bool revert)
514 {
515         assert(0);
516 }
517 #endif
518
519 void md_handle_exception(int32_t *regs, int64_t *fregs, int32_t *out) {
520
521         uint8_t *xptr;
522         uint8_t *xpc;
523         uint8_t *sp;
524         uint8_t *pv;
525         uint8_t *ra;
526         uint8_t *handler;
527         int32_t framesize;
528         int32_t intsave;
529         int32_t fltsave;
530         int64_t *savearea;
531         int i;
532         int reg;
533         int loops = 0;
534
535         /* get registers */
536
537         xptr = *(uint8_t **)(regs + REG_ITMP3_XPTR);
538         xpc = *(uint8_t **)(regs + REG_ITMP1_XPC);
539         sp = *(uint8_t **)(regs + REG_SP);
540
541
542         /* initialize number of calle saved int regs to restore to 0 */
543         out[0] = 0;
544
545         /* initialize number of calle saved flt regs to restore to 0 */
546         out[1] = 0;
547
548         do {
549
550                 ++loops;
551
552                 pv = methodtree_find(xpc);
553
554                 handler = exceptions_handle_exception((java_object_t *)xptr, xpc, pv, sp);
555
556                 if (handler == NULL) {
557
558                         /* exception was not handled
559                          * get values of calee saved registers and remove stack frame 
560                          */
561
562                         /* read stuff from data segment */
563
564                         framesize = *(int32_t *)(pv + FrameSize);
565
566                         intsave = *(int32_t *)(pv + IntSave);
567                         if (intsave > out[0]) {
568                                 out[0] = intsave;
569                         }
570
571                         fltsave = *(int32_t *)(pv + FltSave);
572                         if (fltsave > out[1]) {
573                                 out[1] = fltsave;
574                         }
575
576                         /* pointer to register save area */
577
578                         savearea = (int64_t *)(sp + framesize - 8);
579
580                         /* return address */
581
582                         ra = *(uint8_t **)(sp + framesize - 8);
583
584                         /* restore saved registers */
585
586                         for (i = 0; i < intsave; ++i) {
587                                 --savearea;
588                                 reg = abi_registers_integer_saved[INT_SAV_CNT - 1 - i];
589                                 regs[reg] = *(int32_t *)(savearea);
590                         }
591
592                         for (i = 0; i < fltsave; ++i) {
593                                 --savearea;
594                                 reg = abi_registers_float_saved[FLT_SAV_CNT - 1 - i];
595                                 fregs[reg] = *savearea;
596                         }
597
598                         /* remove stack frame */
599
600                         sp += framesize;
601
602                         /* new xpc is call before return address */
603
604                         xpc = ra - 2;
605
606                 } else {
607                         xpc = handler;
608                 }
609         } while (handler == NULL);
610
611         /* write new values for registers */
612
613         *(uint8_t **)(regs + REG_ITMP3_XPTR) = xptr;
614         *(uint8_t **)(regs + REG_ITMP1_XPC) = xpc;
615         *(uint8_t **)(regs + REG_SP) = sp;
616         *(uint8_t **)(regs + REG_PV) = pv - 0XFFC;
617
618         /* maybe leaf flag */
619
620         out[2] = (loops == 1);
621 }
622
623 /*
624  * These are local overrides for various environment variables in Emacs.
625  * Please do not remove this and leave it at the end of the file, where
626  * Emacs will automagically detect them.
627  * ---------------------------------------------------------------------
628  * Local variables:
629  * mode: c
630  * indent-tabs-mode: t
631  * c-basic-offset: 4
632  * tab-width: 4
633  * End:
634  * vim:noexpandtab:sw=4:ts=4:
635  */