Fixes PR122.
[cacao.git] / src / vm / jit / arm / md.c
1 /* src/vm/jit/arm/md.c - machine dependent ARM functions
2
3    Copyright (C) 1996-2005, 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 #include "config.h"
27
28 #include <assert.h>
29 #include <stdint.h>
30
31 #include "vm/jit/arm/codegen.h"
32 #include "vm/jit/arm/md.h"
33 #include "vm/jit/arm/md-abi.h"
34
35
36 /* md_init *********************************************************************
37
38    Do some machine dependent initialization.
39
40 *******************************************************************************/
41
42 void md_init(void)
43 {
44         /* do nothing here */
45 }
46
47
48 /* md_jit_method_patch_address *************************************************
49
50    Gets the patch address of the currently compiled method. The offset
51    is extracted from the load instruction(s) before the jump and added
52    to the right base address (PV or REG_METHODPTR).
53
54    Machine code:
55
56    e51cc040    ldr   ip, [ip, #-64]
57    e1a0e00f    mov   lr, pc
58    e1a0f00c    mov   pc, ip
59
60    or
61
62    e590b000    ldr   fp, [r0]
63    e59bc004    ldr   ip, [fp, #4]
64    e1a0e00f    mov   lr, pc
65    e1a0f00c    mov   pc, ip
66
67    or
68
69    e590b000    ldr      fp, [r0]
70    e28bca01    add      ip, fp, #4096   ; 0x1000
71    e59cc004    ldr      ip, [ip, #4]
72    e1a0e00f    mov      lr, pc
73    e1a0f00c    mov      pc, ip
74
75    How we find out the patching address to store new method pointer:
76     - loaded IP with LDR IP,[METHODPTR]?
77         yes=INVOKEVIRTUAL or INVOKEINTERFACE (things are easy!)
78     - loaded IP from data segment
79         yes=INVOKESTATIC or INVOKESPECIAL (things are complicated)
80         recompute pointer to data segment, maybe larger offset 
81
82 *******************************************************************************/
83
84 void *md_jit_method_patch_address(void *pv, void *ra, void *mptr)
85 {
86         uint32_t *pc;
87         uint32_t  mcode;
88         int32_t   disp;
89         void     *pa;                       /* patch address                      */
90
91         /* Go back to the actual load instruction. */
92
93         pc = ((uint32_t *) ra) - 3;
94
95         /* Get first instruction word on current PC. */
96
97         mcode = pc[0];
98
99         /* Sanity check: Are we inside jit code? */
100
101         assert(pc[1] == 0xe1a0e00f /*MOV LR,PC*/);
102         assert(pc[2] == 0xe1a0f00c /*MOV PC,IP*/);
103
104         /* Sanity check: We unconditionally loaded a word into REG_PV? */
105
106         assert ((mcode & 0xff70f000) == 0xe510c000);
107
108         /* Get load displacement. */
109
110         disp = (int32_t) (mcode & 0x0fff);
111
112         /* Case: We loaded from base REG_PV with negative displacement. */
113
114         if (M_MEM_GET_Rbase(mcode) == REG_PV && (mcode & 0x00800000) == 0) {
115                 /* We loaded from data segment, displacement can be larger. */
116
117                 mcode = pc[-1];
118
119                 /* check for "SUB IP, IP, #??, ROTL 12" */
120
121                 if ((mcode & 0xffffff00) == 0xe24cca00)
122                         disp += (int32_t) ((mcode & 0x00ff) << 12);
123
124                 /* and get the final data segment address */
125
126                 pa = ((uint8_t *) pv) - disp;
127         }
128
129         /* Case: We loaded from base REG_METHODPTR with positive displacement. */
130
131         else if (M_MEM_GET_Rbase(mcode) == REG_METHODPTR && (mcode & 0x00800000) == 0x00800000) {
132                 /* return NULL if no mptr was specified (used for replacement) */
133
134                 if (mptr == NULL)
135                         return NULL;
136
137                 /* we loaded from REG_METHODPTR */
138
139                 pa = ((uint8_t *) mptr) + disp;
140         }
141
142         /* Case: We loaded from base REG_PV with positive offset. */
143
144         else if (M_MEM_GET_Rbase(mcode) == REG_PV && (mcode & 0x00800000) == 0x00800000) {
145                 /* We loaded with a larger displacement. Normally this means we loaded
146                    from REG_METHODPTR. However there is a corner case if we loaded
147                    from the data segment at an address aligned to 12 bit, which leads to a
148                    zero (positive) displacement for the last instruction. */
149
150                 mcode = pc[-1];
151
152                 /* check for "ADD IP, FP, #??, ROTL 12" */
153
154                 if ((mcode & 0xffffff00) == 0xe28bca00) {
155                         /* We loaded from REG_METHODPTR with a larger displacement. */
156
157                         assert(mptr != NULL);
158                         disp += (int32_t) ((mcode & 0x00ff) << 12);
159                         pa = ((uint8_t *) mptr) + disp;
160                 }
161
162                 /* check for "SUB IP, IP, #??, ROTL 12" (corner case) */
163
164                 else if ((mcode & 0xffffff00) == 0xe24cca00 && disp == 0) {
165                         /* We loaded from data segment with a larger displacement aligned to 12 bit. */
166
167                         disp += (int32_t) ((mcode & 0x00ff) << 12);
168                         pa = ((uint8_t *) pv) - disp;
169                 }
170
171                 /* Case is not covered, something is severely wrong. */
172
173                 else {
174                         vm_abort_disassemble(pc - 1, 4, "md_jit_method_patch_address: unknown instruction %x", mcode);
175
176                         /* Keep compiler happy. */
177
178                         pa = NULL;
179                 }
180         }
181
182         /* Case is not covered, something is severely wrong. */
183
184         else {
185                 vm_abort_disassemble(pc, 3, "md_jit_method_patch_address: unknown instruction %x", mcode);
186
187                 /* Keep compiler happy. */
188
189                 pa = NULL;
190         }
191
192         return pa;
193 }
194
195
196 /**
197  * Patch the given replacement point.
198  */
199 #if defined(ENABLE_REPLACEMENT)
200 void md_patch_replacement_point(u1 *pc, u1 *savedmcode, bool revert)
201 {
202         vm_abort("md_patch_replacement_point: IMPLEMENT ME!");
203 }
204 #endif
205
206
207 /*
208  * These are local overrides for various environment variables in Emacs.
209  * Please do not remove this and leave it at the end of the file, where
210  * Emacs will automagically detect them.
211  * ---------------------------------------------------------------------
212  * Local variables:
213  * mode: c
214  * indent-tabs-mode: t
215  * c-basic-offset: 4
216  * tab-width: 4
217  * End:
218  * vim:noexpandtab:sw=4:ts=4:
219  */