SUPPORT_ONLY_ZERO_ASTORE bugfix.
[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 1265 2004-07-01 20:36:38Z 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_addxboundrefs(void *branchptr, s4 reg);
127 static void codegen_addxnullrefs(void *branchptr);
128 static void codegen_addxcastrefs(void *branchptr);
129 static void codegen_addxdivrefs(void *branchptr);
130 static void codegen_addxexceptionrefs(void *branchptr);
131 static void codegen_threadcritrestart(int offset);
132 static void codegen_threadcritstart(int offset);
133 static void codegen_threadcritstop(int offset);
134
135 #if defined(__I386__) || defined(__X86_64__)
136 typedef struct _methodtree_element methodtree_element;
137
138 struct _methodtree_element {
139         void *startpc;
140         void *endpc;
141 };
142
143 static struct avl_table *methodtree=0;
144 static int methodtree_comparator(const void *pc, const void *element,
145                                                                  void *param);
146 #endif
147
148 void dseg_display(s4 *s4ptr);
149
150
151
152 /* codegen_init allocates and initialises code area, data area and references */
153
154 void codegen_init()
155 {
156         if (!mcodebase) {
157                 mcodebase = MNEW(u1, MCODEINITSIZE);
158                 mcodesize = MCODEINITSIZE;
159         }
160
161         if (!dsegtop) {
162                 dsegtop = MNEW(u1, DSEGINITSIZE);
163                 dsegsize = DSEGINITSIZE;
164                 dsegtop += dsegsize;
165         }
166
167         dseglen = 0;
168
169         linenumberreferences = NULL;
170         linenumbertablesizepos = 0;
171         linenumbertablestartpos = 0;
172         linenumbertab = 0;
173         jumpreferences = NULL;
174         datareferences = NULL;
175         xboundrefs = NULL;
176         xcheckarefs = NULL;
177         xnullrefs = NULL;
178         xcastrefs = NULL;
179         xdivrefs = NULL;
180         xexceptionrefs = NULL;
181
182 #if defined(__I386__) || defined(__X86_64__)
183         if (!methodtree) {
184                 methodtree_element *mte;
185
186                 methodtree = avl_create(methodtree_comparator, NULL, NULL);
187
188                 mte = NEW(methodtree_element);
189
190                 mte->startpc = asm_calljavafunction;
191                 mte->endpc = asm_calljavafunction2 - 1;
192
193                 avl_insert(methodtree, mte);
194
195                 mte = NEW(methodtree_element);
196
197                 mte->startpc = asm_calljavafunction2;
198                 mte->endpc = asm_call_jit_compiler - 1;
199
200                 avl_insert(methodtree, mte);
201         }
202 #endif
203
204 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
205         threadcritcurrent.next = NULL;
206         threadcritcount = 0;
207 #endif
208 }
209
210
211
212 /* codegen_close releases temporary code and data area                        */
213
214 void codegen_close()
215 {
216         if (mcodebase) {
217                 MFREE(mcodebase, u1, mcodesize);
218                 mcodebase = NULL;
219         }
220
221         if (dsegtop) {
222                 MFREE(dsegtop - dsegsize, u1, dsegsize);
223                 dsegtop = NULL;
224         }
225 }
226
227
228
229 /* codegen_increase doubles code area                                         */
230
231 static s4 *codegen_increase(u1 *codeptr)
232 {
233         long len;
234
235         len = codeptr - mcodebase;
236         mcodebase = MREALLOC(mcodebase, u1, mcodesize, mcodesize * 2);
237         mcodesize *= 2;
238         mcodeend = (s4*) (mcodebase + mcodesize);
239         return (s4*) (mcodebase + len);
240 }
241
242
243
244 /* desg_increase doubles data area                                            */
245
246 static void dseg_increase()
247 {
248         u1 *newstorage = MNEW(u1, dsegsize * 2);
249         memcpy(newstorage + dsegsize, dsegtop - dsegsize, dsegsize);
250         MFREE(dsegtop - dsegsize, u1, dsegsize);
251         dsegtop = newstorage;
252         dsegsize *= 2;
253         dsegtop += dsegsize;
254 }
255
256
257
258 static s4 dseg_adds4_increase(s4 value)
259 {
260         dseg_increase();
261         *((s4 *) (dsegtop - dseglen)) = value;
262         return -dseglen;
263 }
264
265
266
267 static s4 dseg_adds4(s4 value)
268 {
269         s4 *dataptr;
270
271         dseglen += 4;
272         dataptr = (s4 *) (dsegtop - dseglen);
273         if (dseglen > dsegsize)
274                 return dseg_adds4_increase(value);
275         *dataptr = value;
276         return -dseglen;
277 }
278
279
280
281 #if !defined(__I386__)
282 static s4 dseg_adds8_increase(s8 value)
283 {
284         dseg_increase();
285         *((s8 *) (dsegtop - dseglen)) = value;
286         return -dseglen;
287 }
288
289
290 static s4 dseg_adds8(s8 value)
291 {
292         s8 *dataptr;
293
294         dseglen = ALIGN (dseglen + 8, 8);
295         dataptr = (s8 *) (dsegtop - dseglen);
296         if (dseglen > dsegsize)
297                 return dseg_adds8_increase(value);
298         *dataptr = value;
299         return -dseglen;
300 }
301 #endif
302
303
304 static s4 dseg_addfloat_increase(float value)
305 {
306         dseg_increase();
307         *((float *) (dsegtop - dseglen)) = value;
308         return -dseglen;
309 }
310
311
312
313 static s4 dseg_addfloat(float value)
314 {
315         float *dataptr;
316
317         dseglen += 4;
318         dataptr = (float *) (dsegtop - dseglen);
319         if (dseglen > dsegsize)
320                 return dseg_addfloat_increase(value);
321         *dataptr = value;
322         return -dseglen;
323 }
324
325
326
327 static s4 dseg_adddouble_increase(double value)
328 {
329         dseg_increase();
330         *((double *) (dsegtop - dseglen)) = value;
331         return -dseglen;
332 }
333
334
335
336 static s4 dseg_adddouble(double value)
337 {
338         double *dataptr;
339
340         dseglen = ALIGN (dseglen + 8, 8);
341         dataptr = (double *) (dsegtop - dseglen);
342         if (dseglen > dsegsize)
343                 return dseg_adddouble_increase(value);
344         *dataptr = value;
345         return -dseglen;
346 }
347
348
349
350 static void dseg_addtarget(basicblock *target)
351 {
352         jumpref *jr = DNEW(jumpref);
353         
354         jr->tablepos = dseg_addaddress(NULL);
355         jr->target = target;
356         jr->next = jumpreferences;
357         jumpreferences = jr;
358 }
359
360
361
362 static void dseg_adddata(u1 *ptr)
363 {
364         dataref *dr = DNEW(dataref);
365
366         dr->pos = (u1 *) (ptr - mcodebase);
367         dr->next = datareferences;
368         datareferences = dr;
369 }
370
371 static void dseg_addlinenumbertablesize() {
372 #ifdef __ALPHA__
373         dseg_adds4(0); /*PADDING*/
374 #endif
375         linenumbertablesizepos=dseg_addaddress(NULL); /*it could be considered to use adds4 here, to avoid 1 double word padding on ALPHA */
376
377         linenumbertablestartpos=dseg_addaddress(NULL);
378 #ifdef __ALPHA__
379         dseg_adds4(0); /*PADDING*/
380 #endif
381 }
382
383 static void dseg_addlinenumber(u2 linenumber,u1 *ptr) {
384         linenumberref *lr=DNEW(linenumberref);
385         lr->linenumber=linenumber;
386         lr->tablepos=0;
387         lr->targetmpc=(ptr-mcodebase);
388         lr->next=linenumberreferences;
389         linenumberreferences=lr;
390 }
391
392
393 /* we need this function externally on i386 and x86_64, but keep the call fast
394    on alpha... */
395
396 #if defined(__I386__) || defined(__X86_64__)
397 void codegen_addreference(basicblock *target, void *branchptr)
398 #else
399 static void codegen_addreference(basicblock *target, void *branchptr)
400 #endif
401 {
402         s4 branchpos = (u1*) branchptr - mcodebase;
403
404         if (target->mpc >= 0) {
405                 gen_resolvebranch((u1*) mcodebase + branchpos, branchpos, target->mpc);
406         }
407         else {
408                 branchref *br = DNEW(branchref);
409
410                 br->branchpos = branchpos;
411                 br->next = target->branchrefs;
412                 target->branchrefs= br;
413         }
414 }
415
416
417
418 static void codegen_addxboundrefs(void *branchptr, s4 reg)
419 {
420         s4 branchpos = (u1*) branchptr - mcodebase;
421
422         branchref *br = DNEW(branchref);
423
424         br->branchpos = branchpos;
425         br->reg = reg;
426         br->next = xboundrefs;
427         xboundrefs = br;
428 }
429
430
431
432 static void codegen_addxcheckarefs(void *branchptr)
433 {
434         s4 branchpos = (u1*) branchptr - mcodebase;
435
436         branchref *br = DNEW(branchref);
437
438         br->branchpos = branchpos;
439         br->next = xcheckarefs;
440         xcheckarefs = br;
441 }
442
443
444
445 static void codegen_addxnullrefs(void *branchptr)
446 {
447         s4 branchpos = (u1*) branchptr - mcodebase;
448
449         branchref *br = DNEW(branchref);
450
451         br->branchpos = branchpos;
452         br->next = xnullrefs;
453         xnullrefs = br;
454 }
455
456
457
458 static void codegen_addxcastrefs(void *branchptr)
459 {
460         s4 branchpos = (u1*) branchptr - mcodebase;
461
462         branchref *br = DNEW(branchref);
463
464         br->branchpos = branchpos;
465         br->next = xcastrefs;
466         xcastrefs = br;
467 }
468
469
470 static void codegen_addxexceptionrefs(void *branchptr)
471 {
472         s4 branchpos = (u1*) branchptr - mcodebase;
473
474         branchref *br = DNEW(branchref);
475
476         br->branchpos = branchpos;
477         br->next = xexceptionrefs;
478         xexceptionrefs = br;
479 }
480
481
482 static void codegen_addxdivrefs(void *branchptr)
483 {
484         s4 branchpos = (u1*) branchptr - mcodebase;
485
486         branchref *br = DNEW(branchref);
487
488         br->branchpos = branchpos;
489         br->next = xdivrefs;
490         xdivrefs = br;
491 }
492
493
494 static void codegen_createlinenumbertable() {
495 #ifdef __I386__
496         /*log_text("codegen_createlinnumbertable");*/
497         {
498                 linenumberref *lr;
499                 for (lr=linenumberreferences;lr!=NULL;lr=lr->next) {
500                         /*log_text("Adding line number entry");*/
501                         lr->tablepos=dseg_addaddress(NULL);
502                         if (linenumbertab==0) linenumbertab=lr->tablepos;
503                         dseg_addaddress(lr->linenumber);
504                 }
505         }
506 #endif
507 }
508
509
510 #if defined(__I386__) || defined(__X86_64__)
511 static int methodtree_comparator(const void *pc, const void *element,
512                                                                  void *param)
513 {
514         methodtree_element *mte;
515         methodtree_element *mtepc;
516
517         mte = (methodtree_element *) element;
518         mtepc = (methodtree_element *) pc;
519
520         /* compare both startpc and endpc of pc, even if they have the same value,
521            otherwise the avl_probe sometime thinks the element is still in the
522            tree */
523         if (mte->startpc <= mtepc->startpc && mtepc->startpc <= mte->endpc &&
524                 mte->startpc <= mtepc->endpc   && mtepc->endpc   <= mte->endpc) {
525                 return 0;
526
527         } else if (mtepc->startpc < mte->startpc) {
528                 return -1;
529
530         } else {
531                 return 1;
532         }
533 }
534
535
536 #if 0
537 void *codegen_findmethod1(void *pc)
538 {
539         void * retVal=findmethod(pc);
540         methodinfo **ma=(methodinfo**)retVal;
541         methodinfo *m=ma[-1];
542         if (m)
543                 if (m->name)
544                         utf_display(m->name);
545                 else 
546                         log_text("No Name");
547         else log_text("No methodinfo");
548         return retVal;
549 }
550 #endif
551
552 void *codegen_findmethod(void *pc)
553 {
554         methodtree_element *mtepc;
555         methodtree_element *mte;
556
557         mtepc = NEW(methodtree_element);
558         mtepc->startpc = pc;
559         mtepc->endpc = pc;
560
561         mte = avl_find(methodtree, mtepc);
562
563         FREE(mtepc, methodtree_element);
564
565         if (!mte)
566                 throw_cacao_exception_exit(string_java_lang_InternalError, "cannot find function");
567
568         return mte->startpc;
569 }
570 #endif
571
572
573 static void codegen_finish(methodinfo *m, int mcodelen)
574 {
575         jumpref *jr;
576         u1 *epoint;
577         s4 extralen = 0;
578         s4 alignedlen;
579
580 /*      printf("codegen_finish\n"); */
581 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
582         extralen += sizeof(threadcritnode) * threadcritcount;
583 #endif
584
585 #if defined(STATISTICS)
586         if (opt_stat) {
587                 count_code_len += mcodelen;
588                 count_data_len += dseglen;
589         }
590 #endif
591
592         dseglen = ALIGN(dseglen, MAX_ALIGN);
593         alignedlen = ALIGN(mcodelen, MAX_ALIGN) + dseglen;
594
595         m->mcodelength = mcodelen + dseglen;
596         m->mcode = CNEW(u1, alignedlen + extralen);
597
598         memcpy(m->mcode, dsegtop - dseglen, dseglen);
599         memcpy(m->mcode + dseglen, mcodebase, mcodelen);
600
601         m->entrypoint = epoint = (u1 *) (m->mcode + dseglen);
602
603         /* jump table resolving */
604         jr = jumpreferences;
605         while (jr != NULL) {
606             *((void**) (epoint + jr->tablepos)) = epoint + jr->target->mpc;
607             jr = jr->next;
608         }
609
610 #ifdef __I386__
611         /* line number table resolving */
612         {
613                 linenumberref *lr;
614                 #if POINTERSIZE == 8
615                         s8  lrtlen=0;
616                 #else
617                         s4  lrtlen=0;
618                 #endif
619
620                 for (lr=linenumberreferences;lr!=NULL;lr=lr->next) {
621                         lrtlen++;
622                         *((void**)(epoint+lr->tablepos))=epoint+lr->targetmpc;
623                         /*log_text("resolving line number information");*/
624                 }
625                 
626                 *((void**)(epoint+linenumbertablestartpos))=epoint+linenumbertab;
627 #if POINTERSIZE == 8
628                 *((s8*)(epoint+linenumbertablesizepos))=lrtlen;
629 #else
630                 *((s4*)(epoint+linenumbertablesizepos))=lrtlen;
631 #endif
632
633         }
634 #endif
635 #if defined(__I386__) || defined(__X86_64__)
636         {
637                 dataref *dr;
638
639                 /* add method into methodtree to find the entrypoint */
640                 methodtree_element *mte;
641
642                 mte = NEW(methodtree_element);
643                 mte->startpc = m->entrypoint;
644                 mte->endpc = m->entrypoint + mcodelen;
645
646                 if (avl_insert(methodtree, mte))
647                         panic("duplicate entry");
648
649                 /* data segment references resolving */
650                 dr = datareferences;
651                 while (dr != NULL) {
652                         *((void**) ((long) epoint + (long) dr->pos - POINTERSIZE)) = epoint;
653                         dr = dr->next;
654                 }
655         }
656 #endif
657
658
659 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
660         {
661                 threadcritnode *n = (threadcritnode*) (m->mcode + alignedlen);
662                 int i;
663                 struct threadcritnodetemp *nt = threadcrit;
664
665                 for (i=0; i<threadcritcount; i++)
666                 {
667                         n->mcodebegin = m->mcode + nt->mcodebegin;
668                         n->mcodeend = m->mcode + nt->mcodeend;
669                         n->mcoderestart = m->mcode + nt->mcoderestart;
670                         thread_registercritical(n);
671                         n++;
672                         nt = nt->next;
673                 }
674         }
675 #endif
676 }
677
678
679 #if defined(__I386__) || defined(__X86_64__)
680 void codegen_insertNative(void *startpc, void *endpc)
681 {
682         methodtree_element *mte;
683
684         if (!methodtree) {
685                 methodtree_element *mte;
686
687                 methodtree = avl_create(methodtree_comparator, NULL, NULL);
688
689                 mte = NEW(methodtree_element);
690                 mte->startpc = asm_calljavafunction;
691                 mte->endpc = asm_calljavafunction2 - 1;
692                 avl_insert(methodtree, mte);
693
694                 mte = NEW(methodtree_element);
695                 mte->startpc = asm_calljavafunction2;
696                 mte->endpc = asm_call_jit_compiler - 1;
697                 avl_insert(methodtree, mte);
698         }
699
700         mte = NEW(methodtree_element);
701         mte->startpc = startpc;
702         mte->endpc = endpc;
703
704         if (avl_insert(methodtree, mte))
705                 panic("duplicate entry");
706 }
707 #endif
708
709
710 void dseg_display(s4 *s4ptr)
711 {
712         int i;
713         
714         printf("  --- dump of datasegment\n");
715         for (i = dseglen; i > 0 ; i -= 4) {
716                 printf("-%6x: %8x\n", i, (int)(*s4ptr++));
717         }
718         printf("  --- begin of data segment: %p\n", s4ptr);
719 }
720
721
722 /* reg_of_var:
723     This function determines a register, to which the result of an operation
724     should go, when it is ultimatively intended to store the result in
725     pseudoregister v.
726     If v is assigned to an actual register, this register will be returned.
727     Otherwise (when v is spilled) this function returns tempregnum.
728     If not already done, regoff and flags are set in the stack location.
729 */        
730
731 static int reg_of_var(methodinfo *m, stackptr v, int tempregnum)
732 {
733         varinfo *var;
734
735         switch (v->varkind) {
736         case TEMPVAR:
737                 if (!(v->flags & INMEMORY))
738                         return(v->regoff);
739                 break;
740         case STACKVAR:
741                 var = &(m->registerdata->interfaces[v->varnum][v->type]);
742                 v->regoff = var->regoff;
743                 if (!(var->flags & INMEMORY))
744                         return(var->regoff);
745                 break;
746         case LOCALVAR:
747                 var = &(m->registerdata->locals[v->varnum][v->type]);
748                 v->regoff = var->regoff;
749                 if (!(var->flags & INMEMORY))
750                         return(var->regoff);
751                 break;
752         case ARGVAR:
753                 v->regoff = v->varnum;
754                 if (IS_FLT_DBL_TYPE(v->type)) {
755                         if (v->varnum < m->registerdata->fltreg_argnum) {
756                                 v->regoff = m->registerdata->argfltregs[v->varnum];
757                                 return(m->registerdata->argfltregs[v->varnum]);
758                         }
759                 }
760                 else
761                         if (v->varnum < m->registerdata->intreg_argnum) {
762                                 v->regoff = m->registerdata->argintregs[v->varnum];
763                                 return(m->registerdata->argintregs[v->varnum]);
764                         }
765                 v->regoff -= m->registerdata->intreg_argnum;
766                 break;
767         }
768         v->flags |= INMEMORY;
769         return tempregnum;
770 }
771
772
773 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
774 void codegen_threadcritrestart(int offset)
775 {
776         threadcritcurrent.mcoderestart = offset;
777 }
778
779 void codegen_threadcritstart(int offset)
780 {
781         threadcritcurrent.mcodebegin = offset;
782 }
783
784 void codegen_threadcritstop(int offset)
785 {
786         threadcritcurrent.next = threadcrit;
787         threadcritcurrent.mcodeend = offset;
788         threadcrit = DNEW(struct threadcritnodetemp);
789         *threadcrit = threadcritcurrent;
790         threadcritcount++;
791 }
792 #endif
793
794 /*
795  * These are local overrides for various environment variables in Emacs.
796  * Please do not remove this and leave it at the end of the file, where
797  * Emacs will automagically detect them.
798  * ---------------------------------------------------------------------
799  * Local variables:
800  * mode: c
801  * indent-tabs-mode: t
802  * c-basic-offset: 4
803  * tab-width: 4
804  * End:
805  */