Moved reg_of_var from codegen.c, cause it's platform independent.
[cacao.git] / src / vm / jit / codegen.inc
1 /* jit/codegen.inc - architecture independent code generator
2
3    Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
4    Institut f. Computersprachen, TU Wien
5    R. Grafl, A. Krall, C. Kruegel, C. Oates, R. Obermaisser, M. Probst,
6    S. Ring, E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich,
7    J. Wenninger
8
9    This file is part of CACAO.
10
11    This program is free software; you can redistribute it and/or
12    modify it under the terms of the GNU General Public License as
13    published by the Free Software Foundation; either version 2, or (at
14    your option) any later version.
15
16    This program is distributed in the hope that it will be useful, but
17    WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19    General Public License for more details.
20
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
24    02111-1307, USA.
25
26    Contact: cacao@complang.tuwien.ac.at
27
28    Authors: Reinhard Grafl
29             Andreas  Krall
30
31    Changes: Michael Gschwind
32             Christian Thalinger
33
34    All functions assume the following code area / data area layout:
35
36    +-----------+
37    |           |
38    | code area | code area grows to higher addresses
39    |           |
40    +-----------+ <-- start of procedure
41    |           |
42    | data area | data area grows to lower addresses
43    |           |
44    +-----------+
45
46    The functions first write into a temporary code/data area allocated by
47    "codegen_init". "codegen_finish" copies the code and data area into permanent
48    memory. All functions writing values into the data area return the offset
49    relative the begin of the code area (start of procedure).    
50
51    $Id: codegen.inc 1259 2004-06-30 21:35:45Z twisti $
52
53 */
54
55
56 #include <string.h>
57 #include "options.h"
58 #include "statistics.h"
59 #include "toolbox/memory.h"
60 #include "toolbox/logging.h"
61 #include "toolbox/avl.h"
62 #include "threads/thread.h"
63
64
65 /************************* critical sections  *********************************/
66
67 struct threadcritnodetemp {
68         struct threadcritnodetemp *next;
69         int mcodebegin, mcodeend, mcoderestart;
70 };
71
72 #define MCODEINITSIZE (1<<15)       /* 32 Kbyte code area initialization size */
73 #define DSEGINITSIZE  (1<<12)       /*  4 Kbyte data area initialization size */
74
75 static u1* mcodebase = NULL;        /* base pointer of code area              */
76 static s4* mcodeend  = NULL;        /* pointer to end of code area            */
77 static int mcodesize;               /* complete size of code area (bytes)     */
78
79 static u1* dsegtop = NULL;          /* pointer to top (end) of data area      */
80 static int dsegsize;                /* complete size of data area (bytes)     */
81 int dseglen;                        /* used size of data area (bytes)         */
82                                     /* data area grows from top to bottom     */
83
84 static jumpref *jumpreferences;     /* list of jumptable target addresses     */
85 static dataref *datareferences;     /* list of data segment references        */
86 static branchref *xboundrefs;       /* list of bound check branches           */
87 static branchref *xcheckarefs;      /* list of array size check branches      */
88 static branchref *xnullrefs;        /* list of null check branches            */
89 static branchref *xcastrefs;        /* list of cast check branches            */
90 static branchref *xdivrefs;         /* list of divide by zero branches        */
91 static branchref *xexceptionrefs;   /* list of exception branches             */
92 static linenumberref *linenumberreferences; /*list of line numbers and the program counters of their first instruction*/
93 static s4 linenumbertablesizepos;
94 static s4 linenumbertablestartpos;
95 static s4 linenumbertab;
96
97 static struct threadcritnodetemp *threadcrit;
98                                     /* List of critical code regions          */
99 static struct threadcritnodetemp threadcritcurrent;
100 static int threadcritcount;         /* Number of critical regions             */
101
102 int parentargs_base; /* offset in stackframe for the parameter from the caller*/
103
104 void codegen_init();                /* allocates code and data area           */
105 void codegen_close();               /* releases temporary storage             */
106 static void codegen_finish();       /* makes code and data area permanent and */
107                                     /* updates branch references to code/data */
108
109 static s4 dseg_adds4(s4 value);         /* adds an int to data area           */
110
111 #if !defined(__I386__)
112 static s4 dseg_adds8(s8 value);         /* adds an long to data area          */
113 #endif
114
115 static s4 dseg_addfloat (float value);  /* adds an float to data area         */
116 static s4 dseg_adddouble(double value); /* adds an double to data area        */
117
118 #if POINTERSIZE == 8
119 #define dseg_addaddress(value)      dseg_adds8((s8)(value))
120 #else
121 #define dseg_addaddress(value)      dseg_adds4((s4)(value))
122 #endif
123
124 static void dseg_addtarget(basicblock *target);
125 static void dseg_adddata(u1 *ptr);
126 static void codegen_addreference(basicblock *target, void *branchptr);
127 static void codegen_addxboundrefs(void *branchptr, s4 reg);
128 static void codegen_addxnullrefs(void *branchptr);
129 static void codegen_addxcastrefs(void *branchptr);
130 static void codegen_addxdivrefs(void *branchptr);
131 static void codegen_addxexceptionrefs(void *branchptr);
132 static void codegen_threadcritrestart(int offset);
133 static void codegen_threadcritstart(int offset);
134 static void codegen_threadcritstop(int offset);
135
136 #if defined(__I386__) || defined(__X86_64__)
137 typedef struct _methodtree_element methodtree_element;
138
139 struct _methodtree_element {
140         void *startpc;
141         void *endpc;
142 };
143
144 static struct avl_table *methodtree=0;
145 static int methodtree_comparator(const void *pc, const void *element,
146                                                                  void *param);
147 #endif
148
149 void dseg_display(s4 *s4ptr);
150
151
152
153 /* codegen_init allocates and initialises code area, data area and references */
154
155 void codegen_init()
156 {
157         if (!mcodebase) {
158                 mcodebase = MNEW(u1, MCODEINITSIZE);
159                 mcodesize = MCODEINITSIZE;
160         }
161
162         if (!dsegtop) {
163                 dsegtop = MNEW(u1, DSEGINITSIZE);
164                 dsegsize = DSEGINITSIZE;
165                 dsegtop += dsegsize;
166         }
167
168         dseglen = 0;
169
170         linenumberreferences = NULL;
171         linenumbertablesizepos = 0;
172         linenumbertablestartpos = 0;
173         linenumbertab = 0;
174         jumpreferences = NULL;
175         datareferences = NULL;
176         xboundrefs = NULL;
177         xcheckarefs = NULL;
178         xnullrefs = NULL;
179         xcastrefs = NULL;
180         xdivrefs = NULL;
181         xexceptionrefs = NULL;
182
183 #if defined(__I386__) || defined(__X86_64__)
184         if (!methodtree) {
185                 methodtree_element *mte;
186
187                 methodtree = avl_create(methodtree_comparator, NULL, NULL);
188
189                 mte = NEW(methodtree_element);
190
191                 mte->startpc = asm_calljavafunction;
192                 mte->endpc = asm_calljavafunction2 - 1;
193
194                 avl_insert(methodtree, mte);
195
196                 mte = NEW(methodtree_element);
197
198                 mte->startpc = asm_calljavafunction2;
199                 mte->endpc = asm_call_jit_compiler - 1;
200
201                 avl_insert(methodtree, mte);
202         }
203 #endif
204
205 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
206         threadcritcurrent.next = NULL;
207         threadcritcount = 0;
208 #endif
209 }
210
211
212
213 /* codegen_close releases temporary code and data area                        */
214
215 void codegen_close()
216 {
217         if (mcodebase) {
218                 MFREE(mcodebase, u1, mcodesize);
219                 mcodebase = NULL;
220         }
221
222         if (dsegtop) {
223                 MFREE(dsegtop - dsegsize, u1, dsegsize);
224                 dsegtop = NULL;
225         }
226 }
227
228
229
230 /* codegen_increase doubles code area                                         */
231
232 static s4 *codegen_increase(u1 *codeptr)
233 {
234         long len;
235
236         len = codeptr - mcodebase;
237         mcodebase = MREALLOC(mcodebase, u1, mcodesize, mcodesize * 2);
238         mcodesize *= 2;
239         mcodeend = (s4*) (mcodebase + mcodesize);
240         return (s4*) (mcodebase + len);
241 }
242
243
244
245 /* desg_increase doubles data area                                            */
246
247 static void dseg_increase()
248 {
249         u1 *newstorage = MNEW(u1, dsegsize * 2);
250         memcpy(newstorage + dsegsize, dsegtop - dsegsize, dsegsize);
251         MFREE(dsegtop - dsegsize, u1, dsegsize);
252         dsegtop = newstorage;
253         dsegsize *= 2;
254         dsegtop += dsegsize;
255 }
256
257
258
259 static s4 dseg_adds4_increase(s4 value)
260 {
261         dseg_increase();
262         *((s4 *) (dsegtop - dseglen)) = value;
263         return -dseglen;
264 }
265
266
267
268 static s4 dseg_adds4(s4 value)
269 {
270         s4 *dataptr;
271
272         dseglen += 4;
273         dataptr = (s4 *) (dsegtop - dseglen);
274         if (dseglen > dsegsize)
275                 return dseg_adds4_increase(value);
276         *dataptr = value;
277         return -dseglen;
278 }
279
280
281
282 #if !defined(__I386__)
283 static s4 dseg_adds8_increase(s8 value)
284 {
285         dseg_increase();
286         *((s8 *) (dsegtop - dseglen)) = value;
287         return -dseglen;
288 }
289
290
291 static s4 dseg_adds8(s8 value)
292 {
293         s8 *dataptr;
294
295         dseglen = ALIGN (dseglen + 8, 8);
296         dataptr = (s8 *) (dsegtop - dseglen);
297         if (dseglen > dsegsize)
298                 return dseg_adds8_increase(value);
299         *dataptr = value;
300         return -dseglen;
301 }
302 #endif
303
304
305 static s4 dseg_addfloat_increase(float value)
306 {
307         dseg_increase();
308         *((float *) (dsegtop - dseglen)) = value;
309         return -dseglen;
310 }
311
312
313
314 static s4 dseg_addfloat(float value)
315 {
316         float *dataptr;
317
318         dseglen += 4;
319         dataptr = (float *) (dsegtop - dseglen);
320         if (dseglen > dsegsize)
321                 return dseg_addfloat_increase(value);
322         *dataptr = value;
323         return -dseglen;
324 }
325
326
327
328 static s4 dseg_adddouble_increase(double value)
329 {
330         dseg_increase();
331         *((double *) (dsegtop - dseglen)) = value;
332         return -dseglen;
333 }
334
335
336
337 static s4 dseg_adddouble(double value)
338 {
339         double *dataptr;
340
341         dseglen = ALIGN (dseglen + 8, 8);
342         dataptr = (double *) (dsegtop - dseglen);
343         if (dseglen > dsegsize)
344                 return dseg_adddouble_increase(value);
345         *dataptr = value;
346         return -dseglen;
347 }
348
349
350
351 static void dseg_addtarget(basicblock *target)
352 {
353         jumpref *jr = DNEW(jumpref);
354         
355         jr->tablepos = dseg_addaddress(NULL);
356         jr->target = target;
357         jr->next = jumpreferences;
358         jumpreferences = jr;
359 }
360
361
362
363 static void dseg_adddata(u1 *ptr)
364 {
365         dataref *dr = DNEW(dataref);
366
367         dr->pos = (u1 *) (ptr - mcodebase);
368         dr->next = datareferences;
369         datareferences = dr;
370 }
371
372 static void dseg_addlinenumbertablesize() {
373 #ifdef __ALPHA__
374         dseg_adds4(0); /*PADDING*/
375 #endif
376         linenumbertablesizepos=dseg_addaddress(NULL); /*it could be considered to use adds4 here, to avoid 1 double word padding on ALPHA */
377
378         linenumbertablestartpos=dseg_addaddress(NULL);
379 #ifdef __ALPHA__
380         dseg_adds4(0); /*PADDING*/
381 #endif
382 }
383
384 static void dseg_addlinenumber(u2 linenumber,u1 *ptr) {
385         linenumberref *lr=DNEW(linenumberref);
386         lr->linenumber=linenumber;
387         lr->tablepos=0;
388         lr->targetmpc=(ptr-mcodebase);
389         lr->next=linenumberreferences;
390         linenumberreferences=lr;
391 }
392
393 static void codegen_addreference(basicblock *target, void *branchptr)
394 {
395         s4 branchpos = (u1*) branchptr - mcodebase;
396
397         if (target->mpc >= 0) {
398                 gen_resolvebranch((u1*) mcodebase + branchpos, branchpos, target->mpc);
399         }
400         else {
401                 branchref *br = DNEW(branchref);
402
403                 br->branchpos = branchpos;
404                 br->next = target->branchrefs;
405                 target->branchrefs= br;
406         }
407 }
408
409
410
411 static void codegen_addxboundrefs(void *branchptr, s4 reg)
412 {
413         s4 branchpos = (u1*) branchptr - mcodebase;
414
415         branchref *br = DNEW(branchref);
416
417         br->branchpos = branchpos;
418         br->reg = reg;
419         br->next = xboundrefs;
420         xboundrefs = br;
421 }
422
423
424
425 static void codegen_addxcheckarefs(void *branchptr)
426 {
427         s4 branchpos = (u1*) branchptr - mcodebase;
428
429         branchref *br = DNEW(branchref);
430
431         br->branchpos = branchpos;
432         br->next = xcheckarefs;
433         xcheckarefs = br;
434 }
435
436
437
438 static void codegen_addxnullrefs(void *branchptr)
439 {
440         s4 branchpos = (u1*) branchptr - mcodebase;
441
442         branchref *br = DNEW(branchref);
443
444         br->branchpos = branchpos;
445         br->next = xnullrefs;
446         xnullrefs = br;
447 }
448
449
450
451 static void codegen_addxcastrefs(void *branchptr)
452 {
453         s4 branchpos = (u1*) branchptr - mcodebase;
454
455         branchref *br = DNEW(branchref);
456
457         br->branchpos = branchpos;
458         br->next = xcastrefs;
459         xcastrefs = br;
460 }
461
462
463 static void codegen_addxexceptionrefs(void *branchptr)
464 {
465         s4 branchpos = (u1*) branchptr - mcodebase;
466
467         branchref *br = DNEW(branchref);
468
469         br->branchpos = branchpos;
470         br->next = xexceptionrefs;
471         xexceptionrefs = br;
472 }
473
474
475 static void codegen_addxdivrefs(void *branchptr)
476 {
477         s4 branchpos = (u1*) branchptr - mcodebase;
478
479         branchref *br = DNEW(branchref);
480
481         br->branchpos = branchpos;
482         br->next = xdivrefs;
483         xdivrefs = br;
484 }
485
486
487 static void codegen_createlinenumbertable() {
488 #ifdef __I386__
489         /*log_text("codegen_createlinnumbertable");*/
490         {
491                 linenumberref *lr;
492                 for (lr=linenumberreferences;lr!=NULL;lr=lr->next) {
493                         /*log_text("Adding line number entry");*/
494                         lr->tablepos=dseg_addaddress(NULL);
495                         if (linenumbertab==0) linenumbertab=lr->tablepos;
496                         dseg_addaddress(lr->linenumber);
497                 }
498         }
499 #endif
500 }
501
502
503 #if defined(__I386__) || defined(__X86_64__)
504 static int methodtree_comparator(const void *pc, const void *element,
505                                                                  void *param)
506 {
507         methodtree_element *mte;
508         methodtree_element *mtepc;
509
510         mte = (methodtree_element *) element;
511         mtepc = (methodtree_element *) pc;
512
513         /* compare both startpc and endpc of pc, even if they have the same value,
514            otherwise the avl_probe sometime thinks the element is still in the
515            tree */
516         if (mte->startpc <= mtepc->startpc && mtepc->startpc <= mte->endpc &&
517                 mte->startpc <= mtepc->endpc   && mtepc->endpc   <= mte->endpc) {
518                 return 0;
519
520         } else if (mtepc->startpc < mte->startpc) {
521                 return -1;
522
523         } else {
524                 return 1;
525         }
526 }
527
528
529 #if 0
530 void *codegen_findmethod1(void *pc)
531 {
532         void * retVal=findmethod(pc);
533         methodinfo **ma=(methodinfo**)retVal;
534         methodinfo *m=ma[-1];
535         if (m)
536                 if (m->name)
537                         utf_display(m->name);
538                 else 
539                         log_text("No Name");
540         else log_text("No methodinfo");
541         return retVal;
542 }
543 #endif
544
545 void *codegen_findmethod(void *pc)
546 {
547         methodtree_element *mtepc;
548         methodtree_element *mte;
549
550         mtepc = NEW(methodtree_element);
551         mtepc->startpc = pc;
552         mtepc->endpc = pc;
553
554         mte = avl_find(methodtree, mtepc);
555
556         FREE(mtepc, methodtree_element);
557
558         if (!mte)
559                 throw_cacao_exception_exit(string_java_lang_InternalError, "cannot find function");
560
561         return mte->startpc;
562 }
563 #endif
564
565
566 static void codegen_finish(methodinfo *m, int mcodelen)
567 {
568         jumpref *jr;
569         u1 *epoint;
570         s4 extralen = 0;
571         s4 alignedlen;
572
573 /*      printf("codegen_finish\n"); */
574 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
575         extralen += sizeof(threadcritnode) * threadcritcount;
576 #endif
577
578 #if defined(STATISTICS)
579         if (opt_stat) {
580                 count_code_len += mcodelen;
581                 count_data_len += dseglen;
582         }
583 #endif
584
585         dseglen = ALIGN(dseglen, MAX_ALIGN);
586         alignedlen = ALIGN(mcodelen, MAX_ALIGN) + dseglen;
587
588         m->mcodelength = mcodelen + dseglen;
589         m->mcode = CNEW(u1, alignedlen + extralen);
590
591         memcpy(m->mcode, dsegtop - dseglen, dseglen);
592         memcpy(m->mcode + dseglen, mcodebase, mcodelen);
593
594         m->entrypoint = epoint = (u1 *) (m->mcode + dseglen);
595
596         /* jump table resolving */
597         jr = jumpreferences;
598         while (jr != NULL) {
599             *((void**) (epoint + jr->tablepos)) = epoint + jr->target->mpc;
600             jr = jr->next;
601         }
602
603 #ifdef __I386__
604         /* line number table resolving */
605         {
606                 linenumberref *lr;
607                 #if POINTERSIZE == 8
608                         s8  lrtlen=0;
609                 #else
610                         s4  lrtlen=0;
611                 #endif
612
613                 for (lr=linenumberreferences;lr!=NULL;lr=lr->next) {
614                         lrtlen++;
615                         *((void**)(epoint+lr->tablepos))=epoint+lr->targetmpc;
616                         /*log_text("resolving line number information");*/
617                 }
618                 
619                 *((void**)(epoint+linenumbertablestartpos))=epoint+linenumbertab;
620 #if POINTERSIZE == 8
621                 *((s8*)(epoint+linenumbertablesizepos))=lrtlen;
622 #else
623                 *((s4*)(epoint+linenumbertablesizepos))=lrtlen;
624 #endif
625
626         }
627 #endif
628 #if defined(__I386__) || defined(__X86_64__)
629         {
630                 dataref *dr;
631
632                 /* add method into methodtree to find the entrypoint */
633                 methodtree_element *mte;
634
635                 mte = NEW(methodtree_element);
636                 mte->startpc = m->entrypoint;
637                 mte->endpc = m->entrypoint + mcodelen;
638
639                 if (avl_insert(methodtree, mte))
640                         panic("duplicate entry");
641
642                 /* data segment references resolving */
643                 dr = datareferences;
644                 while (dr != NULL) {
645                         *((void**) ((long) epoint + (long) dr->pos - POINTERSIZE)) = epoint;
646                         dr = dr->next;
647                 }
648         }
649 #endif
650
651
652 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
653         {
654                 threadcritnode *n = (threadcritnode*) (m->mcode + alignedlen);
655                 int i;
656                 struct threadcritnodetemp *nt = threadcrit;
657
658                 for (i=0; i<threadcritcount; i++)
659                 {
660                         n->mcodebegin = m->mcode + nt->mcodebegin;
661                         n->mcodeend = m->mcode + nt->mcodeend;
662                         n->mcoderestart = m->mcode + nt->mcoderestart;
663                         thread_registercritical(n);
664                         n++;
665                         nt = nt->next;
666                 }
667         }
668 #endif
669 }
670
671
672 #if defined(__I386__) || defined(__X86_64__)
673 void codegen_insertNative(void *startpc, void *endpc)
674 {
675         methodtree_element *mte;
676
677         if (!methodtree) {
678                 methodtree_element *mte;
679
680                 methodtree = avl_create(methodtree_comparator, NULL, NULL);
681
682                 mte = NEW(methodtree_element);
683                 mte->startpc = asm_calljavafunction;
684                 mte->endpc = asm_calljavafunction2 - 1;
685                 avl_insert(methodtree, mte);
686
687                 mte = NEW(methodtree_element);
688                 mte->startpc = asm_calljavafunction2;
689                 mte->endpc = asm_call_jit_compiler - 1;
690                 avl_insert(methodtree, mte);
691         }
692
693         mte = NEW(methodtree_element);
694         mte->startpc = startpc;
695         mte->endpc = endpc;
696
697         if (avl_insert(methodtree, mte))
698                 panic("duplicate entry");
699 }
700 #endif
701
702
703 void dseg_display(s4 *s4ptr)
704 {
705         int i;
706         
707         printf("  --- dump of datasegment\n");
708         for (i = dseglen; i > 0 ; i -= 4) {
709                 printf("-%6x: %8x\n", i, (int)(*s4ptr++));
710         }
711         printf("  --- begin of data segment: %p\n", s4ptr);
712 }
713
714
715 /* reg_of_var:
716     This function determines a register, to which the result of an operation
717     should go, when it is ultimatively intended to store the result in
718     pseudoregister v.
719     If v is assigned to an actual register, this register will be returned.
720     Otherwise (when v is spilled) this function returns tempregnum.
721     If not already done, regoff and flags are set in the stack location.
722 */        
723
724 static int reg_of_var(methodinfo *m, stackptr v, int tempregnum)
725 {
726         varinfo *var;
727
728         switch (v->varkind) {
729         case TEMPVAR:
730                 if (!(v->flags & INMEMORY))
731                         return(v->regoff);
732                 break;
733         case STACKVAR:
734                 var = &(m->registerdata->interfaces[v->varnum][v->type]);
735                 v->regoff = var->regoff;
736                 if (!(var->flags & INMEMORY))
737                         return(var->regoff);
738                 break;
739         case LOCALVAR:
740                 var = &(m->registerdata->locals[v->varnum][v->type]);
741                 v->regoff = var->regoff;
742                 if (!(var->flags & INMEMORY))
743                         return(var->regoff);
744                 break;
745         case ARGVAR:
746                 v->regoff = v->varnum;
747                 if (IS_FLT_DBL_TYPE(v->type)) {
748                         if (v->varnum < m->registerdata->fltreg_argnum) {
749                                 v->regoff = m->registerdata->argfltregs[v->varnum];
750                                 return(m->registerdata->argfltregs[v->varnum]);
751                         }
752                 }
753                 else
754                         if (v->varnum < m->registerdata->intreg_argnum) {
755                                 v->regoff = m->registerdata->argintregs[v->varnum];
756                                 return(m->registerdata->argintregs[v->varnum]);
757                         }
758                 v->regoff -= m->registerdata->intreg_argnum;
759                 break;
760         }
761         v->flags |= INMEMORY;
762         return tempregnum;
763 }
764
765
766 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
767 void codegen_threadcritrestart(int offset)
768 {
769         threadcritcurrent.mcoderestart = offset;
770 }
771
772 void codegen_threadcritstart(int offset)
773 {
774         threadcritcurrent.mcodebegin = offset;
775 }
776
777 void codegen_threadcritstop(int offset)
778 {
779         threadcritcurrent.next = threadcrit;
780         threadcritcurrent.mcodeend = offset;
781         threadcrit = DNEW(struct threadcritnodetemp);
782         *threadcrit = threadcritcurrent;
783         threadcritcount++;
784 }
785 #endif
786
787 /*
788  * These are local overrides for various environment variables in Emacs.
789  * Please do not remove this and leave it at the end of the file, where
790  * Emacs will automagically detect them.
791  * ---------------------------------------------------------------------
792  * Local variables:
793  * mode: c
794  * indent-tabs-mode: t
795  * c-basic-offset: 4
796  * tab-width: 4
797  * End:
798  */