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