2 * tramp-hppa.c: JIT trampoline code for hppa
4 * Copyright (c) 2007 Randolph Chung
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29 #include <mono/arch/hppa/hppa-codegen.h>
30 #include <mono/metadata/appdomain.h>
31 #include <mono/metadata/marshal.h>
32 #include <mono/metadata/tabledefs.h>
35 #include "mini-hppa.h"
37 /* sizeof (MonoLMF) == 320 + HPPA_STACK_LMF_OFFSET + linkage area (64 bytes)
38 * leave some room to spare
40 #define TRAMP_STACK_SIZE 512
41 /* Method-specific trampoline code fragment size */
42 #define METHOD_TRAMPOLINE_SIZE 128
43 /* Jump-specific trampoline code fragment size */
44 #define JUMP_TRAMPOLINE_SIZE 64
47 * mono_arch_get_unbox_trampoline:
48 * @gsctx: the generic sharing context
50 * @addr: pointer to native code for @m
52 * when value type methods are called through the vtable we need to unbox the
53 * this argument. This method returns a pointer to a trampoline which does
54 * unboxing before calling the method
57 mono_arch_get_unbox_trampoline (MonoGenericSharingContext
*gsctx
, MonoMethod
*m
, gpointer addr
)
60 int this_pos
= hppa_r26
;
61 MonoDomain
*domain
= mono_domain_get ();
63 if (MONO_TYPE_ISSTRUCT (mono_method_signature (m
)->ret
))
66 start
= code
= mono_domain_code_reserve (domain
, 20);
68 hppa_set (code
, addr
, hppa_r1
);
69 hppa_ldo (code
, sizeof (MonoObject
), this_pos
, this_pos
);
70 hppa_bv (code
, hppa_r0
, hppa_r1
);
73 mono_arch_flush_icache (start
, code
- start
);
74 g_assert ((code
- start
) <= 20);
79 mono_arch_patch_callsite (guint8
*method_start
, guint8
*p
, guint8
*addr
)
81 guint32
*code
= (void *)p
;
82 /* Search for and patch the calling sequence
86 * CEE_CALL outputs the following sequence:
90 * bb,>=,n r1, 30, .+16
96 * XXXXXXXXXXXXXXXX <- code points here
99 /* Go back to the branching insn */
102 /* We can patch the code only if it is a direct call. In some cases
103 * we can reach here via a reg-indirect call. In that case we can't
106 if ((code
[0] >> 26) == 0x39 && /* ble */
107 (code
[-4] >> 26) == 0x31 && /* bb */
108 (code
[-6] >> 26) == 0x08) /* ldil */ {
109 hppa_patch (&code
[-6], addr
);
110 mono_arch_flush_icache (&code
[-6], 8);
112 printf("Can't patch callsite!\n");
117 mono_arch_patch_plt_entry (guint8
*code
, guint8
*addr
)
119 g_assert_not_reached ();
123 mono_arch_nullify_class_init_trampoline (guint8
*code8
, gssize
*regs
)
125 guint32
*buf
= (guint32
*)((unsigned long)code8
& ~3);
129 if (code
[0] == 0x08000240) /* nop - already nullified */
131 g_assert ((code
[0] >> 26) == 0x39); /* ble */
135 mono_arch_flush_icache (buf
, 8);
140 mono_arch_nullify_plt_entry (guint8
*code
)
142 g_assert_not_reached ();
145 #define ALIGN_TO(val,align) (((val) + ((align) - 1)) & ~((align) - 1))
148 * Stack frame description when the generic trampoline is called.
150 * --------------------
152 * -------------------
153 * incoming argument registers
154 * -------------------
156 * -------------------
159 mono_arch_create_trampoline_code (MonoTrampolineType tramp_type
)
161 guint8
*buf
, *code
= NULL
;
164 code
= buf
= mono_global_codeman_reserve (1024);
166 /* Trampoline is called with "method" in r20 */
167 hppa_stw (buf
, hppa_r2
, -20, hppa_sp
);
168 hppa_copy (buf
, hppa_sp
, hppa_r1
);
169 hppa_ldo (buf
, TRAMP_STACK_SIZE
, hppa_sp
, hppa_sp
);
171 /* Save the MonoLMF structure on the stack */
172 offset
= HPPA_STACK_LMF_OFFSET
+ G_STRUCT_OFFSET (MonoLMF
, regs
);
173 for (i
= 0; i
< 32; i
++) {
174 if (HPPA_IS_SAVED_GREG (i
)) {
175 hppa_stw (buf
, i
, offset
, hppa_r1
);
176 offset
+= sizeof(ulong
);
179 hppa_copy (buf
, hppa_r1
, hppa_r3
);
180 hppa_set (buf
, HPPA_STACK_LMF_OFFSET
+ G_STRUCT_OFFSET (MonoLMF
, fregs
), hppa_r1
);
181 for (i
= 0; i
< 32; i
++) {
182 if (HPPA_IS_SAVED_FREG (i
)) {
183 hppa_fstdx (buf
, i
, hppa_r1
, hppa_r3
);
184 hppa_ldo (buf
, sizeof(double), hppa_r1
, hppa_r1
);
187 /* Save the method info stored in r20 */
188 hppa_stw (buf
, hppa_r20
, HPPA_STACK_LMF_OFFSET
+ G_STRUCT_OFFSET (MonoLMF
, method
), hppa_r3
);
189 hppa_stw (buf
, hppa_r3
, HPPA_STACK_LMF_OFFSET
+ G_STRUCT_OFFSET (MonoLMF
, ebp
), hppa_sp
);
190 if (tramp_type
== MONO_TRAMPOLINE_JUMP
) {
191 hppa_stw (buf
, hppa_r0
, HPPA_STACK_LMF_OFFSET
+ G_STRUCT_OFFSET (MonoLMF
, eip
), hppa_r3
);
193 hppa_stw (buf
, hppa_r2
, HPPA_STACK_LMF_OFFSET
+ G_STRUCT_OFFSET (MonoLMF
, eip
), hppa_r3
);
196 /* Save the incoming arguments too, before they can trashed by the
197 * call to the magic trampoline
199 offset
= HPPA_STACK_LMF_OFFSET
+ sizeof (MonoLMF
);
200 for (i
= hppa_r23
; i
<= hppa_r26
; i
++) {
201 hppa_stw (buf
, i
, offset
, hppa_r3
);
202 offset
+= sizeof(ulong
);
204 hppa_stw (buf
, hppa_r28
, offset
, hppa_r3
);
205 offset
+= sizeof(ulong
);
206 offset
= ALIGN_TO (offset
, sizeof(double));
207 hppa_ldo (buf
, offset
, hppa_r3
, hppa_r1
);
208 for (i
= hppa_fr4
; i
<= hppa_fr7
; i
++) {
209 hppa_fstds (buf
, i
, 0, hppa_r1
);
210 hppa_ldo (buf
, sizeof(double), hppa_r1
, hppa_r1
);
213 /* Call mono_get_lmf_addr */
214 hppa_set (buf
, mono_get_lmf_addr
, hppa_r1
);
215 hppa_depi (buf
, 0, 31, 2, hppa_r1
);
216 hppa_ldw (buf
, 0, hppa_r1
, hppa_r1
);
217 hppa_ble (buf
, 0, hppa_r1
);
218 hppa_copy (buf
, hppa_r31
, hppa_r2
);
220 /* r28 now points at the (MonoLMF **) for the current thread. */
222 /* new_lmf->lmf_addr = lmf_addr */
223 hppa_stw (buf
, hppa_r28
, HPPA_STACK_LMF_OFFSET
+ G_STRUCT_OFFSET (MonoLMF
, lmf_addr
), hppa_r3
);
225 /* new_lmf->previous_lmf = *lmf_addr */
226 hppa_ldw (buf
, 0, hppa_r28
, hppa_r1
);
227 hppa_stw (buf
, hppa_r1
, HPPA_STACK_LMF_OFFSET
+ G_STRUCT_OFFSET (MonoLMF
, previous_lmf
), hppa_r3
);
228 /* *lmf_addr = new_lmf */
229 hppa_ldo (buf
, HPPA_STACK_LMF_OFFSET
, hppa_r3
, hppa_r1
);
230 hppa_stw (buf
, hppa_r1
, 0, hppa_r28
);
232 /* Call mono_magic_trampoline (gssize *regs, guint8 *code, MonoMethod *m, guint8* tramp) */
233 hppa_ldo (buf
, HPPA_STACK_LMF_OFFSET
+ G_STRUCT_OFFSET (MonoLMF
, regs
), hppa_r3
, hppa_r26
);
234 hppa_ldw (buf
, HPPA_STACK_LMF_OFFSET
+ G_STRUCT_OFFSET (MonoLMF
, method
), hppa_r3
, hppa_r24
);
235 if (tramp_type
== MONO_TRAMPOLINE_JUMP
)
236 hppa_copy (buf
, hppa_r0
, hppa_r25
);
238 hppa_ldw (buf
, -20, hppa_r3
, hppa_r25
);
239 /* clear the lower two (privilege) bits */
240 hppa_depi (buf
, 0, 31, 2, hppa_r25
);
242 hppa_copy (buf
, hppa_r0
, hppa_r23
);
244 if (tramp_type
== MONO_TRAMPOLINE_CLASS_INIT
)
245 hppa_set (buf
, mono_class_init_trampoline
, hppa_r1
);
247 hppa_set (buf
, mono_magic_trampoline
, hppa_r1
);
249 hppa_depi (buf
, 0, 31, 2, hppa_r1
);
250 hppa_ldw (buf
, 0, hppa_r1
, hppa_r1
);
251 hppa_ble (buf
, 0, hppa_r1
);
252 hppa_copy (buf
, hppa_r31
, hppa_r2
);
254 /* Code address is now in r28 */
257 hppa_ldw (buf
, HPPA_STACK_LMF_OFFSET
+ G_STRUCT_OFFSET (MonoLMF
, previous_lmf
), hppa_r3
, hppa_r20
);
258 hppa_ldw (buf
, HPPA_STACK_LMF_OFFSET
+ G_STRUCT_OFFSET (MonoLMF
, lmf_addr
), hppa_r3
, hppa_r21
);
259 hppa_stw (buf
, hppa_r20
, 0, hppa_r21
);
261 hppa_copy (buf
, hppa_r28
, hppa_r20
);
263 /* Restore arguments */
264 offset
= HPPA_STACK_LMF_OFFSET
+ sizeof (MonoLMF
);
265 for (i
= hppa_r23
; i
<= hppa_r26
; i
++) {
266 hppa_ldw (buf
, offset
, hppa_r3
, i
);
267 offset
+= sizeof(ulong
);
269 hppa_ldw (buf
, offset
, hppa_r3
, hppa_r28
);
270 offset
+= sizeof(ulong
);
271 offset
= ALIGN_TO (offset
, sizeof(double));
272 hppa_ldo (buf
, offset
, hppa_r3
, hppa_r1
);
273 for (i
= hppa_fr4
; i
<= hppa_fr7
; i
++) {
274 hppa_fldds (buf
, 0, hppa_r1
, i
);
275 hppa_ldo (buf
, sizeof(double), hppa_r1
, hppa_r1
);
278 /* Restore registers */
279 hppa_set (buf
, HPPA_STACK_LMF_OFFSET
+ G_STRUCT_OFFSET (MonoLMF
, fregs
), hppa_r1
);
280 for (i
= 0; i
< 32; i
++) {
281 if (HPPA_IS_SAVED_FREG (i
)) {
282 hppa_flddx (buf
, hppa_r1
, hppa_r3
, i
);
283 hppa_ldo (buf
, sizeof(double), hppa_r1
, hppa_r1
);
287 hppa_copy (buf
, hppa_r3
, hppa_r1
);
288 offset
= HPPA_STACK_LMF_OFFSET
+ G_STRUCT_OFFSET (MonoLMF
, regs
);
289 for (i
= 0; i
< 32; i
++) {
290 if (HPPA_IS_SAVED_GREG (i
)) {
291 hppa_ldw (buf
, offset
, hppa_r1
, i
);
292 offset
+= sizeof(ulong
);
295 /* Jump to the compiled code in hppa_r28 */
296 hppa_ldw (buf
, -20, hppa_r1
, hppa_r2
);
297 hppa_bv (buf
, hppa_r0
, hppa_r20
);
298 hppa_ldo (buf
, -TRAMP_STACK_SIZE
, hppa_sp
, hppa_sp
);
300 g_assert ((buf
- code
) <= 1024);
305 * mono_arch_create_class_init_trampoline:
306 * @vtable: the type to initialize
308 * Creates a trampoline function to run a type initializer.
309 * If the trampoline is called, it calls mono_runtime_class_init with the
310 * given vtable, then patches the caller code so it does not get called any
313 * Returns: a pointer to the newly created code
316 mono_arch_create_class_init_trampoline (MonoVTable
*vtable
)
318 guint8
*code
, *buf
, *tramp
;
319 tramp
= mono_get_trampoline_code (MONO_TRAMPOLINE_CLASS_INIT
);
320 /* This is the method-specific part of the trampoline. Its purpose is
321 to provide the generic part with the MonoMethod *method pointer. */
322 code
= buf
= mono_domain_code_reserve (vtable
->domain
, METHOD_TRAMPOLINE_SIZE
);
324 hppa_stw (buf
, hppa_r2
, -20, hppa_sp
);
325 hppa_copy (buf
, hppa_r3
, hppa_r1
);
326 hppa_copy (buf
, hppa_sp
, hppa_r3
);
327 hppa_stwm (buf
, hppa_r1
, 64, hppa_sp
);
329 /* mono_class_init_trampoline (regs, code, vtable, tramp) */
330 hppa_copy (buf
, hppa_r0
, hppa_r26
);
331 hppa_copy (buf
, hppa_r2
, hppa_r25
);
332 hppa_set (buf
, vtable
, hppa_r24
);
333 hppa_copy (buf
, hppa_r0
, hppa_r23
);
335 hppa_set (buf
, mono_class_init_trampoline
, hppa_r1
);
336 hppa_depi (buf
, 0, 31, 2, hppa_r1
);
337 hppa_ldw (buf
, 0, hppa_r1
, hppa_r1
);
338 hppa_ble (buf
, 0, hppa_r1
);
339 hppa_copy (buf
, hppa_r31
, hppa_r2
);
341 hppa_ldw (buf
, -20, hppa_r3
, hppa_r2
);
342 hppa_ldo (buf
, 64, hppa_r3
, hppa_sp
);
343 hppa_bv (buf
, hppa_r0
, hppa_r2
);
344 hppa_ldwm (buf
, -64, hppa_sp
, hppa_r3
);
346 /* Flush instruction cache, since we've generated code */
347 mono_arch_flush_icache (code
, buf
- code
);
350 g_assert ((buf
- code
) <= METHOD_TRAMPOLINE_SIZE
);
352 mono_jit_stats
.method_trampolines
++;
358 create_specific_tramp (MonoMethod
*method
, guint8
* tramp
, MonoDomain
*domain
)
363 code
= buf
= mono_domain_code_reserve (domain
, 20);
365 /* Call trampoline, with the "method" pointer in r20 */
366 hppa_set (buf
, tramp
, hppa_r1
);
367 hppa_ldil (buf
, hppa_lsel (method
), hppa_r20
);
368 hppa_bv (buf
, hppa_r0
, hppa_r1
);
369 hppa_ldo (buf
, hppa_rsel (method
), hppa_r20
, hppa_r20
);
371 /* Flush instruction cache, since we've generated code */
372 mono_arch_flush_icache (code
, buf
- code
);
374 g_assert ((buf
- code
) <= 20);
376 ji
= g_new0 (MonoJitInfo
, 1);
378 ji
->code_start
= code
;
379 ji
->code_size
= buf
- code
;
381 mono_jit_stats
.method_trampolines
++;
388 mono_arch_create_jump_trampoline (MonoMethod
*method
)
391 MonoDomain
* domain
= mono_domain_get ();
393 tramp
= mono_get_trampoline_code (MONO_TRAMPOLINE_JUMP
);
394 return create_specific_tramp (method
, tramp
, domain
);
398 * arch_create_jit_trampoline:
399 * @method: pointer to the method info
401 * Creates a trampoline function for virtual methods. If the created
402 * code is called it first starts JIT compilation of method,
403 * and then calls the newly created method. It also replaces the
404 * corresponding vtable entry (see mono_magic_trampoline).
406 * A trampoline consists of two parts: a main fragment, shared by all method
407 * trampolines, and some code specific to each method, which hard-codes a
408 * reference to that method and then calls the main fragment.
410 * The main fragment contains a call to 'arm_magic_trampoline', which performs
411 * call to the JIT compiler and substitutes the method-specific fragment with
412 * some code that directly calls the JIT-compiled method.
414 * Returns: a pointer to the newly created code
417 mono_arch_create_jit_trampoline (MonoMethod
*method
)
421 MonoDomain
* domain
= mono_domain_get ();
424 tramp
= mono_get_trampoline_code (MONO_TRAMPOLINE_JIT
);
425 ji
= create_specific_tramp (method
, tramp
, domain
);
426 code_start
= ji
->code_start
;
433 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 encoded_offset
)
435 /* FIXME: implement! */
436 g_assert_not_reached ();