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