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