/src/binutils-gdb/gas/cond.c
Line | Count | Source |
1 | | /* cond.c - conditional assembly pseudo-ops, and .include |
2 | | Copyright (C) 1990-2026 Free Software Foundation, Inc. |
3 | | |
4 | | This file is part of GAS, the GNU Assembler. |
5 | | |
6 | | GAS 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 3, or (at your option) |
9 | | any later version. |
10 | | |
11 | | GAS 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 GAS; see the file COPYING. If not, write to the Free |
18 | | Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA |
19 | | 02110-1301, USA. */ |
20 | | |
21 | | #include "as.h" |
22 | | #include "sb.h" |
23 | | #include "macro.h" |
24 | | |
25 | | #include "obstack.h" |
26 | | |
27 | | /* This is allocated to grow and shrink as .ifdef/.endif pairs are |
28 | | scanned. */ |
29 | | struct obstack cond_obstack; |
30 | | |
31 | | struct file_line |
32 | | { |
33 | | const char *file; |
34 | | unsigned int line; |
35 | | }; |
36 | | |
37 | | /* We push one of these structures for each .if, and pop it at the |
38 | | .endif. */ |
39 | | |
40 | | struct conditional_frame |
41 | | { |
42 | | /* The source file & line number of the "if". */ |
43 | | struct file_line if_file_line; |
44 | | /* The source file & line of the "else". */ |
45 | | struct file_line else_file_line; |
46 | | /* The previous conditional. */ |
47 | | struct conditional_frame *previous_cframe; |
48 | | /* Have we seen an else yet? */ |
49 | | int else_seen; |
50 | | /* Whether we are currently ignoring input. */ |
51 | | int ignoring; |
52 | | /* Whether a conditional at a higher level is ignoring input. |
53 | | Set also when a branch of an "if .. elseif .." tree has matched |
54 | | to prevent further matches. */ |
55 | | int dead_tree; |
56 | | /* Macro nesting level at which this conditional was created. */ |
57 | | int macro_nest; |
58 | | }; |
59 | | |
60 | | static void initialize_cframe (struct conditional_frame *cframe); |
61 | | static char *get_mri_string (int, int *); |
62 | | |
63 | | static struct conditional_frame *current_cframe = NULL; |
64 | | |
65 | | /* Performs the .ifdef (test_defined == 1) and |
66 | | the .ifndef (test_defined == 0) pseudo op. */ |
67 | | |
68 | | void |
69 | | s_ifdef (int test_defined) |
70 | 3 | { |
71 | | /* Points to name of symbol. */ |
72 | 3 | char *name; |
73 | | /* Points to symbol. */ |
74 | 3 | symbolS *symbolP; |
75 | 3 | struct conditional_frame cframe; |
76 | 3 | char c; |
77 | | |
78 | | /* Leading whitespace is part of operand. */ |
79 | 3 | SKIP_WHITESPACE (); |
80 | 3 | name = input_line_pointer; |
81 | | |
82 | 3 | if (!is_name_beginner (*name) && *name != '"') |
83 | 0 | { |
84 | 0 | as_bad (_("invalid identifier for \".ifdef\"")); |
85 | 0 | obstack_1grow (&cond_obstack, 0); |
86 | 0 | ignore_rest_of_line (); |
87 | 0 | return; |
88 | 0 | } |
89 | | |
90 | 3 | c = get_symbol_name (& name); |
91 | 3 | symbolP = symbol_find (name); |
92 | 3 | (void) restore_line_pointer (c); |
93 | | |
94 | 3 | initialize_cframe (&cframe); |
95 | | |
96 | 3 | if (cframe.dead_tree) |
97 | 0 | cframe.ignoring = 1; |
98 | 3 | else |
99 | 3 | { |
100 | 3 | int is_defined; |
101 | | |
102 | | /* Use the same definition of 'defined' as .equiv so that a symbol |
103 | | which has been referenced but not yet given a value/address is |
104 | | considered to be undefined. */ |
105 | 3 | is_defined = |
106 | 3 | symbolP != NULL |
107 | 0 | && (S_IS_DEFINED (symbolP) || symbol_equated_p (symbolP)) |
108 | 0 | && S_GET_SEGMENT (symbolP) != reg_section; |
109 | | |
110 | 3 | cframe.ignoring = ! (test_defined ^ is_defined); |
111 | 3 | } |
112 | | |
113 | 3 | current_cframe = obstack_alloc (&cond_obstack, sizeof cframe); |
114 | 3 | memcpy (current_cframe, &cframe, sizeof cframe); |
115 | | |
116 | 3 | if (LISTING_SKIP_COND () |
117 | 0 | && cframe.ignoring |
118 | 0 | && (cframe.previous_cframe == NULL |
119 | 0 | || ! cframe.previous_cframe->ignoring)) |
120 | 0 | listing_list (2); |
121 | | |
122 | 3 | demand_empty_rest_of_line (); |
123 | 3 | } |
124 | | |
125 | | void |
126 | | s_if (int arg) |
127 | 295 | { |
128 | 295 | expressionS operand; |
129 | 295 | struct conditional_frame cframe; |
130 | 295 | int t; |
131 | 295 | char *stop = NULL; |
132 | 295 | char stopc = 0; |
133 | | |
134 | 295 | if (flag_mri) |
135 | 283 | stop = mri_comment_field (&stopc); |
136 | | |
137 | | /* Leading whitespace is part of operand. */ |
138 | 295 | SKIP_WHITESPACE (); |
139 | | |
140 | 295 | if (current_cframe != NULL && current_cframe->ignoring) |
141 | 10 | { |
142 | 10 | operand.X_add_number = 0; |
143 | 109 | while (! is_end_of_stmt (*input_line_pointer)) |
144 | 99 | ++input_line_pointer; |
145 | 10 | } |
146 | 285 | else |
147 | 285 | { |
148 | 285 | expression_and_evaluate (&operand); |
149 | 285 | if (operand.X_op != O_constant) |
150 | 0 | as_bad (_("non-constant expression in \".if\" statement")); |
151 | 285 | } |
152 | | |
153 | 295 | switch ((operatorT) arg) |
154 | 295 | { |
155 | 0 | case O_eq: t = operand.X_add_number == 0; break; |
156 | 2 | case O_ne: t = operand.X_add_number != 0; break; |
157 | 11 | case O_lt: t = operand.X_add_number < 0; break; |
158 | 282 | case O_le: t = operand.X_add_number <= 0; break; |
159 | 0 | case O_ge: t = operand.X_add_number >= 0; break; |
160 | 0 | case O_gt: t = operand.X_add_number > 0; break; |
161 | 0 | default: |
162 | 0 | abort (); |
163 | 0 | return; |
164 | 295 | } |
165 | | |
166 | | /* If the above error is signaled, this will dispatch |
167 | | using an undefined result. No big deal. */ |
168 | 295 | initialize_cframe (&cframe); |
169 | 295 | cframe.ignoring = cframe.dead_tree || ! t; |
170 | 295 | current_cframe = obstack_alloc (&cond_obstack, sizeof cframe); |
171 | 295 | memcpy (current_cframe, & cframe, sizeof cframe); |
172 | | |
173 | 295 | if (LISTING_SKIP_COND () |
174 | 0 | && cframe.ignoring |
175 | 0 | && (cframe.previous_cframe == NULL |
176 | 0 | || ! cframe.previous_cframe->ignoring)) |
177 | 0 | listing_list (2); |
178 | | |
179 | 295 | if (flag_mri) |
180 | 283 | mri_comment_end (stop, stopc); |
181 | | |
182 | 295 | demand_empty_rest_of_line (); |
183 | 295 | } |
184 | | |
185 | | /* Performs the .ifb (test_blank == 1) and |
186 | | the .ifnb (test_blank == 0) pseudo op. */ |
187 | | |
188 | | void |
189 | | s_ifb (int test_blank) |
190 | 0 | { |
191 | 0 | struct conditional_frame cframe; |
192 | |
|
193 | 0 | initialize_cframe (&cframe); |
194 | |
|
195 | 0 | if (cframe.dead_tree) |
196 | 0 | cframe.ignoring = 1; |
197 | 0 | else |
198 | 0 | { |
199 | 0 | int is_eol; |
200 | |
|
201 | 0 | SKIP_WHITESPACE (); |
202 | 0 | is_eol = is_end_of_stmt (*input_line_pointer); |
203 | 0 | cframe.ignoring = (test_blank == !is_eol); |
204 | 0 | } |
205 | |
|
206 | 0 | current_cframe = obstack_alloc (&cond_obstack, sizeof cframe); |
207 | 0 | memcpy (current_cframe, &cframe, sizeof cframe); |
208 | |
|
209 | 0 | if (LISTING_SKIP_COND () |
210 | 0 | && cframe.ignoring |
211 | 0 | && (cframe.previous_cframe == NULL |
212 | 0 | || ! cframe.previous_cframe->ignoring)) |
213 | 0 | listing_list (2); |
214 | |
|
215 | 0 | ignore_rest_of_line (); |
216 | 0 | } |
217 | | |
218 | | /* Get a string for the MRI IFC or IFNC pseudo-ops. */ |
219 | | |
220 | | static char * |
221 | | get_mri_string (int terminator, int *len) |
222 | 20 | { |
223 | 20 | char *ret; |
224 | 20 | char *s; |
225 | | |
226 | 20 | SKIP_WHITESPACE (); |
227 | 20 | s = ret = input_line_pointer; |
228 | 20 | if (*input_line_pointer == '\'') |
229 | 0 | { |
230 | 0 | ++s; |
231 | 0 | ++input_line_pointer; |
232 | 0 | while (! is_end_of_stmt (*input_line_pointer)) |
233 | 0 | { |
234 | 0 | *s++ = *input_line_pointer++; |
235 | 0 | if (s[-1] == '\'') |
236 | 0 | { |
237 | 0 | if (*input_line_pointer != '\'') |
238 | 0 | break; |
239 | 0 | ++input_line_pointer; |
240 | 0 | } |
241 | 0 | } |
242 | 0 | SKIP_WHITESPACE (); |
243 | 0 | } |
244 | 20 | else |
245 | 20 | { |
246 | 74 | while (*input_line_pointer != terminator |
247 | 64 | && ! is_end_of_stmt (*input_line_pointer)) |
248 | 54 | ++input_line_pointer; |
249 | 20 | s = input_line_pointer; |
250 | 21 | while (s > ret && is_whitespace (s[-1])) |
251 | 1 | --s; |
252 | 20 | } |
253 | | |
254 | 20 | *len = s - ret; |
255 | 20 | return ret; |
256 | 20 | } |
257 | | |
258 | | /* The MRI IFC and IFNC pseudo-ops. */ |
259 | | |
260 | | void |
261 | | s_ifc (int arg) |
262 | 10 | { |
263 | 10 | char *stop = NULL; |
264 | 10 | char stopc = 0; |
265 | 10 | char *s1, *s2; |
266 | 10 | int len1, len2; |
267 | 10 | int res; |
268 | 10 | struct conditional_frame cframe; |
269 | | |
270 | 10 | if (flag_mri) |
271 | 10 | stop = mri_comment_field (&stopc); |
272 | | |
273 | 10 | s1 = get_mri_string (',', &len1); |
274 | | |
275 | 10 | if (*input_line_pointer != ',') |
276 | 0 | as_bad (_("bad format for ifc or ifnc")); |
277 | 10 | else |
278 | 10 | ++input_line_pointer; |
279 | | |
280 | 10 | s2 = get_mri_string (';', &len2); |
281 | | |
282 | 10 | res = len1 == len2 && strncmp (s1, s2, len1) == 0; |
283 | | |
284 | 10 | initialize_cframe (&cframe); |
285 | 10 | cframe.ignoring = cframe.dead_tree || ! (res ^ arg); |
286 | 10 | current_cframe = obstack_alloc (&cond_obstack, sizeof cframe); |
287 | 10 | memcpy (current_cframe, &cframe, sizeof cframe); |
288 | | |
289 | 10 | if (LISTING_SKIP_COND () |
290 | 0 | && cframe.ignoring |
291 | 0 | && (cframe.previous_cframe == NULL |
292 | 0 | || ! cframe.previous_cframe->ignoring)) |
293 | 0 | listing_list (2); |
294 | | |
295 | 10 | if (flag_mri) |
296 | 10 | mri_comment_end (stop, stopc); |
297 | | |
298 | 10 | demand_empty_rest_of_line (); |
299 | 10 | } |
300 | | |
301 | | void |
302 | | s_elseif (int arg) |
303 | 11 | { |
304 | 11 | if (current_cframe == NULL) |
305 | 8 | { |
306 | 8 | as_bad (_("\".elseif\" without matching \".if\"")); |
307 | 8 | } |
308 | 3 | else if (current_cframe->else_seen) |
309 | 2 | { |
310 | 2 | as_bad (_("\".elseif\" after \".else\"")); |
311 | 2 | as_bad_where (current_cframe->else_file_line.file, |
312 | 2 | current_cframe->else_file_line.line, |
313 | 2 | _("here is the previous \".else\"")); |
314 | 2 | as_bad_where (current_cframe->if_file_line.file, |
315 | 2 | current_cframe->if_file_line.line, |
316 | 2 | _("here is the previous \".if\"")); |
317 | 2 | } |
318 | 1 | else |
319 | 1 | { |
320 | 1 | current_cframe->else_file_line.file |
321 | 1 | = as_where (¤t_cframe->else_file_line.line); |
322 | | |
323 | 1 | current_cframe->dead_tree |= !current_cframe->ignoring; |
324 | 1 | current_cframe->ignoring = current_cframe->dead_tree; |
325 | 1 | } |
326 | | |
327 | 11 | if (current_cframe == NULL || current_cframe->ignoring) |
328 | 9 | { |
329 | 9 | while (! is_end_of_stmt (*input_line_pointer)) |
330 | 0 | ++input_line_pointer; |
331 | | |
332 | 9 | if (current_cframe == NULL) |
333 | 8 | return; |
334 | 9 | } |
335 | 2 | else |
336 | 2 | { |
337 | 2 | expressionS operand; |
338 | 2 | int t; |
339 | | |
340 | | /* Leading whitespace is part of operand. */ |
341 | 2 | SKIP_WHITESPACE (); |
342 | | |
343 | 2 | expression_and_evaluate (&operand); |
344 | 2 | if (operand.X_op != O_constant) |
345 | 2 | as_bad (_("non-constant expression in \".elseif\" statement")); |
346 | | |
347 | 2 | switch (arg) |
348 | 2 | { |
349 | 0 | case O_eq: t = operand.X_add_number == 0; break; |
350 | 2 | case O_ne: t = operand.X_add_number != 0; break; |
351 | 0 | case O_lt: t = operand.X_add_number < 0; break; |
352 | 0 | case O_le: t = operand.X_add_number <= 0; break; |
353 | 0 | case O_ge: t = operand.X_add_number >= 0; break; |
354 | 0 | case O_gt: t = operand.X_add_number > 0; break; |
355 | 0 | default: |
356 | 0 | abort (); |
357 | 0 | return; |
358 | 2 | } |
359 | | |
360 | 2 | current_cframe->ignoring = current_cframe->dead_tree || ! t; |
361 | 2 | } |
362 | | |
363 | 3 | if (LISTING_SKIP_COND () |
364 | 0 | && (current_cframe->previous_cframe == NULL |
365 | 0 | || ! current_cframe->previous_cframe->ignoring)) |
366 | 0 | { |
367 | 0 | if (! current_cframe->ignoring) |
368 | 0 | listing_list (1); |
369 | 0 | else |
370 | 0 | listing_list (2); |
371 | 0 | } |
372 | | |
373 | 3 | demand_empty_rest_of_line (); |
374 | 3 | } |
375 | | |
376 | | void |
377 | | s_endif (int arg ATTRIBUTE_UNUSED) |
378 | 8 | { |
379 | 8 | struct conditional_frame *hold; |
380 | | |
381 | 8 | if (current_cframe == NULL) |
382 | 2 | { |
383 | 2 | as_bad (_("\".endif\" without \".if\"")); |
384 | 2 | } |
385 | 6 | else |
386 | 6 | { |
387 | 6 | if (LISTING_SKIP_COND () |
388 | 0 | && current_cframe->ignoring |
389 | 0 | && (current_cframe->previous_cframe == NULL |
390 | 0 | || ! current_cframe->previous_cframe->ignoring)) |
391 | 0 | listing_list (1); |
392 | | |
393 | 6 | hold = current_cframe; |
394 | 6 | current_cframe = current_cframe->previous_cframe; |
395 | 6 | obstack_free (&cond_obstack, hold); |
396 | 6 | } /* if one pop too many */ |
397 | | |
398 | 8 | if (flag_mri) |
399 | 8 | { |
400 | 8 | while (! is_end_of_stmt (*input_line_pointer)) |
401 | 0 | ++input_line_pointer; |
402 | 8 | } |
403 | | |
404 | 8 | demand_empty_rest_of_line (); |
405 | 8 | } |
406 | | |
407 | | void |
408 | | s_else (int arg ATTRIBUTE_UNUSED) |
409 | 5 | { |
410 | 5 | if (current_cframe == NULL) |
411 | 4 | { |
412 | 4 | as_bad (_("\".else\" without matching \".if\"")); |
413 | 4 | } |
414 | 1 | else if (current_cframe->else_seen) |
415 | 0 | { |
416 | 0 | as_bad (_("duplicate \".else\"")); |
417 | 0 | as_bad_where (current_cframe->else_file_line.file, |
418 | 0 | current_cframe->else_file_line.line, |
419 | 0 | _("here is the previous \".else\"")); |
420 | 0 | as_bad_where (current_cframe->if_file_line.file, |
421 | 0 | current_cframe->if_file_line.line, |
422 | 0 | _("here is the previous \".if\"")); |
423 | 0 | } |
424 | 1 | else |
425 | 1 | { |
426 | 1 | current_cframe->else_file_line.file |
427 | 1 | = as_where (¤t_cframe->else_file_line.line); |
428 | | |
429 | 1 | current_cframe->ignoring = |
430 | 1 | current_cframe->dead_tree | !current_cframe->ignoring; |
431 | | |
432 | 1 | if (LISTING_SKIP_COND () |
433 | 0 | && (current_cframe->previous_cframe == NULL |
434 | 0 | || ! current_cframe->previous_cframe->ignoring)) |
435 | 0 | { |
436 | 0 | if (! current_cframe->ignoring) |
437 | 0 | listing_list (1); |
438 | 0 | else |
439 | 0 | listing_list (2); |
440 | 0 | } |
441 | | |
442 | 1 | current_cframe->else_seen = 1; |
443 | 1 | } |
444 | | |
445 | 5 | if (flag_mri) |
446 | 0 | { |
447 | 0 | while (! is_end_of_stmt (*input_line_pointer)) |
448 | 0 | ++input_line_pointer; |
449 | 0 | } |
450 | | |
451 | 5 | demand_empty_rest_of_line (); |
452 | 5 | } |
453 | | |
454 | | void |
455 | | s_ifeqs (int arg) |
456 | 8 | { |
457 | 8 | char *s1, *s2; |
458 | 8 | int len1, len2; |
459 | 8 | int res; |
460 | 8 | struct conditional_frame cframe; |
461 | | |
462 | 8 | s1 = demand_copy_C_string (&len1); |
463 | | |
464 | 8 | SKIP_WHITESPACE (); |
465 | 8 | if (*input_line_pointer != ',') |
466 | 8 | { |
467 | 8 | as_bad (_(".ifeqs syntax error")); |
468 | 8 | ignore_rest_of_line (); |
469 | 8 | return; |
470 | 8 | } |
471 | | |
472 | 0 | ++input_line_pointer; |
473 | |
|
474 | 0 | s2 = demand_copy_C_string (&len2); |
475 | |
|
476 | 0 | res = len1 == len2 && strncmp (s1, s2, len1) == 0; |
477 | |
|
478 | 0 | initialize_cframe (&cframe); |
479 | 0 | cframe.ignoring = cframe.dead_tree || ! (res ^ arg); |
480 | 0 | current_cframe = obstack_alloc (&cond_obstack, sizeof cframe); |
481 | 0 | memcpy (current_cframe, &cframe, sizeof cframe); |
482 | |
|
483 | 0 | if (LISTING_SKIP_COND () |
484 | 0 | && cframe.ignoring |
485 | 0 | && (cframe.previous_cframe == NULL |
486 | 0 | || ! cframe.previous_cframe->ignoring)) |
487 | 0 | listing_list (2); |
488 | |
|
489 | 0 | demand_empty_rest_of_line (); |
490 | 0 | } |
491 | | |
492 | | int |
493 | | ignore_input (void) |
494 | 96.0k | { |
495 | 96.0k | char *s; |
496 | | |
497 | 96.0k | s = input_line_pointer; |
498 | | |
499 | 96.0k | if (NO_PSEUDO_DOT || flag_m68k_mri) |
500 | 0 | { |
501 | 0 | if (s[-1] != '.') |
502 | 0 | --s; |
503 | 0 | } |
504 | 96.0k | else |
505 | 96.0k | { |
506 | 96.0k | if (s[-1] != '.') |
507 | 43.7k | return current_cframe != NULL && current_cframe->ignoring; |
508 | 96.0k | } |
509 | | |
510 | | /* We cannot ignore certain pseudo ops. */ |
511 | 52.3k | switch (s[0]) |
512 | 52.3k | { |
513 | 2.54k | case 'i': case 'I': |
514 | 2.54k | if (s[1] == 'f' || s[1] == 'F') |
515 | 343 | return 0; |
516 | 2.20k | break; |
517 | 2.20k | case 'e': case 'E': |
518 | 739 | if (!strncasecmp (s, "else", 4) |
519 | 718 | || !strncasecmp (s, "endif", 5) |
520 | 704 | || !strncasecmp (s, "endc", 4)) |
521 | 49 | return 0; |
522 | 690 | break; |
523 | 17.2k | case 'l': case 'L': |
524 | 17.2k | if (!strncasecmp (s, "linefile", 8)) |
525 | 11.4k | return 0; |
526 | 5.78k | break; |
527 | 52.3k | } |
528 | | |
529 | 40.4k | return current_cframe != NULL && current_cframe->ignoring; |
530 | 52.3k | } |
531 | | |
532 | | static void |
533 | | initialize_cframe (struct conditional_frame *cframe) |
534 | 308 | { |
535 | 308 | memset (cframe, 0, sizeof (*cframe)); |
536 | 308 | cframe->if_file_line.file = as_where (&cframe->if_file_line.line); |
537 | 308 | cframe->previous_cframe = current_cframe; |
538 | 308 | cframe->dead_tree = current_cframe != NULL && current_cframe->ignoring; |
539 | 308 | cframe->macro_nest = macro_nest; |
540 | 308 | } |
541 | | |
542 | | /* Give an error if a conditional is unterminated inside a macro or |
543 | | the assembly as a whole. If NEST is non negative, we are being |
544 | | called because of the end of a macro expansion. If NEST is |
545 | | negative, we are being called at the of the input files. */ |
546 | | |
547 | | void |
548 | | cond_finish_check (int nest) |
549 | 209 | { |
550 | 209 | if (current_cframe != NULL && current_cframe->macro_nest >= nest) |
551 | 9 | { |
552 | 9 | if (nest >= 0) |
553 | 0 | as_bad (_("end of macro inside conditional")); |
554 | 9 | else |
555 | 9 | as_bad (_("end of file inside conditional")); |
556 | | |
557 | 9 | as_bad_where (current_cframe->if_file_line.file, |
558 | 9 | current_cframe->if_file_line.line, |
559 | 9 | _("here is the start of the unterminated conditional")); |
560 | 9 | if (current_cframe->else_seen) |
561 | 0 | as_bad_where (current_cframe->else_file_line.file, |
562 | 0 | current_cframe->else_file_line.line, |
563 | 0 | _("here is the \"else\" of the unterminated conditional")); |
564 | 9 | cond_exit_macro (nest); |
565 | 9 | } |
566 | 209 | } |
567 | | |
568 | | /* This function is called when we exit out of a macro. We assume |
569 | | that any conditionals which began within the macro are correctly |
570 | | nested, and just pop them off the stack. */ |
571 | | |
572 | | void |
573 | | cond_exit_macro (int nest) |
574 | 9 | { |
575 | 311 | while (current_cframe != NULL && current_cframe->macro_nest >= nest) |
576 | 302 | { |
577 | 302 | struct conditional_frame *hold; |
578 | | |
579 | 302 | hold = current_cframe; |
580 | 302 | current_cframe = current_cframe->previous_cframe; |
581 | 302 | obstack_free (&cond_obstack, hold); |
582 | 302 | } |
583 | 9 | } |