A _BitInt value may rely on a conversion to become properly extended. So a conversion to _BitInt is not trivially removable even if the types of the result and the operand have the same precision and size.
This patches fixes gcc.dg/torture/bitint-64.c at -O2 on LoongArch, which fails because extension of the result is dropped in a compare-and-swap loop generated for incrementing an _Atomic _BitInt, causing an ABI violation. gcc/ChangeLog: * match.pd: Preserve conversion to _BitInt before a VCE if the _BitInt is extended. --- gcc/match.pd | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/gcc/match.pd b/gcc/match.pd index f4416d9172c..1df52155a05 100644 --- a/gcc/match.pd +++ b/gcc/match.pd @@ -5420,16 +5420,31 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) (convert @0))) /* Strip inner integral conversions that do not change precision or size, or - zero-extend while keeping the same size (for bool-to-char). */ + zero-extend while keeping the same size (for bool-to-char). + However, keep this conversion if the result is an extended _BitInt, + since it may rely on this conversion to extend properly. */ + (simplify (view_convert (convert@0 @1)) + (with { + bool extended_bitint = false; + if (BITINT_TYPE_P (TREE_TYPE (@0))) + { + struct bitint_info info; + extended_bitint + = targetm.c.bitint_type_info (TYPE_PRECISION (TREE_TYPE (@0)), + &info); + extended_bitint = extended_bitint && info.extended; + } + } (if ((INTEGRAL_TYPE_P (TREE_TYPE (@0)) || POINTER_TYPE_P (TREE_TYPE (@0))) && (INTEGRAL_TYPE_P (TREE_TYPE (@1)) || POINTER_TYPE_P (TREE_TYPE (@1))) + && !extended_bitint && TYPE_SIZE (TREE_TYPE (@0)) == TYPE_SIZE (TREE_TYPE (@1)) && (TYPE_PRECISION (TREE_TYPE (@0)) == TYPE_PRECISION (TREE_TYPE (@1)) || (TYPE_PRECISION (TREE_TYPE (@0)) > TYPE_PRECISION (TREE_TYPE (@1)) && TYPE_UNSIGNED (TREE_TYPE (@1))))) - (view_convert @1))) + (view_convert @1)))) /* Simplify a view-converted empty or single-element constructor. */ (simplify -- 2.46.0