PR66178 has some testcases where we construct expressions involving
additions and subtractions of label addresses, and we crash when trying
to expand these. There are two different issues here, shown by various
testcases in the PR:
* expand_expr_real_2 can drop EXPAND_INITIALIZER and then go into
a path where it wants to gen_reg_rtx.
* simplify-rtx can turn subtractions into NOT operations in some
cases. That seems inadvisable for symbolic expressions.
The following was bootstrapped and tested on x86_64-linux. Ok for all
branches?
PR middle-end/66178
* expr.c (expand_expr_real_2) [PLUS_EXPR, MINUS_EXPR]: Don't
drop EXPAND_INITIALIZER.
* rtl.h (contains_symbolic_reference_p): Declare.
* rtlanal.c (contains_symbolic_reference_p): New function.
* simplify-rtx.c (simplify_binary_operation_1): Don't turn
a subtraction into a NOT if symbolic constants are involved.
testsuite/
PR middle-end/66178
gcc.dg/torture/pr66178.c: New test.
Index: gcc/expr.c
===================================================================
--- gcc/expr.c (revision 232555)
+++ gcc/expr.c (working copy)
@@ -8381,11 +8381,11 @@ expand_expr_real_2 (sepops ops, rtx targ
if it's all in the wrong mode to form part of an address.
And force_operand won't know whether to sign-extend or
zero-extend. */
- if ((modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER)
- || mode != ptr_mode)
+ if (modifier != EXPAND_INITIALIZER
+ && (modifier != EXPAND_SUM || mode != ptr_mode))
{
expand_operands (treeop0, treeop1,
- subtarget, &op0, &op1, EXPAND_NORMAL);
+ subtarget, &op0, &op1, modifier);
if (op0 == const0_rtx)
return op1;
if (op1 == const0_rtx)
@@ -8424,8 +8424,8 @@ expand_expr_real_2 (sepops ops, rtx targ
if it's all in the wrong mode to form part of an address.
And force_operand won't know whether to sign-extend or
zero-extend. */
- if ((modifier != EXPAND_SUM && modifier != EXPAND_INITIALIZER)
- || mode != ptr_mode)
+ if (modifier != EXPAND_INITIALIZER
+ && (modifier != EXPAND_SUM || mode != ptr_mode))
goto binop;
expand_operands (treeop0, treeop1,
Index: gcc/rtl.h
===================================================================
--- gcc/rtl.h (revision 232555)
+++ gcc/rtl.h (working copy)
@@ -2931,6 +2931,7 @@ extern void set_insn_deleted (rtx);
extern rtx single_set_2 (const rtx_insn *, const_rtx);
extern bool contains_symbol_ref_p (const_rtx);
+extern bool contains_symbolic_reference_p (const_rtx);
/* Handle the cheap and common cases inline for performance. */
Index: gcc/rtlanal.c
===================================================================
--- gcc/rtlanal.c (revision 232555)
+++ gcc/rtlanal.c (working copy)
@@ -6243,6 +6243,19 @@ contains_symbol_ref_p (const_rtx x)
return false;
}
+/* Return true if RTL X contains a SYMBOL_REF or LABEL_REF. */
+
+bool
+contains_symbolic_reference_p (const_rtx x)
+{
+ subrtx_iterator::array_type array;
+ FOR_EACH_SUBRTX (iter, array, x, ALL)
+ if (SYMBOL_REF_P (*iter) || GET_CODE (*iter) == LABEL_REF)
+ return true;
+
+ return false;
+}
+
/* Return true if X contains a thread-local symbol. */
bool
Index: gcc/simplify-rtx.c
===================================================================
--- gcc/simplify-rtx.c (revision 232555)
+++ gcc/simplify-rtx.c (working copy)
@@ -2277,8 +2277,11 @@ simplify_binary_operation_1 (enum rtx_co
if (!HONOR_SIGNED_ZEROS (mode) && trueop0 == CONST0_RTX (mode))
return simplify_gen_unary (NEG, mode, op1, mode);
- /* (-1 - a) is ~a. */
- if (trueop0 == constm1_rtx)
+ /* (-1 - a) is ~a, unless the expression avoids symbolic constants,
+ in which case not retaining additions and subtractions could
+ cause invalid assembly to be produced. */
+ if (trueop0 == constm1_rtx
+ && !contains_symbolic_reference_p (op1))
return simplify_gen_unary (NOT, mode, op1, mode);
/* Subtracting 0 has no effect unless the mode has signed zeros
Index: gcc/testsuite/gcc.dg/torture/pr66178.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/pr66178.c (revision 0)
+++ gcc/testsuite/gcc.dg/torture/pr66178.c (working copy)
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+int test(void)
+{
+ static int a = ((char *)&&l1-(char *)&&l2)-1;
+l1:
+l2:
+ return a;
+}
+
+int test2(void)
+{
+ static int a = ((char *)&&l2-(char *)&&l3)+((char *)&&l1-(char *)&&l2);
+l1:
+l2:
+l3:
+ return a;
+}