522a39c2a5b3b9f0727cebc497639b7ca0da08df
[cacao.git] / src / vm / jit / i386 / asmpart.S
1 /* src/vm/jit/i386/asmpart.S - Java-C interface functions for i386
2
3    Copyright (C) 1996-2005, 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    $Id: asmpart.S 8274 2007-08-08 15:58:17Z twisti $
26
27 */
28
29
30 #include "config.h"
31
32 #include "md-asm.h"
33
34 #include "vm/jit/i386/arch.h"
35 #include "vm/jit/i386/md-abi.h"
36
37 #include "vm/jit/abi-asm.h"
38 #include "vm/jit/methodheader.h"
39
40
41         .text
42
43
44 /* export functions ***********************************************************/
45
46         .globl asm_md_init
47
48         .globl asm_vm_call_method
49         .globl asm_vm_call_method_int
50         .globl asm_vm_call_method_long
51         .globl asm_vm_call_method_float
52         .globl asm_vm_call_method_double
53         .globl asm_vm_call_method_exception_handler
54         .globl asm_vm_call_method_end
55
56         .globl asm_call_jit_compiler
57         .globl asm_handle_nat_exception
58         .globl asm_handle_exception
59
60         .globl asm_abstractmethoderror
61
62         .globl asm_patcher_wrapper
63
64 #if defined(ENABLE_REPLACEMENT)
65         .globl asm_replacement_out
66         .globl asm_replacement_in
67 #endif
68
69         .globl asm_builtin_f2i
70         .globl asm_builtin_f2l
71         .globl asm_builtin_d2i
72         .globl asm_builtin_d2l
73
74         .globl asm_compare_and_swap
75         .globl asm_memory_barrier
76
77         .globl asm_get_cycle_count
78
79
80 /* asm_md_init *****************************************************************
81
82    Initialize machine dependent stuff.
83
84    See: http://www.srware.com/linux_numerics.txt
85
86    This puts the X86 FPU in 64-bit precision mode.  The default under
87    Linux is to use 80-bit mode, which produces subtle differences from
88    FreeBSD and other systems, eg, (int)(1000*atof("0.3")) is 300 in
89    64-bit mode, 299 in 80-bit mode.
90
91    Fixes: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=350729
92
93 *******************************************************************************/
94
95 asm_md_init:
96         sub     $4,sp                       /* allocate space for the FPU state   */
97         fnstcw  (sp)                        /* get the FPU state                  */
98         mov     (sp),%eax
99         and     $0xfcff,%ax                 /* remove the extended mode flag      */
100         or      $0x0200,%ax                 /* put the double mode flag           */
101         mov     %eax,(sp)                   /* store new FPU state                */
102         fldcw   (sp)                        /* setup new FPU state                */
103         add     $4,sp
104         ret
105
106
107 /********************* function asm_calljavafunction ***************************
108 *                                                                              *
109 *   This function calls a Java-method (which possibly needs compilation)       *
110 *   with up to 4 address parameters.                                           *
111 *                                                                              *
112 *   This functions calls the JIT-compiler which eventually translates the      *
113 *   method into machine code.                                                  *
114 *                                                                              *
115 *   C-prototype:                                                               *
116 *    javaobject_header *asm_vm_call_method(methodinfo *m,                      *
117 *         u4 count, u4 size, void *callblock);                                 *
118 *                                                                              *
119 *******************************************************************************/
120
121         .align  8
122
123         .long   0                           /* catch type all                     */
124         .long   0                           /* handler pc                         */
125         .long   0                           /* end pc                             */
126         .long   0                           /* start pc                           */
127         .long   1                           /* extable size                       */
128         .long   0                           /* line number table start            */
129         .long   0                           /* line number table size             */
130         .long   0                           /* fltsave                            */
131         .long   0                           /* intsave                            */
132         .long   0                           /* isleaf                             */
133         .long   0                           /* IsSync                             */
134         .long   0                           /* frame size                         */
135         .long   0                           /* codeinfo pointer                   */
136
137 asm_vm_call_method:
138 asm_vm_call_method_int:
139 asm_vm_call_method_long:
140 asm_vm_call_method_float:
141 asm_vm_call_method_double:
142         push    bp
143         mov     sp,bp                       /* save stackptr                      */
144         sub     $(4*4),sp                   /* create stackframe                  */
145         and     $0xfffffff0,sp              /* align stack to 16-byte             */
146
147         mov     t0,0*4(sp)                  /* save registers                     */
148         mov     s1,1*4(sp)
149         mov     s2,2*4(sp)
150
151         mov     sp,s1                       /* save stack pointer                 */
152
153         mov     3*4(bp),t0                  /* address of data structure          */
154         mov     4*4(bp),itmp1               /* number of stack arguments          */
155
156         cmp     $0,itmp1
157         je      L_asm_vm_call_method_stack_copy_done
158
159         mov     itmp1,itmp2
160         add     $1,itmp2                    /* keep stack 16-byte aligned         */
161         and     $0xfffffffe,itmp2
162         shl     $3,itmp2                    /* calculate stack size               */
163         sub     itmp2,sp                    /* create stack frame                 */
164         mov     sp,itmp2                    /* temporary stack pointer            */
165
166 L_asm_vm_call_method_stack_copy_loop:
167         mov     0(t0),itmp3                 /* load argument                      */
168         mov     itmp3,0(itmp2)              /* store argument on stack            */
169         mov     4(t0),itmp3
170         mov     itmp3,4(itmp2)
171
172         sub     $1,itmp1                    /* subtract 1 argument                */
173         add     $8,t0                       /* set address of next argument       */
174         add     $8,itmp2                    /* increase SP                        */
175
176         cmp     $0,itmp1
177         jg      L_asm_vm_call_method_stack_copy_loop
178
179 L_asm_vm_call_method_stack_copy_done:
180         lea     (2*4-256)(bp),mptr          /* We subtract 256 to force the next  */
181                                             /* move instruction to have a 32-bit  */
182                                             /* offset.                            */
183
184         mov     (0*4+256)(mptr),itmp3       /* method call as in Java             */
185         call    *itmp3                      /* call JIT compiler                  */
186
187 L_asm_vm_call_method_return:
188         mov     s1,sp                       /* restore stackpointer               */
189
190         mov     0*4(sp),t0                  /* restore registers                  */
191         mov     1*4(sp),s1
192         mov     2*4(sp),s2
193
194         leave
195         ret
196
197 asm_vm_call_method_exception_handler:
198         push    xptr                        /* pass exception pointer             */
199         call    builtin_throw_exception
200         add     $4,sp
201 asm_vm_call_method_end:
202         jmp     L_asm_vm_call_method_return
203
204
205 /* asm_call_jit_compiler *******************************************************
206
207    Invokes the compiler for untranslated JavaVM methods.
208
209    Register R0 contains a pointer to the method info structure (prepared
210    by createcompilerstub). Using the return address in R26 and the
211    offset in the LDA instruction or using the value in methodptr R28 the
212    patching address for storing the method address can be computed:
213
214    Method address was either loaded using
215
216    i386_mov_imm_reg(a, REG_ITMP2)                ; invokestatic/special
217    i386_call_reg(REG_ITMP2)
218
219    or
220
221    i386_mov_membase_reg(REG_SP, 0, REG_ITMP1)    ; invokevirtual/interface
222    i386_mov_membase_reg(REG_ITMP1, OFFSET(, vftbl), REG_ITMP2)
223    i386_mov_membase_reg(REG_ITMP2, OFFSET(vftbl, table[0]) + \
224        sizeof(methodptr) * m->vftblindex, REG_ITMP1)
225    i386_call_reg(REG_ITMP1)
226
227    In the static case the method pointer can be computed using the
228    return address and the lda function following the jmp instruction.
229
230 *******************************************************************************/
231
232 asm_call_jit_compiler:
233 L_asm_call_jit_compiler:                /* required for PIC code              */
234         sub     $(4*4),sp                   /* keep stack 16-byte aligned         */
235
236         mov     itmp1,0*4(sp)               /* pass methodinfo pointer            */
237         mov     mptr,1*4(sp)                /* pass method pointer                */
238         mov     sp,itmp2                    /* pass java sp                       */
239         add     $((1+4)*4),itmp2
240         mov     itmp2,2*4(sp)
241         mov     4*4(sp),itmp3               /* pass java ra                       */
242         mov     itmp3,3*4(sp)
243         call    jit_asm_compile
244
245         add     $(4*4),sp                   /* remove stack frame                 */
246
247         test    v0,v0                       /* check for exception                */
248         je      L_asm_call_jit_compiler_exception
249
250         jmp             *v0                         /* ...and now call the new method     */
251
252 L_asm_call_jit_compiler_exception:
253         call    exceptions_get_and_clear_exception
254                                             /* v0 == xptr                         */
255         pop     xpc                         /* get return address                 */
256         sub     $2,xpc                      /* faulting address is ra - 2         */
257         jmp     L_asm_handle_exception
258
259
260 /* asm_handle_exception ********************************************************
261 *                                                                              *
262 *   This function handles an exception. It does not use the usual calling      *
263 *   conventions. The exception pointer is passed in REG_ITMP1 and the          *
264 *   pc from the exception raising position is passed in REG_ITMP2. It searches *
265 *   the local exception table for a handler. If no one is found, it unwinds    *
266 *   stacks and continues searching the callers.                                *
267 *                                                                              *
268 *******************************************************************************/
269
270 asm_handle_nat_exception:
271         add     $4,sp                       /* clear return address of native stub*/
272                 
273 asm_handle_exception:
274 L_asm_handle_exception:                 /* required for PIC code              */
275         sub     $((ARG_CNT+TMP_CNT+3)*4),sp /* keep stack 16-byte aligned         */
276
277         SAVE_ARGUMENT_REGISTERS(0)          /* we save arg and temp registers in  */
278         SAVE_TEMPORARY_REGISTERS(ARG_CNT)   /* case this is a leaf method         */
279
280         mov     $((ARG_CNT+TMP_CNT+3)*4),itmp3 /* prepare a3 for handle_exception */
281         mov     $1,t0                       /* set maybe-leaf flag                */
282
283 L_asm_handle_exception_stack_loop:
284         sub     $(12*4),sp                  /* keep stack 16-byte aligned         */
285         mov     xptr,4*4(sp)                /* save exception pointer             */
286         mov     xpc,5*4(sp)                 /* save exception pc                  */
287         add     sp,itmp3                    /* calculate Java sp into a3...       */
288         add     $(12*4),itmp3
289         mov     itmp3,7*4(sp)               /* ...and save it                     */
290         mov     t0,8*4(sp)                  /* save maybe-leaf flag               */
291
292         mov     xpc,0*4(sp)                 /* pass exception pc                  */
293         call    codegen_get_pv_from_pc
294         mov     v0,6*4(sp)                  /* save data segment pointer          */
295
296         mov     4*4(sp),itmp3               /* pass exception pointer             */
297         mov     itmp3,0*4(sp)
298         mov     5*4(sp),itmp3               /* pass exception pc                  */
299         mov     itmp3,1*4(sp)
300         mov     v0,2*4(sp)                  /* pass data segment pointer          */
301         mov     7*4(sp),itmp3               /* pass Java stack pointer            */
302         mov     itmp3,3*4(sp)
303         call    exceptions_handle_exception
304
305         test    v0,v0
306         jz      L_asm_handle_exception_not_catched
307
308         mov     v0,xpc                      /* move handlerpc into xpc            */
309         mov     4*4(sp),xptr                /* restore exception pointer          */
310         mov     8*4(sp),t0                  /* get maybe-leaf flag                */
311         add     $(12*4),sp                  /* free stackframe                    */
312
313         test    t0,t0                       /* test for maybe-leaf flag           */
314         jz      L_asm_handle_exception_no_leaf
315
316         RESTORE_ARGUMENT_REGISTERS(0)       /* if this is a leaf method, we have  */
317         RESTORE_TEMPORARY_REGISTERS(ARG_CNT)/* to restore arg and temp registers  */
318
319         add     $((ARG_CNT+TMP_CNT+3)*4),sp /* remove maybe-leaf stackframe       */
320
321 L_asm_handle_exception_no_leaf:
322         jmp     *xpc                        /* jump to exception handler          */
323
324 L_asm_handle_exception_not_catched:
325         mov     4*4(sp),xptr                /* restore exception pointer          */
326         mov     6*4(sp),itmp3               /* restore data segment pointer       */
327         mov     8*4(sp),t0                  /* get maybe-leaf flag                */
328         add     $(12*4),sp                  /* free stackframe                    */
329
330         test    t0,t0
331         jz      L_asm_handle_exception_no_leaf_stack
332
333         add     $((ARG_CNT+TMP_CNT+3)*4),sp /* remove maybe-leaf stackframe       */
334         xor     t0,t0                       /* clear the maybe-leaf flag          */
335
336 L_asm_handle_exception_no_leaf_stack:
337         mov     FrameSize(itmp3),itmp2      /* get frame size                     */
338         add     sp,itmp2                    /* pointer to save area               */
339
340         push    xptr                        /* we are out of registers            */
341
342         mov     IntSave(itmp3),itmp1        /* itmp1 = saved int register count   */
343         test    itmp1,itmp1
344         je      noint
345
346         cmp     $1,itmp1
347         je      int1
348         cmp     $2,itmp1
349         je      int2
350
351         mov     -3*8(itmp2),s0
352 int2:   
353         mov     -2*8(itmp2),s1
354 int1:   
355         mov     -1*8(itmp2),s2
356
357         shl     $2,itmp1                    /* multiply by 4 bytes                */
358         sub     itmp1,itmp2
359                 
360 noint:
361 #if 0
362         mov     FltSave(itmp3),itmp1        /* itmp1 = saved flt register count   */
363         test    itmp1,itmp1
364         je      noflt
365
366         cmp     $1,itmp1
367         je      flt1
368         cmp     $2,itmp1
369         je      flt2
370         cmp     $3,itmp1
371         je      flt3
372                 
373         fldl    -4*8(itmp2)
374         fstp    %st(1)
375 flt3:
376         fldl    -3*8(itmp2)
377         fstp    %st(2)
378 flt2:
379         fldl    -2*8(itmp2)
380         fstp    %st(3)
381 flt1:
382         fldl    -1*8(itmp2)
383         fstp    %st(4)
384                 
385 noflt:
386 #endif
387         pop     xptr                        /* restore exception pointer          */
388         mov     FrameSize(itmp3),itmp2      /* get frame size                     */
389         add     itmp2,sp                    /* unwind stack                       */
390
391         pop     xpc                         /* the new xpc is return address      */
392         sub     $2,xpc                      /* subtract 2-bytes for call          */
393
394         xor     itmp3,itmp3                 /* prepare a3 for handle_exception    */
395
396         jmp     L_asm_handle_exception_stack_loop
397                 
398
399 /* asm_abstractmethoderror *****************************************************
400
401    Creates and throws an AbstractMethodError.
402
403 *******************************************************************************/
404
405 asm_abstractmethoderror:
406         sub     $(3*4),sp                   /* keep stack 16-byte aligned         */
407         mov     sp,itmp1                    /* pass java sp                       */
408         add     $((1+3)*4),itmp1
409         mov     itmp1,0*4(sp)
410         mov     3*4(sp),itmp2               /* pass exception address             */
411         sub     $2,itmp2
412         mov     itmp2,1*4(sp)
413         call    exceptions_asm_new_abstractmethoderror
414                                             /* exception pointer is return value  */
415         add     $(3*4),sp                   /* remove stack frame                 */
416
417         pop     xpc                         /* get exception address              */
418         sub     $2,xpc                      /* exception address is ra - 2        */
419         jmp     L_asm_handle_exception
420
421
422 /* asm_patcher_wrapper *********************************************************
423
424    XXX
425
426    Stack layout:
427      24   return address
428      20   REG_ITMP3
429      16   pointer to virtual java_objectheader
430      12   last byte of machine code (xmcode)
431       8   machine code (which is patched back later)
432       4   unresolved field reference
433       0   patcher function pointer to call
434
435 *******************************************************************************/
436
437 asm_patcher_wrapper:
438         sub     $((1+4+4)*4),sp             /* keep stack 16-byte aligned         */
439
440         mov     itmp1,(0+4)*4(sp)           /* save itmp1 and itmp2               */
441         mov     itmp2,(1+4)*4(sp)
442
443         mov     sp,itmp1                    /* pass SP of patcher stub            */
444         add     $((1+4+4)*4),itmp1
445         mov     itmp1,0*4(sp)
446         movl    $0,1*4(sp)                  /* pass PV (if NULL, use findmethod)  */
447         movl    $0,2*4(sp)                  /* pass RA (it's on the stack)        */
448         call    patcher_wrapper
449         mov     v0,itmp3                    /* save return value                  */
450
451         mov     (0+4)*4(sp),itmp1           /* restore itmp1 and itmp2            */
452         mov     (1+4)*4(sp),itmp2
453
454         test    itmp3,itmp3                 /* exception thrown?                  */
455         jne     L_asm_patcher_wrapper_exception
456
457         mov     (5+1+4+4)*4(sp),itmp3       /* restore itmp3                      */
458         add     $((6+1+4+4)*4),sp           /* remove stack frame, keep RA        */
459
460         ret                                 /* jump to new patched code           */
461
462 L_asm_patcher_wrapper_exception:
463         add     $((6+1+4+4)*4),sp           /* remove stack frame, keep RA        */
464         mov     itmp3,xptr                  /* get exception                      */
465         pop     xpc                         /* get and remove return address      */
466         jmp     L_asm_handle_exception
467
468 #if defined(ENABLE_REPLACEMENT)
469
470 /* asm_replacement_out *********************************************************
471
472    This code is jumped to from the replacement-out stubs that are executed
473    when a thread reaches an activated replacement point.
474
475    The purpose of asm_replacement_out is to read out the parts of the
476    execution state that cannot be accessed from C code, store this state,
477    and then call the C function replace_me.
478
479    Stack layout:
480       4                 start of stack inside method to replace
481       0   rplpoint *    info on the replacement point that was reached
482
483 *******************************************************************************/
484
485 /* some room to accomodate changes of the stack frame size during replacement */
486         /* XXX we should find a cleaner solution here */
487 #define REPLACEMENT_ROOM  512
488
489 asm_replacement_out:
490     /* create stack frame */
491         sub     $(sizeexecutionstate + REPLACEMENT_ROOM),sp
492
493         /* save registers in execution state */
494         mov     %eax,(EAX*4+offes_intregs)(sp)
495         mov     %ebx,(EBX*4+offes_intregs)(sp)
496         mov     %ecx,(ECX*4+offes_intregs)(sp)
497         mov     %edx,(EDX*4+offes_intregs)(sp)
498         mov     %esi,(ESI*4+offes_intregs)(sp)
499         mov     %edi,(EDI*4+offes_intregs)(sp)
500         mov     %ebp,(EBP*4+offes_intregs)(sp)
501         movl    $0  ,(ESP*4+offes_intregs)(sp) /* not used */
502
503         /* calculate sp of method */
504         mov     sp,itmp1
505         add     $(sizeexecutionstate + REPLACEMENT_ROOM + 4),itmp1
506         mov     itmp1,(offes_sp)(sp)
507
508         /* pv must be looked up via AVL tree */
509         movl    $0,(offes_pv)(sp)
510
511         /* call replace_me */
512         mov     -4(itmp1),itmp1             /* rplpoint *                         */
513     push    sp                          /* arg1: execution state              */
514     push    itmp1                       /* arg0: replacement point            */
515     call    replace_me                  /* call C function replace_me         */
516
517
518 /* asm_replacement_in **********************************************************
519
520    This code writes the given execution state and jumps to the replacement
521    code.
522
523    This function never returns!
524
525    C prototype:
526       void asm_replacement_in(executionstate *es, replace_safestack_t *st);
527
528 *******************************************************************************/
529
530 asm_replacement_in:
531         /* get arguments */
532         mov     8(sp),%esi                  /* replace_safestack_t *st            */
533         mov     4(sp),%ebp                  /* executionstate *es == safe stack   */
534
535         /* switch to the safe stack and build a stack frame */
536         mov     %ebp,sp
537         sub             $(1*4),sp
538
539         /* call replace_build_execution_state(st) */
540         mov             %esi,(0*4)(sp)
541         call    replace_build_execution_state
542
543         /* set new sp */
544         mov     (offes_sp)(%ebp),sp
545
546         /* push address of new code */
547         push    (offes_pc)(%ebp)
548
549         /* allocate an executionstate_t on the stack */
550         sub             $(sizeexecutionstate),sp
551
552         /* call replace_free_safestack(st,& of allocated executionstate_t) */
553         push    sp   /* tmpes */
554         push    %esi /* st    */
555         call    replace_free_safestack
556         add     $(2*4),sp
557
558         /* copy registers from execution state */
559         mov     (EAX*4+offes_intregs)(sp),%eax
560         mov     (EBX*4+offes_intregs)(sp),%ebx
561         mov     (ECX*4+offes_intregs)(sp),%ecx
562         mov     (EDX*4+offes_intregs)(sp),%edx
563         mov     (ESI*4+offes_intregs)(sp),%esi
564         mov     (EDI*4+offes_intregs)(sp),%edi
565         mov     (EBP*4+offes_intregs)(sp),%ebp
566
567         /* pop the execution state off the stack */
568         add             $(sizeexecutionstate),sp
569
570         /* jump to new code, hold your thumbs! ;) */
571         ret
572
573 #endif /* defined(ENABLE_REPLACEMENT) */
574
575
576 /************************ function asm_builtin_x2x *****************************
577 *                                                                              *
578 *   Wrapper functions for corner cases                                         *
579 *                                                                              *
580 *******************************************************************************/
581
582 asm_builtin_f2i:
583         sub     $(3*4),%esp
584         fsts    (%esp)
585         call    builtin_f2i
586         add     $(3*4),%esp
587         ret
588
589 asm_builtin_d2i:
590         sub     $(3*4),%esp
591         fstl    (%esp)
592         call    builtin_d2i
593         add     $(3*4),%esp
594         ret
595
596 asm_builtin_f2l:
597         sub     $(3*4),%esp
598         fsts    (%esp)
599         call    builtin_f2l
600         add     $(3*4),%esp
601         ret
602
603 asm_builtin_d2l:
604         sub     $(3*4),%esp
605         fstl    (%esp)
606         call    builtin_d2l
607         add     $(3*4),%esp
608         ret
609
610
611 /* asm_compare_and_swap ********************************************************
612
613    Does an atomic compare and swap.  Required for the lock
614    implementation.
615
616    Atomically do the following: Check if the location still contains
617    `oldval`. If so, replace it by `newval` and return `oldval`.
618
619    RETURN VALUE:
620        the old value at *p
621
622    long compare_and_swap(volatile long *p, long oldval, long newval);
623
624 *******************************************************************************/
625
626 asm_compare_and_swap:
627         mov     1*4(sp),%ecx            /* load p into a register                 */
628         mov     2*4(sp),%eax            /* load oldval into return register       */
629         mov     3*4(sp),%edx            /* load newval into a register            */
630         lock; cmpxchgl %edx,0(%ecx)
631         ret
632
633
634 /* asm_memory_barrier **********************************************************
635
636    A memory barrier for the Java Memory Model.
637
638 *******************************************************************************/
639
640 asm_memory_barrier:
641         lock; add $0,0(sp)
642         ret
643
644                 
645 /* asm_get_cycle_count *********************************************************
646
647    Get the current time-stamp counter from the CPU.
648
649 *******************************************************************************/
650
651 asm_get_cycle_count:
652         rdtsc
653         ret
654
655
656 /* disable exec-stacks ********************************************************/
657
658 #if defined(__linux__) && defined(__ELF__)
659         .section .note.GNU-stack,"",%progbits
660 #endif
661
662
663 /*
664  * These are local overrides for various environment variables in Emacs.
665  * Please do not remove this and leave it at the end of the file, where
666  * Emacs will automagically detect them.
667  * ---------------------------------------------------------------------
668  * Local variables:
669  * mode: asm
670  * indent-tabs-mode: t
671  * c-basic-offset: 4
672  * tab-width: 4
673  * End:
674  * vim:noexpandtab:sw=4:ts=4:
675  */