Hi,

this is a regression present on the mainline and 6 branch: the compiler now 
generates wrong code for the attached testcase at -O because of an internal 
conflict about boolean types.  The sequence is as follows.  In .mergephi3:

  boolean _22;
  p__enum res;

  <bb 9>:
  if (_22 != 0)
    goto <bb 10>;
  else
    goto <bb 11>;

  <bb 10>:

  <bb 11>:
  # res_17 = PHI <2(8), 0(9), 1(10)>

is turned into:

COND_EXPR in block 9 and PHI in block 11 converted to straightline code.
PHI res_17 changed to factor conversion out from COND_EXPR.
New stmt with CAST that defines res_17.

  boolean _33;

  <bb 10>:
  # _33 = PHI <2(8), _22(9)>
  res_17 = (p__enum) _33;

[...]

  <bb 12>:
  if (res_17 != 0)
    goto <bb 13>;
  else
    goto <bb 14>;

  <bb 13>:
  _12 = res_17 == 2;
  _13 = (integer) _12

in .phiopt1.  So boolean _33 can have value 2.  Later forwprop3 propagates _33
into the uses of res_17:

  <bb 12>:
  if (_33 != 0)
    goto <bb 13>;
  else
    goto <bb 14>;

  <bb 13>:
  _12 = _33 == 2;
  _13 = (integer) _12;

and DOM3 deduces:

  <bb 13>:
  _12 = 0;
  _13 = 0;

because it computes that _33 has value 1 in BB 13 since it's a boolean.

The problem was introduced by the new factor_out_conditional_conversion:

      /* If arg1 is an INTEGER_CST, fold it to new type.  */
      if (INTEGRAL_TYPE_P (TREE_TYPE (new_arg0))
          && int_fits_type_p (arg1, TREE_TYPE (new_arg0)))
        {
          if (gimple_assign_cast_p (arg0_def_stmt))
            new_arg1 = fold_convert (TREE_TYPE (new_arg0), arg1);
          else
            return NULL;
        }
      else
        return NULL

int_fits_type_p is documented as taking only INTEGER_TYPE, but is invoked
on constant 2 and a BOOLEAN_TYPE and returns true.

BOOLEAN_TYPE is special in Ada: it has precision 8 and range [0;255] so the
outcome of int_fits_type_p is not unreasonable.  But this goes against the
various transformations applied to boolean types in the compiler, which all
assume that they can only take values 0 or 1.

Hence the attached fix (which should be a no-op except for Ada), tested on 
x86_64-suse-linux, OK for mainline and 6 branch?


2016-10-18  Eric Botcazou  <ebotca...@adacore.com>

        * tree.c (int_fits_type_p): Accept only 0 and 1 for boolean types.


2016-10-18  Eric Botcazou  <ebotca...@adacore.com>

        * gnat.dg/opt59.adb: New test.
        * gnat.dg/opt59_pkg.ad[sb]: New helper.

-- 
Eric Botcazou
Index: tree.c
===================================================================
--- tree.c	(revision 241294)
+++ tree.c	(working copy)
@@ -9065,8 +9065,8 @@ get_narrower (tree op, int *unsignedp_pt
   return win;
 }
 
-/* Returns true if integer constant C has a value that is permissible
-   for type TYPE (an INTEGER_TYPE).  */
+/* Return true if integer constant C has a value that is permissible
+   for TYPE, an integral type.  */
 
 bool
 int_fits_type_p (const_tree c, const_tree type)
@@ -9075,6 +9075,11 @@ int_fits_type_p (const_tree c, const_tre
   bool ok_for_low_bound, ok_for_high_bound;
   signop sgn_c = TYPE_SIGN (TREE_TYPE (c));
 
+  /* Short-circuit boolean types since various transformations assume that
+     they can only take values 0 and 1.  */
+  if (TREE_CODE (type) == BOOLEAN_TYPE)
+    return integer_zerop (c) || integer_onep (c);
+
 retry:
   type_low_bound = TYPE_MIN_VALUE (type);
   type_high_bound = TYPE_MAX_VALUE (type);
-- { dg-do run }
-- { dg-options "-O" }

with Opt59_Pkg; use Opt59_Pkg;

procedure Opt59 is

  type Enum is (Zero, One, Two);

  function Has_True (V : Boolean_Vector) return Boolean is
  begin
     for I in V'Range loop
        if V (I) then
           return True;
        end if;
     end loop;
     return False;
  end;

  Data1  : constant Boolean_Vector := Get_BV1;
  Data2  : constant Boolean_Vector := Get_BV2;
  Result : Boolean_Vector;

  function F return Enum is
    Res  : Enum := Zero;
    Set1 : constant Boolean := Has_True (Data1);
    Set2 : constant Boolean := Has_True (Data2);
  begin
    if Set1 then
      Res := Two;
    elsif Set2 then
      Res := One;
    end if;
    return Res;
  end;

  Val : constant Enum := F;

begin

  for I in Result'Range loop
    Result (I) := Data1 (I) or Data2 (I);
  end loop;

  if Val /= Zero then
    Test (Val = Two);
  end if;

end;
package body Opt59_Pkg is

  function Get_BV1 return Boolean_Vector is
  begin
    return (others => True);
  end;

  function Get_BV2 return Boolean_Vector is
  begin
    return (others => False);
  end;

  procedure Test (B : Boolean) is
  begin
    if not B then
      raise Program_Error;
    end if;
  end;

end Opt59_Pkg;
package Opt59_Pkg is

  type Boolean_Vector is array (1 .. 8) of Boolean;

  function Get_BV1 return Boolean_Vector;

  function Get_BV2 return Boolean_Vector;

  procedure Test (B : Boolean);

end Opt59_Pkg;

Reply via email to