Coverage Report

Created: 2025-06-24 06:45

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