* src/vm/jit/trap.cpp: Finally switched s390 to the new trap decoding method.
[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, 2010
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/abi.h"
43 #include "vm/jit/executionstate.h"
44 #include "vm/jit/methodheader.h"
45 #include "vm/jit/methodtree.h"
46 #include "vm/jit/stacktrace.hpp"
47 #include "vm/jit/trap.hpp"
48
49 #if !defined(NDEBUG) && defined(ENABLE_DISASSEMBLER)
50 #include "vm/options.h" /* XXX debug */
51 #endif
52
53 #include "vm/jit/codegen-common.hpp"
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 /**
131  * NullPointerException signal handler for hardware null pointer check.
132  */
133 void md_signal_handler_sigsegv(int sig, siginfo_t *siginfo, void *_p)
134 {
135         ucontext_t* _uc = (ucontext_t *) _p;
136         mcontext_t* _mc = &_uc->uc_mcontext;
137
138         void* xpc = (u1 *) _mc->psw.addr;
139
140         // Handle the trap.
141         trap_handle(TRAP_SIGSEGV, xpc, _p);
142 }
143
144 /**
145   * Illegal Instruction signal handler for hardware exception checks.
146   */
147 void md_signal_handler_sigill(int sig, siginfo_t *siginfo, void *_p)
148 {
149         ucontext_t* _uc = (ucontext_t *) _p;
150         mcontext_t* _mc = &_uc->uc_mcontext;
151
152         void* xpc = siginfo->si_addr;
153
154         // Handle the trap.
155         trap_handle(TRAP_SIGILL, xpc, _p);
156 }
157
158 /* md_signal_handler_sigfpe ****************************************************
159
160    ArithmeticException signal handler for hardware divide by zero
161    check.
162
163 *******************************************************************************/
164
165 void md_signal_handler_sigfpe(int sig, siginfo_t *siginfo, void *_p)
166 {
167         ucontext_t* _uc = (ucontext_t *) _p;
168         mcontext_t* _mc = &_uc->uc_mcontext;
169
170         void* xpc = siginfo->si_addr;
171
172         if (N_RR_GET_OPC(xpc) == OPC_DR) { /* DR */
173
174                 int r1 = N_RR_GET_REG1(xpc);
175                 int r2 = N_RR_GET_REG2(xpc);
176
177                 if (
178                         (_mc->gregs[r1] == 0xFFFFFFFF) &&
179                         (_mc->gregs[r1 + 1] == 0x80000000) && 
180                         (_mc->gregs[r2] == 0xFFFFFFFF)
181                 ) {
182                         /* handle special case 0x80000000 / 0xFFFFFFFF that fails on hardware */
183                         /* next instruction */
184                         u1 *pc = (u1 *)_mc->psw.addr;
185                         /* remainder */
186                         _mc->gregs[r1] = 0;
187                         /* quotient */
188                         _mc->gregs[r1 + 1] = 0x80000000;
189                         /* continue at next instruction */
190                         _mc->psw.addr = (ptrint) pc;
191
192                         return;
193                 }
194         }
195
196         // Handle the trap.
197         trap_handle(TRAP_SIGFPE, xpc, _p);
198 }
199
200
201 /* md_signal_handler_sigusr2 ***************************************************
202
203    Signal handler for profiling sampling.
204
205 *******************************************************************************/
206
207 #if defined(ENABLE_THREADS)
208 void md_signal_handler_sigusr2(int sig, siginfo_t *siginfo, void *_p)
209 {
210         threadobject *t;
211         ucontext_t   *_uc;
212         mcontext_t   *_mc;
213         u1           *pc;
214
215         t = THREADOBJECT;
216
217         _uc = (ucontext_t *) _p;
218         _mc = &_uc->uc_mcontext;
219
220         /* ATTENTION: Don't use CACAO's internal REG_* defines as they are
221            different to the ones in <ucontext.h>. */
222
223         pc = (u1 *) _mc->psw.addr;
224
225         t->pc = pc;
226 }
227 #endif
228
229
230 /**
231  * Read the given context into an executionstate.
232  *
233  * @param es      execution state
234  * @param context machine context
235  */
236 void md_executionstate_read(executionstate_t* es, void* context)
237 {
238         ucontext_t *_uc;
239         mcontext_t *_mc;
240         int         i;
241
242         _uc = (ucontext_t *) context;
243         _mc = &_uc->uc_mcontext;
244
245         /* read special registers */
246         es->pc = (u1 *) _mc->psw.addr;
247         es->sp = (u1 *) _mc->gregs[REG_SP];
248         es->pv = (u1 *) _mc->gregs[REG_PV] - N_PV_OFFSET;
249         es->ra = (u1 *) _mc->gregs[REG_RA];
250
251         /* read integer registers */
252         for (i = 0; i < INT_REG_CNT; i++)
253                 es->intregs[i] = _mc->gregs[i];
254
255         /* read float registers */
256         /* Do not use the assignment operator '=', as the type of
257          * the _mc->sc_fpregs[i] can cause invalid conversions. */
258
259         assert(sizeof(_mc->fpregs.fprs) == sizeof(es->fltregs));
260         os_memcpy(&es->fltregs, &_mc->fpregs.fprs, sizeof(_mc->fpregs.fprs));
261 }
262
263
264 /**
265  * Write the given executionstate back to the context.
266  *
267  * @param es      execution state
268  * @param context machine context
269  */
270 void md_executionstate_write(executionstate_t* es, void* context)
271 {
272         ucontext_t *_uc;
273         mcontext_t *_mc;
274         int         i;
275
276         _uc = (ucontext_t *) context;
277         _mc = &_uc->uc_mcontext;
278
279         /* write integer registers */
280         for (i = 0; i < INT_REG_CNT; i++)
281                 _mc->gregs[i] = es->intregs[i];
282
283         /* write float registers */
284         /* Do not use the assignment operator '=', as the type of
285          * the _mc->sc_fpregs[i] can cause invalid conversions. */
286
287         assert(sizeof(_mc->fpregs.fprs) == sizeof(es->fltregs));
288         os_memcpy(&_mc->fpregs.fprs, &es->fltregs, sizeof(_mc->fpregs.fprs));
289
290         /* write special registers */
291         _mc->psw.addr      = (ptrint) es->pc;
292         _mc->gregs[REG_SP] = (ptrint) es->sp;
293         _mc->gregs[REG_PV] = (ptrint) es->pv + N_PV_OFFSET;
294         _mc->gregs[REG_RA] = (ptrint) es->ra;
295 }
296
297
298 /* md_jit_method_patch_address *************************************************
299
300    Gets the patch address of the currently compiled method. The offset
301    is extracted from the load instruction(s) before the jump and added
302    to the right base address (PV or REG_METHODPTR).
303
304    INVOKESTATIC/SPECIAL:
305
306 0x7748d7b2:   a7 18 ff d4                      lhi      %r1,-44  
307 (load dseg offset)
308 0x7748d7b6:   58 d1 d0 00                      l        %r13,0(%r1,%r13)
309 (load pv)
310 0x7748d7ba:   0d ed                            basr     %r14,%r13
311 (jump to pv)
312
313    INVOKEVIRTUAL:
314
315 0x7748d82a:   58 c0 20 00                      l        %r12,0(%r2)
316 (load mptr)
317 0x7748d82e:   58 d0 c0 00                      l        %r13,0(%r12)
318 (load pv from mptr)
319 0x7748d832:   0d ed                            basr     %r14,%r13
320 (jump to pv)
321
322
323    INVOKEINTERFACE:
324
325 last 2 instructions the same as in invokevirtual
326
327 *******************************************************************************/
328
329 void *md_jit_method_patch_address(void* pv, void *ra, void *mptr)
330 {
331         uint8_t *pc;
332         uint8_t  base, index;
333         int32_t  offset;
334         void    *pa;                        /* patch address                      */
335
336         /* go back to the load before the call instruction */
337
338         pc = ((uint8_t *) ra) - SZ_BCR - SZ_L;
339
340         /* get the base register of the load */
341
342         base  = N_RX_GET_BASE(pc);
343         index = N_RX_GET_INDEX(pc);
344
345         /* check for the different calls */
346
347         switch (base) {
348                 case REG_PV:
349                         /* INVOKESTATIC/SPECIAL */
350
351                         switch (index) {
352                                 case R0:
353                                         /* the offset is in the load instruction */
354                                         offset = N_RX_GET_DISP(pc) + N_PV_OFFSET;
355                                         break;
356                                 case REG_ITMP1:
357                                         /* the offset is in the immediate load before the load */
358                                         offset = N_RI_GET_IMM(pc - SZ_L);
359                                         break;
360                                 default:
361                                         assert(0);
362                         }
363
364                         /* add the offset to the procedure vector */
365
366                         pa = ((uint8_t *) pv) + offset;
367                         break;
368
369                 case REG_METHODPTR:
370                         /* mptr relative */
371                         /* INVOKEVIRTUAL/INTERFACE */
372
373                         offset = N_RX_GET_DISP(pc);
374
375                         /* return NULL if no mptr was specified (used for replacement) */
376
377                         if (mptr == NULL)
378                                 return NULL;
379
380                         /* add offset to method pointer */
381                         
382                         pa = (uint8_t *)mptr + offset;
383                         break;
384
385                 default:
386                         /* catch any problems */
387                         vm_abort("md_jit_method_patch_address");
388                         break;
389         }
390
391         return pa;
392 }
393
394
395 /**
396  * Decode the trap instruction at the given PC.
397  *
398  * @param trp information about trap to be filled
399  * @param sig signal number
400  * @param xpc exception PC
401  * @param es execution state of the machine
402  * @return true if trap was decoded successfully, false otherwise.
403  */
404 bool md_trap_decode(trapinfo_t* trp, int sig, void* xpc, executionstate_t* es)
405 {
406         switch (sig) {
407         case TRAP_SIGILL:
408                 if (N_RR_GET_OPC(xpc) == OPC_ILL) {
409                         int32_t reg = N_ILL_GET_REG(xpc);
410                         trp->type  = N_ILL_GET_TYPE(xpc);
411                         trp->value = es->intregs[reg];
412                         return true;
413                 }
414                 return false;
415
416         case TRAP_SIGSEGV:
417         {
418                 int is_null;
419                 int32_t base;
420                 switch (N_RX_GET_OPC(xpc)) {
421                         case OPC_L:
422                         case OPC_ST:
423                         case OPC_CL: /* array size check on NULL array */
424                                 base = N_RX_GET_BASE(xpc);
425                                 if (base == 0) {
426                                         is_null = 1;
427                                 } else if (es->intregs[base] == 0) {
428                                         is_null = 1;
429                                 } else {
430                                         is_null = 0;
431                                 }
432                                 break;
433                         default:
434                                 is_null = 0;
435                                 break;
436                 }
437
438                 // Check for implicit NullPointerException.
439                 if (is_null) {
440                         trp->type  = TRAP_NullPointerException;
441                         trp->value = 0;
442                         return true;
443                 }
444
445                 return false;
446         }
447
448         case TRAP_SIGFPE:
449         {
450                 if (N_RR_GET_OPC(xpc) == OPC_DR) {
451                         int r2 = N_RR_GET_REG2(xpc);
452                         if (es->intregs[r2] == 0) {
453                                 trp->type = TRAP_ArithmeticException;
454                                 trp->value = 0;
455                                 return true;
456                         }
457                 }
458
459                 return false;
460         }
461
462         default:
463                 return false;
464         }
465 }
466
467
468
469 /* md_patch_replacement_point **************************************************
470
471    Patch the given replacement point.
472
473 *******************************************************************************/
474 #if defined(ENABLE_REPLACEMENT)
475 void md_patch_replacement_point(u1 *pc, u1 *savedmcode, bool revert)
476 {
477         assert(0);
478 }
479 #endif
480
481 void md_handle_exception(int32_t *regs, int64_t *fregs, int32_t *out) {
482
483         uint8_t *xptr;
484         uint8_t *xpc;
485         uint8_t *sp;
486         uint8_t *pv;
487         uint8_t *ra;
488         uint8_t *handler;
489         int32_t framesize;
490         int32_t intsave;
491         int32_t fltsave;
492         int64_t *savearea;
493         int i;
494         int reg;
495         int loops = 0;
496
497         /* get registers */
498
499         xptr = *(uint8_t **)(regs + REG_ITMP1_XPTR);
500         xpc = *(uint8_t **)(regs + REG_ITMP2_XPC);
501         sp = *(uint8_t **)(regs + REG_SP);
502
503
504         /* initialize number of calle saved int regs to restore to 0 */
505         out[0] = 0;
506
507         /* initialize number of calle saved flt regs to restore to 0 */
508         out[1] = 0;
509
510         do {
511
512                 ++loops;
513
514                 pv = methodtree_find(xpc);
515
516                 handler = exceptions_handle_exception((java_object_t *)xptr, xpc, pv, sp);
517
518                 if (handler == NULL) {
519
520                         /* exception was not handled
521                          * get values of calee saved registers and remove stack frame 
522                          */
523
524                         /* read stuff from data segment */
525
526                         framesize = *(int32_t *)(pv + FrameSize);
527
528                         intsave = *(int32_t *)(pv + IntSave);
529                         if (intsave > out[0]) {
530                                 out[0] = intsave;
531                         }
532
533                         fltsave = *(int32_t *)(pv + FltSave);
534                         if (fltsave > out[1]) {
535                                 out[1] = fltsave;
536                         }
537
538                         /* pointer to register save area */
539
540                         savearea = (int64_t *)(sp + framesize - 8);
541
542                         /* return address */
543
544                         ra = *(uint8_t **)(sp + framesize - 8);
545
546                         /* restore saved registers */
547
548                         for (i = 0; i < intsave; ++i) {
549                                 --savearea;
550                                 reg = abi_registers_integer_saved[INT_SAV_CNT - 1 - i];
551                                 regs[reg] = *(int32_t *)(savearea);
552                         }
553
554                         for (i = 0; i < fltsave; ++i) {
555                                 --savearea;
556                                 reg = abi_registers_float_saved[FLT_SAV_CNT - 1 - i];
557                                 fregs[reg] = *savearea;
558                         }
559
560                         /* remove stack frame */
561
562                         sp += framesize;
563
564                         /* new xpc is call before return address */
565
566                         xpc = ra - 2;
567
568                 } else {
569                         xpc = handler;
570                 }
571         } while (handler == NULL);
572
573         /* write new values for registers */
574
575         *(uint8_t **)(regs + REG_ITMP1_XPTR) = xptr;
576         *(uint8_t **)(regs + REG_ITMP2_XPC) = xpc;
577         *(uint8_t **)(regs + REG_SP) = sp;
578         *(uint8_t **)(regs + REG_PV) = pv - 0XFFC;
579
580         /* maybe leaf flag */
581
582         out[2] = (loops == 1);
583 }
584
585 /*
586  * These are local overrides for various environment variables in Emacs.
587  * Please do not remove this and leave it at the end of the file, where
588  * Emacs will automagically detect them.
589  * ---------------------------------------------------------------------
590  * Local variables:
591  * mode: c
592  * indent-tabs-mode: t
593  * c-basic-offset: 4
594  * tab-width: 4
595  * End:
596  * vim:noexpandtab:sw=4:ts=4:
597  */