* Removed all Id tags.
[cacao.git] / src / vm / jit / patcher-common.c
1 /* src/vm/jit/patcher-common.c - architecture independent code patching stuff
2
3    Copyright (C) 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 #include "config.h"
29
30 #include <assert.h>
31 #include <stdint.h>
32
33 #include "codegen.h"                   /* for PATCHER_NOPS */
34
35 #include "mm/memory.h"
36
37 #include "threads/lock-common.h"
38
39 #include "toolbox/list.h"
40 #include "toolbox/logging.h"           /* XXX remove me! */
41
42 #include "vm/exceptions.h"
43 #include "vm/vm.h"                     /* for vm_abort */
44
45 #include "vm/jit/code.h"
46 #include "vm/jit/jit.h"
47 #include "vm/jit/patcher-common.h"
48
49 #include "vmcore/options.h"
50
51
52 /* patcher_list_create *********************************************************
53
54    TODO
55
56 *******************************************************************************/
57
58 void patcher_list_create(codeinfo *code)
59 {
60         code->patchers = list_create(OFFSET(patchref_t, linkage));
61 }
62
63
64 /* patcher_list_reset **********************************************************
65
66    TODO
67
68 *******************************************************************************/
69
70 void patcher_list_reset(codeinfo *code)
71 {
72         patchref_t *pr;
73
74         /* free all elements of the list */
75
76         while((pr = list_first(code->patchers)) != NULL) {
77                 list_remove(code->patchers, pr);
78
79                 FREE(pr, patchref_t);
80
81 #if defined(ENABLE_STATISTICS)
82                 if (opt_stat)
83                         size_patchref -= sizeof(patchref_t);
84 #endif
85         }
86 }
87
88 /* patcher_list_free ***********************************************************
89
90    TODO
91
92 *******************************************************************************/
93
94 void patcher_list_free(codeinfo *code)
95 {
96         /* free all elements of the list */
97
98         patcher_list_reset(code);
99
100         /* free the list itself */
101
102         FREE(code->patchers, list_t);
103 }
104
105
106 /* patcher_list_find ***********************************************************
107
108    TODO
109
110    NOTE: Caller should hold the patcher list lock or maintain
111    exclusive access otherwise.
112
113 *******************************************************************************/
114
115 static patchref_t *patcher_list_find(codeinfo *code, u1 *pc)
116 {
117         patchref_t *pr;
118
119         /* walk through all patcher references for the given codeinfo */
120
121         pr = list_first_unsynced(code->patchers);
122         while (pr) {
123
124                 if (pr->mpc == (ptrint) pc)
125                         return pr;
126
127                 pr = list_next_unsynced(code->patchers, pr);
128         }
129
130         return NULL;
131 }
132
133
134 /* patcher_add_patch_ref *******************************************************
135
136    Appends a new patcher reference to the list of patching positions.
137
138 *******************************************************************************/
139
140 void patcher_add_patch_ref(jitdata *jd, functionptr patcher, voidptr ref,
141                            s4 disp)
142 {
143         codegendata *cd;
144     codeinfo    *code;
145     patchref_t  *pr;
146     s4           patchmpc;
147
148         cd       = jd->cd;
149     code     = jd->code;
150     patchmpc = cd->mcodeptr - cd->mcodebase;
151
152 #if !defined(NDEBUG)
153         if (patcher_list_find(code, (u1 *) (intptr_t) patchmpc) != NULL)
154                 vm_abort("patcher_add_patch_ref: different patchers at same position.");
155 #endif
156
157     /* allocate patchref on heap (at least freed together with codeinfo) */
158
159         pr = NEW(patchref_t);
160         list_add_first_unsynced(code->patchers, pr);
161
162 #if defined(ENABLE_STATISTICS)
163         if (opt_stat)
164                 size_patchref += sizeof(patchref_t);
165 #endif
166
167     /* set patcher information (mpc is resolved later) */
168
169     pr->mpc     = patchmpc;
170     pr->disp    = disp;
171     pr->patcher = patcher;
172     pr->ref     = ref;
173         pr->mcode   = 0;
174         pr->done    = false;
175
176     /* Generate NOPs for opt_shownops. */
177
178     if (opt_shownops)
179         PATCHER_NOPS;
180 }
181
182
183 /* patcher_handler *************************************************************
184
185    TODO
186
187 *******************************************************************************/
188
189 /*#define TRACE_PATCHER*/
190
191 #ifdef TRACE_PATCHER
192 /* XXX this indent is not thread safe! */
193 /* XXX if you want it thread safe, place patcher_depth in threadobject! */
194 static int patcher_depth = 0;
195 # define TRACE_PATCHER_INDENT for (i=0; i<patcher_depth; i++) printf("\t")
196 #endif
197
198 java_handle_t *patcher_handler(u1 *pc)
199 {
200         codeinfo      *code;
201         patchref_t    *pr;
202         bool           result;
203         java_handle_t *e;
204 #ifdef TRACE_PATCHER
205         int            i;
206 #endif
207
208         /* define the patcher function */
209
210         bool (*patcher_function)(patchref_t *);
211
212         /* search the codeinfo for the given PC */
213
214         code = code_find_codeinfo_for_pc(pc);
215         assert(code);
216
217         /* enter a monitor on the patcher list */
218
219         LOCK_MONITOR_ENTER(code->patchers);
220
221         /* search the patcher information for the given PC */
222
223         pr = patcher_list_find(code, pc);
224
225         if (pr == NULL)
226                 vm_abort("patcher_handler: Unable to find patcher reference.");
227
228         if (pr->done) {
229                 log_println("patcher_handler: double-patching detected!");
230                 LOCK_MONITOR_EXIT(code->patchers);
231                 return NULL;
232         }
233
234 #ifdef TRACE_PATCHER
235         TRACE_PATCHER_INDENT; printf("patching in "); method_print(code->m); printf("\n");
236         TRACE_PATCHER_INDENT; printf("\texception program counter = %p\n", (void *) pr->mpc);
237         TRACE_PATCHER_INDENT; printf("\tmcodes before = "); for (i=0; i<5; i++) printf("0x%08x ", *((u4 *) pr->mpc + i)); printf("\n");
238         patcher_depth++;
239         assert(patcher_depth > 0);
240 #endif
241
242         /* cast the passed function to a patcher function */
243
244         patcher_function = (bool (*)(patchref_t *)) (ptrint) pr->patcher;
245
246         /* call the proper patcher function */
247
248         result = (patcher_function)(pr);
249
250 #ifdef TRACE_PATCHER
251         assert(patcher_depth > 0);
252         patcher_depth--;
253         TRACE_PATCHER_INDENT; printf("\tmcodes after  = "); for (i=0; i<5; i++) printf("0x%08x ", *((u4 *) pr->mpc + i)); printf("\n");
254         if (result == false) {
255                 TRACE_PATCHER_INDENT; printf("\tPATCHER EXCEPTION!\n");
256         }
257 #endif
258
259         /* check for return value and exit accordingly */
260
261         if (result == false) {
262                 e = exceptions_get_and_clear_exception();
263
264                 LOCK_MONITOR_EXIT(code->patchers);
265
266                 return e;
267         }
268
269         pr->done = true; /* XXX this is only preliminary to prevent double-patching */
270
271         LOCK_MONITOR_EXIT(code->patchers);
272
273         return NULL;
274 }
275
276
277 /*
278  * These are local overrides for various environment variables in Emacs.
279  * Please do not remove this and leave it at the end of the file, where
280  * Emacs will automagically detect them.
281  * ---------------------------------------------------------------------
282  * Local variables:
283  * mode: c
284  * indent-tabs-mode: t
285  * c-basic-offset: 4
286  * tab-width: 4
287  * End:
288  * vim:noexpandtab:sw=4:ts=4:
289  */
290