* src/vm/jit/mips/md-abi.h: Merged MIPS32 code.
[cacao.git] / src / vm / jit / mips / asmpart.S
1 /* src/vm/jit/mips/asmpart.S - Java-C interface functions for MIPS
2
3    Copyright (C) 1996-2005, 2006 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    Contact: cacao@cacaojvm.org
26
27    Authors: Andreas Krall
28
29    Changes: Christian Thalinger
30             Edwin Steiner
31
32    $Id: asmpart.S 7206 2007-01-11 22:39:52Z twisti $
33
34 */
35
36
37 #include "config.h"
38
39 #include "vm/jit/mips/md-abi.h"
40 #include "vm/jit/mips/md-asm.h"
41 #include "vm/jit/mips/offsets.h"
42
43 #include "vm/jit/abi-asm.h"
44 #include "vm/jit/methodheader.h"
45
46
47         .text
48         .set    noat
49
50
51 /* export functions ***********************************************************/
52
53         .globl asm_vm_call_method
54         .globl asm_vm_call_method_int
55         .globl asm_vm_call_method_long
56         .globl asm_vm_call_method_float
57         .globl asm_vm_call_method_double
58         .globl asm_vm_call_method_exception_handler
59
60         .globl asm_call_jit_compiler
61
62         .globl asm_handle_exception
63         .globl asm_handle_nat_exception
64
65         .globl asm_abstractmethoderror
66
67         .globl asm_patcher_wrapper
68
69 #if defined(ENABLE_REPLACEMENT)
70         .globl asm_replacement_out
71         .globl asm_replacement_in
72 #endif
73
74         .globl asm_getclassvalues_atomic
75         .globl asm_criticalsections
76
77         .globl compare_and_swap
78
79
80 /* asm_vm_call_method **********************************************************
81 *                                                                              *
82 *   This function calls a Java-method (which possibly needs compilation)       *
83 *   with up to 4 address parameters.                                           *
84 *                                                                              *
85 *   This functions calls the JIT-compiler which eventually translates the      *
86 *   method into machine code.                                                  *
87 *                                                                              *
88 *   A possibly throwed exception will be returned to the caller as function    *
89 *   return value, so the java method cannot return a fucntion value (this      *
90 *   function usually calls 'main' and '<clinit>' which do not return a         *
91 *   function value).                                                           *
92 *                                                                              *
93 *   C-prototype:                                                               *
94 *    javaobject_header *asm_calljavafunction (methodinfo *m,                   *
95 *         void *arg1, void *arg2, void *arg3, void *arg4);                     *
96 *                                                                              *
97 *******************************************************************************/
98
99         .ent    asm_vm_call_method
100
101         .align  3
102
103 #if SIZEOF_VOID_P == 8
104
105         .dword  0                           /* catch type all                     */
106         .dword  0                           /* handler pc                         */
107         .dword  0                           /* end pc                             */
108         .dword  0                           /* start pc                           */
109         .word   1                           /* extable size                       */
110         .word   0                           /* 4-byte ALIGNMENT PADDING           */
111         .dword  0                           /* line number table start            */
112         .dword  0                           /* line number table size             */
113         .word   0                           /* 4-byte ALIGNMENT PADDING           */
114         .word   0                           /* fltsave                            */
115         .word   0                           /* intsave                            */
116         .word   0                           /* isleaf                             */
117         .word   0                           /* IsSync                             */
118         .word   0                           /* frame size                         */
119         .dword  0                           /* codeinfo pointer                   */
120
121 #else /* SIZEOF_VOID_P == 8 */
122
123         .word   0                           /* catch type all                     */
124         .word   0                           /* handler pc                         */
125         .word   0                           /* end pc                             */
126         .word   0                           /* start pc                           */
127         .word   1                           /* extable size                       */
128         .word   0                           /* line number table start            */
129         .word   0                           /* line number table size             */
130         .word   0                           /* fltsave                            */
131         .word   0                           /* intsave                            */
132         .word   0                           /* isleaf                             */
133         .word   0                           /* IsSync                             */
134         .word   0                           /* frame size                         */
135         .word   0                           /* method pointer (pointer to name)   */
136
137 #endif /* SIZEOF_VOID_P == 8 */
138
139 asm_vm_call_method:
140 asm_vm_call_method_int:
141 asm_vm_call_method_long:
142 asm_vm_call_method_float:
143 asm_vm_call_method_double:
144         .set    noreorder                 /* XXX we need to recompute pv          */
145
146         aaddiu  sp,sp,-12*8               /* allocate stack space (only 11 needed)*/
147         ast     ra,0*8(sp)                /* save return address                  */
148
149         bal     L_asm_vm_call_method_compute_pv
150         ast     pv,1*8(sp)                /* procedure vector                     */
151 L_asm_vm_call_method_compute_pv:
152         aaddiu  pv,ra,-4*4
153
154         ast     s7,3*8(sp)
155
156 #if SIZEOF_VOID_P == 8
157         sdc1    fss0,5*8(sp)              /* save non JavaABI saved flt registers */
158         sdc1    fss1,6*8(sp)
159         sdc1    fss2,7*8(sp)
160         sdc1    fss3,8*8(sp)
161         sdc1    fss4,9*8(sp)
162         sdc1    fss5,10*8(sp)
163 #endif
164
165         ast     a0,4*8(sp)                /* save method pointer for compiler     */
166
167         move    t0,a2                     /* address of first block               */
168         move    s7,a1                     /* argument count                       */
169         blez    s7,calljava_argsloaded
170         nop
171
172 #if SIZEOF_VOID_P == 8
173
174         ald     a0,offvmargdata(t0)
175         ldc1    fa0,offvmargdata(t0)
176         aaddi   s7,s7,-1
177         blez    s7,calljava_argsloaded
178         nop
179
180         ald     a1,offvmargdata+sizevmarg*1(t0)
181         ldc1    fa1,offvmargdata+sizevmarg*1(t0)
182         aaddi   s7,s7,-1
183         blez    s7,calljava_argsloaded
184         nop
185
186         ald     a2,offvmargdata+sizevmarg*2(t0)
187         ldc1    fa2,offvmargdata+sizevmarg*2(t0)
188         aaddi   s7,s7,-1
189         blez    s7,calljava_argsloaded
190         nop
191
192         ald     a3,offvmargdata+sizevmarg*3(t0)
193         ldc1    fa3,offvmargdata+sizevmarg*3(t0)
194         aaddi   s7,s7,-1
195         blez    s7,calljava_argsloaded
196         nop
197
198         ald     a4,offvmargdata+sizevmarg*4(t0)
199         ldc1    fa4,offvmargdata+sizevmarg*4(t0)
200         aaddi   s7,s7,-1
201         blez    s7,calljava_argsloaded
202         nop
203
204         ald     a5,offvmargdata+sizevmarg*5(t0)
205         ldc1    fa5,offvmargdata+sizevmarg*5(t0)
206         aaddi   s7,s7,-1
207         blez    s7,calljava_argsloaded
208         nop
209
210         ald     a6,offvmargdata+sizevmarg*6(t0)
211         ldc1    fa6,offvmargdata+sizevmarg*6(t0)
212         aaddi   s7,s7,-1
213         blez    s7,calljava_argsloaded
214         nop
215
216         ald     a7,offvmargdata+sizevmarg*7(t0)
217         ldc1    fa7,offvmargdata+sizevmarg*7(t0)
218         aaddi   s7,s7,-1
219
220 #else /* SIZEOF_VOID_P == 8 */
221
222 #if WORDS_BIGENDIAN == 1
223         ald     a0,offvmargdata+4(t0)
224 #else
225         ald     a0,offvmargdata(t0)
226 #endif
227 #if !defined(ENABLE_SOFT_FLOAT)
228         ldc1    fa0,offvmargdata(t0)
229 #endif
230         aaddi   s7,s7,-1
231         blez    s7,calljava_argsloaded
232
233 #if WORDS_BIGENDIAN == 1
234         ald     a1,offvmargdata+4+sizevmarg*1(t0)
235 #else
236         ald     a1,offvmargdata+sizevmarg*1(t0)
237 #endif
238 #if !defined(ENABLE_SOFT_FLOAT)
239         ldc1    fa1,offvmargdata+sizevmarg*1(t0)
240 #endif
241         aaddi   s7,s7,-1
242         blez    s7,calljava_argsloaded
243
244 #if WORDS_BIGENDIAN == 1
245         ald     a2,offvmargdata+4+sizevmarg*2(t0)
246 #else
247         ald     a2,offvmargdata+sizevmarg*2(t0)
248 #endif
249         aaddi   s7,s7,-1
250         blez    s7,calljava_argsloaded
251
252 #if WORDS_BIGENDIAN == 1
253         ald     a3,offvmargdata+4+sizevmarg*3(t0)
254 #else
255         ald     a3,offvmargdata+sizevmarg*3(t0)
256 #endif
257         aaddi   s7,s7,-1
258         blez    s7,calljava_argsloaded
259
260 #endif /* SIZEOF_VOID_P == 8 */
261
262 calljava_argsloaded:
263         move    t4,sp                     /* save stack pointer                   */
264         blez    s7,calljava_nocopy
265         nop
266
267 #if SIZEOF_VOID_P == 4
268         aaddiu  s7,s7,4                   /* add stack space for 4 arguments      */
269 #endif
270         subu    t1,zero,s7                /* remaining argument count (negative)  */
271         sll     t2,t1,3                   /* calculate stackframe size            */
272         aaddu   sp,sp,t2                  /* create stackframe                    */
273         aaddu   t2,t2,t4                  /* also set temp sp                     */
274 #if SIZEOF_VOID_P == 4
275         aaddiu  t2,t2,4*8                 /* skip stack space for 4 arguments     */
276         addiu   t1,t1,4
277 #endif
278
279 calljava_copyloop:
280 #if SIZEOF_VOID_P == 8
281         ald     t3,offvmargdata+sizevmarg*8(t0)
282 #else
283 # if WORDS_BIGENDIAN == 1
284         ald     t3,offvmargdata+4+sizevmarg*4(t0)
285 # else
286         ald     t3,offvmargdata+sizevmarg*4(t0)
287 # endif
288 #endif
289         ast     t3,0(t2)                  /* store argument on stack              */
290         addi    t1,t1,1                   /* count 1 argument                     */
291         aaddi   t0,t0,sizevmarg           /* load address of next block           */
292         aaddi   t2,t2,8                   /* increase stack position              */
293         bnez    t1,calljava_copyloop      /* all arguments copied?                */
294         nop
295
296 calljava_nocopy:
297         ald     itmp1,4*8(t4)             /* pass method pointer via itmp1        */
298
299         ala     mptr,asm_call_jit_compiler/* fake virtual function call (2 instr) */
300         ast     mptr,2*8(t4)              /* store function address               */
301         ala     mptr,1*8(t4)              /* set method pointer                   */
302
303         ald     pv,1*8(mptr)              /* method call as in Java               */
304         jalr    pv                        /* call JIT compiler                    */
305         nop
306 L_asm_vm_call_method_recompute_pv:
307 #if SIZEOF_VOID_P == 8
308         aaddiu  pv,ra,-76*4               /* recompute procedure vector           */
309 #else
310         aaddiu  pv,ra,(asm_vm_call_method - L_asm_vm_call_method_recompute_pv)
311 #endif
312
313         .set    reorder                   /* XXX we need to recompute pv          */
314
315         sll     t1,s7,3                   /* remove argument stackframe           */
316         aaddu   sp,sp,t1
317
318 calljava_return2:
319         ald     ra,0*8(sp)                /* restore return address               */
320         ald     pv,1*8(sp)                /* restore procedure vector             */
321         ald     s7,3*8(sp)
322
323 #if SIZEOF_VOID_P == 8
324         ldc1    fss0,5*8(sp)              /* restore non JavaABI saved flt regs   */
325         ldc1    fss1,6*8(sp)
326         ldc1    fss2,7*8(sp)
327         ldc1    fss3,8*8(sp)
328         ldc1    fss4,9*8(sp)
329         ldc1    fss5,10*8(sp)
330 #endif
331
332         aaddiu  sp,sp,12*8                /* free stack space                     */
333         j       ra                        /* return                               */
334
335 asm_vm_call_method_exception_handler:
336         sll     t1,s7,3                   /* remove stackframe                    */
337         aaddu   sp,sp,t1
338 #if SIZEOF_VOID_P == 4
339         aaddiu  sp,sp,-4*4                /* reserve space for 1 argument         */
340 #endif
341
342         move    a0,itmp1                  
343         jal     builtin_throw_exception
344 #if SIZEOF_VOID_P == 4
345         aaddiu  sp,sp,4*4
346 #endif
347         b       calljava_return2
348
349         .end    asm_vm_call_method
350
351
352 /****************** function asm_call_jit_compiler *****************************
353 *                                                                              *
354 *   invokes the compiler for untranslated JavaVM methods.                      *
355 *                                                                              *
356 *   Register REG_ITEMP1 contains a pointer to the method info structure        *
357 *   (prepared by createcompilerstub). Using the return address in R31 and the  *
358 *   offset in the LDA instruction or using the value in methodptr R25 the      *
359 *   patching address for storing the method address can be computed:           *
360 *                                                                              *
361 *   method address was either loaded using                                     *
362 *   M_ALD (REG_PV, REG_PV, a)        ; invokestatic/special    ($28)           *
363 *   M_JSR (REG_RA, REG_PV);                                                    *
364 *   M_NOP                                                                      *
365 *   M_LDA (REG_PV, REG_RA, val)                                                *
366 *   or                                                                         *
367 *   M_ALD (REG_PV, REG_METHODPTR, m) ; invokevirtual/interface ($25)           *
368 *   M_JSR (REG_RA, REG_PV);                                                    *
369 *   M_NOP                                                                      *
370 *   in the static case the method pointer can be computed using the            *
371 *   return address and the lda function following the jmp instruction          *
372 *                                                                              *
373 *******************************************************************************/
374
375         .ent    asm_call_jit_compiler
376
377 asm_call_jit_compiler:
378 #if SIZEOF_VOID_P == 8
379
380         aaddiu  sp,sp,-(ARG_CNT+2)*8  /* +2: keep stack 16-bytes aligned          */
381
382         ast     ra,0*8(sp)            /* save return address                      */
383
384         SAVE_ARGUMENT_REGISTERS(1)
385
386         move    a0,itmp1              /* pass methodinfo pointer                  */
387         move    a1,mptr               /* pass method pointer                      */
388         aaddiu  a2,sp,(ARG_CNT+2)*8   /* pass java sp                             */
389         move    a3,ra
390         jal     jit_asm_compile       /* call jit compiler                        */
391         move    pv,v0
392
393         ald     ra,0*8(sp)            /* restore return address                   */
394
395         RESTORE_ARGUMENT_REGISTERS(1)
396
397         aaddiu  sp,sp,(ARG_CNT+2)*8   /* remove stack frame                       */
398
399 #else /* SIZEOF_VOID_P == 8 */
400
401         aaddiu  sp,sp,-(ARG_CNT+2)*8  /* +4: keep stack 16-bytes aligned          */
402
403         ast     ra,4*4+0*4(sp)        /* save return address                      */
404
405         SAVE_ARGUMENT_REGISTERS(6)
406
407         move    a0,itmp1              /* pass methodinfo pointer                  */
408         move    a1,mptr               /* pass method pointer                      */
409         aaddiu  a2,sp,(ARG_CNT+2)*8   /* pass java sp                             */
410         move    a3,ra
411         jal     jit_asm_compile       /* call jit compiler                        */
412         move    pv,v0
413
414         ald     ra,4*4+0*4(sp)        /* restore return address                   */
415
416         RESTORE_ARGUMENT_REGISTERS(6)
417
418         aaddiu  sp,sp,(ARG_CNT+2)*8   /* remove stack frame                       */
419
420 #endif /* SIZEOF_VOID_P == 8 */
421
422         beqz    pv,L_asm_call_jit_compiler_exception
423
424         jr      pv                    /* and call method. The method returns      */
425                                       /* directly to the caller (ra).             */
426
427 L_asm_call_jit_compiler_exception:
428         aaddiu  sp,sp,-2*8
429         ast     ra,0*8(sp)
430         jal     exceptions_get_and_clear_exception
431         ald     ra,0*8(sp)
432         aaddiu  sp,sp,2*8
433
434         move    xptr,v0               /* get exception                            */
435         aaddiu  xpc,ra,-4             /* exception address is RA - 4              */
436         b       asm_handle_nat_exception
437
438         .end    asm_call_jit_compiler
439
440
441 /* asm_handle_exception ********************************************************
442
443    This function handles an exception. It does not use the usual calling
444    conventions. The exception pointer is passed in REG_ITMP1 and the
445    pc from the exception raising position is passed in REG_ITMP2. It searches
446    the local exception table for a handler. If no one is found, it unwinds
447    stacks and continues searching the callers.
448
449 *******************************************************************************/
450
451         .ent    asm_handle_nat_exception
452
453 asm_handle_nat_exception:
454 L_asm_handle_exception_stack_loop:
455 #if SIZEOF_VOID_P == 8
456         aaddiu  sp,sp,-6*8                  /* keep stack 16-byte aligned         */
457         ast     xptr,0*8(sp)                /* save exception pointer             */
458         ast     xpc,1*8(sp)                 /* save exception pc                  */
459         ast     ra,3*8(sp)                  /* save RA                            */
460         ast     zero,4*8(sp)                /* save maybe-leaf flag (cleared)     */
461 #else
462         aaddiu  sp,sp,-(4*4+6*8)            /* allocate stack                     */
463         ast     xptr,4*4+0*8(sp)            /* save exception pointer             */
464         ast     xpc,4*4+1*8(sp)             /* save exception pc                  */
465         ast     ra,4*4+3*8(sp)              /* save return address                */
466         ast     zero,4*4+4*8(sp)            /* save maybe-leaf flag (cleared)     */
467 #endif
468
469         move    a0,ra                       /* pass RA                            */
470         jal     md_codegen_get_pv_from_pc   /* get PV from RA                     */
471
472 #if SIZEOF_VOID_P == 8
473         ast     v0,2*8(sp)                  /* save PV                            */
474
475         ald     a0,0*8(sp)                  /* pass xptr                          */
476         ald     a1,1*8(sp)                  /* pass xpc                           */
477         move    a2,v0                       /* pass PV                            */
478         aaddiu  a3,sp,6*8                   /* pass Java SP                       */
479 #else
480         ast     v0,4*4+2*8(sp)              /* save data segment pointer          */
481
482         ald     a0,4*4+0*8(sp)              /* pass exception pointer             */
483         ald     a1,4*4+1*8(sp)              /* pass exception pc                  */
484         move    a2,v0                       /* pass data segment pointer          */
485         aaddiu  a3,sp,(4*4+6*8)             /* pass Java stack pointer            */
486 #endif
487
488         b       L_asm_handle_exception_continue
489
490         .aent    asm_handle_exception
491
492 asm_handle_exception:
493         aaddiu  sp,sp,-(ARG_CNT+TMP_CNT)*8  /* create maybe-leaf stackframe       */
494
495         SAVE_ARGUMENT_REGISTERS(0)          /* we save arg and temp registers in  */
496         SAVE_TEMPORARY_REGISTERS(ARG_CNT)   /* case this is a leaf method         */
497
498 #if SIZEOF_VOID_P == 8
499         aaddiu  sp,sp,-6*8                  /* allocate stack                     */
500         ast     xptr,0*8(sp)                /* save exception pointer             */
501         ast     pv,2*8(sp)                  /* save PV                            */
502         ast     ra,3*8(sp)                  /* save RA                            */
503         addu    t0,zero,1                   /* set maybe-leaf flag                */
504         ast     t0,4*8(sp)                  /* save maybe-leaf flag               */
505 #else
506         aaddiu  sp,sp,-(4*4+6*8)            /* allocate stack                     */
507         ast     xptr,4*4+0*8(sp)            /* save exception pointer             */
508         ast     xpc,4*4+1*8(sp)             /* save exception pc                  */
509         ast     pv,4*4+2*8(sp)              /* save data segment pointer          */
510         ast     ra,4*4+3*8(sp)              /* save return address                */
511         addu    t0,zero,1                   /* set maybe-leaf flag                */
512         ast     t0,4*4+4*8(sp)              /* save maybe-leaf flag               */
513 #endif
514
515         move    a0,xptr                     /* pass xptr                          */
516         move    a1,xpc                      /* pass xpc                           */
517         move    a2,pv                       /* pass PV                            */
518
519 #if SIZEOF_VOID_P == 8
520         aaddiu  a3,sp,(ARG_CNT+TMP_CNT+6)*8 /* pass Java SP                       */
521 #else
522         aaddiu  a3,sp,4*4+(ARG_CNT+TMP_CNT+6)*8 /* pass Java stack pointer        */
523 #endif
524
525 L_asm_handle_exception_continue:
526         jal     exceptions_handle_exception
527         
528         beqz    v0,L_asm_handle_exception_not_catched
529
530         move    xpc,v0                      /* move handlerpc into xpc            */
531
532 #if SIZEOF_VOID_P == 8
533         ald     xptr,0*8(sp)                /* restore exception pointer          */
534         ald     pv,2*8(sp)                  /* restore PV                         */
535         ald     ra,3*8(sp)                  /* restore RA                         */
536         ald     t0,4*8(sp)                  /* get maybe-leaf flag                */
537         aaddiu  sp,sp,6*8                   /* free stackframe                    */
538 #else
539         ald     xptr,4*4+0*8(sp)            /* restore exception pointer          */
540         ald     pv,4*4+2*8(sp)              /* restore data segment pointer       */
541         ald     ra,4*4+3*8(sp)              /* restore return address             */
542         ald     t0,4*4+4*8(sp)              /* get maybe-leaf flag                */
543         aaddiu  sp,sp,4*4+6*8               /* free stackframe                    */
544 #endif
545         
546         beqz    t0,L_asm_handle_exception_no_leaf
547
548         RESTORE_ARGUMENT_REGISTERS(0)       /* if this is a leaf method, we have  */
549         RESTORE_TEMPORARY_REGISTERS(ARG_CNT)/* to restore arg and temp registers  */
550         
551         aaddiu  sp,sp,(ARG_CNT+TMP_CNT)*8   /* remove maybe-leaf stackframe       */
552
553 L_asm_handle_exception_no_leaf:
554         jr      xpc                         /* jump to the handler                */
555
556 L_asm_handle_exception_not_catched:
557 #if SIZEOF_VOID_P == 8
558         ald     xptr,0*8(sp)                /* restore xptr                       */
559         ald     pv,2*8(sp)                  /* restore PV                         */
560         ald     ra,3*8(sp)                  /* restore RA                         */
561         ald     t0,4*8(sp)                  /* get maybe-leaf flag                */
562         aaddiu  sp,sp,6*8                   /* free stackframe                    */
563 #else
564         ald     xptr,4*4+0*8(sp)            /* restore xptr                       */
565         ald     pv,4*4+2*8(sp)              /* restore PV                         */
566         ald     ra,4*4+3*8(sp)              /* restore RA                         */
567         ald     t0,4*4+4*8(sp)              /* get maybe-leaf flag                */
568         aaddiu  sp,sp,4*4+6*8               /* free stackframe                    */
569 #endif
570         
571         beqz    t0,L_asm_handle_exception_no_leaf_stack
572
573         aaddiu  sp,sp,(ARG_CNT+TMP_CNT)*8   /* remove maybe-leaf stackframe       */
574         move    t0,zero                     /* clear the maybe-leaf flag          */
575
576 L_asm_handle_exception_no_leaf_stack:
577         lw      t1,FrameSize(pv)            /* get frame size                     */
578         aaddu   t1,sp,t1                    /* pointer to save area               */
579
580         lw      t2,IsLeaf(pv)               /* is leaf procedure                  */
581         bnez    t2,L_asm_handle_exception_no_ra_restore
582
583         ald     ra,-1*8(t1)                 /* restore ra                         */
584         aaddiu  t1,t1,-8                    /* t1--                               */
585
586 L_asm_handle_exception_no_ra_restore:
587         move    xpc,ra                      /* the new xpc is ra                  */
588         lw      t2,IntSave(pv)              /* t1 = saved int register count      */
589         ala     t3,ex_int2                  /* t3 = current pc                    */
590         sll     t2,t2,2                     /* t2 = register count * 4            */
591         asubu   t3,t3,t2                    /* t3 = IntSave - 4 * register count  */
592         jr      t3                          /* jump to save position              */
593
594         ald     s0,-8*8(t1)
595         ald     s1,-7*8(t1)
596         ald     s2,-6*8(t1)
597         ald     s3,-5*8(t1)
598         ald     s4,-4*8(t1)
599         ald     s5,-3*8(t1)
600         ald     s6,-2*8(t1)
601         ald     s7,-1*8(t1)
602
603 ex_int2:
604         sll     t2,t2,1               /* t2 = register count * 4 * 2              */
605         asubu   t1,t1,t2              /* t1 = t0 - 8 * register count             */
606
607         lw      t2,FltSave(pv)        /* t2 = saved flt register count            */
608         ala     t3,ex_flt2            /* t3 = current pc                          */
609         sll     t2,t2,2               /* t2 = register count * 4                  */
610         asubu   t3,t3,t2              /* t3 = ex_int_sav - 4 * register count     */
611         jr      t3                          /* jump to save position              */
612
613 #if SIZEOF_VOID_P == 8
614         ldc1    fs0,-4*8(t1)
615         ldc1    fs1,-3*8(t1)
616         ldc1    fs2,-2*8(t1)
617         ldc1    fs3,-1*8(t1)
618 #else /* SIZEOF_VOID_P == 8 */
619 # if !defined(ENABLE_SOFT_FLOAT)
620         ldc1    fs0,-4*8(t1)
621         ldc1    fs1,-3*8(t1)
622         ldc1    fs2,-2*8(t1)
623         ldc1    fs3,-1*8(t1)
624         ldc1    fs4,-1*8(t1)
625         ldc1    fs5,-1*8(t1)
626 # endif /* !defined(ENABLE_SOFT_FLOAT) */
627 #endif /* SIZEOF_VOID_P == 8 */
628
629 ex_flt2:
630         lw      t1,FrameSize(pv)            /* get frame size                     */
631         aaddu   sp,sp,t1                    /* unwind stack                       */
632         b       L_asm_handle_exception_stack_loop
633
634         .end    asm_handle_nat_exception
635
636
637 /* asm_abstractmethoderror *****************************************************
638
639    Creates and throws an AbstractMethodError.
640
641 *******************************************************************************/
642
643         .ent    asm_abstractmethoderror
644
645 asm_abstractmethoderror:
646         aaddiu  sp,sp,-2*8                  /* create stackframe                  */
647         ast     ra,0*8(sp)                  /* save return address                */
648         aaddiu  a0,sp,2*8                   /* pass java sp                       */
649         move    a1,ra                       /* pass exception address             */
650         jal     exceptions_asm_new_abstractmethoderror
651         ald     ra,0*8(sp)                  /* restore return address             */
652         aaddiu  sp,sp,2*8                   /* remove stackframe                  */
653
654         move    xptr,v0                     /* get exception pointer              */
655         aaddiu  xpc,ra,-4                   /* exception address is ra - 4        */
656         b       asm_handle_nat_exception
657
658         .end    asm_abstractmethoderror
659
660
661 /* asm_patcher_wrapper *********************************************************
662
663    XXX
664
665    Stack layout:
666      56   return address into JIT code (patch position)
667      48   pointer to virtual java_objectheader
668      40   machine code (which is patched back later)
669      32   machine code (which is patched back later)
670      24   machine code (which is patched back later)
671      16   unresolved class/method/field reference
672       8   data segment displacement from load instructions
673       0   patcher function pointer to call
674
675 *******************************************************************************/
676                 
677     .ent    asm_patcher_wrapper
678
679 asm_patcher_wrapper:
680 #if SIZEOF_VOID_P == 8
681
682         aaddiu  sp,sp,-((2+16+22+4)*8)/* create stack frame                       */
683
684         SAVE_RETURN_REGISTERS(0)      /* save 1 int/1 float return registers      */
685         SAVE_ARGUMENT_REGISTERS(2)    /* save 8 int/8 float argument registers    */
686         SAVE_TEMPORARY_REGISTERS(18)  /* save 5 int/16 float temporary registers  */
687
688         ast     itmp1,(2+16+22+0)*8(sp) /* save itmp1                             */
689         ast     itmp2,(2+16+22+1)*8(sp) /* save itmp2                             */
690         ast     ra,(2+16+22+2)*8(sp)  /* save method return address (for leafs)   */
691         ast     pv,(2+16+22+3)*8(sp)  /* save pv of calling java function         */
692
693         aaddiu  a0,sp,(2+16+22+4)*8   /* pass SP of patcher stub                  */
694         move    a1,pv                 /* pass PV                                  */
695         move    a2,ra                 /* pass RA (correct for leafs)              */
696         jal     patcher_wrapper
697         move    itmp3,v0
698
699         RESTORE_RETURN_REGISTERS(0)   /* restore 1 int/1 float return registers   */
700         RESTORE_ARGUMENT_REGISTERS(2) /* restore 8 int/8 float argument registers */
701         RESTORE_TEMPORARY_REGISTERS(18) /* restore 5 int/16 float temporary reg.  */
702
703         ald     itmp1,(2+16+22+0)*8(sp) /* restore itmp1                          */
704         ald     itmp2,(2+16+22+1)*8(sp) /* restore itmp2                          */
705         ald     ra,(2+16+22+2)*8(sp)  /* restore method return address (for leafs)*/
706         ald     pv,(2+16+22+3)*8(sp)  /* restore pv of calling java function      */
707
708         bnez    itmp3,L_asm_patcher_wrapper_exception
709
710         ald     itmp3,(7+2+16+22+4)*8(sp) /* load RA                              */
711         aaddiu  sp,sp,(8+2+16+22+4)*8 /* remove stack frame                       */
712
713         jr      itmp3                 /* jump to new patched code                 */
714
715 L_asm_patcher_wrapper_exception:
716         move    xptr,itmp3            /* get exception                            */
717         ald     xpc,(7+2+16+22+4)*8(sp) /* xpc is RA                              */
718         aaddiu  sp,sp,(8+2+16+22+4)*8 /* remove stack frame                       */
719
720 #else /* SIZEOF_VOID_P == 8 */
721
722         aaddiu  sp,sp,-((5+4+4+8+7)*4) /* create stack frame                      */
723                                       /* +7 keeps the SP 16-bytes aligned         */
724
725         SAVE_RETURN_REGISTERS(5)      /* save 2 int / 2 float return registers    */
726         SAVE_ARGUMENT_REGISTERS(9)    /* save 4 int argument registers            */
727         SAVE_TEMPORARY_REGISTERS(13)  /* save 8 int temporary registers           */
728
729         ast     itmp1,(5+4+4+8+0)*4(sp) /* save itmp1                             */
730         ast     itmp2,(5+4+4+8+1)*4(sp) /* save itmp2                             */
731         ast     ra,(5+4+4+8+2)*4(sp)  /* save method return address (for leafs)   */
732         ast     pv,(5+4+4+8+3)*4(sp)  /* save pv of calling java function         */
733
734         aaddiu  a0,sp,(5+4+4+8+7)*4   /* pass SP of patcher stub                  */
735         move    a1,pv                 /* pass PV                                  */
736         move    a2,ra                 /* pass RA (correct for leafs)              */
737         jal     patcher_wrapper
738         move    itmp3,v0
739
740         RESTORE_RETURN_REGISTERS(5)   /* restore 2 int / 2 float return registers */
741         RESTORE_ARGUMENT_REGISTERS(9) /* restore 4 int argument registers         */
742         RESTORE_TEMPORARY_REGISTERS(13) /* restore 9 int temporary registers      */
743
744         ald     itmp1,(5+4+4+8+0)*4(sp) /* restore itmp1                          */
745         ald     itmp2,(5+4+4+8+1)*4(sp) /* restore itmp2                          */
746         ald     ra,(5+4+4+8+2)*4(sp)  /* restore method return address (for leafs)*/
747         ald     pv,(5+4+4+8+3)*4(sp)  /* restore pv of calling java function      */
748
749         bnez    itmp3,L_asm_wrapper_patcher_exception
750
751         ald     itmp3,7*8+(5+4+4+8+7)*4(sp) /* load RA                            */
752         aaddiu  sp,sp,8*8+(5+4+4+8+7)*4 /* remove stack frame                     */
753
754         jr      itmp3                 /* jump to new patched code                 */
755
756 L_asm_wrapper_patcher_exception:
757         move    xptr,itmp3            /* get exception                            */
758         ald     xpc,7*8+(5+4+4+8+7)*4(sp) /* xpc is RA                            */
759         aaddiu  sp,sp,8*8+(5+4+4+8+7)*4 /* remove stack frame                     */
760
761 #endif /* SIZEOF_VOID_P == 8 */
762
763         b       asm_handle_exception
764
765         .end    asm_patcher_wrapper
766
767 #if defined(ENABLE_REPLACEMENT)
768                 
769 /* asm_replacement_out *********************************************************
770
771    This code is jumped to from the replacement-out stubs that are executed
772    when a thread reaches an activated replacement point.
773
774    The purpose of asm_replacement_out is to read out the parts of the
775    execution state that cannot be accessed from C code, store this state,
776    and then call the C function replace_me.
777
778    Stack layout:
779      16                 start of stack inside method to replace
780       0   rplpoint *    info on the replacement point that was reached
781
782    NOTE: itmp3 has been clobbered by the replacement-out stub!
783
784 *******************************************************************************/
785
786 /* some room to accomodate changes of the stack frame size during replacement */
787         /* XXX we should find a cleaner solution here */
788 #define REPLACEMENT_ROOM  512
789
790 #define REPLACEMENT_STACK_OFFSET ((sizeexecutionstate + REPLACEMENT_ROOM + 0xf) & ~0xf)
791
792         .ent asm_replacement_out
793
794 asm_replacement_out:
795     /* create stack frame */
796         aaddiu  sp,sp,-REPLACEMENT_STACK_OFFSET
797
798         /* save registers in execution state */
799         ast     $0 ,( 0*8+offes_intregs)(sp)
800         ast     $1 ,( 1*8+offes_intregs)(sp)
801         ast     $2 ,( 2*8+offes_intregs)(sp)
802         ast     $3 ,( 3*8+offes_intregs)(sp)
803         ast     $4 ,( 4*8+offes_intregs)(sp)
804         ast     $5 ,( 5*8+offes_intregs)(sp)
805         ast     $6 ,( 6*8+offes_intregs)(sp)
806         ast     $7 ,( 7*8+offes_intregs)(sp)
807         ast     $8 ,( 8*8+offes_intregs)(sp)
808         ast     $9 ,( 9*8+offes_intregs)(sp)
809         ast     $10,(10*8+offes_intregs)(sp)
810         ast     $11,(11*8+offes_intregs)(sp)
811         ast     $12,(12*8+offes_intregs)(sp)
812         ast     $13,(13*8+offes_intregs)(sp)
813         ast     $14,(14*8+offes_intregs)(sp)
814         ast     $15,(15*8+offes_intregs)(sp)
815         ast     $16,(16*8+offes_intregs)(sp)
816         ast     $17,(17*8+offes_intregs)(sp)
817         ast     $18,(18*8+offes_intregs)(sp)
818         ast     $19,(19*8+offes_intregs)(sp)
819         ast     $20,(20*8+offes_intregs)(sp)
820         ast     $21,(21*8+offes_intregs)(sp)
821         ast     $22,(22*8+offes_intregs)(sp)
822         ast     $23,(23*8+offes_intregs)(sp)
823         ast     $24,(24*8+offes_intregs)(sp)
824         ast     $25,(25*8+offes_intregs)(sp)
825         ast     $26,(26*8+offes_intregs)(sp)
826         ast     $27,(27*8+offes_intregs)(sp)
827         ast     $28,(28*8+offes_intregs)(sp)
828         ast     $29,(29*8+offes_intregs)(sp)
829         ast     $30,(30*8+offes_intregs)(sp)
830         ast     $31,(31*8+offes_intregs)(sp)
831
832 #if SIZEOF_VOID_P == 8
833
834         sdc1    $f0 ,( 0*8+offes_fltregs)(sp)
835         sdc1    $f1 ,( 1*8+offes_fltregs)(sp)
836         sdc1    $f2 ,( 2*8+offes_fltregs)(sp)
837         sdc1    $f3 ,( 3*8+offes_fltregs)(sp)
838         sdc1    $f4 ,( 4*8+offes_fltregs)(sp)
839         sdc1    $f5 ,( 5*8+offes_fltregs)(sp)
840         sdc1    $f6 ,( 6*8+offes_fltregs)(sp)
841         sdc1    $f7 ,( 7*8+offes_fltregs)(sp)
842         sdc1    $f8 ,( 8*8+offes_fltregs)(sp)
843         sdc1    $f9 ,( 9*8+offes_fltregs)(sp)
844         sdc1    $f10,(10*8+offes_fltregs)(sp)
845         sdc1    $f11,(11*8+offes_fltregs)(sp)
846         sdc1    $f12,(12*8+offes_fltregs)(sp)
847         sdc1    $f13,(13*8+offes_fltregs)(sp)
848         sdc1    $f14,(14*8+offes_fltregs)(sp)
849         sdc1    $f15,(15*8+offes_fltregs)(sp)
850         sdc1    $f16,(16*8+offes_fltregs)(sp)
851         sdc1    $f17,(17*8+offes_fltregs)(sp)
852         sdc1    $f18,(18*8+offes_fltregs)(sp)
853         sdc1    $f19,(19*8+offes_fltregs)(sp)
854         sdc1    $f20,(20*8+offes_fltregs)(sp)
855         sdc1    $f21,(21*8+offes_fltregs)(sp)
856         sdc1    $f22,(22*8+offes_fltregs)(sp)
857         sdc1    $f23,(23*8+offes_fltregs)(sp)
858         sdc1    $f24,(24*8+offes_fltregs)(sp)
859         sdc1    $f25,(25*8+offes_fltregs)(sp)
860         sdc1    $f26,(26*8+offes_fltregs)(sp)
861         sdc1    $f27,(27*8+offes_fltregs)(sp)
862         sdc1    $f28,(28*8+offes_fltregs)(sp)
863         sdc1    $f29,(29*8+offes_fltregs)(sp)
864         sdc1    $f30,(30*8+offes_fltregs)(sp)
865         sdc1    $f31,(31*8+offes_fltregs)(sp)
866
867 #else /* SIZEOF_VOID_P == 8 */
868
869         sdc1    $f0 ,( 0*8+offes_fltregs)(sp)
870         sdc1    $f2 ,( 2*8+offes_fltregs)(sp)
871         sdc1    $f4 ,( 4*8+offes_fltregs)(sp)
872         sdc1    $f6 ,( 6*8+offes_fltregs)(sp)
873         sdc1    $f8 ,( 8*8+offes_fltregs)(sp)
874         sdc1    $f10,(10*8+offes_fltregs)(sp)
875         sdc1    $f12,(12*8+offes_fltregs)(sp)
876         sdc1    $f14,(14*8+offes_fltregs)(sp)
877         sdc1    $f16,(16*8+offes_fltregs)(sp)
878         sdc1    $f18,(18*8+offes_fltregs)(sp)
879         sdc1    $f20,(20*8+offes_fltregs)(sp)
880         sdc1    $f22,(22*8+offes_fltregs)(sp)
881         sdc1    $f24,(24*8+offes_fltregs)(sp)
882         sdc1    $f26,(26*8+offes_fltregs)(sp)
883         sdc1    $f28,(28*8+offes_fltregs)(sp)
884         sdc1    $f30,(30*8+offes_fltregs)(sp)
885
886 #endif /* SIZEOF_VOID_P == 8 */
887         
888         /* calculate sp of method */
889         aaddiu  itmp1,sp,(REPLACEMENT_STACK_OFFSET + 2*8)
890         ast     itmp1,(offes_sp)(sp)
891
892         /* store pv */
893         ast     pv,(offes_pv)(sp)
894
895         /* call replace_me */
896         ald     a0,-(2*8)(itmp1)            /* arg0: rplpoint *                   */
897     move    a1,sp                       /* arg1: execution state              */
898     jal     replace_me                  /* call C function replace_me         */
899         jal     abort                       /* NEVER REACHED                      */
900
901         .end asm_replacement_out
902
903 /* asm_replacement_in **********************************************************
904
905    This code writes the given execution state and jumps to the replacement
906    code.
907
908    This function never returns!
909
910    NOTE: itmp3 is not restored!
911
912    C prototype:
913       void asm_replacement_in(executionstate *es);
914
915 *******************************************************************************/
916
917         .ent asm_replacement_in
918         
919 asm_replacement_in:
920         /* a0 == executionstate *es */
921
922         /* set new sp and pv */
923         ald     sp,(offes_sp)(a0)
924         ald     pv,(offes_pv)(a0)
925         
926         /* copy registers from execution state */
927         /* $0 is zero                     */
928         ald     $1 ,( 1*8+offes_intregs)(a0)
929         ald     $2 ,( 2*8+offes_intregs)(a0)
930         ald     $3 ,( 2*8+offes_intregs)(a0)
931         /* a0 is loaded below             */
932         ald     $5 ,( 5*8+offes_intregs)(a0)
933         ald     $6 ,( 6*8+offes_intregs)(a0)
934         ald     $7 ,( 7*8+offes_intregs)(a0)
935         ald     $8 ,( 8*8+offes_intregs)(a0)
936         ald     $9 ,( 9*8+offes_intregs)(a0)
937         ald     $10,(10*8+offes_intregs)(a0)
938         ald     $11,(11*8+offes_intregs)(a0)
939         ald     $12,(12*8+offes_intregs)(a0)
940         ald     $13,(13*8+offes_intregs)(a0)
941         ald     $14,(14*8+offes_intregs)(a0)
942         ald     $15,(15*8+offes_intregs)(a0)
943         ald     $16,(16*8+offes_intregs)(a0)
944         ald     $17,(17*8+offes_intregs)(a0)
945         ald     $18,(18*8+offes_intregs)(a0)
946         ald     $19,(19*8+offes_intregs)(a0)
947         ald     $20,(20*8+offes_intregs)(a0)
948         ald     $21,(21*8+offes_intregs)(a0)
949         ald     $22,(22*8+offes_intregs)(a0)
950         ald     $23,(23*8+offes_intregs)(a0)
951         ald     $24,(24*8+offes_intregs)(a0)
952         ald     $25,(25*8+offes_intregs)(a0)
953         ald     $26,(26*8+offes_intregs)(a0)
954         ald     $27,(27*8+offes_intregs)(a0)
955         ald     $28,(28*8+offes_intregs)(a0)
956         /* $29 is sp                      */
957         /* $30 is pv                      */
958         ald     $31,(31*8+offes_intregs)(a0)
959         
960 #if SIZEOF_VOID_P == 8
961
962         ldc1    $f0 ,( 0*8+offes_fltregs)(a0)
963         ldc1    $f1 ,( 1*8+offes_fltregs)(a0)
964         ldc1    $f2 ,( 2*8+offes_fltregs)(a0)
965         ldc1    $f3 ,( 3*8+offes_fltregs)(a0)
966         ldc1    $f4 ,( 4*8+offes_fltregs)(a0)
967         ldc1    $f5 ,( 5*8+offes_fltregs)(a0)
968         ldc1    $f6 ,( 6*8+offes_fltregs)(a0)
969         ldc1    $f7 ,( 7*8+offes_fltregs)(a0)
970         ldc1    $f8 ,( 8*8+offes_fltregs)(a0)
971         ldc1    $f9 ,( 9*8+offes_fltregs)(a0)
972         ldc1    $f10,(10*8+offes_fltregs)(a0)
973         ldc1    $f11,(11*8+offes_fltregs)(a0)
974         ldc1    $f12,(12*8+offes_fltregs)(a0)
975         ldc1    $f13,(13*8+offes_fltregs)(a0)
976         ldc1    $f14,(14*8+offes_fltregs)(a0)
977         ldc1    $f15,(15*8+offes_fltregs)(a0)
978         ldc1    $f16,(16*8+offes_fltregs)(a0)
979         ldc1    $f17,(17*8+offes_fltregs)(a0)
980         ldc1    $f18,(18*8+offes_fltregs)(a0)
981         ldc1    $f19,(19*8+offes_fltregs)(a0)
982         ldc1    $f20,(20*8+offes_fltregs)(a0)
983         ldc1    $f21,(21*8+offes_fltregs)(a0)
984         ldc1    $f22,(22*8+offes_fltregs)(a0)
985         ldc1    $f23,(23*8+offes_fltregs)(a0)
986         ldc1    $f24,(24*8+offes_fltregs)(a0)
987         ldc1    $f25,(25*8+offes_fltregs)(a0)
988         ldc1    $f26,(26*8+offes_fltregs)(a0)
989         ldc1    $f27,(27*8+offes_fltregs)(a0)
990         ldc1    $f28,(28*8+offes_fltregs)(a0)
991         ldc1    $f29,(29*8+offes_fltregs)(a0)
992         ldc1    $f30,(30*8+offes_fltregs)(a0)
993         ldc1    $f31,(31*8+offes_fltregs)(a0)
994
995 #else /* SIZEOF_VOID_P == 8 */
996
997         ldc1    $f0 ,( 0*8+offes_fltregs)(a0)
998         ldc1    $f2 ,( 2*8+offes_fltregs)(a0)
999         ldc1    $f4 ,( 4*8+offes_fltregs)(a0)
1000         ldc1    $f6 ,( 6*8+offes_fltregs)(a0)
1001         ldc1    $f8 ,( 8*8+offes_fltregs)(a0)
1002         ldc1    $f10,(10*8+offes_fltregs)(a0)
1003         ldc1    $f12,(12*8+offes_fltregs)(a0)
1004         ldc1    $f14,(14*8+offes_fltregs)(a0)
1005         ldc1    $f16,(16*8+offes_fltregs)(a0)
1006         ldc1    $f18,(18*8+offes_fltregs)(a0)
1007         ldc1    $f20,(20*8+offes_fltregs)(a0)
1008         ldc1    $f22,(22*8+offes_fltregs)(a0)
1009         ldc1    $f24,(24*8+offes_fltregs)(a0)
1010         ldc1    $f26,(26*8+offes_fltregs)(a0)
1011         ldc1    $f28,(28*8+offes_fltregs)(a0)
1012         ldc1    $f30,(30*8+offes_fltregs)(a0)
1013
1014 #endif /* SIZEOF_VOID_P == 8 */
1015
1016         /* load new pc */
1017
1018         ald     itmp3,offes_pc(a0)
1019
1020         /* load a0 */
1021         
1022         ald     a0,(4*8+offes_intregs)(a0)
1023
1024         /* jump to new code */
1025
1026         jr      itmp3
1027
1028         .end asm_replacement_in
1029
1030 #endif /* defined(ENABLE_REPLACEMENT) */
1031
1032
1033         .ent    asm_getclassvalues_atomic
1034
1035 asm_getclassvalues_atomic:
1036 _crit_restart:
1037 _crit_begin:
1038         lw      t0,offbaseval(a0)
1039         lw      t1,offdiffval(a0)
1040         lw      t2,offbaseval(a1)
1041 _crit_end:
1042         sw      t0,offcast_super_baseval(a2)
1043         sw      t1,offcast_super_diffval(a2)
1044         sw      t2,offcast_sub_baseval(a2)
1045         j       ra
1046
1047         .end    asm_getclassvalues_atomic
1048
1049     .data
1050
1051 asm_criticalsections:
1052 #if defined(ENABLE_THREADS)
1053     .dword  _crit_begin
1054     .dword  _crit_end
1055     .dword  _crit_restart
1056 #endif
1057     .dword  0
1058
1059
1060         .text
1061
1062         .ent    compare_and_swap
1063
1064 compare_and_swap:
1065 1:
1066         all     v0,0(a0)
1067         bne     v0,a1,2f
1068         move    t0,a2
1069         asc     t0,0(a0)
1070         beqz    t0,1b
1071 2:
1072         sync
1073         j       ra
1074
1075         .end    compare_and_swap
1076
1077
1078 /* Disable exec-stacks, required for Gentoo ***********************************/
1079
1080 #if defined(__GCC__) && defined(__ELF__)
1081         .section .note.GNU-stack,"",@progbits
1082 #endif
1083
1084
1085 /*
1086  * These are local overrides for various environment variables in Emacs.
1087  * Please do not remove this and leave it at the end of the file, where
1088  * Emacs will automagically detect them.
1089  * ---------------------------------------------------------------------
1090  * Local variables:
1091  * mode: asm
1092  * indent-tabs-mode: t
1093  * c-basic-offset: 4
1094  * tab-width: 4
1095  * End:
1096  * vim:noexpandtab:sw=4:ts=4:
1097  */