Coverage Report

Created: 2026-07-12 09:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
47
{
71
  /* Points to name of symbol.  */
72
47
  char *name;
73
  /* Points to symbol.  */
74
47
  symbolS *symbolP;
75
47
  struct conditional_frame cframe;
76
47
  char c;
77
78
  /* Leading whitespace is part of operand.  */
79
47
  SKIP_WHITESPACE ();
80
47
  name = input_line_pointer;
81
82
47
  if (!is_name_beginner (*name) && *name != '"')
83
2
    {
84
2
      as_bad (_("invalid identifier for \".ifdef\""));
85
2
      obstack_1grow (&cond_obstack, 0);
86
2
      ignore_rest_of_line ();
87
2
      return;
88
2
    }
89
90
45
  c = get_symbol_name (& name);
91
45
  symbolP = symbol_find (name);
92
45
  (void) restore_line_pointer (c);
93
94
45
  initialize_cframe (&cframe);
95
96
45
  if (cframe.dead_tree)
97
0
    cframe.ignoring = 1;
98
45
  else
99
45
    {
100
45
      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
45
      is_defined =
106
45
  symbolP != NULL
107
16
  && (S_IS_DEFINED (symbolP) || symbol_equated_p (symbolP))
108
16
  && S_GET_SEGMENT (symbolP) != reg_section;
109
110
45
      cframe.ignoring = ! (test_defined ^ is_defined);
111
45
    }
112
113
45
  current_cframe = obstack_alloc (&cond_obstack, sizeof cframe);
114
45
  memcpy (current_cframe, &cframe, sizeof cframe);
115
116
45
  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
45
  demand_empty_rest_of_line ();
123
45
}
124
125
void
126
s_if (int arg)
127
2.82k
{
128
2.82k
  expressionS operand;
129
2.82k
  struct conditional_frame cframe;
130
2.82k
  int t;
131
2.82k
  char *stop = NULL;
132
2.82k
  char stopc = 0;
133
134
2.82k
  if (flag_mri)
135
2.74k
    stop = mri_comment_field (&stopc);
136
137
  /* Leading whitespace is part of operand.  */
138
2.82k
  SKIP_WHITESPACE ();
139
140
2.82k
  if (current_cframe != NULL && current_cframe->ignoring)
141
2.79k
    {
142
2.79k
      operand.X_add_number = 0;
143
38.0k
      while (! is_end_of_stmt (*input_line_pointer))
144
35.2k
  ++input_line_pointer;
145
2.79k
    }
146
29
  else
147
29
    {
148
29
      expression_and_evaluate (&operand);
149
29
      if (operand.X_op != O_constant)
150
26
  as_bad (_("non-constant expression in \".if\" statement"));
151
29
    }
152
153
2.82k
  switch ((operatorT) arg)
154
2.82k
    {
155
6
    case O_eq: t = operand.X_add_number == 0; break;
156
2.80k
    case O_ne: t = operand.X_add_number != 0; break;
157
0
    case O_lt: t = operand.X_add_number < 0; break;
158
12
    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
2.82k
    }
165
166
  /* If the above error is signaled, this will dispatch
167
     using an undefined result.  No big deal.  */
168
2.82k
  initialize_cframe (&cframe);
169
2.82k
  cframe.ignoring = cframe.dead_tree || ! t;
170
2.82k
  current_cframe = obstack_alloc (&cond_obstack, sizeof cframe);
171
2.82k
  memcpy (current_cframe, & cframe, sizeof cframe);
172
173
2.82k
  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
2.82k
  if (flag_mri)
180
2.74k
    mri_comment_end (stop, stopc);
181
182
2.82k
  demand_empty_rest_of_line ();
183
2.82k
}
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
2.34k
{
191
2.34k
  struct conditional_frame cframe;
192
193
2.34k
  initialize_cframe (&cframe);
194
195
2.34k
  if (cframe.dead_tree)
196
0
    cframe.ignoring = 1;
197
2.34k
  else
198
2.34k
    {
199
2.34k
      int is_eol;
200
201
2.34k
      SKIP_WHITESPACE ();
202
2.34k
      is_eol = is_end_of_stmt (*input_line_pointer);
203
2.34k
      cframe.ignoring = (test_blank == !is_eol);
204
2.34k
    }
205
206
2.34k
  current_cframe = obstack_alloc (&cond_obstack, sizeof cframe);
207
2.34k
  memcpy (current_cframe, &cframe, sizeof cframe);
208
209
2.34k
  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
2.34k
  ignore_rest_of_line ();
216
2.34k
}
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
896
{
223
896
  char *ret;
224
896
  char *s;
225
226
896
  SKIP_WHITESPACE ();
227
896
  s = ret = input_line_pointer;
228
896
  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
896
  else
245
896
    {
246
6.60k
      while (*input_line_pointer != terminator
247
6.16k
       && ! is_end_of_stmt (*input_line_pointer))
248
5.71k
  ++input_line_pointer;
249
896
      s = input_line_pointer;
250
952
      while (s > ret && is_whitespace (s[-1]))
251
56
  --s;
252
896
    }
253
254
896
  *len = s - ret;
255
896
  return ret;
256
896
}
257
258
/* The MRI IFC and IFNC pseudo-ops.  */
259
260
void
261
s_ifc (int arg)
262
448
{
263
448
  char *stop = NULL;
264
448
  char stopc = 0;
265
448
  char *s1, *s2;
266
448
  int len1, len2;
267
448
  int res;
268
448
  struct conditional_frame cframe;
269
270
448
  if (flag_mri)
271
448
    stop = mri_comment_field (&stopc);
272
273
448
  s1 = get_mri_string (',', &len1);
274
275
448
  if (*input_line_pointer != ',')
276
0
    as_bad (_("bad format for ifc or ifnc"));
277
448
  else
278
448
    ++input_line_pointer;
279
280
448
  s2 = get_mri_string (';', &len2);
281
282
448
  res = len1 == len2 && strncmp (s1, s2, len1) == 0;
283
284
448
  initialize_cframe (&cframe);
285
448
  cframe.ignoring = cframe.dead_tree || ! (res ^ arg);
286
448
  current_cframe = obstack_alloc (&cond_obstack, sizeof cframe);
287
448
  memcpy (current_cframe, &cframe, sizeof cframe);
288
  
289
448
 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
448
  if (flag_mri)
296
448
    mri_comment_end (stop, stopc);
297
298
448
  demand_empty_rest_of_line ();
299
448
}
300
301
void
302
s_elseif (int arg)
303
5.91k
{
304
5.91k
  if (current_cframe == NULL)
305
3
    {
306
3
      as_bad (_("\".elseif\" without matching \".if\""));
307
3
    }
308
5.90k
  else if (current_cframe->else_seen)
309
0
    {
310
0
      as_bad (_("\".elseif\" after \".else\""));
311
0
      as_bad_where (current_cframe->else_file_line.file,
312
0
        current_cframe->else_file_line.line,
313
0
        _("here is the previous \".else\""));
314
0
      as_bad_where (current_cframe->if_file_line.file,
315
0
        current_cframe->if_file_line.line,
316
0
        _("here is the previous \".if\""));
317
0
    }
318
5.90k
  else
319
5.90k
    {
320
5.90k
      current_cframe->else_file_line.file
321
5.90k
        = as_where (&current_cframe->else_file_line.line);
322
323
5.90k
      current_cframe->dead_tree |= !current_cframe->ignoring;
324
5.90k
      current_cframe->ignoring = current_cframe->dead_tree;
325
5.90k
    }
326
327
5.91k
  if (current_cframe == NULL || current_cframe->ignoring)
328
3
    {
329
6
      while (! is_end_of_stmt (*input_line_pointer))
330
3
  ++input_line_pointer;
331
332
3
      if (current_cframe == NULL)
333
3
  return;
334
3
    }
335
5.90k
  else
336
5.90k
    {
337
5.90k
      expressionS operand;
338
5.90k
      int t;
339
340
      /* Leading whitespace is part of operand.  */
341
5.90k
      SKIP_WHITESPACE ();
342
343
5.90k
      expression_and_evaluate (&operand);
344
5.90k
      if (operand.X_op != O_constant)
345
5.90k
  as_bad (_("non-constant expression in \".elseif\" statement"));
346
347
5.90k
      switch (arg)
348
5.90k
  {
349
0
  case O_eq: t = operand.X_add_number == 0; break;
350
5.90k
  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
5.90k
  }
359
360
5.90k
      current_cframe->ignoring = current_cframe->dead_tree || ! t;
361
5.90k
    }
362
363
5.90k
  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
5.90k
  demand_empty_rest_of_line ();
374
5.90k
}
375
376
void
377
s_endif (int arg ATTRIBUTE_UNUSED)
378
2.23k
{
379
2.23k
  struct conditional_frame *hold;
380
381
2.23k
  if (current_cframe == NULL)
382
53
    {
383
53
      as_bad (_("\".endif\" without \".if\""));
384
53
    }
385
2.18k
  else
386
2.18k
    {
387
2.18k
      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
2.18k
      hold = current_cframe;
394
2.18k
      current_cframe = current_cframe->previous_cframe;
395
2.18k
      obstack_free (&cond_obstack, hold);
396
2.18k
    }        /* if one pop too many */
397
398
2.23k
  if (flag_mri)
399
2.23k
    {
400
2.50k
      while (! is_end_of_stmt (*input_line_pointer))
401
267
  ++input_line_pointer;
402
2.23k
    }
403
404
2.23k
  demand_empty_rest_of_line ();
405
2.23k
}
406
407
void
408
s_else (int arg ATTRIBUTE_UNUSED)
409
3.96k
{
410
3.96k
  if (current_cframe == NULL)
411
26
    {
412
26
      as_bad (_("\".else\" without matching \".if\""));
413
26
    }
414
3.94k
  else if (current_cframe->else_seen)
415
1.96k
    {
416
1.96k
      as_bad (_("duplicate \".else\""));
417
1.96k
      as_bad_where (current_cframe->else_file_line.file,
418
1.96k
        current_cframe->else_file_line.line,
419
1.96k
        _("here is the previous \".else\""));
420
1.96k
      as_bad_where (current_cframe->if_file_line.file,
421
1.96k
        current_cframe->if_file_line.line,
422
1.96k
        _("here is the previous \".if\""));
423
1.96k
    }
424
1.97k
  else
425
1.97k
    {
426
1.97k
      current_cframe->else_file_line.file
427
1.97k
        = as_where (&current_cframe->else_file_line.line);
428
429
1.97k
      current_cframe->ignoring =
430
1.97k
  current_cframe->dead_tree | !current_cframe->ignoring;
431
432
1.97k
      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.97k
      current_cframe->else_seen = 1;
443
1.97k
    }
444
445
3.96k
  if (flag_mri)
446
23
    {
447
71
      while (! is_end_of_stmt (*input_line_pointer))
448
48
  ++input_line_pointer;
449
23
    }
450
451
3.96k
  demand_empty_rest_of_line ();
452
3.96k
}
453
454
void
455
s_ifeqs (int arg)
456
114
{
457
114
  char *s1, *s2;
458
114
  int len1, len2;
459
114
  int res;
460
114
  struct conditional_frame cframe;
461
462
114
  s1 = demand_copy_C_string (&len1);
463
114
  if (s1 == NULL)
464
28
    {
465
28
      ignore_rest_of_line ();
466
28
      return;
467
28
    }
468
469
86
  SKIP_WHITESPACE ();
470
86
  if (*input_line_pointer != ',')
471
10
    {
472
10
      as_bad (_(".ifeqs syntax error"));
473
10
      ignore_rest_of_line ();
474
10
      return;
475
10
    }
476
477
76
  ++input_line_pointer;
478
479
76
  s2 = demand_copy_C_string (&len2);
480
76
  if (s2 == NULL)
481
10
    {
482
10
      ignore_rest_of_line ();
483
10
      return;
484
10
    }
485
486
66
  res = len1 == len2 && strncmp (s1, s2, len1) == 0;
487
488
66
  initialize_cframe (&cframe);
489
66
  cframe.ignoring = cframe.dead_tree || ! (res ^ arg);
490
66
  current_cframe = obstack_alloc (&cond_obstack, sizeof cframe);
491
66
  memcpy (current_cframe, &cframe, sizeof cframe);
492
493
66
  if (LISTING_SKIP_COND ()
494
0
      && cframe.ignoring
495
0
      && (cframe.previous_cframe == NULL
496
0
    || ! cframe.previous_cframe->ignoring))
497
0
    listing_list (2);
498
499
66
  demand_empty_rest_of_line ();
500
66
}
501
502
int
503
ignore_input (void)
504
681k
{
505
681k
  char *s;
506
507
681k
  s = input_line_pointer;
508
509
681k
  if (NO_PSEUDO_DOT || flag_m68k_mri)
510
0
    {
511
0
      if (s[-1] != '.')
512
0
  --s;
513
0
    }
514
681k
  else
515
681k
    {
516
681k
      if (s[-1] != '.')
517
281k
  return current_cframe != NULL && current_cframe->ignoring;
518
681k
    }
519
520
  /* We cannot ignore certain pseudo ops.  */
521
399k
  switch (s[0])
522
399k
    {
523
26.1k
    case 'i': case  'I':
524
26.1k
      if (s[1] == 'f' || s[1] == 'F')
525
5.84k
  return 0;
526
20.2k
      break;
527
93.6k
    case 'e': case 'E':
528
93.6k
      if (!strncasecmp (s, "else", 4)
529
81.7k
    || !strncasecmp (s, "endif", 5)
530
79.0k
    || !strncasecmp (s, "endc", 4))
531
14.6k
  return 0;
532
78.9k
      break;
533
91.7k
    case 'l': case 'L':
534
91.7k
      if (!strncasecmp (s, "linefile", 8))
535
81.8k
  return 0;
536
9.84k
      break;
537
399k
    }
538
539
297k
  return current_cframe != NULL && current_cframe->ignoring;
540
399k
}
541
542
static void
543
initialize_cframe (struct conditional_frame *cframe)
544
5.73k
{
545
5.73k
  memset (cframe, 0, sizeof (*cframe));
546
5.73k
  cframe->if_file_line.file = as_where (&cframe->if_file_line.line);
547
5.73k
  cframe->previous_cframe = current_cframe;
548
5.73k
  cframe->dead_tree = current_cframe != NULL && current_cframe->ignoring;
549
5.73k
  cframe->macro_nest = macro_nest;
550
5.73k
}
551
552
/* Give an error if a conditional is unterminated inside a macro or
553
   the assembly as a whole.  If NEST is non negative, we are being
554
   called because of the end of a macro expansion.  If NEST is
555
   negative, we are being called at the of the input files.  */
556
557
void
558
cond_finish_check (int nest)
559
656
{
560
656
  if (current_cframe != NULL && current_cframe->macro_nest >= nest)
561
38
    {
562
38
      if (nest >= 0)
563
4
  as_bad (_("end of macro inside conditional"));
564
34
      else
565
34
  as_bad (_("end of file inside conditional"));
566
567
38
      as_bad_where (current_cframe->if_file_line.file,
568
38
        current_cframe->if_file_line.line,
569
38
        _("here is the start of the unterminated conditional"));
570
38
      if (current_cframe->else_seen)
571
0
  as_bad_where (current_cframe->else_file_line.file,
572
0
          current_cframe->else_file_line.line,
573
0
          _("here is the \"else\" of the unterminated conditional"));
574
38
      cond_exit_macro (nest);
575
38
    }
576
656
}
577
578
/* This function is called when we exit out of a macro.  We assume
579
   that any conditionals which began within the macro are correctly
580
   nested, and just pop them off the stack.  */
581
582
void
583
cond_exit_macro (int nest)
584
51
{
585
3.59k
  while (current_cframe != NULL && current_cframe->macro_nest >= nest)
586
3.54k
    {
587
3.54k
      struct conditional_frame *hold;
588
589
3.54k
      hold = current_cframe;
590
3.54k
      current_cframe = current_cframe->previous_cframe;
591
3.54k
      obstack_free (&cond_obstack, hold);
592
3.54k
    }
593
51
}