public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] Add 'switch' statement to match.pd language
@ 2015-07-14 13:02 Richard Biener
  2015-07-15 14:28 ` Michael Matz
  0 siblings, 1 reply; 8+ messages in thread
From: Richard Biener @ 2015-07-14 13:02 UTC (permalink / raw)
  To: gcc-patches


The following as promised adds a 'switch' statement.  This way

 (if (A)
  B
  (if (B)
   C
   (if (C)
    D
    E)))

can now be written as

 (switch
  (if (A)
   B)
  (if (B)
   C)
  (if (C)
   D)
  E)

the ifs immediately nested in the switch cannot have else clauses and
I reject switches that can be expressed as single if.

Bootstrapped on x86_64-unknown-linux-gnu, testing in progress.

I know Micha detests the extra 'if' as much as the extra braces thus
would have prefered

 (switch
  (A) B
  (B) C
  (C) D
  E)

but that's hard to unambiguously parse and adding a keyword
without braces like

 (switch
  if (A) B
  if (B) C
  if (C) D
  E)

looked too inconsistent with all the rest of the language.

Thus - barring any comments I plan to commit this tomorrow
and then (being done with IL changes) will update the internals
documentation to reflect recent changes.

Note that internally the switch statement is represented as
a if () { } else { if () {} else { if () .... chain.

Thanks,
Richard.

2015-07-14  Richard Biener  <rguenther@suse.de>

	* genmatch.c (parser::peek, parser::peek_ident): Add argument
	to tell how many tokens to peek ahead (default 1).
	(parser::eat_token, parser::eat_ident): Return token consumed.
	(parser::parse_result): Parse new switch statement.
	* match.pd: Use case statements where appropriate.

Index: gcc/genmatch.c
===================================================================
*** gcc/genmatch.c	(revision 225765)
--- gcc/genmatch.c	(working copy)
*************** public:
*** 3014,3026 ****
  
  private:
    const cpp_token *next ();
!   const cpp_token *peek ();
!   const cpp_token *peek_ident (const char * = NULL);
    const cpp_token *expect (enum cpp_ttype);
!   void eat_token (enum cpp_ttype);
    const char *get_string ();
    const char *get_ident ();
!   void eat_ident (const char *);
    const char *get_number ();
  
    id_base *parse_operation ();
--- 3014,3026 ----
  
  private:
    const cpp_token *next ();
!   const cpp_token *peek (unsigned = 1);
!   const cpp_token *peek_ident (const char * = NULL, unsigned = 1);
    const cpp_token *expect (enum cpp_ttype);
!   const cpp_token *eat_token (enum cpp_ttype);
    const char *get_string ();
    const char *get_ident ();
!   const cpp_token *eat_ident (const char *);
    const char *get_number ();
  
    id_base *parse_operation ();
*************** parser::next ()
*** 3078,3084 ****
  /* Peek at the next non-whitespace token from R.  */
  
  const cpp_token *
! parser::peek ()
  {
    const cpp_token *token;
    unsigned i = 0;
--- 3078,3084 ----
  /* Peek at the next non-whitespace token from R.  */
  
  const cpp_token *
! parser::peek (unsigned num)
  {
    const cpp_token *token;
    unsigned i = 0;
*************** parser::peek ()
*** 3086,3093 ****
      {
        token = cpp_peek_token (r, i++);
      }
!   while (token->type == CPP_PADDING
! 	 && token->type != CPP_EOF);
    /* If we peek at EOF this is a fatal error as it leaves the
       cpp_reader in unusable state.  Assume we really wanted a
       token and thus this EOF is unexpected.  */
--- 3086,3094 ----
      {
        token = cpp_peek_token (r, i++);
      }
!   while ((token->type == CPP_PADDING
! 	  && token->type != CPP_EOF)
! 	 || (--num > 0));
    /* If we peek at EOF this is a fatal error as it leaves the
       cpp_reader in unusable state.  Assume we really wanted a
       token and thus this EOF is unexpected.  */
*************** parser::peek ()
*** 3100,3108 ****
     token is not an identifier or equal to ID if supplied).  */
  
  const cpp_token *
! parser::peek_ident (const char *id)
  {
!   const cpp_token *token = peek ();
    if (token->type != CPP_NAME)
      return 0;
  
--- 3101,3109 ----
     token is not an identifier or equal to ID if supplied).  */
  
  const cpp_token *
! parser::peek_ident (const char *id, unsigned num)
  {
!   const cpp_token *token = peek (num);
    if (token->type != CPP_NAME)
      return 0;
  
*************** parser::expect (enum cpp_ttype tk)
*** 3131,3140 ****
  
  /* Consume the next token from R and assert it is of type TK.  */
  
! void
  parser::eat_token (enum cpp_ttype tk)
  {
!   expect (tk);
  }
  
  /* Read the next token from R and assert it is of type CPP_STRING and
--- 3132,3141 ----
  
  /* Consume the next token from R and assert it is of type TK.  */
  
! const cpp_token *
  parser::eat_token (enum cpp_ttype tk)
  {
!   return expect (tk);
  }
  
  /* Read the next token from R and assert it is of type CPP_STRING and
*************** parser::get_ident ()
*** 3159,3171 ****
  
  /* Eat an identifier token with value S from R.  */
  
! void
  parser::eat_ident (const char *s)
  {
    const cpp_token *token = peek ();
    const char *t = get_ident ();
    if (strcmp (s, t) != 0)
      fatal_at (token, "expected '%s' got '%s'\n", s, t);
  }
  
  /* Read the next token from R and assert it is of type CPP_NUMBER and
--- 3160,3173 ----
  
  /* Eat an identifier token with value S from R.  */
  
! const cpp_token *
  parser::eat_ident (const char *s)
  {
    const cpp_token *token = peek ();
    const char *t = get_ident ();
    if (strcmp (s, t) != 0)
      fatal_at (token, "expected '%s' got '%s'\n", s, t);
+   return token;
  }
  
  /* Read the next token from R and assert it is of type CPP_NUMBER and
*************** parser::parse_result (operand *result, p
*** 3557,3562 ****
--- 3559,3616 ----
        eat_token (CPP_CLOSE_PAREN);
        return withe;
      }
+   else if (peek_ident ("switch"))
+     {
+       token = eat_ident ("switch");
+       eat_token (CPP_OPEN_PAREN);
+       eat_ident ("if");
+       if_expr *ife = new if_expr ();
+       operand *res = ife;
+       ife->cond = parse_c_expr (CPP_OPEN_PAREN);
+       if (peek ()->type == CPP_OPEN_PAREN)
+ 	ife->trueexpr = parse_result (result, matcher);
+       else
+ 	ife->trueexpr = parse_op ();
+       eat_token (CPP_CLOSE_PAREN);
+       if (peek ()->type != CPP_OPEN_PAREN
+ 	  || !peek_ident ("if", 2))
+ 	fatal_at (token, "switch can be implemented with a single if");
+       while  (peek ()->type != CPP_CLOSE_PAREN)
+ 	{
+ 	  if (peek ()->type == CPP_OPEN_PAREN)
+ 	    {
+ 	      if (peek_ident ("if", 2))
+ 		{
+ 		  eat_token (CPP_OPEN_PAREN);
+ 		  eat_ident ("if");
+ 		  ife->falseexpr = new if_expr ();
+ 		  ife = as_a <if_expr *> (ife->falseexpr);
+ 		  ife->cond = parse_c_expr (CPP_OPEN_PAREN);
+ 		  if (peek ()->type == CPP_OPEN_PAREN)
+ 		    ife->trueexpr = parse_result (result, matcher);
+ 		  else
+ 		    ife->trueexpr = parse_op ();
+ 		  eat_token (CPP_CLOSE_PAREN);
+ 		}
+ 	      else
+ 		{
+ 		  /* switch default clause */
+ 		  ife->falseexpr = parse_result (result, matcher);
+ 		  eat_token (CPP_CLOSE_PAREN);
+ 		  return res;
+ 		}
+ 	    }
+ 	  else
+ 	    {
+ 	      /* switch default clause */
+ 	      ife->falseexpr = parse_op ();
+ 	      eat_token (CPP_CLOSE_PAREN);
+ 	      return res;
+ 	    }
+ 	}
+       eat_token (CPP_CLOSE_PAREN);
+       return res;
+     }
    else
      {
        operand *op = result;
Index: gcc/match.pd
===================================================================
*** gcc/match.pd	(revision 225764)
--- gcc/match.pd	(working copy)
*************** (define_operator_list CBRT BUILT_IN_CBRT
*** 1107,1149 ****
        unsigned int final_prec = TYPE_PRECISION (type);
        int final_unsignedp = TYPE_UNSIGNED (type);
      }
!    /* In addition to the cases of two conversions in a row
!       handled below, if we are converting something to its own
!       type via an object of identical or wider precision, neither
!       conversion is needed.  */
!    (if (((GIMPLE && useless_type_conversion_p (type, inside_type))
! 	 || (GENERIC
! 	     && TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (inside_type)))
! 	&& (((inter_int || inter_ptr) && final_int)
! 	    || (inter_float && final_float))
! 	&& inter_prec >= final_prec)
!     (ocvt @0)
! 
!    /* Likewise, if the intermediate and initial types are either both
!       float or both integer, we don't need the middle conversion if the
!       former is wider than the latter and doesn't change the signedness
!       (for integers).  Avoid this if the final type is a pointer since
!       then we sometimes need the middle conversion.  Likewise if the
!       final type has a precision not equal to the size of its mode.  */
!    (if (((inter_int && inside_int) || (inter_float && inside_float))
! 	&& (final_int || final_float)
! 	&& inter_prec >= inside_prec
! 	&& (inter_float || inter_unsignedp == inside_unsignedp)
! 	&& ! (final_prec != GET_MODE_PRECISION (TYPE_MODE (type))
! 	      && TYPE_MODE (type) == TYPE_MODE (inter_type)))
!     (ocvt @0)
! 
!    /* If we have a sign-extension of a zero-extended value, we can
!       replace that by a single zero-extension.  Likewise if the
!       final conversion does not change precision we can drop the
!       intermediate conversion.  */
!    (if (inside_int && inter_int && final_int
! 	&& ((inside_prec < inter_prec && inter_prec < final_prec
! 	     && inside_unsignedp && !inter_unsignedp)
! 	    || final_prec == inter_prec))
!     (ocvt @0)
  
!    /* Two conversions in a row are not needed unless:
  	- some conversion is floating-point (overstrict for now), or
  	- some conversion is a vector (overstrict for now), or
  	- the intermediate type is narrower than both initial and
--- 1167,1210 ----
        unsigned int final_prec = TYPE_PRECISION (type);
        int final_unsignedp = TYPE_UNSIGNED (type);
      }
!    (switch
!     /* In addition to the cases of two conversions in a row
!        handled below, if we are converting something to its own
!        type via an object of identical or wider precision, neither
!        conversion is needed.  */
!     (if (((GIMPLE && useless_type_conversion_p (type, inside_type))
! 	  || (GENERIC
! 	      && TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (inside_type)))
! 	 && (((inter_int || inter_ptr) && final_int)
! 	     || (inter_float && final_float))
! 	 && inter_prec >= final_prec)
!      (ocvt @0))
! 
!     /* Likewise, if the intermediate and initial types are either both
!        float or both integer, we don't need the middle conversion if the
!        former is wider than the latter and doesn't change the signedness
!        (for integers).  Avoid this if the final type is a pointer since
!        then we sometimes need the middle conversion.  Likewise if the
!        final type has a precision not equal to the size of its mode.  */
!     (if (((inter_int && inside_int) || (inter_float && inside_float))
! 	 && (final_int || final_float)
! 	 && inter_prec >= inside_prec
! 	 && (inter_float || inter_unsignedp == inside_unsignedp)
! 	 && ! (final_prec != GET_MODE_PRECISION (TYPE_MODE (type))
! 	       && TYPE_MODE (type) == TYPE_MODE (inter_type)))
!      (ocvt @0))
! 
!     /* If we have a sign-extension of a zero-extended value, we can
!        replace that by a single zero-extension.  Likewise if the
!        final conversion does not change precision we can drop the
!        intermediate conversion.  */
!     (if (inside_int && inter_int && final_int
! 	 && ((inside_prec < inter_prec && inter_prec < final_prec
! 	      && inside_unsignedp && !inter_unsignedp)
! 	     || final_prec == inter_prec))
!      (ocvt @0))
  
!     /* Two conversions in a row are not needed unless:
  	- some conversion is floating-point (overstrict for now), or
  	- some conversion is a vector (overstrict for now), or
  	- the intermediate type is narrower than both initial and
*************** (define_operator_list CBRT BUILT_IN_CBRT
*** 1154,1192 ****
  	  intermediate and final types differ, or
  	- the final type is a pointer type and the precisions of the
  	  initial and intermediate types differ.  */
!    (if (! inside_float && ! inter_float && ! final_float
! 	&& ! inside_vec && ! inter_vec && ! final_vec
! 	&& (inter_prec >= inside_prec || inter_prec >= final_prec)
! 	&& ! (inside_int && inter_int
! 	      && inter_unsignedp != inside_unsignedp
! 	      && inter_prec < final_prec)
! 	&& ((inter_unsignedp && inter_prec > inside_prec)
! 	    == (final_unsignedp && final_prec > inter_prec))
! 	&& ! (inside_ptr && inter_prec != final_prec)
! 	&& ! (final_ptr && inside_prec != inter_prec)
! 	&& ! (final_prec != GET_MODE_PRECISION (TYPE_MODE (type))
! 	      && TYPE_MODE (type) == TYPE_MODE (inter_type)))
!     (ocvt @0)
! 
!    /* A truncation to an unsigned type (a zero-extension) should be
!       canonicalized as bitwise and of a mask.  */
!    (if (final_int && inter_int && inside_int
! 	&& final_prec == inside_prec
! 	&& final_prec > inter_prec
! 	&& inter_unsignedp)
!     (convert (bit_and @0 { wide_int_to_tree
! 	                     (inside_type,
! 			      wi::mask (inter_prec, false,
! 					TYPE_PRECISION (inside_type))); }))
! 
!    /* If we are converting an integer to a floating-point that can
!       represent it exactly and back to an integer, we can skip the
!       floating-point conversion.  */
!    (if (GIMPLE /* PR66211 */
! 	&& inside_int && inter_float && final_int &&
! 	(unsigned) significand_size (TYPE_MODE (inter_type))
! 	>= inside_prec - !inside_unsignedp)
!     (convert @0)))))))))))
  
  /* If we have a narrowing conversion to an integral type that is fed by a
     BIT_AND_EXPR, we might be able to remove the BIT_AND_EXPR if it merely
--- 1215,1253 ----
  	  intermediate and final types differ, or
  	- the final type is a pointer type and the precisions of the
  	  initial and intermediate types differ.  */
!     (if (! inside_float && ! inter_float && ! final_float
! 	 && ! inside_vec && ! inter_vec && ! final_vec
! 	 && (inter_prec >= inside_prec || inter_prec >= final_prec)
! 	 && ! (inside_int && inter_int
! 	       && inter_unsignedp != inside_unsignedp
! 	       && inter_prec < final_prec)
! 	 && ((inter_unsignedp && inter_prec > inside_prec)
! 	     == (final_unsignedp && final_prec > inter_prec))
! 	 && ! (inside_ptr && inter_prec != final_prec)
! 	 && ! (final_ptr && inside_prec != inter_prec)
! 	 && ! (final_prec != GET_MODE_PRECISION (TYPE_MODE (type))
! 	       && TYPE_MODE (type) == TYPE_MODE (inter_type)))
!      (ocvt @0))
! 
!     /* A truncation to an unsigned type (a zero-extension) should be
!        canonicalized as bitwise and of a mask.  */
!     (if (final_int && inter_int && inside_int
! 	 && final_prec == inside_prec
! 	 && final_prec > inter_prec
! 	 && inter_unsignedp)
!      (convert (bit_and @0 { wide_int_to_tree
! 	                      (inside_type,
! 			       wi::mask (inter_prec, false,
! 					 TYPE_PRECISION (inside_type))); })))
! 
!     /* If we are converting an integer to a floating-point that can
!        represent it exactly and back to an integer, we can skip the
!        floating-point conversion.  */
!     (if (GIMPLE /* PR66211 */
! 	 && inside_int && inter_float && final_int &&
! 	 (unsigned) significand_size (TYPE_MODE (inter_type))
! 	 >= inside_prec - !inside_unsignedp)
!      (convert @0)))))))
  
  /* If we have a narrowing conversion to an integral type that is fed by a
     BIT_AND_EXPR, we might be able to remove the BIT_AND_EXPR if it merely
*************** (define_operator_list CBRT BUILT_IN_CBRT
*** 1463,1520 ****
   (simplify
    (cmp @0 REAL_CST@1)
    /* IEEE doesn't distinguish +0 and -0 in comparisons.  */
!   /* a CMP (-0) -> a CMP 0  */
!   (if (REAL_VALUE_MINUS_ZERO (TREE_REAL_CST (@1)))
!    (cmp @0 { build_real (TREE_TYPE (@1), dconst0); })
!   /* x != NaN is always true, other ops are always false.  */
!   (if (REAL_VALUE_ISNAN (TREE_REAL_CST (@1))
!        && ! HONOR_SNANS (@1))
!    { constant_boolean_node (cmp == NE_EXPR, type); }
!   /* Fold comparisons against infinity.  */
!   (if (REAL_VALUE_ISINF (TREE_REAL_CST (@1))
!        && MODE_HAS_INFINITIES (TYPE_MODE (TREE_TYPE (@1))))
!    (with
!     {
!       REAL_VALUE_TYPE max;
!       enum tree_code code = cmp;
!       bool neg = REAL_VALUE_NEGATIVE (TREE_REAL_CST (@1));
!       if (neg)
!         code = swap_tree_comparison (code);
!     }
!     /* x > +Inf is always false, if with ignore sNANs.  */
!     (if (code == GT_EXPR
!     	 && ! HONOR_SNANS (@0))
!      { constant_boolean_node (false, type); }
!     (if (code == LE_EXPR)
!      /* x <= +Inf is always true, if we don't case about NaNs.  */
!      (if (! HONOR_NANS (@0))
!       { constant_boolean_node (true, type); }
!       /* x <= +Inf is the same as x == x, i.e. isfinite(x).  */
!       (eq @0 @0))
!     /* x == +Inf and x >= +Inf are always equal to x > DBL_MAX.  */
!     (if (code == EQ_EXPR || code == GE_EXPR)
!      (with { real_maxval (&max, neg, TYPE_MODE (TREE_TYPE (@0))); }
!       (if (neg)
!        (lt @0 { build_real (TREE_TYPE (@0), max); })
!        (gt @0 { build_real (TREE_TYPE (@0), max); })))
!     /* x < +Inf is always equal to x <= DBL_MAX.  */
!     (if (code == LT_EXPR)
!      (with { real_maxval (&max, neg, TYPE_MODE (TREE_TYPE (@0))); }
!       (if (neg)
!        (ge @0 { build_real (TREE_TYPE (@0), max); })
!        (le @0 { build_real (TREE_TYPE (@0), max); })))
!     /* x != +Inf is always equal to !(x > DBL_MAX).  */
!     (if (code == NE_EXPR)
!      (with { real_maxval (&max, neg, TYPE_MODE (TREE_TYPE (@0))); }
!       (if (! HONOR_NANS (@0))
!        (if (neg)
!         (ge @0 { build_real (TREE_TYPE (@0), max); })
! 	(le @0 { build_real (TREE_TYPE (@0), max); }))
!        (if (neg)
! 	(bit_xor (lt @0 { build_real (TREE_TYPE (@0), max); })
! 			{ build_one_cst (type); })
! 	(bit_xor (gt @0 { build_real (TREE_TYPE (@0), max); })
! 			{ build_one_cst (type); }))))))))))))))
  
   /* If this is a comparison of a real constant with a PLUS_EXPR
      or a MINUS_EXPR of a real constant, we can convert it into a
--- 1537,1596 ----
   (simplify
    (cmp @0 REAL_CST@1)
    /* IEEE doesn't distinguish +0 and -0 in comparisons.  */
!   (switch
!    /* a CMP (-0) -> a CMP 0  */
!    (if (REAL_VALUE_MINUS_ZERO (TREE_REAL_CST (@1)))
!     (cmp @0 { build_real (TREE_TYPE (@1), dconst0); }))
!    /* x != NaN is always true, other ops are always false.  */
!    (if (REAL_VALUE_ISNAN (TREE_REAL_CST (@1))
! 	&& ! HONOR_SNANS (@1))
!     { constant_boolean_node (cmp == NE_EXPR, type); })
!    /* Fold comparisons against infinity.  */
!    (if (REAL_VALUE_ISINF (TREE_REAL_CST (@1))
! 	&& MODE_HAS_INFINITIES (TYPE_MODE (TREE_TYPE (@1))))
!     (with
!      {
!        REAL_VALUE_TYPE max;
!        enum tree_code code = cmp;
!        bool neg = REAL_VALUE_NEGATIVE (TREE_REAL_CST (@1));
!        if (neg)
!          code = swap_tree_comparison (code);
!      }
!      (switch
!       /* x > +Inf is always false, if with ignore sNANs.  */
!       (if (code == GT_EXPR
! 	   && ! HONOR_SNANS (@0))
!        { constant_boolean_node (false, type); })
!       (if (code == LE_EXPR)
!        /* x <= +Inf is always true, if we don't case about NaNs.  */
!        (if (! HONOR_NANS (@0))
! 	{ constant_boolean_node (true, type); }
! 	/* x <= +Inf is the same as x == x, i.e. isfinite(x).  */
! 	(eq @0 @0)))
!       /* x == +Inf and x >= +Inf are always equal to x > DBL_MAX.  */
!       (if (code == EQ_EXPR || code == GE_EXPR)
!        (with { real_maxval (&max, neg, TYPE_MODE (TREE_TYPE (@0))); }
! 	(if (neg)
! 	 (lt @0 { build_real (TREE_TYPE (@0), max); })
! 	 (gt @0 { build_real (TREE_TYPE (@0), max); }))))
!       /* x < +Inf is always equal to x <= DBL_MAX.  */
!       (if (code == LT_EXPR)
!        (with { real_maxval (&max, neg, TYPE_MODE (TREE_TYPE (@0))); }
! 	(if (neg)
! 	 (ge @0 { build_real (TREE_TYPE (@0), max); })
! 	 (le @0 { build_real (TREE_TYPE (@0), max); }))))
!       /* x != +Inf is always equal to !(x > DBL_MAX).  */
!       (if (code == NE_EXPR)
!        (with { real_maxval (&max, neg, TYPE_MODE (TREE_TYPE (@0))); }
! 	(if (! HONOR_NANS (@0))
! 	 (if (neg)
! 	  (ge @0 { build_real (TREE_TYPE (@0), max); })
! 	  (le @0 { build_real (TREE_TYPE (@0), max); }))
! 	 (if (neg)
! 	  (bit_xor (lt @0 { build_real (TREE_TYPE (@0), max); })
! 	   { build_one_cst (type); })
! 	  (bit_xor (gt @0 { build_real (TREE_TYPE (@0), max); })
! 	   { build_one_cst (type); }))))))))))
  
   /* If this is a comparison of a real constant with a PLUS_EXPR
      or a MINUS_EXPR of a real constant, we can convert it into a
*************** (define_operator_list CBRT BUILT_IN_CBRT
*** 1549,1613 ****
    (for sq (SQRT)
     (simplify
      (cmp (sq @0) REAL_CST@1)
!     (if (REAL_VALUE_NEGATIVE (TREE_REAL_CST (@1)))
!      /* sqrt(x) < y is always false, if y is negative.  */
!      (if (cmp == EQ_EXPR || cmp == LT_EXPR || cmp == LE_EXPR)
!       { constant_boolean_node (false, type); }
!      /* sqrt(x) > y is always true, if y is negative and we
! 	don't care about NaNs, i.e. negative values of x.  */
!      (if (cmp == NE_EXPR || !HONOR_NANS (@0))
!       { constant_boolean_node (true, type); }
!      /* sqrt(x) > y is the same as x >= 0, if y is negative.  */
!      (ge @0 { build_real (TREE_TYPE (@0), dconst0); })))
!     (if (cmp == GT_EXPR || cmp == GE_EXPR)
!      (with
!       {
!        	REAL_VALUE_TYPE c2;
! 	REAL_ARITHMETIC (c2, MULT_EXPR, TREE_REAL_CST (@1), TREE_REAL_CST (@1));
! 	real_convert (&c2, TYPE_MODE (TREE_TYPE (@0)), &c2);
!       }
!       (if (REAL_VALUE_ISINF (c2))
!        /* sqrt(x) > y is x == +Inf, when y is very large.  */
!        (if (HONOR_INFINITIES (@0))
!         (eq @0 { build_real (TREE_TYPE (@0), c2); })
  	{ constant_boolean_node (false, type); })
!        /* sqrt(x) > c is the same as x > c*c.  */
!        (cmp @0 { build_real (TREE_TYPE (@0), c2); })))
!     (if (cmp == LT_EXPR || cmp == LE_EXPR)
!      (with
!       {
!        	REAL_VALUE_TYPE c2;
! 	REAL_ARITHMETIC (c2, MULT_EXPR, TREE_REAL_CST (@1), TREE_REAL_CST (@1));
! 	real_convert (&c2, TYPE_MODE (TREE_TYPE (@0)), &c2);
!       }
!       (if (REAL_VALUE_ISINF (c2))
!        /* sqrt(x) < y is always true, when y is a very large
! 	  value and we don't care about NaNs or Infinities.  */
!        (if (! HONOR_NANS (@0) && ! HONOR_INFINITIES (@0))
!         { constant_boolean_node (true, type); }
!        /* sqrt(x) < y is x != +Inf when y is very large and we
! 	  don't care about NaNs.  */
!        (if (! HONOR_NANS (@0))
!         (ne @0 { build_real (TREE_TYPE (@0), c2); })
!        /* sqrt(x) < y is x >= 0 when y is very large and we
! 	  don't care about Infinities.  */
!        (if (! HONOR_INFINITIES (@0))
!         (ge @0 { build_real (TREE_TYPE (@0), dconst0); })
!        /* sqrt(x) < y is x >= 0 && x != +Inf, when y is large.  */
!        (if (GENERIC)
!         (truth_andif
! 	 (ge @0 { build_real (TREE_TYPE (@0), dconst0); })
! 	 (ne @0 { build_real (TREE_TYPE (@0), c2); }))))))
!       /* sqrt(x) < c is the same as x < c*c, if we ignore NaNs.  */
!       (if (! REAL_VALUE_ISINF (c2)
!            && ! HONOR_NANS (@0))
!        (cmp @0 { build_real (TREE_TYPE (@0), c2); })
!       /* sqrt(x) < c is the same as x >= 0 && x < c*c.  */
!       (if (! REAL_VALUE_ISINF (c2)
!            && GENERIC)
!        (truth_andif
!         (ge @0 { build_real (TREE_TYPE (@0), dconst0); })
! 	(cmp @0 { build_real (TREE_TYPE (@0), c2); })))))))))))))
  
  /* Unordered tests if either argument is a NaN.  */
  (simplify
--- 1625,1692 ----
    (for sq (SQRT)
     (simplify
      (cmp (sq @0) REAL_CST@1)
!     (switch
!      (if (REAL_VALUE_NEGATIVE (TREE_REAL_CST (@1)))
!       (switch
!        /* sqrt(x) < y is always false, if y is negative.  */
!        (if (cmp == EQ_EXPR || cmp == LT_EXPR || cmp == LE_EXPR)
  	{ constant_boolean_node (false, type); })
!        /* sqrt(x) > y is always true, if y is negative and we
! 	  don't care about NaNs, i.e. negative values of x.  */
!        (if (cmp == NE_EXPR || !HONOR_NANS (@0))
! 	{ constant_boolean_node (true, type); })
!        /* sqrt(x) > y is the same as x >= 0, if y is negative.  */
!        (ge @0 { build_real (TREE_TYPE (@0), dconst0); })))
!      (if (cmp == GT_EXPR || cmp == GE_EXPR)
!       (with
!        {
!          REAL_VALUE_TYPE c2;
! 	 REAL_ARITHMETIC (c2, MULT_EXPR,
! 			  TREE_REAL_CST (@1), TREE_REAL_CST (@1));
! 	 real_convert (&c2, TYPE_MODE (TREE_TYPE (@0)), &c2);
!        }
!        (if (REAL_VALUE_ISINF (c2))
! 	/* sqrt(x) > y is x == +Inf, when y is very large.  */
! 	(if (HONOR_INFINITIES (@0))
! 	 (eq @0 { build_real (TREE_TYPE (@0), c2); })
! 	 { constant_boolean_node (false, type); })
! 	/* sqrt(x) > c is the same as x > c*c.  */
! 	(cmp @0 { build_real (TREE_TYPE (@0), c2); }))))
!      (if (cmp == LT_EXPR || cmp == LE_EXPR)
!       (with
!        {
!        	 REAL_VALUE_TYPE c2;
! 	 REAL_ARITHMETIC (c2, MULT_EXPR,
! 			  TREE_REAL_CST (@1), TREE_REAL_CST (@1));
! 	 real_convert (&c2, TYPE_MODE (TREE_TYPE (@0)), &c2);
!        }
!        (if (REAL_VALUE_ISINF (c2))
!         (switch
! 	 /* sqrt(x) < y is always true, when y is a very large
! 	    value and we don't care about NaNs or Infinities.  */
! 	 (if (! HONOR_NANS (@0) && ! HONOR_INFINITIES (@0))
! 	  { constant_boolean_node (true, type); })
! 	 /* sqrt(x) < y is x != +Inf when y is very large and we
! 	    don't care about NaNs.  */
! 	 (if (! HONOR_NANS (@0))
! 	  (ne @0 { build_real (TREE_TYPE (@0), c2); }))
! 	 /* sqrt(x) < y is x >= 0 when y is very large and we
! 	    don't care about Infinities.  */
! 	 (if (! HONOR_INFINITIES (@0))
! 	  (ge @0 { build_real (TREE_TYPE (@0), dconst0); }))
! 	 /* sqrt(x) < y is x >= 0 && x != +Inf, when y is large.  */
! 	 (if (GENERIC)
! 	  (truth_andif
! 	   (ge @0 { build_real (TREE_TYPE (@0), dconst0); })
! 	   (ne @0 { build_real (TREE_TYPE (@0), c2); }))))
! 	/* sqrt(x) < c is the same as x < c*c, if we ignore NaNs.  */
! 	(if (! HONOR_NANS (@0))
! 	 (cmp @0 { build_real (TREE_TYPE (@0), c2); })
! 	 /* sqrt(x) < c is the same as x >= 0 && x < c*c.  */
! 	 (if (GENERIC)
! 	  (truth_andif
! 	   (ge @0 { build_real (TREE_TYPE (@0), dconst0); })
! 	   (cmp @0 { build_real (TREE_TYPE (@0), c2); }))))))))))))
  
  /* Unordered tests if either argument is a NaN.  */
  (simplify

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] Add 'switch' statement to match.pd language
  2015-07-14 13:02 [PATCH] Add 'switch' statement to match.pd language Richard Biener
@ 2015-07-15 14:28 ` Michael Matz
  2015-07-15 15:04   ` Richard Biener
  0 siblings, 1 reply; 8+ messages in thread
From: Michael Matz @ 2015-07-15 14:28 UTC (permalink / raw)
  To: Richard Biener; +Cc: gcc-patches

Hi,

On Tue, 14 Jul 2015, Richard Biener wrote:

> I know Micha detests the extra 'if' as much as the extra braces thus
> would have prefered
> 
>  (switch
>   (A) B
>   (B) C
>   (C) D
>   E)

The lispy way would have been

   (switch
    (A) (B)
    (C) (D)
    (E) (F)
    G)

i.e. parenthesize the result as well, which then would be unambiguously 
parsable.  But hey, it's your language ;)


Ciao,
Michael.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] Add 'switch' statement to match.pd language
  2015-07-15 14:28 ` Michael Matz
@ 2015-07-15 15:04   ` Richard Biener
  2015-07-15 15:21     ` Michael Matz
  0 siblings, 1 reply; 8+ messages in thread
From: Richard Biener @ 2015-07-15 15:04 UTC (permalink / raw)
  To: Michael Matz, Richard Biener; +Cc: gcc-patches

On July 15, 2015 4:21:03 PM GMT+02:00, Michael Matz <matz@suse.de> wrote:
>Hi,
>
>On Tue, 14 Jul 2015, Richard Biener wrote:
>
>> I know Micha detests the extra 'if' as much as the extra braces thus
>> would have prefered
>> 
>>  (switch
>>   (A) B
>>   (B) C
>>   (C) D
>>   E)
>
>The lispy way would have been
>
>   (switch
>    (A) (B)
>    (C) (D)
>    (E) (F)
>    G)
>
>i.e. parenthesize the result as well, which then would be unambiguously

That's just atoms vs. Expressions.  Like
(Plus @0 @1) vs. Plain @1.  So you suggest to require ((plus @0 @1)) here to make it unambiguous?

>parsable.  But hey, it's your language ;)
>
>
>Ciao,
>Michael.


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] Add 'switch' statement to match.pd language
  2015-07-15 15:04   ` Richard Biener
@ 2015-07-15 15:21     ` Michael Matz
  2015-07-15 18:49       ` Richard Sandiford
  2015-07-16  7:09       ` Richard Biener
  0 siblings, 2 replies; 8+ messages in thread
From: Michael Matz @ 2015-07-15 15:21 UTC (permalink / raw)
  To: Richard Biener; +Cc: Richard Biener, gcc-patches

Hi,

On Wed, 15 Jul 2015, Richard Biener wrote:

> >>  (switch
> >>   (A) B
> >>   (B) C
> >>   (C) D
> >>   E)
> >
> >The lispy way would have been
> >
> >   (switch
> >    (A) (B)
> >    (C) (D)
> >    (E) (F)
> >    G)
> >
> >i.e. parenthesize the result as well, which then would be unambiguously
> 
> That's just atoms vs. Expressions.

But if the result is no atom, you'd want parentheses.  Similar if the 
condition is no expression but an atom, you'd want to leave out 
parentheses as well.  My point is, that both condition and result are at 
the same level, and hence should be subject to the same parenthesis rules, 
namely: surrounding parens by default, optional for atoms.

> Like (Plus @0 @1) vs. Plain @1.  So you suggest to require ((plus @0 
> @1)) here to make it unambiguous?

No :)  Just look at your example again:

 (switch
  (A) B
 )

Both A and B are at the same level, and are both expressions, but still 
you parenthesize them differently; that can't be right.  You also don't 
write

 (switch
  ((plus @0 @1))  (@0)
 )

You write

 (switch
  (plus @0 @1) (@0)
 )

And as syntactic sugar you are allowed to leave out the parens around @0 
as it's an atom:

 (switch
  (plus @0 @1) @0
 )

Similar, if the condition is an atom you should be able to leave the 
parens away:

 (switch
  cond (minus @0 @1)
 )

(given a predicate 'cond' defined appropriately).


Ciao,
Michael.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] Add 'switch' statement to match.pd language
  2015-07-15 15:21     ` Michael Matz
@ 2015-07-15 18:49       ` Richard Sandiford
  2015-07-16  7:29         ` Richard Biener
  2015-07-16  7:09       ` Richard Biener
  1 sibling, 1 reply; 8+ messages in thread
From: Richard Sandiford @ 2015-07-15 18:49 UTC (permalink / raw)
  To: Michael Matz; +Cc: Richard Biener, Richard Biener, gcc-patches

Michael Matz <matz@suse.de> writes:
>> >>  (switch
>> >>   (A) B
>> >>   (B) C
>> >>   (C) D
>> >>   E)
>> >
>> >The lispy way would have been
>> >
>> >   (switch
>> >    (A) (B)
>> >    (C) (D)
>> >    (E) (F)
>> >    G)
>> >
>> >i.e. parenthesize the result as well, which then would be unambiguously
>> 
>> That's just atoms vs. Expressions.
>
> But if the result is no atom, you'd want parentheses.  Similar if the 
> condition is no expression but an atom, you'd want to leave out 
> parentheses as well.  My point is, that both condition and result are at 
> the same level, and hence should be subject to the same parenthesis rules, 
> namely: surrounding parens by default, optional for atoms.
>
>> Like (Plus @0 @1) vs. Plain @1.  So you suggest to require ((plus @0 
>> @1)) here to make it unambiguous?
>
> No :)  Just look at your example again:
>
>  (switch
>   (A) B
>  )
>
> Both A and B are at the same level, and are both expressions, but still 
> you parenthesize them differently; that can't be right.  You also don't 
> write
>
>  (switch
>   ((plus @0 @1))  (@0)
>  )
>
> You write
>
>  (switch
>   (plus @0 @1) (@0)
>  )
>
> And as syntactic sugar you are allowed to leave out the parens around @0 
> as it's an atom:
>
>  (switch
>   (plus @0 @1) @0
>  )
>
> Similar, if the condition is an atom you should be able to leave the 
> parens away:
>
>  (switch
>   cond (minus @0 @1)
>  )
>
> (given a predicate 'cond' defined appropriately).

Agreed FWIW.  The rtx equivalent (unfortunately called "cond",
so the clash with "cond"==COND_EXPR prevents naming consistency)
uses the lispy syntax without any ambiguity.

Thanks,
Richard

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] Add 'switch' statement to match.pd language
  2015-07-15 15:21     ` Michael Matz
  2015-07-15 18:49       ` Richard Sandiford
@ 2015-07-16  7:09       ` Richard Biener
  2015-07-16 15:00         ` Michael Matz
  1 sibling, 1 reply; 8+ messages in thread
From: Richard Biener @ 2015-07-16  7:09 UTC (permalink / raw)
  To: Michael Matz; +Cc: gcc-patches

On Wed, 15 Jul 2015, Michael Matz wrote:

> Hi,
> 
> On Wed, 15 Jul 2015, Richard Biener wrote:
> 
> > >>  (switch
> > >>   (A) B
> > >>   (B) C
> > >>   (C) D
> > >>   E)
> > >
> > >The lispy way would have been
> > >
> > >   (switch
> > >    (A) (B)
> > >    (C) (D)
> > >    (E) (F)
> > >    G)
> > >
> > >i.e. parenthesize the result as well, which then would be unambiguously
> > 
> > That's just atoms vs. Expressions.
> 
> But if the result is no atom, you'd want parentheses.  Similar if the 
> condition is no expression but an atom, you'd want to leave out 
> parentheses as well.  My point is, that both condition and result are at 
> the same level, and hence should be subject to the same parenthesis rules, 
> namely: surrounding parens by default, optional for atoms.
> 
> > Like (Plus @0 @1) vs. Plain @1.  So you suggest to require ((plus @0 
> > @1)) here to make it unambiguous?
> 
> No :)  Just look at your example again:
> 
>  (switch
>   (A) B
>  )
> 
> Both A and B are at the same level, and are both expressions, but still 
> you parenthesize them differently; that can't be right.  You also don't 
> write
> 
>  (switch
>   ((plus @0 @1))  (@0)
>  )
> 
> You write
> 
>  (switch
>   (plus @0 @1) (@0)
>  )
> 
> And as syntactic sugar you are allowed to leave out the parens around @0 
> as it's an atom:
> 
>  (switch
>   (plus @0 @1) @0
>  )
> 
> Similar, if the condition is an atom you should be able to leave the 
> parens away:
> 
>  (switch
>   cond (minus @0 @1)
>  )
> 
> (given a predicate 'cond' defined appropriately).

Yes.  Though techincally the condition cannot be an atom because
it has to be a c-expr and that has no notion of atom vs. no-atom.

But the issue is to unambiguously parse the else clause, thus

 (switch
  (integer_zerop (@0)) (plus @1 @2)   <--- if - then
  (integer_onep (@0)) (minus @2 @1)   <--- if - then
  (minus @3 @2))                      <--- else

whether or not the then or else clauses are atoms.  The parser
currently cannot backtrack and optimistically parse the next
expression or atom as 'else' and fall back to 'if' (a condition)
if that fails.  The parser has to know upfront by peeking at N
tokens whether the next entry of the switch is a if - then or
the else.

Thus the extra "redundant" keyword.

Richard.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] Add 'switch' statement to match.pd language
  2015-07-15 18:49       ` Richard Sandiford
@ 2015-07-16  7:29         ` Richard Biener
  0 siblings, 0 replies; 8+ messages in thread
From: Richard Biener @ 2015-07-16  7:29 UTC (permalink / raw)
  To: Richard Sandiford; +Cc: Michael Matz, gcc-patches

On Wed, 15 Jul 2015, Richard Sandiford wrote:

> Michael Matz <matz@suse.de> writes:
> >> >>  (switch
> >> >>   (A) B
> >> >>   (B) C
> >> >>   (C) D
> >> >>   E)
> >> >
> >> >The lispy way would have been
> >> >
> >> >   (switch
> >> >    (A) (B)
> >> >    (C) (D)
> >> >    (E) (F)
> >> >    G)
> >> >
> >> >i.e. parenthesize the result as well, which then would be unambiguously
> >> 
> >> That's just atoms vs. Expressions.
> >
> > But if the result is no atom, you'd want parentheses.  Similar if the 
> > condition is no expression but an atom, you'd want to leave out 
> > parentheses as well.  My point is, that both condition and result are at 
> > the same level, and hence should be subject to the same parenthesis rules, 
> > namely: surrounding parens by default, optional for atoms.
> >
> >> Like (Plus @0 @1) vs. Plain @1.  So you suggest to require ((plus @0 
> >> @1)) here to make it unambiguous?
> >
> > No :)  Just look at your example again:
> >
> >  (switch
> >   (A) B
> >  )
> >
> > Both A and B are at the same level, and are both expressions, but still 
> > you parenthesize them differently; that can't be right.  You also don't 
> > write
> >
> >  (switch
> >   ((plus @0 @1))  (@0)
> >  )
> >
> > You write
> >
> >  (switch
> >   (plus @0 @1) (@0)
> >  )
> >
> > And as syntactic sugar you are allowed to leave out the parens around @0 
> > as it's an atom:
> >
> >  (switch
> >   (plus @0 @1) @0
> >  )
> >
> > Similar, if the condition is an atom you should be able to leave the 
> > parens away:
> >
> >  (switch
> >   cond (minus @0 @1)
> >  )
> >
> > (given a predicate 'cond' defined appropriately).
> 
> Agreed FWIW.  The rtx equivalent (unfortunately called "cond",
> so the clash with "cond"==COND_EXPR prevents naming consistency)
> uses the lispy syntax without any ambiguity.

It also puts an extra pair of [] parens around the if-then cases
and leaves a single else case.  It's

 ('cond' [cond1 then1
        cond2 then2
        ...]
  else)

That's of course also possible to mimic with

 (switch
  (
   (integer_zerop (@1)) @0
   (INTEGRAL_TYPE_P (type)) (plus @1 @2)
  )
  (minus @3 @2))

not sure if that is visually easier to parse than the 'if' keyword.
One of the current switch stmts we have is

     (switch
      /* x > +Inf is always false, if with ignore sNANs.  */
      (if (code == GT_EXPR
           && ! HONOR_SNANS (@0))
       { constant_boolean_node (false, type); })
      (if (code == LE_EXPR)
       /* x <= +Inf is always true, if we don't case about NaNs.  */
       (if (! HONOR_NANS (@0))
        { constant_boolean_node (true, type); }
        /* x <= +Inf is the same as x == x, i.e. isfinite(x).  */
        (eq @0 @0)))
      /* x == +Inf and x >= +Inf are always equal to x > DBL_MAX.  */
      (if (code == EQ_EXPR || code == GE_EXPR)
       (with { real_maxval (&max, neg, TYPE_MODE (TREE_TYPE (@0))); }
        (if (neg)
         (lt @0 { build_real (TREE_TYPE (@0), max); })
         (gt @0 { build_real (TREE_TYPE (@0), max); }))))
      /* x < +Inf is always equal to x <= DBL_MAX.  */
      (if (code == LT_EXPR)
       (with { real_maxval (&max, neg, TYPE_MODE (TREE_TYPE (@0))); }
        (if (neg)
         (ge @0 { build_real (TREE_TYPE (@0), max); })
         (le @0 { build_real (TREE_TYPE (@0), max); }))))
      /* x != +Inf is always equal to !(x > DBL_MAX).  */
      (if (code == NE_EXPR)
       (with { real_maxval (&max, neg, TYPE_MODE (TREE_TYPE (@0))); }
        (if (! HONOR_NANS (@0))
         (if (neg)
          (ge @0 { build_real (TREE_TYPE (@0), max); })
          (le @0 { build_real (TREE_TYPE (@0), max); }))
         (if (neg)
          (bit_xor (lt @0 { build_real (TREE_TYPE (@0), max); })
           { build_one_cst (type); })
          (bit_xor (gt @0 { build_real (TREE_TYPE (@0), max); })
           { build_one_cst (type); }))))))

(this doesn't have an else clause)

Richard.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] Add 'switch' statement to match.pd language
  2015-07-16  7:09       ` Richard Biener
@ 2015-07-16 15:00         ` Michael Matz
  0 siblings, 0 replies; 8+ messages in thread
From: Michael Matz @ 2015-07-16 15:00 UTC (permalink / raw)
  To: Richard Biener; +Cc: gcc-patches

Hi,

On Thu, 16 Jul 2015, Richard Biener wrote:

> > Similar, if the condition is an atom you should be able to leave the 
> > parens away:
> > 
> >  (switch
> >   cond (minus @0 @1)
> >  )
> > 
> > (given a predicate 'cond' defined appropriately).
> 
> Yes.  Though techincally the condition cannot be an atom because
> it has to be a c-expr and that has no notion of atom vs. no-atom.

"1" is a valid c-expr, and quite atomy :)  (Or "true")

> But the issue is to unambiguously parse the else clause, thus

Ah, yes, I remember, the c-expr vs expr case; the parser is too limited :)  
In that case I find the extra keyword without parens even better:

(switch
 when (bla) (foo)
 when (bar) (boo)
 (blob))

I.e. following 'when' it's an c-expr (when single token, parens optional), 
when not following 'when' its a result expr (atomic or not).  Think of it 
as an infix multi-part keyword (like smalltalk has multi-part method 
names), the keyword(s) being "switch(when)*".

I'm undecided if I'd allow function calls as atoms as well (because they 
contain parens), like so:

(switch
 when integer_zero(@0) @1
 when integer_zero(@1) @0
 (plus @0 @1))

This would mean that there would be no single-token conditions without 
parens where one could leave out outer parens, as otherwise you have a 
ambiguity between:

(switch
 when true (@0)    // (@0) is the result
 ...)

and

(switch
 when token(@0) @1    // (@0) belongs to the when-expr
 ...)

One has to chose between one or the other, and I think the latter (i.e. 
function calls as lone when-expr) occur more often.

(Limiting the number of parens is worthwhile IMHO, but you probably 
guessed that much already :))


Ciao,
Michael.

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2015-07-16 14:51 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-07-14 13:02 [PATCH] Add 'switch' statement to match.pd language Richard Biener
2015-07-15 14:28 ` Michael Matz
2015-07-15 15:04   ` Richard Biener
2015-07-15 15:21     ` Michael Matz
2015-07-15 18:49       ` Richard Sandiford
2015-07-16  7:29         ` Richard Biener
2015-07-16  7:09       ` Richard Biener
2015-07-16 15:00         ` Michael Matz

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).