e5d1c28caf5f3a3e21fd34fdf40a3a9fa63ebcb9
[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 8348 2007-08-19 09:27:03Z 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                         m->class->name->text, m->name->text, m->descriptor->text
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                 default:
179                         is_null = 0;
180                         break;
181         }
182
183         if (! is_null) {
184 #if !defined(NDEBUG)
185                 md_dump_context(xpc, _mc);
186 #endif
187                 vm_abort("%s: segmentation fault at %p, aborting.", __FUNCTION__, xpc);
188         }
189
190         pv = (u1 *)_mc->gregs[REG_PV] - N_PV_OFFSET;
191         sp = (u1 *)_mc->gregs[REG_SP];
192         ra = xpc;
193         type = EXCEPTION_HARDWARE_NULLPOINTER;
194         val = 0;
195
196         /* create stackframeinfo */
197
198         stacktrace_create_extern_stackframeinfo(&sfi, pv, sp, ra, xpc);
199
200         /* Handle the type. */
201
202         p = signal_handle(xpc, type, val);
203
204         /* remove stackframeinfo */
205
206         stacktrace_remove_stackframeinfo(&sfi);
207
208         if (p != NULL) {
209                 _mc->gregs[REG_ITMP1_XPTR] = (intptr_t) p;
210                 _mc->gregs[REG_ITMP2_XPC]  = (intptr_t) xpc;
211                 _mc->psw.addr              = (intptr_t) asm_handle_exception;
212         }
213         else {
214                 _mc->psw.addr              = (intptr_t) xpc;
215         }
216 }
217
218 void md_signal_handler_sigill(int sig, siginfo_t *siginfo, void *_p)
219 {
220         stackframeinfo  sfi;
221         ucontext_t     *_uc;
222         mcontext_t     *_mc;
223         u1             *xpc;
224         u1             *ra;
225         u1             *pv;
226         u1             *sp;
227         int             type;
228         intptr_t        val;
229         void           *p;
230         s4              reg;
231
232         _uc = (ucontext_t *) _p;
233         _mc = &_uc->uc_mcontext;
234         xpc = ra = siginfo->si_addr;
235
236         /* Our trap instruction has the format: { 0x02, one_byte_of_data }. */
237
238         if ((siginfo->si_code == ILL_ILLOPC) && (xpc[0] == 0x02)) {
239
240                 /* bits 7-4 contain a register holding a value */
241                 reg = (xpc[1] >> 4) & 0xF;
242
243                 /* bits 3-0 designate the exception type */
244                 type = xpc[1] & 0xF;  
245
246                 pv = (u1 *)_mc->gregs[REG_PV] - N_PV_OFFSET;
247                 sp = (u1 *)_mc->gregs[REG_SP];
248                 val = (ptrint)_mc->gregs[reg];
249
250                 /* create stackframeinfo */
251
252                 stacktrace_create_extern_stackframeinfo(&sfi, pv, sp, ra, xpc);
253
254                 /* Handle the type. */
255
256                 p = signal_handle(xpc, type, val);
257
258                 /* remove stackframeinfo */
259
260                 stacktrace_remove_stackframeinfo(&sfi);
261
262                 if (p != NULL) {
263                         _mc->gregs[REG_ITMP1_XPTR] = (intptr_t) p;
264                         _mc->gregs[REG_ITMP2_XPC]  = (intptr_t) xpc;
265                         _mc->psw.addr              = (intptr_t) asm_handle_exception;
266                 }
267                 else {
268                         _mc->psw.addr              = (intptr_t) xpc;
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         stackframeinfo  sfi;
288         ucontext_t     *_uc;
289         mcontext_t     *_mc;
290         u1             *pv;
291         u1             *sp;
292         u1             *ra;
293         u1             *xpc;
294         u1             *pc;
295         int             r1, r2;
296         int             type;
297         intptr_t        val;
298         void           *p;
299
300         _uc = (ucontext_t *) _p;
301         _mc = &_uc->uc_mcontext;
302
303         /* Instruction that raised signal */
304         xpc = siginfo->si_addr;
305
306         /* Check opcodes */
307
308         if (xpc[0] == 0x1D) { /* DR */
309
310                 r1 = (xpc[1] >> 4) & 0xF;
311                 r2 = xpc[1] & 0xF;
312
313                 if (
314                         (_mc->gregs[r1] == 0xFFFFFFFF) &&
315                         (_mc->gregs[r1 + 1] == 0x80000000) && 
316                         (_mc->gregs[r2] == 0xFFFFFFFF)
317                 ) {
318                         /* handle special case 0x80000000 / 0xFFFFFFFF that fails on hardware */
319                         /* next instruction */
320                         pc = (u1 *)_mc->psw.addr;
321                         /* reminder */
322                         _mc->gregs[r1] = 0;
323                         /* quotient */
324                         _mc->gregs[r1 + 1] = 0x80000000;
325                         /* continue at next instruction */
326                         _mc->psw.addr = (ptrint) pc;
327
328                         return;
329                 }
330                 else if (_mc->gregs[r2] == 0) {
331                         /* division by 0 */
332
333                         pv = (u1 *)_mc->gregs[REG_PV] - N_PV_OFFSET;
334                         sp = (u1 *)_mc->gregs[REG_SP];
335                         ra = xpc;
336
337                         type = EXCEPTION_HARDWARE_ARITHMETIC;
338                         val = 0;
339
340                         /* create stackframeinfo */
341
342                         stacktrace_create_extern_stackframeinfo(&sfi, pv, sp, ra, xpc);
343
344                         /* Handle the type. */
345
346                         p = signal_handle(xpc, type, val);
347
348                         /* remove stackframeinfo */
349
350                         stacktrace_remove_stackframeinfo(&sfi);
351
352                         _mc->gregs[REG_ITMP1_XPTR] = (intptr_t) p;
353                         _mc->gregs[REG_ITMP2_XPC]  = (intptr_t) xpc;
354                         _mc->psw.addr              = (intptr_t) asm_handle_exception;
355
356                         return;
357                 }
358         }
359
360         /* Could not handle signal */
361
362 #if !defined(NDEBUG)
363         md_dump_context(xpc, _mc);
364 #endif
365         vm_abort("%s: floating point exception at %p, aborting.", __FUNCTION__, xpc);
366 }
367
368
369 /* md_signal_handler_sigusr2 ***************************************************
370
371    Signal handler for profiling sampling.
372
373 *******************************************************************************/
374
375 #if defined(ENABLE_THREADS)
376 void md_signal_handler_sigusr2(int sig, siginfo_t *siginfo, void *_p)
377 {
378         threadobject *t;
379         ucontext_t   *_uc;
380         mcontext_t   *_mc;
381         u1           *pc;
382
383         t = THREADOBJECT;
384
385         _uc = (ucontext_t *) _p;
386         _mc = &_uc->uc_mcontext;
387
388         /* ATTENTION: Don't use CACAO's internal REG_* defines as they are
389            different to the ones in <ucontext.h>. */
390
391         pc = (u1 *) _mc->psw.addr;
392
393         t->pc = pc;
394 }
395 #endif
396
397
398 #if defined(ENABLE_THREADS)
399 void md_critical_section_restart(ucontext_t *_uc)
400 {
401         mcontext_t *_mc;
402         u1         *pc;
403         void       *npc;
404
405         _mc = &_uc->uc_mcontext;
406
407         pc = (u1 *)_mc->psw.addr;
408
409         npc = critical_find_restart_point(pc);
410
411         if (npc != NULL) {
412                 log_println("%s: pc=%p, npc=%p", __FUNCTION__, pc, npc);
413                 _mc->psw.addr = (ptrint) npc;
414         }
415 }
416 #endif
417
418
419 /* md_codegen_patch_branch *****************************************************
420
421    Back-patches a branch instruction.
422
423 *******************************************************************************/
424
425 void md_codegen_patch_branch(codegendata *cd, s4 branchmpc, s4 targetmpc)
426 {
427
428         s4 *mcodeptr;
429         s4  disp;                           /* branch displacement                */
430
431         /* calculate the patch position */
432
433         mcodeptr = (s4 *) (cd->mcodebase + branchmpc);
434
435         /* Calculate the branch displacement. */
436
437         disp = targetmpc - branchmpc;
438         disp += 4; /* size of branch */
439         disp /= 2; /* specified in halfwords */
440
441         ASSERT_VALID_BRANCH(disp);      
442
443         /* patch the branch instruction before the mcodeptr */
444
445         mcodeptr[-1] |= (disp & 0xFFFF);
446 }
447
448
449 /* md_stacktrace_get_returnaddress *********************************************
450
451    Returns the return address of the current stackframe, specified by
452    the passed stack pointer and the stack frame size.
453
454 *******************************************************************************/
455
456 u1 *md_stacktrace_get_returnaddress(u1 *sp, u4 framesize)
457 {
458         u1 *ra;
459
460         /* on S390 the return address is located on the top of the stackframe */
461
462         ra = *((u1 **) (sp + framesize - 8));
463
464         return ra;
465 }
466
467
468 /* md_get_method_patch_address *************************************************
469
470    Gets the patch address of the currently compiled method. The offset
471    is extracted from the load instruction(s) before the jump and added
472    to the right base address (PV or REG_METHODPTR).
473
474    INVOKESTATIC/SPECIAL:
475
476 0x7748d7b2:   a7 18 ff d4                      lhi      %r1,-44  
477 (load dseg offset)
478 0x7748d7b6:   58 d1 d0 00                      l        %r13,0(%r1,%r13)
479 (load pv)
480 0x7748d7ba:   0d ed                            basr     %r14,%r13
481 (jump to pv)
482
483    INVOKEVIRTUAL:
484
485 0x7748d82a:   58 c0 20 00                      l        %r12,0(%r2)
486 (load mptr)
487 0x7748d82e:   58 d0 c0 00                      l        %r13,0(%r12)
488 (load pv from mptr)
489 0x7748d832:   0d ed                            basr     %r14,%r13
490 (jump to pv)
491
492
493    INVOKEINTERFACE:
494
495 last 2 instructions the same as in invokevirtual
496
497 *******************************************************************************/
498
499 u1 *md_get_method_patch_address(u1 *ra, stackframeinfo *sfi, u1 *mptr)
500 {
501         u1  base, index;
502         s4  offset;
503         u1 *pa;                             /* patch address                      */
504
505         /* go back to the load before the call instruction */
506
507         ra = ra - 2 /* sizeof bcr */ - 4 /* sizeof l */;
508
509         /* get the base register of the load */
510
511         base = ra[2] >> 4;
512         index = ra[1] & 0xF;
513
514         /* check for the different calls */
515
516         switch (base) {
517                 case 0xd:
518                         /* INVOKESTATIC/SPECIAL */
519
520                 
521                         switch (index) {
522                                 case 0x0:
523                                         /* the offset is in the load instruction */
524                                         offset = ((*(u2 *)(ra + 2)) & 0xFFF) + N_PV_OFFSET;
525                                         break;
526                                 case 0x1:
527                                         /* the offset is in the immediate load before the load */
528                                         offset = *((s2 *) (ra - 2));
529                                         break;
530                                 default:
531                                         assert(0);
532                         }
533
534                         /* add the offset to the procedure vector */
535
536                         pa = sfi->pv + offset;
537
538                         break;
539
540                 case 0xc:
541                         /* mptr relative */
542                         /* INVOKEVIRTUAL/INTERFACE */
543
544                         offset = *((u2 *)(ra + 2)) & 0xFFF;
545
546                         /* return NULL if no mptr was specified (used for replacement) */
547
548                         if (mptr == NULL)
549                                 return NULL;
550
551                         /* add offset to method pointer */
552                         
553                         pa = mptr + offset;
554                         break;
555                 default:
556                         /* catch any problems */
557                         assert(0); 
558                         break;
559         }
560
561         return pa;
562 }
563
564
565 /* md_codegen_get_pv_from_pc ***************************************************
566
567    On this architecture just a wrapper function to
568    codegen_get_pv_from_pc.
569
570 *******************************************************************************/
571
572 u1 *md_codegen_get_pv_from_pc(u1 *ra)
573 {
574         u1 *pv;
575
576         /* Get the start address of the function which contains this
577        address from the method table. */
578
579         pv = codegen_get_pv_from_pc(ra);
580
581         return pv;
582 }
583
584
585 /* md_cacheflush ***************************************************************
586
587    Calls the system's function to flush the instruction and data
588    cache.
589
590 *******************************************************************************/
591
592 void md_cacheflush(u1 *addr, s4 nbytes)
593 {
594         /* do nothing */
595 }
596
597
598 /* md_icacheflush **************************************************************
599
600    Calls the system's function to flush the instruction cache.
601
602 *******************************************************************************/
603
604 void md_icacheflush(u1 *addr, s4 nbytes)
605 {
606         /* do nothing */
607 }
608
609
610 /* md_dcacheflush **************************************************************
611
612    Calls the system's function to flush the data cache.
613
614 *******************************************************************************/
615
616 void md_dcacheflush(u1 *addr, s4 nbytes)
617 {
618         /* do nothing */
619 }
620
621
622 /* md_patch_replacement_point **************************************************
623
624    Patch the given replacement point.
625
626 *******************************************************************************/
627 #if defined(ENABLE_REPLACEMENT)
628 void md_patch_replacement_point(codeinfo *code, s4 index, rplpoint *rp, u1 *savedmcode)
629 {
630         assert(0);
631 }
632 #endif
633
634 void md_handle_exception(int32_t *regs, int64_t *fregs, int32_t *out) {
635
636         uint8_t *xptr;
637         uint8_t *xpc;
638         uint8_t *sp;
639         uint8_t *pv;
640         uint8_t *ra;
641         uint8_t *handler;
642         int32_t framesize;
643         int32_t intsave;
644         int32_t fltsave;
645         int64_t *savearea;
646         int i;
647         int reg;
648         int loops = 0;
649
650         /* get registers */
651
652         xptr = *(uint8_t **)(regs + REG_ITMP1_XPTR);
653         xpc = *(uint8_t **)(regs + REG_ITMP2_XPC);
654         sp = *(uint8_t **)(regs + REG_SP);
655
656
657         /* initialize number of calle saved int regs to restore to 0 */
658         out[0] = 0;
659
660         /* initialize number of calle saved flt regs to restore to 0 */
661         out[1] = 0;
662
663         do {
664
665                 ++loops;
666
667                 pv = codegen_get_pv_from_pc(xpc);
668
669                 handler = exceptions_handle_exception(xptr, xpc, pv, sp);
670
671                 if (handler == NULL) {
672
673                         /* exception was not handled
674                          * get values of calee saved registers and remove stack frame 
675                          */
676
677                         /* read stuff from data segment */
678
679                         framesize = *(int32_t *)(pv + FrameSize);
680
681                         intsave = *(int32_t *)(pv + IntSave);
682                         if (intsave > out[0]) {
683                                 out[0] = intsave;
684                         }
685
686                         fltsave = *(int32_t *)(pv + FltSave);
687                         if (fltsave > out[1]) {
688                                 out[1] = fltsave;
689                         }
690
691                         /* pointer to register save area */
692
693                         savearea = (int64_t *)(sp + framesize - 8);
694
695                         /* return address */
696
697                         ra = *(uint8_t **)(sp + framesize - 8);
698
699                         /* restore saved registers */
700
701                         for (i = 0; i < intsave; ++i) {
702                                 --savearea;
703                                 reg = abi_registers_integer_saved[INT_SAV_CNT - 1 - i];
704                                 regs[reg] = *(int32_t *)(savearea);
705                         }
706
707                         for (i = 0; i < fltsave; ++i) {
708                                 --savearea;
709                                 reg = abi_registers_float_saved[FLT_SAV_CNT - 1 - i];
710                                 fregs[reg] = *savearea;
711                         }
712
713                         /* remove stack frame */
714
715                         sp += framesize;
716
717                         /* new xpc is call before return address */
718
719                         xpc = ra;
720
721                 } else {
722                         xpc = handler;
723                 }
724         } while (handler == NULL);
725
726         /* write new values for registers */
727
728         *(uint8_t **)(regs + REG_ITMP1_XPTR) = xptr;
729         *(uint8_t **)(regs + REG_ITMP2_XPC) = xpc;
730         *(uint8_t **)(regs + REG_SP) = sp;
731         *(uint8_t **)(regs + REG_PV) = pv - 0XFFC;
732
733         /* maybe leaf flag */
734
735         out[2] = (loops == 1);
736 }
737
738 /*
739  * These are local overrides for various environment variables in Emacs.
740  * Please do not remove this and leave it at the end of the file, where
741  * Emacs will automagically detect them.
742  * ---------------------------------------------------------------------
743  * Local variables:
744  * mode: c
745  * indent-tabs-mode: t
746  * c-basic-offset: 4
747  * tab-width: 4
748  * End:
749  * vim:noexpandtab:sw=4:ts=4:
750  */