bc63d9550039f1f0ab6d44868716813a00264928
[cacao.git] / src / vm / jit / codegen-common.c
1 /* src/vm/jit/codegen-common.c - architecture independent code generator stuff
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    All functions assume the following code area / data area layout:
26
27    +-----------+
28    |           |
29    | code area | code area grows to higher addresses
30    |           |
31    +-----------+ <-- start of procedure
32    |           |
33    | data area | data area grows to lower addresses
34    |           |
35    +-----------+
36
37    The functions first write into a temporary code/data area allocated by
38    "codegen_init". "codegen_finish" copies the code and data area into permanent
39    memory. All functions writing values into the data area return the offset
40    relative the begin of the code area (start of procedure).    
41
42    $Id: codegen-common.c 7797 2007-04-23 20:12:39Z michi $
43
44 */
45
46
47 #include "config.h"
48
49 #include <assert.h>
50 #include <string.h>
51
52 #include "vm/types.h"
53
54 #if defined(ENABLE_JIT)
55 /* this is required PATCHER_CALL_SIZE */
56 # include "codegen.h"
57 #endif
58
59 #if defined(__ARM__)
60 /* this is required for REG_SPLIT */
61 # include "md-abi.h"
62 #endif
63
64 #include "mm/memory.h"
65
66 #include "toolbox/avl.h"
67 #include "toolbox/list.h"
68 #include "toolbox/logging.h"
69
70 #include "native/jni.h"
71 #include "native/native.h"
72
73 #include "threads/threads-common.h"
74
75 #include "vm/builtin.h"
76 #include "vm/exceptions.h"
77 #include "vm/stringlocal.h"
78
79 #include "vm/jit/abi.h"
80 #include "vm/jit/asmpart.h"
81 #include "vm/jit/codegen-common.h"
82
83 #if defined(ENABLE_DISASSEMBLER)
84 # include "vm/jit/disass.h"
85 #endif
86
87 #include "vm/jit/dseg.h"
88 #include "vm/jit/emit-common.h"
89 #include "vm/jit/jit.h"
90 #include "vm/jit/md.h"
91 #include "vm/jit/replace.h"
92 #include "vm/jit/stacktrace.h"
93
94 #if defined(ENABLE_INTRP)
95 #include "vm/jit/intrp/intrp.h"
96 #endif
97
98 #include "vmcore/method.h"
99 #include "vmcore/options.h"
100
101 # include "vmcore/statistics.h"
102
103 #if defined(ENABLE_VMLOG)
104 #include <vmlog_cacao.h>
105 #endif
106
107
108 /* in this tree we store all method addresses *********************************/
109
110 static avl_tree *methodtree = NULL;
111 static s4 methodtree_comparator(const void *pc, const void *element);
112
113
114 /* codegen_init ****************************************************************
115
116    TODO
117
118 *******************************************************************************/
119
120 void codegen_init(void)
121 {
122         /* this tree is global, not method specific */
123
124         if (!methodtree) {
125 #if defined(ENABLE_JIT)
126                 methodtree_element *mte;
127 #endif
128
129                 methodtree = avl_create(&methodtree_comparator);
130
131 #if defined(ENABLE_JIT)
132                 /* insert asm_vm_call_method */
133
134                 mte = NEW(methodtree_element);
135
136                 mte->startpc = (u1 *) (ptrint) asm_vm_call_method;
137                 mte->endpc   = (u1 *) (ptrint) asm_vm_call_method_end;
138
139                 avl_insert(methodtree, mte);
140 #endif /* defined(ENABLE_JIT) */
141         }
142 }
143
144
145 /* codegen_setup ***************************************************************
146
147    Allocates and initialises code area, data area and references.
148
149 *******************************************************************************/
150
151 void codegen_setup(jitdata *jd)
152 {
153         methodinfo  *m;
154         codegendata *cd;
155
156         /* get required compiler data */
157
158         m  = jd->m;
159         cd = jd->cd;
160
161         /* initialize members */
162
163         cd->flags        = 0;
164
165         cd->mcodebase    = DMNEW(u1, MCODEINITSIZE);
166         cd->mcodeend     = cd->mcodebase + MCODEINITSIZE;
167         cd->mcodesize    = MCODEINITSIZE;
168
169         /* initialize mcode variables */
170
171         cd->mcodeptr     = cd->mcodebase;
172         cd->lastmcodeptr = cd->mcodebase;
173
174 #if defined(ENABLE_INTRP)
175         /* native dynamic superinstructions variables */
176
177         if (opt_intrp) {
178                 cd->ncodebase = DMNEW(u1, NCODEINITSIZE);
179                 cd->ncodesize = NCODEINITSIZE;
180
181                 /* initialize ncode variables */
182         
183                 cd->ncodeptr = cd->ncodebase;
184
185                 cd->lastinstwithoutdispatch = ~0; /* no inst without dispatch */
186                 cd->superstarts = NULL;
187         }
188 #endif
189
190         cd->dseg           = NULL;
191         cd->dseglen        = 0;
192
193         cd->jumpreferences = NULL;
194
195 #if defined(__I386__) || defined(__X86_64__) || defined(__XDSPCORE__) || defined(__M68K__) || defined(ENABLE_INTRP)
196         cd->datareferences = NULL;
197 #endif
198
199 /*      cd->patchrefs      = list_create_dump(OFFSET(patchref, linkage)); */
200         cd->patchrefs      = NULL;
201         cd->brancheslabel  = list_create_dump(OFFSET(branch_label_ref_t, linkage));
202
203         cd->linenumberreferences = NULL;
204         cd->linenumbertablesizepos = 0;
205         cd->linenumbertablestartpos = 0;
206         cd->linenumbertab = 0;
207         
208 #if defined(ENABLE_THREADS)
209         cd->threadcritcurrent.next = NULL;
210         cd->threadcritcount = 0;
211 #endif
212 }
213
214
215 /* codegen_reset ***************************************************************
216
217    Resets the codegen data structure so we can recompile the method.
218
219 *******************************************************************************/
220
221 static void codegen_reset(jitdata *jd)
222 {
223         codeinfo    *code;
224         codegendata *cd;
225         basicblock  *bptr;
226
227         /* get required compiler data */
228
229         code = jd->code;
230         cd   = jd->cd;
231
232         /* reset error flag */
233
234         cd->flags          &= ~CODEGENDATA_FLAG_ERROR;
235
236         /* reset some members, we reuse the code memory already allocated
237            as this should have almost the correct size */
238
239         cd->mcodeptr        = cd->mcodebase;
240         cd->lastmcodeptr    = cd->mcodebase;
241
242         cd->dseg            = NULL;
243         cd->dseglen         = 0;
244
245         cd->jumpreferences  = NULL;
246
247 #if defined(__I386__) || defined(__X86_64__) || defined(__XDSPCORE__) || defined(__M68K__) || defined(ENABLE_INTRP)
248         cd->datareferences  = NULL;
249 #endif
250
251 /*      cd->patchrefs       = list_create_dump(OFFSET(patchref, linkage)); */
252         cd->patchrefs       = NULL;
253         cd->brancheslabel   = list_create_dump(OFFSET(branch_label_ref_t, linkage));
254
255         cd->linenumberreferences    = NULL;
256         cd->linenumbertablesizepos  = 0;
257         cd->linenumbertablestartpos = 0;
258         cd->linenumbertab           = 0;
259         
260 #if defined(ENABLE_THREADS)
261         cd->threadcritcurrent.next = NULL;
262         cd->threadcritcount        = 0;
263 #endif
264
265         /* We need to clear the mpc and the branch references from all
266            basic blocks as they will definitely change. */
267
268         for (bptr = jd->basicblocks; bptr != NULL; bptr = bptr->next) {
269                 bptr->mpc        = -1;
270                 bptr->branchrefs = NULL;
271         }
272
273 #if defined(ENABLE_REPLACEMENT)
274         code->rplpoints     = NULL;
275         code->rplpointcount = 0;
276         code->regalloc      = NULL;
277         code->regalloccount = 0;
278         code->globalcount   = 0;
279 #endif
280 }
281
282
283 /* codegen_generate ************************************************************
284
285    Generates the code for the currently compiled method.
286
287 *******************************************************************************/
288
289 bool codegen_generate(jitdata *jd)
290 {
291         codegendata *cd;
292
293         /* get required compiler data */
294
295         cd = jd->cd;
296
297         /* call the machine-dependent code generation function */
298
299         if (!codegen_emit(jd))
300                 return false;
301
302         /* check for an error */
303
304         if (CODEGENDATA_HAS_FLAG_ERROR(cd)) {
305                 /* check for long-branches flag, if it is set we recompile the
306                    method */
307
308 #if !defined(NDEBUG)
309         if (compileverbose)
310             log_message_method("Re-generating code: ", jd->m);
311 #endif
312
313                 /* XXX maybe we should tag long-branches-methods for recompilation */
314
315                 if (CODEGENDATA_HAS_FLAG_LONGBRANCHES(cd)) {
316                         /* we have to reset the codegendata structure first */
317
318                         codegen_reset(jd);
319
320                         /* and restart the compiler run */
321
322                         if (!codegen_emit(jd))
323                                 return false;
324                 }
325                 else {
326                         vm_abort("codegen_generate: unknown error occurred during codegen_emit: flags=%x\n", cd->flags);
327                 }
328
329 #if !defined(NDEBUG)
330         if (compileverbose)
331             log_message_method("Re-generating code done: ", jd->m);
332 #endif
333         }
334
335         /* reallocate the memory and finish the code generation */
336
337         codegen_finish(jd);
338
339         /* everything's ok */
340
341         return true;
342 }
343
344
345 /* codegen_close ***************************************************************
346
347    TODO
348
349 *******************************************************************************/
350
351 void codegen_close(void)
352 {
353         /* TODO: release avl tree on i386 and x86_64 */
354 }
355
356
357 /* codegen_increase ************************************************************
358
359    Doubles code area.
360
361 *******************************************************************************/
362
363 void codegen_increase(codegendata *cd)
364 {
365         u1 *oldmcodebase;
366
367         /* save old mcodebase pointer */
368
369         oldmcodebase = cd->mcodebase;
370
371         /* reallocate to new, doubled memory */
372
373         cd->mcodebase = DMREALLOC(cd->mcodebase,
374                                                           u1,
375                                                           cd->mcodesize,
376                                                           cd->mcodesize * 2);
377         cd->mcodesize *= 2;
378         cd->mcodeend   = cd->mcodebase + cd->mcodesize;
379
380         /* set new mcodeptr */
381
382         cd->mcodeptr = cd->mcodebase + (cd->mcodeptr - oldmcodebase);
383
384 #if defined(__I386__) || defined(__MIPS__) || defined(__X86_64__) || defined(ENABLE_INTRP)
385         /* adjust the pointer to the last patcher position */
386
387         if (cd->lastmcodeptr != NULL)
388                 cd->lastmcodeptr = cd->mcodebase + (cd->lastmcodeptr - oldmcodebase);
389 #endif
390 }
391
392
393 /* codegen_ncode_increase ******************************************************
394
395    Doubles code area.
396
397 *******************************************************************************/
398
399 #if defined(ENABLE_INTRP)
400 u1 *codegen_ncode_increase(codegendata *cd, u1 *ncodeptr)
401 {
402         u1 *oldncodebase;
403
404         /* save old ncodebase pointer */
405
406         oldncodebase = cd->ncodebase;
407
408         /* reallocate to new, doubled memory */
409
410         cd->ncodebase = DMREALLOC(cd->ncodebase,
411                                                           u1,
412                                                           cd->ncodesize,
413                                                           cd->ncodesize * 2);
414         cd->ncodesize *= 2;
415
416         /* return the new ncodeptr */
417
418         return (cd->ncodebase + (ncodeptr - oldncodebase));
419 }
420 #endif
421
422
423 /* codegen_add_branch_ref ******************************************************
424
425    Prepends an branch to the list.
426
427 *******************************************************************************/
428
429 void codegen_add_branch_ref(codegendata *cd, basicblock *target, s4 condition, s4 reg, u4 options)
430 {
431         branchref *br;
432         s4         branchmpc;
433
434         STATISTICS(count_branches_unresolved++);
435
436         /* calculate the mpc of the branch instruction */
437
438         branchmpc = cd->mcodeptr - cd->mcodebase;
439
440         br = DNEW(branchref);
441
442         br->branchmpc = branchmpc;
443         br->condition = condition;
444         br->reg       = reg;
445         br->options   = options;
446         br->next      = target->branchrefs;
447
448         target->branchrefs = br;
449 }
450
451
452 /* codegen_resolve_branchrefs **************************************************
453
454    Resolves and patches the branch references of a given basic block.
455
456 *******************************************************************************/
457
458 void codegen_resolve_branchrefs(codegendata *cd, basicblock *bptr)
459 {
460         branchref *br;
461         u1        *mcodeptr;
462
463         /* Save the mcodeptr because in the branch emitting functions
464            we generate code somewhere inside already generated code,
465            but we're still in the actual code generation phase. */
466
467         mcodeptr = cd->mcodeptr;
468
469         /* just to make sure */
470
471         assert(bptr->mpc >= 0);
472
473         for (br = bptr->branchrefs; br != NULL; br = br->next) {
474                 /* temporary set the mcodeptr */
475
476                 cd->mcodeptr = cd->mcodebase + br->branchmpc;
477
478                 /* emit_bccz and emit_branch emit the correct code, even if we
479                    pass condition == BRANCH_UNCONDITIONAL or reg == -1. */
480
481                 emit_bccz(cd, bptr, br->condition, br->reg, br->options);
482         }
483
484         /* restore mcodeptr */
485
486         cd->mcodeptr = mcodeptr;
487 }
488
489
490 /* codegen_branch_label_add ****************************************************
491
492    Append an branch to the label-branch list.
493
494 *******************************************************************************/
495
496 void codegen_branch_label_add(codegendata *cd, s4 label, s4 condition, s4 reg, u4 options)
497 {
498         list_t             *list;
499         branch_label_ref_t *br;
500         s4                  mpc;
501
502         /* get the label list */
503
504         list = cd->brancheslabel;
505         
506         /* calculate the current mpc */
507
508         mpc = cd->mcodeptr - cd->mcodebase;
509
510         br = DNEW(branch_label_ref_t);
511
512         br->mpc       = mpc;
513         br->label     = label;
514         br->condition = condition;
515         br->reg       = reg;
516         br->options   = options;
517
518         /* add the branch to the list */
519
520         list_add_last_unsynced(list, br);
521 }
522
523
524 /* codegen_add_patch_ref *******************************************************
525
526    Appends a new patcher reference to the list of patching positions.
527
528 *******************************************************************************/
529
530 void codegen_add_patch_ref(codegendata *cd, functionptr patcher, voidptr ref,
531                                                    s4 disp)
532 {
533         patchref *pr;
534         s4        branchmpc;
535
536         branchmpc = cd->mcodeptr - cd->mcodebase;
537
538         pr = DNEW(patchref);
539
540         pr->branchpos = branchmpc;
541         pr->disp      = disp;
542         pr->patcher   = patcher;
543         pr->ref       = ref;
544
545 /*      list_add_first(cd->patchrefs, pr); */
546         pr->next      = cd->patchrefs;
547         cd->patchrefs = pr;
548
549         /* Generate NOPs for opt_shownops. */
550
551         if (opt_shownops)
552                 PATCHER_NOPS;
553
554 #if defined(ENABLE_JIT) && (defined(__I386__) || defined(__MIPS__) || defined(__X86_64__))
555         /* On some architectures the patcher stub call instruction might
556            be longer than the actual instruction generated.  On this
557            architectures we store the last patcher call position and after
558            the basic block code generation is completed, we check the
559            range and maybe generate some nop's. */
560
561         cd->lastmcodeptr = cd->mcodeptr + PATCHER_CALL_SIZE;
562 #endif
563 }
564
565
566 /* methodtree_comparator *******************************************************
567
568    Comparator function used for the AVL tree of methods.
569
570 *******************************************************************************/
571
572 static s4 methodtree_comparator(const void *pc, const void *element)
573 {
574         methodtree_element *mte;
575         methodtree_element *mtepc;
576
577         mte = (methodtree_element *) element;
578         mtepc = (methodtree_element *) pc;
579
580         /* compare both startpc and endpc of pc, even if they have the same value,
581            otherwise the avl_probe sometimes thinks the element is already in the
582            tree */
583
584 #ifdef __S390__
585         /* On S390 addresses are 31 bit. Compare only 31 bits of value.
586          */
587 #       define ADDR_MASK(a) ((a) & 0x7FFFFFFF)
588 #else
589 #       define ADDR_MASK(a) (a)
590 #endif
591
592         if (ADDR_MASK((long) mte->startpc) <= ADDR_MASK((long) mtepc->startpc) &&
593                 ADDR_MASK((long) mtepc->startpc) <= ADDR_MASK((long) mte->endpc) &&
594                 ADDR_MASK((long) mte->startpc) <= ADDR_MASK((long) mtepc->endpc) &&
595                 ADDR_MASK((long) mtepc->endpc) <= ADDR_MASK((long) mte->endpc)) {
596                 return 0;
597
598         } else if (ADDR_MASK((long) mtepc->startpc) < ADDR_MASK((long) mte->startpc)) {
599                 return -1;
600
601         } else {
602                 return 1;
603         }
604
605 #       undef ADDR_MASK
606 }
607
608
609 /* codegen_insertmethod ********************************************************
610
611    Insert the machine code range of a method into the AVL tree of methods.
612
613 *******************************************************************************/
614
615 void codegen_insertmethod(u1 *startpc, u1 *endpc)
616 {
617         methodtree_element *mte;
618
619         /* allocate new method entry */
620
621         mte = NEW(methodtree_element);
622
623         mte->startpc = startpc;
624         mte->endpc   = endpc;
625
626         /* this function does not return an error, but asserts for
627            duplicate entries */
628
629         avl_insert(methodtree, mte);
630 }
631
632
633 /* codegen_get_pv_from_pc ******************************************************
634
635    Find the PV for the given PC by searching in the AVL tree of
636    methods.
637
638 *******************************************************************************/
639
640 u1 *codegen_get_pv_from_pc(u1 *pc)
641 {
642         methodtree_element  mtepc;
643         methodtree_element *mte;
644
645         /* allocation of the search structure on the stack is much faster */
646
647         mtepc.startpc = pc;
648         mtepc.endpc   = pc;
649
650         mte = avl_find(methodtree, &mtepc);
651
652         if (mte == NULL) {
653                 /* No method was found.  Let's dump a stacktrace. */
654
655 #if defined(ENABLE_VMLOG)
656                 vmlog_cacao_signl("SIGSEGV");
657 #endif
658
659                 log_println("We received a SIGSEGV and tried to handle it, but we were");
660                 log_println("unable to find a Java method at:");
661                 log_println("");
662 #if SIZEOF_VOID_P == 8
663                 log_println("PC=0x%016lx", pc);
664 #else
665                 log_println("PC=0x%08x", pc);
666 #endif
667                 log_println("");
668                 log_println("Dumping the current stacktrace:");
669
670 #if defined(ENABLE_THREADS)
671                 /* XXX michi: This should be available even without threads! */
672                 threads_print_stacktrace();
673 #endif
674
675                 vm_abort("Exiting...");
676         }
677
678         return mte->startpc;
679 }
680
681
682 /* codegen_get_pv_from_pc_nocheck **********************************************
683
684    Find the PV for the given PC by searching in the AVL tree of
685    methods.  This method does not check the return value and is used
686    by the profiler.
687
688 *******************************************************************************/
689
690 u1 *codegen_get_pv_from_pc_nocheck(u1 *pc)
691 {
692         methodtree_element  mtepc;
693         methodtree_element *mte;
694
695         /* allocation of the search structure on the stack is much faster */
696
697         mtepc.startpc = pc;
698         mtepc.endpc   = pc;
699
700         mte = avl_find(methodtree, &mtepc);
701
702         if (mte == NULL)
703                 return NULL;
704         else
705                 return mte->startpc;
706 }
707
708
709 /* codegen_set_replacement_point_notrap ****************************************
710
711    Record the position of a non-trappable replacement point.
712
713 *******************************************************************************/
714
715 #if defined(ENABLE_REPLACEMENT)
716 #if !defined(NDEBUG)
717 void codegen_set_replacement_point_notrap(codegendata *cd, s4 type)
718 #else
719 void codegen_set_replacement_point_notrap(codegendata *cd)
720 #endif
721 {
722         assert(cd->replacementpoint);
723         assert(cd->replacementpoint->type == type);
724         assert(cd->replacementpoint->flags & RPLPOINT_FLAG_NOTRAP);
725
726         cd->replacementpoint->pc = (u1*) (ptrint) (cd->mcodeptr - cd->mcodebase);
727
728         cd->replacementpoint++;
729 }
730 #endif /* defined(ENABLE_REPLACEMENT) */
731
732
733 /* codegen_set_replacement_point ***********************************************
734
735    Record the position of a trappable replacement point.
736
737 *******************************************************************************/
738
739 #if defined(ENABLE_REPLACEMENT)
740 #if !defined(NDEBUG)
741 void codegen_set_replacement_point(codegendata *cd, s4 type)
742 #else
743 void codegen_set_replacement_point(codegendata *cd)
744 #endif
745 {
746         assert(cd->replacementpoint);
747         assert(cd->replacementpoint->type == type);
748         assert(!(cd->replacementpoint->flags & RPLPOINT_FLAG_NOTRAP));
749
750         cd->replacementpoint->pc = (u1*) (ptrint) (cd->mcodeptr - cd->mcodebase);
751
752         cd->replacementpoint++;
753
754         /* XXX assert(cd->lastmcodeptr <= cd->mcodeptr); */
755
756         cd->lastmcodeptr = cd->mcodeptr + PATCHER_CALL_SIZE;
757 }
758 #endif /* defined(ENABLE_REPLACEMENT) */
759
760
761 /* codegen_finish **************************************************************
762
763    Finishes the code generation. A new memory, large enough for both
764    data and code, is allocated and data and code are copied together
765    to their final layout, unresolved jumps are resolved, ...
766
767 *******************************************************************************/
768
769 void codegen_finish(jitdata *jd)
770 {
771         codeinfo    *code;
772         codegendata *cd;
773         s4           mcodelen;
774 #if defined(ENABLE_INTRP)
775         s4           ncodelen;
776 #endif
777         s4           alignedmcodelen;
778         jumpref     *jr;
779         u1          *epoint;
780         s4           extralen;
781         s4           alignedlen;
782
783         /* get required compiler data */
784
785         code = jd->code;
786         cd   = jd->cd;
787
788         /* prevent compiler warning */
789
790 #if defined(ENABLE_INTRP)
791         ncodelen = 0;
792 #endif
793
794         /* calculate the code length */
795
796         mcodelen = (s4) (cd->mcodeptr - cd->mcodebase);
797
798 #if defined(ENABLE_THREADS)
799         extralen = sizeof(critical_section_node_t) * cd->threadcritcount;
800 #else
801         extralen = 0;
802 #endif
803
804 #if defined(ENABLE_STATISTICS)
805         if (opt_stat) {
806                 count_code_len += mcodelen;
807                 count_data_len += cd->dseglen;
808         }
809 #endif
810
811         alignedmcodelen = MEMORY_ALIGN(mcodelen, MAX_ALIGN);
812
813 #if defined(ENABLE_INTRP)
814         if (opt_intrp)
815                 ncodelen = cd->ncodeptr - cd->ncodebase;
816         else {
817                 ncodelen = 0; /* avoid compiler warning */
818         }
819 #endif
820
821         cd->dseglen = MEMORY_ALIGN(cd->dseglen, MAX_ALIGN);
822         alignedlen = alignedmcodelen + cd->dseglen;
823
824 #if defined(ENABLE_INTRP)
825         if (opt_intrp) {
826                 alignedlen += ncodelen;
827         }
828 #endif
829
830         /* allocate new memory */
831
832         code->mcodelength = mcodelen + cd->dseglen;
833         code->mcode       = CNEW(u1, alignedlen + extralen);
834
835         /* set the entrypoint of the method */
836         
837         assert(code->entrypoint == NULL);
838         code->entrypoint = epoint = (code->mcode + cd->dseglen);
839
840         /* fill the data segment (code->entrypoint must already be set!) */
841
842         dseg_finish(jd);
843
844         /* copy code to the new location */
845
846         MCOPY((void *) code->entrypoint, cd->mcodebase, u1, mcodelen);
847
848 #if defined(ENABLE_INTRP)
849         /* relocate native dynamic superinstruction code (if any) */
850
851         if (opt_intrp) {
852                 cd->mcodebase = code->entrypoint;
853
854                 if (ncodelen > 0) {
855                         u1 *ncodebase = code->mcode + cd->dseglen + alignedmcodelen;
856
857                         MCOPY((void *) ncodebase, cd->ncodebase, u1, ncodelen);
858
859                         /* flush the instruction and data caches */
860
861                         md_cacheflush(ncodebase, ncodelen);
862
863                         /* set some cd variables for dynamic_super_rerwite */
864
865                         cd->ncodebase = ncodebase;
866
867                 } else {
868                         cd->ncodebase = NULL;
869                 }
870
871                 dynamic_super_rewrite(cd);
872         }
873 #endif
874
875         /* jump table resolving */
876
877         for (jr = cd->jumpreferences; jr != NULL; jr = jr->next)
878                 *((functionptr *) ((ptrint) epoint + jr->tablepos)) =
879                         (functionptr) ((ptrint) epoint + (ptrint) jr->target->mpc);
880
881         /* line number table resolving */
882         {
883                 linenumberref *lr;
884                 ptrint lrtlen = 0;
885                 ptrint target;
886
887                 for (lr = cd->linenumberreferences; lr != NULL; lr = lr->next) {
888                         lrtlen++;
889                         target = lr->targetmpc;
890                         /* if the entry contains an mcode pointer (normal case), resolve it */
891                         /* (see doc/inlining_stacktrace.txt for details)                    */
892                         if (lr->linenumber >= -2) {
893                             target += (ptrint) epoint;
894                         }
895                         *((functionptr *) ((ptrint) epoint + (ptrint) lr->tablepos)) = 
896                                 (functionptr) target;
897                 }
898                 
899                 *((functionptr *) ((ptrint) epoint + cd->linenumbertablestartpos)) =
900                         (functionptr) ((ptrint) epoint + cd->linenumbertab);
901
902                 *((ptrint *) ((ptrint) epoint + cd->linenumbertablesizepos)) = lrtlen;
903         }
904
905 #if defined(ENABLE_REPLACEMENT)
906         /* replacement point resolving */
907         {
908                 int i;
909                 rplpoint *rp;
910
911                 code->replacementstubs += (ptrint) epoint;
912
913                 rp = code->rplpoints;
914                 for (i=0; i<code->rplpointcount; ++i, ++rp) {
915                         rp->pc = (u1*) ((ptrint) epoint + (ptrint) rp->pc);
916                 }
917         }
918 #endif /* defined(ENABLE_REPLACEMENT) */
919
920         /* add method into methodtree to find the entrypoint */
921
922         codegen_insertmethod(code->entrypoint, code->entrypoint + mcodelen);
923
924 #if defined(__I386__) || defined(__X86_64__) || defined(__XDSPCORE__) || defined(__M68K__) || defined(ENABLE_INTRP)
925         /* resolve data segment references */
926
927         dseg_resolve_datareferences(jd);
928 #endif
929
930 #if defined(ENABLE_THREADS)
931         {
932                 critical_section_node_t *n = (critical_section_node_t *) ((ptrint) code->mcode + alignedlen);
933                 s4 i;
934                 codegen_critical_section_t *nt = cd->threadcrit;
935
936                 for (i = 0; i < cd->threadcritcount; i++) {
937                         n->mcodebegin = (u1 *) (ptrint) code->mcode + nt->mcodebegin;
938                         n->mcodeend = (u1 *) (ptrint) code->mcode + nt->mcodeend;
939                         n->mcoderestart = (u1 *) (ptrint) code->mcode + nt->mcoderestart;
940                         critical_register_critical_section(n);
941                         n++;
942                         nt = nt->next;
943                 }
944         }
945 #endif
946
947         /* flush the instruction and data caches */
948
949         md_cacheflush(code->mcode, code->mcodelength);
950 }
951
952
953 /* codegen_generate_stub_compiler **********************************************
954
955    Wrapper for codegen_emit_stub_compiler.
956
957    Returns:
958        pointer to the compiler stub code.
959
960 *******************************************************************************/
961
962 u1 *codegen_generate_stub_compiler(methodinfo *m)
963 {
964         jitdata     *jd;
965         codegendata *cd;
966         ptrint      *d;                     /* pointer to data memory             */
967         u1          *c;                     /* pointer to code memory             */
968         s4           dumpsize;
969
970         /* mark dump memory */
971
972         dumpsize = dump_size();
973
974         /* allocate required data structures */
975
976         jd = DNEW(jitdata);
977
978         jd->m     = m;
979         jd->cd    = DNEW(codegendata);
980         jd->flags = 0;
981
982         /* get required compiler data */
983
984         cd = jd->cd;
985
986         /* allocate code memory */
987
988         c = CNEW(u1, 3 * SIZEOF_VOID_P + COMPILERSTUB_CODESIZE);
989
990         /* set pointers correctly */
991
992         d = (ptrint *) c;
993
994         cd->mcodebase = c;
995
996         c = c + 3 * SIZEOF_VOID_P;
997         cd->mcodeptr = c;
998
999         /* NOTE: The codeinfo pointer is actually a pointer to the
1000            methodinfo (this fakes a codeinfo structure). */
1001
1002         d[0] = (ptrint) asm_call_jit_compiler;
1003         d[1] = (ptrint) m;
1004         d[2] = (ptrint) &d[1];                                    /* fake code->m */
1005
1006         /* call the emit function */
1007
1008         codegen_emit_stub_compiler(jd);
1009
1010 #if defined(ENABLE_STATISTICS)
1011         if (opt_stat)
1012                 count_cstub_len += 3 * SIZEOF_VOID_P + COMPILERSTUB_CODESIZE;
1013 #endif
1014
1015         /* flush caches */
1016
1017         md_cacheflush(cd->mcodebase, 3 * SIZEOF_VOID_P + COMPILERSTUB_CODESIZE);
1018
1019         /* release dump memory */
1020
1021         dump_release(dumpsize);
1022
1023         /* return native stub code */
1024
1025         return c;
1026 }
1027
1028
1029 /* codegen_generate_stub_builtin ***********************************************
1030
1031    Wrapper for codegen_emit_stub_builtin.
1032
1033    Returns:
1034        Pointer to the entrypoint of the stub.
1035
1036 *******************************************************************************/
1037
1038 void codegen_generate_stub_builtin(builtintable_entry *bte)
1039 {
1040         jitdata  *jd;
1041         codeinfo *code;
1042         s4        dumpsize;
1043
1044         /* mark dump memory */
1045
1046         dumpsize = dump_size();
1047
1048         jd = DNEW(jitdata);
1049
1050         jd->m     = NULL;
1051         jd->cd    = DNEW(codegendata);
1052         jd->rd    = NULL;
1053         jd->flags = 0;
1054
1055         /* Allocate codeinfo memory from the heap as we need to keep them. */
1056
1057         jd->code  = code_codeinfo_new(NULL);
1058
1059         /* get required compiler data */
1060
1061         code = jd->code;
1062
1063         /* setup code generation stuff */
1064
1065         codegen_setup(jd);
1066
1067         /* generate the code */
1068
1069 #if defined(ENABLE_JIT)
1070 # if defined(ENABLE_INTRP)
1071         if (!opt_intrp)
1072 # endif
1073                 codegen_emit_stub_builtin(jd, bte);
1074 #endif
1075
1076         /* reallocate the memory and finish the code generation */
1077
1078         codegen_finish(jd);
1079
1080         /* set the stub entry point in the builtin table */
1081
1082         bte->stub = code->entrypoint;
1083
1084 #if defined(ENABLE_STATISTICS)
1085         if (opt_stat)
1086                 count_nstub_len += code->mcodelength;
1087 #endif
1088
1089         /* release memory */
1090
1091         dump_release(dumpsize);
1092 }
1093
1094
1095 /* codegen_generate_stub_native ************************************************
1096
1097    Wrapper for codegen_emit_stub_native.
1098
1099    Returns:
1100        the codeinfo representing the stub code.
1101
1102 *******************************************************************************/
1103
1104 codeinfo *codegen_generate_stub_native(methodinfo *m, functionptr f)
1105 {
1106         jitdata     *jd;
1107         codeinfo    *code;
1108         s4           dumpsize;
1109         methoddesc  *md;
1110         methoddesc  *nmd;       
1111         s4           nativeparams;
1112
1113         /* mark dump memory */
1114
1115         dumpsize = dump_size();
1116
1117         jd = DNEW(jitdata);
1118
1119         jd->m     = m;
1120         jd->cd    = DNEW(codegendata);
1121         jd->rd    = DNEW(registerdata);
1122         jd->flags = 0;
1123
1124         /* Allocate codeinfo memory from the heap as we need to keep them. */
1125
1126         jd->code  = code_codeinfo_new(m); /* XXX check allocation */
1127
1128         /* get required compiler data */
1129
1130         code = jd->code;
1131
1132         /* set the flags for the current JIT run */
1133
1134 #if defined(ENABLE_PROFILING)
1135         if (opt_prof)
1136                 jd->flags |= JITDATA_FLAG_INSTRUMENT;
1137 #endif
1138
1139         if (opt_verbosecall)
1140                 jd->flags |= JITDATA_FLAG_VERBOSECALL;
1141
1142         /* setup code generation stuff */
1143
1144 #if defined(ENABLE_JIT)
1145 # if defined(ENABLE_INTRP)
1146         if (!opt_intrp)
1147 # endif
1148                 reg_setup(jd);
1149 #endif
1150
1151         codegen_setup(jd);
1152
1153         /* create new method descriptor with additional native parameters */
1154
1155         md = m->parseddesc;
1156         nativeparams = (m->flags & ACC_STATIC) ? 2 : 1;
1157         
1158         nmd = (methoddesc *) DMNEW(u1, sizeof(methoddesc) - sizeof(typedesc) +
1159                                                            md->paramcount * sizeof(typedesc) +
1160                                                            nativeparams * sizeof(typedesc));
1161
1162         nmd->paramcount = md->paramcount + nativeparams;
1163
1164         nmd->params = DMNEW(paramdesc, nmd->paramcount);
1165
1166         nmd->paramtypes[0].type = TYPE_ADR; /* add environment pointer            */
1167
1168         if (m->flags & ACC_STATIC)
1169                 nmd->paramtypes[1].type = TYPE_ADR; /* add class pointer              */
1170
1171         MCOPY(nmd->paramtypes + nativeparams, md->paramtypes, typedesc,
1172                   md->paramcount);
1173
1174 #if defined(ENABLE_JIT)
1175 # if defined(ENABLE_INTRP)
1176         if (!opt_intrp)
1177 # endif
1178                 /* pre-allocate the arguments for the native ABI */
1179
1180                 md_param_alloc_native(nmd);
1181 #endif
1182
1183         /* generate the code */
1184
1185 #if defined(ENABLE_JIT)
1186 # if defined(ENABLE_INTRP)
1187         if (opt_intrp)
1188                 intrp_createnativestub(f, jd, nmd);
1189         else
1190 # endif
1191                 codegen_emit_stub_native(jd, nmd, f);
1192 #else
1193         intrp_createnativestub(f, jd, nmd);
1194 #endif
1195
1196         /* reallocate the memory and finish the code generation */
1197
1198         codegen_finish(jd);
1199
1200 #if defined(ENABLE_STATISTICS)
1201         if (opt_stat)
1202                 count_nstub_len += code->mcodelength;
1203 #endif
1204
1205 #if !defined(NDEBUG)
1206         /* disassemble native stub */
1207
1208         if (opt_shownativestub) {
1209 #if defined(ENABLE_DISASSEMBLER)
1210                 codegen_disassemble_nativestub(m,
1211                                                                            (u1 *) (ptrint) code->entrypoint,
1212                                                                            (u1 *) (ptrint) code->entrypoint + (code->mcodelength - jd->cd->dseglen));
1213 #endif
1214
1215                 /* show data segment */
1216
1217                 if (opt_showddatasegment)
1218                         dseg_display(jd);
1219         }
1220 #endif /* !defined(NDEBUG) */
1221
1222         /* release memory */
1223
1224         dump_release(dumpsize);
1225
1226         /* return native stub code */
1227
1228         return code;
1229 }
1230
1231
1232 /* codegen_disassemble_nativestub **********************************************
1233
1234    Disassembles the generated native stub.
1235
1236 *******************************************************************************/
1237
1238 #if defined(ENABLE_DISASSEMBLER)
1239 void codegen_disassemble_nativestub(methodinfo *m, u1 *start, u1 *end)
1240 {
1241         printf("Native stub: ");
1242         utf_fprint_printable_ascii_classname(stdout, m->class->name);
1243         printf(".");
1244         utf_fprint_printable_ascii(stdout, m->name);
1245         utf_fprint_printable_ascii(stdout, m->descriptor);
1246         printf("\n\nLength: %d\n\n", (s4) (end - start));
1247
1248         DISASSEMBLE(start, end);
1249 }
1250 #endif
1251
1252
1253 /* codegen_stub_builtin_enter **************************************************
1254
1255    Prepares the stuff required for a builtin function call:
1256
1257    - adds a stackframe info structure to the chain, for stacktraces
1258
1259    The layout of the builtin stub stackframe should look like this:
1260
1261    +---------------------------+ <- SP (of parent Java function)
1262    | return address            |
1263    +---------------------------+
1264    |                           |
1265    | stackframe info structure |
1266    |                           |
1267    +---------------------------+
1268    |                           |
1269    | arguments (if any)        |
1270    |                           |
1271    +---------------------------+ <- SP (native stub)
1272
1273 *******************************************************************************/
1274
1275 void codegen_stub_builtin_enter(u1 *datasp, u1 *pv, u1 *sp, u1 *ra)
1276 {
1277         stackframeinfo *sfi;
1278
1279         /* get data structures from stack */
1280
1281         sfi = (stackframeinfo *) (datasp - sizeof(stackframeinfo));
1282
1283         /* add a stackframeinfo to the chain */
1284
1285         stacktrace_create_native_stackframeinfo(sfi, pv, sp, ra);
1286
1287 #if defined(ENABLE_THREADS) && defined(ENABLE_GC_CACAO)
1288         /* set the native world flag */
1289
1290         THREADOBJECT->flags |= THREAD_FLAG_IN_NATIVE;
1291 #endif
1292 }
1293
1294
1295 /* codegen_stub_builtin_exit ***************************************************
1296
1297    Removes the stuff required for a builtin function call.
1298
1299 *******************************************************************************/
1300
1301 void codegen_stub_builtin_exit(u1 *datasp)
1302 {
1303         stackframeinfo     *sfi;
1304         stackframeinfo    **psfi;
1305
1306         /* get data structures from stack */
1307
1308         sfi = (stackframeinfo *) (datasp - sizeof(stackframeinfo));
1309
1310         /* remove current stackframeinfo from chain */
1311
1312         psfi = &STACKFRAMEINFO;
1313
1314         *psfi = sfi->prev;
1315
1316 #if defined(ENABLE_THREADS) && defined(ENABLE_GC_CACAO)
1317         /* clear the native world flag */
1318
1319         THREADOBJECT->flags &= ~THREAD_FLAG_IN_NATIVE;
1320 #endif
1321 }
1322
1323
1324 /* codegen_start_native_call ***************************************************
1325
1326    Prepares the stuff required for a native (JNI) function call:
1327
1328    - adds a stackframe info structure to the chain, for stacktraces
1329    - prepares the local references table on the stack
1330
1331    The layout of the native stub stackframe should look like this:
1332
1333    +---------------------------+ <- SP (of parent Java function)
1334    | return address            |
1335    +---------------------------+
1336    |                           |
1337    | stackframe info structure |
1338    |                           |
1339    +---------------------------+
1340    |                           |
1341    | local references table    |
1342    |                           |
1343    +---------------------------+
1344    |                           |
1345    | arguments (if any)        |
1346    |                           |
1347    +---------------------------+ <- SP (native stub)
1348
1349 *******************************************************************************/
1350
1351 void codegen_start_native_call(u1 *datasp, u1 *pv, u1 *sp, u1 *ra)
1352 {
1353         stackframeinfo *sfi;
1354         localref_table *lrt;
1355
1356         /* get data structures from stack */
1357
1358         sfi = (stackframeinfo *) (datasp - sizeof(stackframeinfo));
1359         lrt = (localref_table *) (datasp - sizeof(stackframeinfo) - 
1360                                                           sizeof(localref_table));
1361
1362         /* add a stackframeinfo to the chain */
1363
1364         stacktrace_create_native_stackframeinfo(sfi, pv, sp, ra);
1365
1366 #if defined(ENABLE_JAVASE)
1367         /* add current JNI local references table to this thread */
1368
1369         lrt->capacity    = LOCALREFTABLE_CAPACITY;
1370         lrt->used        = 0;
1371         lrt->localframes = 1;
1372         lrt->prev        = LOCALREFTABLE;
1373
1374         /* clear the references array (memset is faster the a for-loop) */
1375
1376         MSET(lrt->refs, 0, java_objectheader*, LOCALREFTABLE_CAPACITY);
1377
1378         LOCALREFTABLE = lrt;
1379 #endif
1380
1381 #if defined(ENABLE_THREADS) && defined(ENABLE_GC_CACAO)
1382         /* set the native world flag */
1383
1384         THREADOBJECT->flags |= THREAD_FLAG_IN_NATIVE;
1385 #endif
1386 }
1387
1388
1389 /* codegen_finish_native_call **************************************************
1390
1391    Removes the stuff required for a native (JNI) function call.
1392    Additionally it checks for an exceptions and in case, get the
1393    exception object and clear the pointer.
1394
1395 *******************************************************************************/
1396
1397 java_objectheader *codegen_finish_native_call(u1 *datasp)
1398 {
1399         stackframeinfo     *sfi;
1400         stackframeinfo    **psfi;
1401 #if defined(ENABLE_JAVASE)
1402         localref_table     *lrt;
1403         localref_table     *plrt;
1404         s4                  localframes;
1405 #endif
1406         java_objectheader  *e;
1407
1408         /* get data structures from stack */
1409
1410         sfi = (stackframeinfo *) (datasp - sizeof(stackframeinfo));
1411
1412 #if defined(ENABLE_THREADS) && defined(ENABLE_GC_CACAO)
1413         /* clear the native world flag */
1414
1415         THREADOBJECT->flags &= ~THREAD_FLAG_IN_NATIVE;
1416 #endif
1417
1418         /* remove current stackframeinfo from chain */
1419
1420         psfi = &STACKFRAMEINFO;
1421
1422         *psfi = sfi->prev;
1423
1424 #if defined(ENABLE_JAVASE)
1425         /* release JNI local references tables for this thread */
1426
1427         lrt = LOCALREFTABLE;
1428
1429         /* release all current local frames */
1430
1431         for (localframes = lrt->localframes; localframes >= 1; localframes--) {
1432                 /* get previous frame */
1433
1434                 plrt = lrt->prev;
1435
1436                 /* Clear all reference entries (only for tables allocated on
1437                    the Java heap). */
1438
1439                 if (localframes > 1)
1440                         MSET(&lrt->refs[0], 0, java_objectheader*, lrt->capacity);
1441
1442                 lrt->prev = NULL;
1443
1444                 /* set new local references table */
1445
1446                 lrt = plrt;
1447         }
1448
1449         /* now store the previous local frames in the thread structure */
1450
1451         LOCALREFTABLE = lrt;
1452 #endif
1453
1454         /* get the exception and return it */
1455
1456         e = exceptions_get_and_clear_exception();
1457
1458         return e;
1459 }
1460
1461
1462 /* removecompilerstub **********************************************************
1463
1464    Deletes a compilerstub from memory (simply by freeing it).
1465
1466 *******************************************************************************/
1467
1468 void removecompilerstub(u1 *stub)
1469 {
1470         /* pass size 1 to keep the intern function happy */
1471
1472         CFREE((void *) stub, 1);
1473 }
1474
1475
1476 /* removenativestub ************************************************************
1477
1478    Removes a previously created native-stub from memory.
1479     
1480 *******************************************************************************/
1481
1482 void removenativestub(u1 *stub)
1483 {
1484         /* pass size 1 to keep the intern function happy */
1485
1486         CFREE((void *) stub, 1);
1487 }
1488
1489
1490 /* codegen_reg_of_var **********************************************************
1491
1492    This function determines a register, to which the result of an
1493    operation should go, when it is ultimatively intended to store the
1494    result in pseudoregister v.  If v is assigned to an actual
1495    register, this register will be returned.  Otherwise (when v is
1496    spilled) this function returns tempregnum.  If not already done,
1497    regoff and flags are set in the stack location.
1498        
1499    On ARM we have to check if a long/double variable is splitted
1500    across reg/stack (HIGH_REG == REG_SPLIT). We return the actual
1501    register of v for LOW_REG and the tempregnum for HIGH_REG in such
1502    cases.  (michi 2005/07/24)
1503
1504 *******************************************************************************/
1505
1506 s4 codegen_reg_of_var(u2 opcode, varinfo *v, s4 tempregnum)
1507 {
1508
1509 #if 0
1510         /* Do we have to generate a conditional move?  Yes, then always
1511            return the temporary register.  The real register is identified
1512            during the store. */
1513
1514         if (opcode & ICMD_CONDITION_MASK)
1515                 return tempregnum;
1516 #endif
1517
1518         if (!(v->flags & INMEMORY)) {
1519 #if defined(__ARM__) && defined(__ARMEL__)
1520                 if (IS_2_WORD_TYPE(v->type) && (GET_HIGH_REG(v->vv.regoff) == REG_SPLIT))
1521                         return PACK_REGS(GET_LOW_REG(v->vv.regoff),
1522                                                          GET_HIGH_REG(tempregnum));
1523 #endif
1524 #if defined(__ARM__) && defined(__ARMEB__)
1525                 if (IS_2_WORD_TYPE(v->type) && (GET_LOW_REG(v->vv.regoff) == REG_SPLIT))
1526                         return PACK_REGS(GET_LOW_REG(tempregnum),
1527                                                          GET_HIGH_REG(v->vv.regoff));
1528 #endif
1529                 return v->vv.regoff;
1530         }
1531
1532 #if defined(ENABLE_STATISTICS)
1533         if (opt_stat)
1534                 count_spills_read++;
1535 #endif
1536
1537         return tempregnum;
1538 }
1539
1540 /* codegen_reg_of_dst **********************************************************
1541
1542    This function determines a register, to which the result of an
1543    operation should go, when it is ultimatively intended to store the
1544    result in iptr->dst.var.  If dst.var is assigned to an actual
1545    register, this register will be returned.  Otherwise (when it is
1546    spilled) this function returns tempregnum.  If not already done,
1547    regoff and flags are set in the stack location.
1548        
1549    On ARM we have to check if a long/double variable is splitted
1550    across reg/stack (HIGH_REG == REG_SPLIT). We return the actual
1551    register of dst.var for LOW_REG and the tempregnum for HIGH_REG in such
1552    cases.  (michi 2005/07/24)
1553
1554 *******************************************************************************/
1555
1556 s4 codegen_reg_of_dst(jitdata *jd, instruction *iptr, s4 tempregnum)
1557 {
1558         return codegen_reg_of_var(iptr->opc, VAROP(iptr->dst), tempregnum);
1559 }
1560
1561
1562 #if defined(ENABLE_THREADS)
1563 void codegen_threadcritrestart(codegendata *cd, int offset)
1564 {
1565         cd->threadcritcurrent.mcoderestart = offset;
1566 }
1567
1568
1569 void codegen_threadcritstart(codegendata *cd, int offset)
1570 {
1571         cd->threadcritcurrent.mcodebegin = offset;
1572 }
1573
1574
1575 void codegen_threadcritstop(codegendata *cd, int offset)
1576 {
1577         cd->threadcritcurrent.next = cd->threadcrit;
1578         cd->threadcritcurrent.mcodeend = offset;
1579         cd->threadcrit = DNEW(codegen_critical_section_t);
1580         *(cd->threadcrit) = cd->threadcritcurrent;
1581         cd->threadcritcount++;
1582 }
1583 #endif
1584
1585
1586 /*
1587  * These are local overrides for various environment variables in Emacs.
1588  * Please do not remove this and leave it at the end of the file, where
1589  * Emacs will automagically detect them.
1590  * ---------------------------------------------------------------------
1591  * Local variables:
1592  * mode: c
1593  * indent-tabs-mode: t
1594  * c-basic-offset: 4
1595  * tab-width: 4
1596  * End:
1597  * vim:noexpandtab:sw=4:ts=4:
1598  */