Merge from vendor branch LESS:
[dragonfly.git] / contrib / gcc / c-pragma.c
1 /* Handle #pragma, system V.4 style.  Supports #pragma weak and #pragma pack.
2    Copyright (C) 1992, 1997, 1998, 1999 Free Software Foundation, Inc.
3
4 This file is part of GNU CC.
5
6 GNU CC is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU CC is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU CC; see the file COPYING.  If not, write to
18 the Free Software Foundation, 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA.  */
20
21 #include "config.h"
22 #include "system.h"
23 #include "rtl.h"
24 #include "tree.h"
25 #include "except.h"
26 #include "function.h"
27 #include "defaults.h"
28 #include "c-pragma.h"
29 #include "flags.h"
30 #include "toplev.h"
31
32 #ifdef HANDLE_GENERIC_PRAGMAS
33
34 #ifdef HANDLE_PRAGMA_PACK
35 /* When structure field packing is in effect, this variable is the
36    number of bits to use as the maximum alignment.  When packing is not
37    in effect, this is zero.  */
38
39 extern int maximum_field_alignment;
40 #endif
41
42
43 #ifdef HANDLE_PRAGMA_PACK_PUSH_POP
44 typedef struct align_stack
45 {
46   int                  alignment;
47   unsigned int         num_pushes;
48   tree                 id;
49   struct align_stack * prev;
50 } align_stack;
51
52 static struct align_stack * alignment_stack = NULL;
53
54 static int  push_alignment PROTO((int, tree));
55 static int  pop_alignment  PROTO((tree));
56
57 /* Push an alignment value onto the stack.  */
58 static int
59 push_alignment (alignment, id)
60      int alignment;
61      tree id;
62 {
63   switch (alignment)
64     {
65     case 0:
66     case 1:
67     case 2:
68     case 4:
69     case 8:
70     case 16:
71       break;
72     default:
73       warning ("\
74 Alignment must be a small power of two, not %d, in #pragma pack",
75                alignment);
76       return 0;
77     }
78   
79   if (alignment_stack == NULL
80       || alignment_stack->alignment != alignment
81       || id != NULL_TREE)
82     {
83       align_stack * entry;
84
85       entry = (align_stack *) xmalloc (sizeof (* entry));
86
87       if (entry == NULL)
88         {
89           warning ("Out of memory pushing #pragma pack");
90           return 0;
91         }
92
93       entry->alignment  = alignment;
94       entry->num_pushes = 1;
95       entry->id         = id;
96       entry->prev       = alignment_stack;
97       
98       alignment_stack = entry;
99
100       maximum_field_alignment = alignment * 8;
101     }
102   else
103     alignment_stack->num_pushes ++;
104
105   return 1;
106 }
107
108 /* Undo a push of an alignment onto the stack.  */
109 static int
110 pop_alignment (id)
111      tree id;
112 {
113   align_stack * entry;
114       
115   if (alignment_stack == NULL)
116     {
117       warning ("\
118 #pragma pack (pop) encountered without matching #pragma pack (push, <n>)"
119                );
120       return 0;
121     }
122
123   /* If we got an identifier, strip away everything above the target
124      entry so that the next step will restore the state just below it.  */
125   if (id)
126     {
127       for (entry = alignment_stack; entry; entry = entry->prev)
128         if (entry->id == id)
129           {
130             entry->num_pushes = 1;
131             alignment_stack = entry;
132             break;
133           }
134       if (entry == NULL)
135         warning ("\
136 #pragma pack(pop, %s) encountered without matching #pragma pack(push, %s, <n>)"
137                  , IDENTIFIER_POINTER (id), IDENTIFIER_POINTER (id));
138     }
139
140   if (-- alignment_stack->num_pushes == 0)
141     {
142       entry = alignment_stack->prev;
143
144       if (entry == NULL)
145         maximum_field_alignment = 0;
146       else
147         maximum_field_alignment = entry->alignment * 8;
148
149       free (alignment_stack);
150
151       alignment_stack = entry;
152     }
153
154   return 1;
155 }
156
157 /* Generate 'packed' and 'aligned' attributes for decls whilst a
158    #pragma pack(push... is in effect.  */
159 void
160 insert_pack_attributes (node, attributes, prefix)
161      tree node;
162      tree * attributes;
163      tree * prefix;
164 {
165   tree a;
166   int field_alignment;
167
168   /* If we are not packing, then there is nothing to do.  */
169   if (maximum_field_alignment == 0
170       || alignment_stack == NULL)
171     return;
172
173   /* We are only interested in fields.  */
174   if (TREE_CODE_CLASS (TREE_CODE (node)) != 'd'
175       || TREE_CODE (node) != FIELD_DECL)
176     return;
177   
178   field_alignment = TYPE_ALIGN (TREE_TYPE (node));
179   if (field_alignment <= 0 || field_alignment > maximum_field_alignment)
180     field_alignment = maximum_field_alignment;
181
182   /* Add a 'packed' attribute.  */
183   * attributes = tree_cons (get_identifier ("packed"), NULL, * attributes);
184   
185   /* If the alignment is > 8 then add an alignment attribute as well.  */
186   if (field_alignment > 8)
187     {
188       /* If the aligned attribute is already present then do not override it.  */
189       for (a = * attributes; a; a = TREE_CHAIN (a))
190         {
191           tree name = TREE_PURPOSE (a);
192           if (strcmp (IDENTIFIER_POINTER (name), "aligned") == 0)
193             break;
194         }
195       
196       if (a == NULL)
197         for (a = * prefix; a; a = TREE_CHAIN (a))
198           {
199             tree name = TREE_PURPOSE (a);
200             if (strcmp (IDENTIFIER_POINTER (name), "aligned") == 0)
201               break;
202           }
203   
204       if (a == NULL)
205         {
206           * attributes = tree_cons
207               (get_identifier ("aligned"),
208                tree_cons (NULL,
209                           build_int_2 (field_alignment / 8, 0),
210                           NULL),
211                * attributes);
212         }
213     }
214
215   return;
216 }
217 #endif /* HANDLE_PRAGMA_PACK_PUSH_POP */
218 \f
219 /* Handle one token of a pragma directive.  TOKEN is the current token, and
220    STRING is its printable form.  Some front ends do not support generating
221    tokens, and will only pass in a STRING.  Also some front ends will reuse
222    the buffer containing STRING, so it must be copied to a local buffer if
223    it needs to be preserved.
224
225    If STRING is non-NULL, then the return value will be ignored, and there
226    will be futher calls to handle_pragma_token() in order to handle the rest of
227    the line containing the #pragma directive.  If STRING is NULL, the entire
228    line has now been presented to handle_pragma_token() and the return value
229    should be zero if the pragma flawed in some way, or if the pragma was not
230    recognised, and non-zero if it was successfully handled.  */
231
232 int
233 handle_pragma_token (string, token)
234      const char * string;
235      tree token;
236 {
237   static enum pragma_state state = ps_start;
238   static enum pragma_state type;
239   static char * name;
240   static char * value;
241   static int align;
242   static tree id;
243
244   /* If we have reached the end of the #pragma directive then
245      determine what value we should return.  */
246   
247   if (string == NULL)
248     {
249       int ret_val = 0;
250
251       switch (type)
252         {
253         default:
254           abort ();
255           break;
256
257         case ps_done:
258           /* The pragma was not recognised.  */
259           break;
260           
261 #ifdef HANDLE_PRAGMA_PACK         
262         case ps_pack:
263           if (state == ps_right)
264             {
265               maximum_field_alignment = align * 8;
266               ret_val = 1;
267             }
268           else
269             warning ("malformed `#pragma pack'");
270           break;
271 #endif /* HANDLE_PRAGMA_PACK */
272           
273 #ifdef HANDLE_PRAGMA_PACK_PUSH_POP
274         case ps_push:
275           if (state == ps_right)
276             ret_val = push_alignment (align, id);
277           else
278             warning ("malformed '#pragma pack(push[,id],<n>)'");
279           break;
280           
281         case ps_pop:
282           if (state == ps_right)
283             ret_val = pop_alignment (id);
284           else
285             warning ("malformed '#pragma pack(pop[,id])'");
286           break;
287 #endif /* HANDLE_PRAGMA_PACK_PUSH_POP */
288           
289 #ifdef HANDLE_PRAGMA_WEAK
290         case ps_weak:
291           if (HANDLE_PRAGMA_WEAK)
292             {
293               if (state == ps_name)
294                 ret_val = add_weak (name, NULL);
295               else if (state == ps_value)
296                 ret_val = add_weak (name, value);
297               else
298                 warning ("malformed `#pragma weak'");
299             }
300           else
301             ret_val = 1; /* Ignore the pragma.  */
302           break;
303 #endif /* HANDLE_PRAGMA_WEAK */
304         }
305
306       type = state = ps_start;
307       id = NULL_TREE;
308       
309       return ret_val;
310     }
311
312   /* If we have been given a token, but it is not an identifier,
313      or a small constant, then something has gone wrong.  */
314   if (token)
315     {
316       switch (TREE_CODE (token))
317         {
318         case IDENTIFIER_NODE:
319           break;
320           
321         case INTEGER_CST:
322           if (TREE_INT_CST_HIGH (token) != 0)
323             return 0;
324           break;
325           
326         default:
327           return 0;
328         }
329     }
330       
331   switch (state)
332     {
333     case ps_start:
334       type = state = ps_done;
335 #ifdef HANDLE_PRAGMA_PACK
336       if (strcmp (string, "pack") == 0)
337         type = state = ps_pack;
338 #endif
339 #ifdef HANDLE_PRAGMA_WEAK
340       if (strcmp (string, "weak") == 0)
341         type = state = ps_weak;
342 #endif    
343       break;
344       
345 #ifdef HANDLE_PRAGMA_WEAK
346     case ps_weak:
347       name = permalloc (strlen (string) + 1);
348       if (name == NULL)
349         {
350           warning ("Out of memory parsing #pragma weak");
351           state = ps_bad;
352         }
353       else
354         {
355           strcpy (name, string);
356           state = ps_name;
357         }
358       break;
359       
360     case ps_name:
361       state = (strcmp (string, "=") ? ps_bad : ps_equals);
362       break;
363
364     case ps_equals:
365       value = permalloc (strlen (string) + 1);
366       if (value == NULL)
367         {
368           warning ("Out of memory parsing #pragma weak");
369           state = ps_bad;
370         }
371       else
372         {
373           strcpy (value, string);
374           state = ps_value;
375         }
376       break;
377
378     case ps_value:
379       state = ps_bad;
380       break;
381 #endif /* HANDLE_PRAGMA_WEAK */
382       
383 #ifdef HANDLE_PRAGMA_PACK
384     case ps_pack:
385       state = (strcmp (string, "(") ? ps_bad : ps_left);
386       break;
387
388     case ps_left:
389
390       if (token == NULL_TREE)
391         {
392           /* #pragma pack () resets packing rules to their
393              defaults.  */
394           if (strcmp (string, ")") == 0)
395             {
396               align = 0;
397               state = ps_right;
398             }
399           else
400             state = ps_bad;
401         }
402       else if (TREE_CODE (token) == INTEGER_CST)
403         goto handle_align;
404
405 #ifdef HANDLE_PRAGMA_PACK_PUSH_POP
406       else if (TREE_CODE (token) == IDENTIFIER_NODE)
407         {
408           if (strcmp (string, "push") == 0)
409             type = state = ps_push;
410           else if (strcmp (string, "pop") == 0)
411             type = state = ps_pop;
412           else
413             state = ps_bad;
414         }
415 #endif
416       else
417         state = ps_bad;
418       break;
419
420     handle_align:
421       align = TREE_INT_CST_LOW (token);
422       switch (align)
423         {
424         case 1:
425         case 2:
426         case 4:
427         case 8:
428         case 16:
429           state = ps_align;
430           break;
431
432         default:
433           state = ps_bad;
434           break;
435         }
436       break;
437
438     case ps_align:
439       state = (strcmp (string, ")") ? ps_bad : ps_right);
440       break;
441
442     case ps_right:
443       state = ps_bad;
444       break;
445 #endif /* HANDLE_PRAGMA_PACK */
446
447 #ifdef HANDLE_PRAGMA_PACK_PUSH_POP
448     case ps_push:
449       state = (strcmp (string, ",") ? ps_bad : ps_pushcomma);
450       break;
451
452     case ps_pushid:
453       state = (strcmp (string, ",") ? ps_bad : ps_pushcomma2);
454       break;
455
456     case ps_pushcomma:
457       if (token && TREE_CODE (token) == IDENTIFIER_NODE)
458         {
459           id = token;
460           state = ps_pushid;
461           break;
462         }
463
464       /* else fall through */
465     case ps_pushcomma2:
466       if (token && TREE_CODE (token) == INTEGER_CST)
467         goto handle_align;
468       else
469         state = ps_bad;
470       break;
471
472     case ps_pop:
473       if (strcmp (string, ",") == 0)
474         state = ps_popcomma;
475       else
476         state = (strcmp (string, ")") ? ps_bad : ps_right);
477       break;
478
479     case ps_popcomma:
480       if (token && TREE_CODE (token) == IDENTIFIER_NODE)
481         {
482           id = token;
483           state = ps_align;
484         }
485       else
486         state = ps_bad;
487       break;
488 #endif /* HANDLE_PRAGMA_PACK_PUSH_POP */
489       
490     case ps_bad:
491     case ps_done:
492       break;
493
494     default:
495       abort ();
496     }
497
498   return 1;
499 }
500 #endif /* HANDLE_GENERIC_PRAGMAS */