* src/vm/jit/emit-common.h (emit_copy): Changed signature.
[cacao.git] / src / vm / jit / m68k / emit.c
1 /* src/vm/jit/m68k/emit.c
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: arch.h 5330 2006-09-05 18:43:12Z edwin $
26
27 */
28
29
30 #include "config.h"
31
32 #include <assert.h>
33
34 #include "emit.h"
35 #include "vm/jit/emit-common.h"
36 #include "vm/exceptions.h"
37 #include "vm/jit/asmpart.h"
38
39 #include "vm/builtin.h"
40 #include "mm/memory.h"
41
42 #include "codegen.h"
43
44 /*
45  *      Loads an immededat operand into data register
46  */
47 void emit_mov_imm_reg (codegendata *cd, s4 imm, s4 dreg)
48 {
49         if ((imm & 0x000000FF) == imm)  {
50                 /* use byte form */
51                 *((s2*)cd->mcodeptr) = 0x7000 | (dreg << 9) | imm;      /* MOVEQ.L */
52                 cd->mcodeptr += 2;
53         } else if ((imm  & 0xFFFF0000) != 0)    {
54                 /* use long form */
55                 OPWORD( ((2<<6) | (dreg << 3) | 0), 7, 4);
56                 *((s4*)cd->mcodeptr) = (s4)imm;
57                 cd->mcodeptr += 4;
58         } else {
59                 /* use word form */
60                 OPWORD( ((3<<6) | (dreg << 3) | 0), 7, 4);
61                 *((s2*)cd->mcodeptr) = (s2)imm;
62                 cd->mcodeptr += 2;
63         }
64 }
65
66
67 /* emit_copy *******************************************************************
68
69    Generates a register/memory to register/memory copy.
70
71 *******************************************************************************/
72
73 void emit_copy(jitdata *jd, instruction *iptr)
74 {
75         codegendata *cd;
76         varinfo     *src;
77         varinfo     *dst;
78         s4           s1, d;
79
80         /* get required compiler data */
81
82         cd = jd->cd;
83
84         /* get source and destination variables */
85
86         src = VAROP(iptr->s1);
87         dst = VAROP(iptr->dst);
88
89         if ((src->vv.regoff != dst->vv.regoff) ||
90                 (IS_INMEMORY(src->flags ^ dst->flags))) {
91
92                 if ((src->type == TYPE_RET) || (dst->type == TYPE_RET)) {
93                         /* emit nothing, as the value won't be used anyway */
94                         return;
95                 }
96
97                 /* If one of the variables resides in memory, we can eliminate
98                    the register move from/to the temporary register with the
99                    order of getting the destination register and the load. */
100
101                 if (IS_INMEMORY(src->flags)) {
102                         if (IS_LNG_TYPE(src->type))
103                                 d = codegen_reg_of_var(iptr->opc, dst, REG_ITMP12_PACKED);
104                         else
105                                 d = codegen_reg_of_var(iptr->opc, dst, REG_IFTMP);
106
107                         s1 = emit_load(jd, iptr, src, d);
108                 }
109                 else {
110                         if (IS_LNG_TYPE(src->type))
111                                 s1 = emit_load(jd, iptr, src, REG_ITMP12_PACKED);
112                         else
113                                 s1 = emit_load(jd, iptr, src, REG_IFTMP);
114
115                         d = codegen_reg_of_var(iptr->opc, dst, s1);
116                 }
117
118                 if (s1 != d) {
119                         switch(src->type)       {
120                         case TYPE_INT: M_INTMOVE(s1, d); break;
121                         case TYPE_ADR: M_ADRMOVE(s1, d); break;
122                         case TYPE_LNG: M_LNGMOVE(s1, d); break;
123 #if !defined(ENABLE_SOFTFLOAT)
124                         case TYPE_FLT: M_FLTMOVE(s1, d); break;
125                         case TYPE_DBL: M_DBLMOVE(s1, d); break;
126 #else
127                         case TYPE_FLT: M_INTMOVE(s1, d); break;
128                         case TYPE_DBL: M_LNGMOVE(s1, d); break;
129 #endif
130                         default:
131                                 vm_abort("emit_copy: unknown type %d", src->type);
132                         }
133                 }
134
135                 emit_store(jd, iptr, dst, d);
136         }
137 }
138
139
140 /* emit_store ******************************************************************
141
142    Emits a possible store of the destination operand.
143
144 *******************************************************************************/
145
146 inline void emit_store(jitdata *jd, instruction *iptr, varinfo *dst, s4 d)
147 {
148         codegendata  *cd;
149
150         /* get required compiler data */
151
152         cd = jd->cd;
153
154         if (IS_INMEMORY(dst->flags)) {
155                 COUNT_SPILLS;
156         
157                 switch(dst->type)       {
158 #if defined(ENABLE_SOFTFLOAT)
159                         case TYPE_DBL:
160 #endif
161                         case TYPE_LNG:
162                                 M_LST(d, REG_SP, dst->vv.regoff * 4);
163                                 break;
164 #if defined(ENABLE_SOFTFLOAT)
165                         case TYPE_FLT:
166 #endif
167                         case TYPE_INT:
168                                 M_IST(d, REG_SP, dst->vv.regoff * 4);
169                                 break;
170                         case TYPE_ADR:
171                                 M_AST(d, REG_SP, dst->vv.regoff * 4);
172                                 break;
173 #if !defined(ENABLE_SOFTFLOAT)
174                         case TYPE_DBL:
175                                 M_DST(d, REG_SP, dst->vv.regoff * 4);
176                                 break;
177                         case TYPE_FLT:
178                                 M_FST(d, REG_SP, dst->vv.regoff * 4);
179                                 break;
180 #endif
181                         default:
182                                 vm_abort("emit_store: unknown type %d", dst->type);
183                 }
184         }
185 }
186
187
188 /* emit_load *******************************************************************
189
190    Emits a possible load of an operand.
191
192 *******************************************************************************/
193
194 s4 emit_load(jitdata *jd, instruction *iptr, varinfo *src, s4 tempreg)
195 {
196         codegendata *cd;
197         s4           disp;
198         s4           reg;
199
200         /* get required compiler data */
201
202         cd = jd->cd;
203
204         if (IS_INMEMORY(src->flags)) {
205                 COUNT_SPILLS;
206
207                 disp = src->vv.regoff * 4;
208         
209                 switch (src->type)      {
210 #if defined(ENABLE_SOFTFLOAT)
211                         case TYPE_FLT:
212 #endif
213                         case TYPE_INT: 
214                                 M_ILD(tempreg, REG_SP, disp);
215                                 break;
216 #if defined(ENABLE_SOFTFLOAT)
217                         case TYPE_DBL:
218 #endif
219                         case TYPE_LNG:
220                                 M_LLD(tempreg, REG_SP, disp);
221                                 break;
222                         case TYPE_ADR:
223                                 M_ALD(tempreg, REG_SP, disp);
224                                 break;
225 #if !defined(ENABLE_SOFTFLOAT)
226                         case TYPE_FLT:
227                                 M_FLD(tempreg, REG_SP, disp);
228                                 break;
229                         case TYPE_DBL:
230                                 M_DLD(tempreg, REG_SP, disp);
231                                 break;
232 #endif
233                         default:
234                                 vm_abort("emit_load: unknown type %d", src->type);
235                 }
236                 #if 0
237                 if (IS_FLT_DBL_TYPE(src->type)) {
238                         if (IS_2_WORD_TYPE(src->type)) {
239                                 M_DLD(tempreg, REG_SP, disp);
240                          } else {
241                                 M_FLD(tempreg, REG_SP, disp);
242                         }
243                 } else {
244                         if (IS_2_WORD_TYPE(src->type)) {
245                                 M_LLD(tempreg, REG_SP, disp);
246                         } else {
247                                 M_ILD(tempreg, REG_SP, disp);
248                         }
249                 }
250                 #endif
251
252                 reg = tempreg;
253         }
254         else
255                 reg = src->vv.regoff;
256
257         return reg;
258 }
259
260
261 /* emit_patcher_stubs **********************************************************
262
263    Generates the code for the patcher stubs.
264
265 *******************************************************************************/
266 void emit_patcher_stubs(jitdata *jd)
267 {
268         codegendata *cd;
269         patchref    *pref;
270         u8           mcode;
271         u1          *savedmcodeptr;
272         u1          *tmpmcodeptr;
273         s4           targetdisp;
274         s4           disp;
275
276         /* get required compiler data */
277
278         cd = jd->cd;
279
280         /* generate code patching stub call code */
281
282         targetdisp = 0;
283
284         for (pref = cd->patchrefs; pref != NULL; pref = pref->next) {
285                 /* check code segment size */
286
287                 MCODECHECK(512);
288
289                 /* Get machine code which is patched back in later. A
290                    `bsr.l' is 6 bytes long. */
291
292                 savedmcodeptr = cd->mcodebase + pref->branchpos;
293                 mcode = *((u8 *) savedmcodeptr);
294
295                 /* patch in `bsr.l' to call the following code */
296
297                 tmpmcodeptr  = cd->mcodeptr;    /* save current mcodeptr              */
298                 cd->mcodeptr = savedmcodeptr;   /* set mcodeptr to patch position     */
299
300                 M_BSR_IMM(tmpmcodeptr - (savedmcodeptr + PATCHER_CALL_SIZE) + 4);
301
302                 cd->mcodeptr = tmpmcodeptr;     /* restore the current mcodeptr       */
303
304                 /* save REG_ITMP3 */
305                 M_IPUSH(REG_ITMP3);     /* FIXME why, and restore where ? */
306
307                 /* move pointer to java_objectheader onto stack */
308
309 #if defined(ENABLE_THREADS)
310                 (void) dseg_add_unique_address(cd, NULL);                  /* flcword */
311                 (void) dseg_add_unique_address(cd, lock_get_initial_lock_word());
312                 disp = dseg_add_unique_address(cd, NULL);                  /* vftbl   */
313
314                 assert(0); /* The next lines are wrong */
315                 M_MOV_IMM(0, REG_ITMP3);
316                 dseg_adddata(cd);
317                 M_AADD_IMM(REG_ITMP3, disp);
318                 M_IPUSH(REG_ITMP3);
319 #else
320                 M_IPUSH_IMM(0);
321 #endif
322
323                 /* push move machine code bytes and classinfo pointer */
324
325                 M_IPUSH_IMM(mcode >> 32);
326                 M_IPUSH_IMM(mcode);
327                 M_IPUSH_IMM(pref->ref);
328                 M_IPUSH_IMM(pref->patcher);
329
330                 M_JMP_IMM(asm_patcher_wrapper);
331         }
332 }
333 s4 emit_load_low(jitdata *jd, instruction *iptr, varinfo *src, s4 tempreg) 
334 {
335         codegendata  *cd;
336         s4            disp;
337         s4            reg;
338
339         assert(src->type == TYPE_LNG);
340
341         /* get required compiler data */
342         cd = jd->cd;
343
344         if (IS_INMEMORY(src->flags)) {
345                 COUNT_SPILLS;
346
347                 disp = src->vv.regoff * 4;
348                 M_ILD(tempreg, REG_SP, disp + 4);
349                 reg = tempreg;
350         } else {
351                 reg = GET_LOW_REG(src->vv.regoff);
352         }
353         return reg;
354 }
355 s4 emit_load_high(jitdata *jd, instruction *iptr, varinfo *src, s4 tempreg)
356 {
357         codegendata  *cd;
358         s4            disp;
359         s4            reg;
360
361         assert(src->type == TYPE_LNG);
362
363         /* get required compiler data */
364         cd = jd->cd;
365
366         if (IS_INMEMORY(src->flags)) {
367                 COUNT_SPILLS;
368                 disp = src->vv.regoff * 4;
369                 M_ILD(tempreg, REG_SP, disp);
370                 reg = tempreg;
371         } else {
372                 reg = GET_HIGH_REG(src->vv.regoff);
373         }
374         return reg;
375 }
376 /* emit_branch *****************************************************************
377
378    Emits the code for conditional and unconditional branchs.
379
380 *******************************************************************************/
381 void emit_branch(codegendata *cd, s4 disp, s4 condition, s4 reg, u4 opt) 
382
383         /* calculate the different displacements */
384         /* PC is a at branch instruction + 2 */
385         /* coditional and uncondition branching work the same way */
386         /* short branches have signed 16 bit offset */
387         /* long branches are signed 32 bit */
388         /* the 8 bit offset branching instructions are not used */
389
390         disp  =  disp - 2;
391
392         /* check displacement for overflow */
393         if ((disp & 0x0000FFFF) != disp)        {
394                 if (!CODEGENDATA_HAS_FLAG_LONGBRANCHES(cd)) {
395                         cd->flags |= (CODEGENDATA_FLAG_ERROR | CODEGENDATA_FLAG_LONGBRANCHES);
396                 }
397         }
398
399         /* check which branch to generate */
400
401         if (condition == BRANCH_UNCONDITIONAL) {
402                 if (CODEGENDATA_HAS_FLAG_LONGBRANCHES(cd))      {
403                         M_BR_32(disp);
404                 } else  {
405                         M_BR_16(disp);
406                 }
407         } else {
408                 if (CODEGENDATA_HAS_FLAG_LONGBRANCHES(cd)) {
409                         switch (condition) {
410                         case BRANCH_EQ:
411                                 M_BEQ_32(disp);
412                                 break;
413                         case BRANCH_NE:
414                                 M_BNE_32(disp);
415                                 break;
416                         case BRANCH_LT:
417                                 M_BLT_32(disp);
418                                 break;
419                         case BRANCH_GE:
420                                 M_BGE_32(disp);
421                                 break;
422                         case BRANCH_GT:
423                                 M_BGT_32(disp);
424                                 break;
425                         case BRANCH_LE:
426                                 M_BLE_32(disp);
427                                 break;
428                         case BRANCH_NAN:
429                                 M_BNAN_32(disp);
430                                 break;
431                         case BRANCH_UGT:
432                                 M_BHI_32(disp);
433                                 break;
434
435                         default:
436                                 vm_abort("emit_branch: unknown condition %d", condition);
437                         }
438                 } else {
439                         switch (condition) {
440                         case BRANCH_EQ:
441                                 M_BEQ_16(disp);
442                                 break;
443                         case BRANCH_NE:
444                                 M_BNE_16(disp);
445                                 break;
446                         case BRANCH_LT:
447                                 M_BLT_16(disp);
448                                 break;
449                         case BRANCH_GE:
450                                 M_BGE_16(disp);
451                                 break;
452                         case BRANCH_GT:
453                                 M_BGT_16(disp);
454                                 break;
455                         case BRANCH_LE:
456                                 M_BLE_16(disp);
457                                 break;
458                         case BRANCH_NAN:
459                                 M_BNAN_16(disp);
460                                 break;
461                         case BRANCH_UGT:
462                                 M_BHI_16(disp);
463                                 break;
464                         default:
465                                 vm_abort("emit_branch: unknown condition %d", condition);
466                         }
467                 }
468         }
469 }
470
471
472 #if !defined(NDEBUG)
473 /*
474  *      Trace functions. Implement -verbose:call flag
475  *      code marked by real NOP, but performance is no matter when using -verbose:call :)
476  */
477 void emit_verbosecall_enter(jitdata* jd) 
478
479         methodinfo   *m;
480         codegendata  *cd;
481         registerdata *rd;
482         methoddesc   *md;
483         s4      disp,i,t;
484
485
486         if (!JITDATA_HAS_FLAG_VERBOSECALL(jd))
487                 return;
488         
489         /* get required compiler data */
490         m  = jd->m;
491         cd = jd->cd;
492         rd = jd->rd;
493         md = m->parseddesc;
494
495         /* mark trace code */
496         M_NOP;
497
498         M_LINK(REG_FP, -16*4);  
499         M_PUSHALL;
500
501         /* builtin_verbosecall_enter takes all args as s8 type */
502         /* TRACE_ARGS_NUM is the number of args the builtin_verbosecall_enter expects */
503         M_IPUSH_IMM(m);
504         
505         disp = 16*4 + 4 + 4;    /* points to old argument stack initially */
506
507         /* travel up stack to the first argument of the function which needs to be copied */
508         for (i=0; (i < md->paramcount) && (i < TRACE_ARGS_NUM); i++)    {
509                 disp += 4;
510                 if (IS_2_WORD_TYPE(md->paramtypes[i].type)) {   
511                         disp += 4;
512                 }
513         }
514
515         /* disp now points to the first arg which gets copied to the trace stack, relative to REG_SP! */
516         for (i=TRACE_ARGS_NUM-1; i>=0; --i) {
517                 if (i < md->paramcount) {
518                         /* traced function has such an argument */
519                         t = md->paramtypes[i].type;
520                         
521                         if (IS_2_WORD_TYPE(t))  {
522                                 /* copy from original argument stack */
523                                 M_ILD(REG_ITMP1, REG_SP, disp);
524                                 M_IPUSH(REG_ITMP1);
525                                 M_ILD(REG_ITMP1, REG_SP, disp);
526                                 M_IPUSH(REG_ITMP1);
527                         } else  {
528                                 /* displacment is increased as 4 byte on original stack but 8 byte on trace stack */
529                                 M_ILD(REG_ITMP1, REG_SP, disp);
530                                 M_IPUSH(REG_ITMP1);
531                                 M_IPUSH_IMM(0);
532                                 disp += 4;
533                         }
534                 } else  {
535                         /* function has no arg here, push nothing and adapt displacement */
536                         M_IPUSH_IMM(0);
537                         M_IPUSH_IMM(0);
538                         disp += 8;
539                 }
540         }
541         M_JSR_IMM(builtin_verbosecall_enter);
542         /* pop arguments off stack */
543         M_AADD_IMM(TRACE_ARGS_NUM*8+4, REG_SP);
544
545         M_POPALL;
546         M_UNLK(REG_FP);
547         M_NOP;
548 }
549 void emit_verbosecall_exit(jitdata* jd) 
550
551         methodinfo   *m;
552         codegendata  *cd;
553         registerdata *rd;
554         methoddesc   *md;
555
556         if (!JITDATA_HAS_FLAG_VERBOSECALL(jd))
557                 return;
558
559         /* get required compiler data */
560         m  = jd->m;
561         cd = jd->cd;
562         rd = jd->rd;
563         md = m->parseddesc;
564
565         /* void builtin_verbosecall_exit(s8 l, double d, float f, methodinfo *m); */
566
567
568         /* mark trace code */
569         M_NOP;
570         M_LINK(REG_FP, 0);
571
572         M_IPUSH_IMM(m);                                 /* push methodinfo */
573
574         M_IPUSH_IMM(0);                                 /* TODO push float result */
575
576         M_IPUSH_IMM(0);                                 /* TODO push double result */
577         M_IPUSH_IMM(0);                                 /* TODO push double result */
578
579         M_IPUSH(GET_HIGH_REG(REG_RESULT_PACKED))
580         M_IPUSH(GET_LOW_REG(REG_RESULT_PACKED))         /* push long result */
581
582
583         M_JSR_IMM(builtin_verbosecall_exit);
584
585         /* poping result registers from stack */
586         M_IPOP(GET_LOW_REG(REG_RESULT_PACKED))
587         M_IPOP(GET_HIGH_REG(REG_RESULT_PACKED))
588
589 #if 0
590         /* that is wrong of course, overwrites registers and stuff */
591         M_IPOP(0);      /* TODO: pop double result */
592         M_IPOP(0);      /* TODO: pop double result */
593
594         M_IPOP(0);      /* TODO: pop float result */
595 #else
596         M_AADD_IMM(3*4, REG_SP);
597 #endif
598         M_AADD_IMM(4, REG_SP);                          /* remove rest of stack */
599         M_UNLK(REG_FP);
600         M_NOP;
601 }
602 #endif
603
604 /* emit_classcast_check ********************************************************
605
606    Emit a ClassCastException check.
607
608 *******************************************************************************/
609
610 void emit_classcast_check(codegendata *cd, instruction *iptr, s4 condition, s4 reg, s4 s1)
611 {
612         if (INSTRUCTION_MUST_CHECK(iptr)) {
613                 switch (condition) {
614                 case BRANCH_LE:
615                         M_BGT(4);
616                         break;
617                 case BRANCH_EQ:
618                         M_BNE(4);
619                         break;
620                 case BRANCH_GT:
621                         M_BLE(4);
622                         break;
623                 case BRANCH_UGT:
624                         M_BHI(4);
625                         break;
626                 default:
627                         vm_abort("emit_classcast_check: unknown condition %d", condition);
628                 }
629                 M_TRAP_SETREGISTER(s1);
630                 M_TRAP(EXCEPTION_HARDWARE_CLASSCAST);
631         }
632 }
633
634 /* emit_arrayindexoutofbounds_check ********************************************
635
636    Emit a ArrayIndexOutOfBoundsException check.
637
638 *******************************************************************************/
639 void emit_arrayindexoutofbounds_check(codegendata *cd, instruction *iptr, s4 s1, s4 s2)
640 {
641         if (INSTRUCTION_MUST_CHECK(iptr)) {
642                 M_ILD(REG_ITMP3, s1, OFFSET(java_arrayheader, size));
643                 M_ICMP(REG_ITMP3, s2);
644                 M_BLT(2);
645                 /*M_ALD_INTERN(s2, REG_ZERO, EXCEPTION_LOAD_DISP_ARRAYINDEXOUTOFBOUNDS);*/
646                 M_ILLEGAL; /*FIXME */
647         }
648 }
649
650 /* emit_nullpointer_check ******************************************************
651
652    Emit a NullPointerException check.
653
654 *******************************************************************************/
655 void emit_nullpointer_check(codegendata *cd, instruction *iptr, s4 reg)
656 {
657         if (INSTRUCTION_MUST_CHECK(iptr)) {
658                 /* did like to assert on TYPE_ADR, but not possible in here */
659                 /* so assert before each emit_nullpointer_check */
660                 M_ATST(reg);
661                 M_BNE(2);
662                 /*M_ALD_INTERN(REG_ZERO, REG_ZERO, EXCEPTION_LOAD_DISP_NULLPOINTER);*/
663                 M_ILLEGAL;
664         }
665 }
666
667 /* emit_arithmetic_check *******************************************************
668
669    Emit an ArithmeticException check.
670
671 *******************************************************************************/
672
673 void emit_arithmetic_check(codegendata *cd, instruction *iptr, s4 reg)
674 {
675         if (INSTRUCTION_MUST_CHECK(iptr)) {
676                 M_ITST(reg);
677                 M_BNE(2);
678                 /*M_ALD_INTERN(REG_ZERO, REG_ZERO, EXCEPTION_HARDWARE_ARITHMETIC);*/
679                 M_ILLEGAL; /* FIXME */
680         }
681 }
682
683 /* emit_exception_check_areg **************************************************
684  *
685    Emit an Exception check, tested register is address REG_RESULT
686
687 *******************************************************************************/
688 void emit_exception_check_areg(codegendata *cd, instruction *iptr)
689 {
690         if (INSTRUCTION_MUST_CHECK(iptr)) {
691                 M_ATST(REG_RESULT);
692                 M_BNE(2);
693                 /*M_ALD_INTERN(REG_ZERO, REG_ZERO, EXCEPTION_HARDWARE_EXCEPTION);*/
694                 M_ILLEGAL; /*FIXME*/
695         }
696 }
697 /* emit_exception_check_ireg **************************************************
698
699    Emit an Exception check. Teste register is integer REG_RESULT
700
701 *******************************************************************************/
702 void emit_exception_check_ireg(codegendata *cd, instruction *iptr)
703 {
704         if (INSTRUCTION_MUST_CHECK(iptr)) {
705                 M_ITST(REG_RESULT);
706                 M_BNE(2);
707                 /*M_ALD_INTERN(REG_ZERO, REG_ZERO, EXCEPTION_HARDWARE_EXCEPTION);*/
708                 M_ILLEGAL; /*FIXME*/
709         }
710 }
711
712
713 /*
714  * These are local overrides for various environment variables in Emacs.
715  * Please do not remove this and leave it at the end of the file, where
716  * Emacs will automagically detect them.
717  * ---------------------------------------------------------------------
718  * Local variables:
719  * mode: c
720  * indent-tabs-mode: t
721  * c-basic-offset: 4
722  * tab-width: 4
723  * End:
724  * vim:noexpandtab:sw=4:ts=4:
725  */