Fix wrong optimization of conditional expression with enumeration type

This is a regression introduced on the mainline and 14 branch by:
  https://gcc.gnu.org/pipermail/gcc-cvs/2023-October/391658.html

The change bypasses int_fits_type_p (essentially) to work around the
signedness constraints, but in doing so disregards the peculiarities
of boolean types whose precision is not 1 dealt with by the predicate,
leading to the creation of a problematic conversion here.

Fixed by special-casing boolean types whose precision is not 1, as done
in several other places.

gcc/
	* tree-ssa-phiopt.cc (factor_out_conditional_operation): Do not
	bypass the int_fits_type_p test for boolean types whose precision
	is not 1.

gcc/testsuite/
	* gnat.dg/opt105.adb: New test.
	* gnat.dg/opt105_pkg.ads, gnat.dg/opt105_pkg.adb: New helper.
This commit is contained in:
Eric Botcazou 2025-04-16 22:01:31 +02:00
parent dbffeadf7f
commit d5d7dfab81
4 changed files with 53 additions and 3 deletions

View file

@ -0,0 +1,30 @@
-- { dg-do run }
-- { dg-options "-O" }
with Opt105_Pkg; use Opt105_Pkg;
procedure Opt105 is
Val : constant Enum :=
(if Enabled then (if Disabled then Two else One) else Three);
begin
if Cond1 then
return;
end if;
if Cond2 then
return;
end if;
case Val is
when One =>
raise Program_Error;
when Two =>
raise Constraint_Error;
when Three =>
null;
end case;
end;

View file

@ -0,0 +1,6 @@
package body Opt105_Pkg is
function Cond1 return Boolean is (False);
function Cond2 return Boolean is (False);
end Opt105_Pkg;

View file

@ -0,0 +1,11 @@
package Opt105_Pkg is
type Enum is (One, Two, Three);
Enabled : Boolean := False;
Disabled : Boolean := False;
function Cond1 return Boolean;
function Cond2 return Boolean;
end Opt105_Pkg;

View file

@ -403,12 +403,15 @@ factor_out_conditional_operation (edge e0, edge e1, basic_block merge,
if (dominated_by_p (CDI_DOMINATORS, gimple_bb (phi), gimple_bb (arg0_def_stmt)))
return false;
/* Only handle if arg1 is a INTEGER_CST and one that fits
into the new type or if it is the same precision. */
/* If arg1 is an INTEGER_CST, fold it to new type if it fits, or else
if the bits will not be modified during the conversion, except for
boolean types whose precision is not 1 (see int_fits_type_p). */
if (!INTEGRAL_TYPE_P (TREE_TYPE (new_arg0))
|| !(int_fits_type_p (arg1, TREE_TYPE (new_arg0))
|| (TYPE_PRECISION (TREE_TYPE (new_arg0))
== TYPE_PRECISION (TREE_TYPE (arg1)))))
== TYPE_PRECISION (TREE_TYPE (arg1))
&& (TREE_CODE (TREE_TYPE (new_arg0)) != BOOLEAN_TYPE
|| TYPE_PRECISION (TREE_TYPE (new_arg0)) == 1))))
return false;
/* For the INTEGER_CST case, we are just moving the