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