This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
Checking out PR4483 (constant overflow on PPC, problems compiling Linux kernel)
- From: Corey Minyard <minyard at acm dot org>
- To: gcc at gcc dot gnu dot org
- Date: Wed, 28 Nov 2001 13:27:32 -0600
- Subject: Checking out PR4483 (constant overflow on PPC, problems compiling Linux kernel)
I tried to compile the Linux kernel on PowerPC, and I ran into a problem
someone previously reports (PR4483,
http://gcc.gnu.org/ml/gcc-prs/2001-10/msg00105.html). I've played
around with it some, and it looks like a constant is overflowing in the
gcse stage. The PR has a testcase in it.
In the situation, an INSN references two other registers that are
constants (one is 0xffffffff80000000, the other is 42) and subtracts the
second from the first (MINUS code). In validate_replace_rtx_1
(recog.c), it replaces the registers with constants, then adds them.
This results in a constant 0xffffffff7fffffd6, which is obviously now
too negative to fit into an SI register. The original constant was
positive (0x80000000UL in the source). Since constants don't have
modes, the addition code doesn't handle this (and it doesn't look like
it cares, anyway).
I have attached a patch that adds a PowerPC production to truncate a
constant that is too large that goes into a register. However, I'm not
sure if this is the right way to handle this, but it at least works
around the problem. The main production that handles this won't work
because it checks for constant overflows.
I compiled the Linux kernel (after working around a few small problems
there) successfully. However, it crashed at startup :-(.
-Corey
Index: config/rs6000/rs6000-protos.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/rs6000/rs6000-protos.h,v
retrieving revision 1.26
diff -u -r1.26 rs6000-protos.h
--- rs6000-protos.h 2001/11/04 02:40:39 1.26
+++ rs6000-protos.h 2001/11/27 17:45:11
@@ -70,6 +70,7 @@
extern int current_file_function_operand PARAMS ((rtx, enum machine_mode));
extern int input_operand PARAMS ((rtx, enum machine_mode));
extern int small_data_operand PARAMS ((rtx, enum machine_mode));
+extern int large_SI_int_operand PARAMS ((rtx, enum machine_mode));
extern int constant_pool_expr_p PARAMS ((rtx));
extern int toc_relative_expr_p PARAMS ((rtx));
extern int expand_block_move PARAMS ((rtx[]));
Index: config/rs6000/rs6000.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/rs6000/rs6000.c,v
retrieving revision 1.242
diff -u -r1.242 rs6000.c
--- rs6000.c 2001/11/24 06:44:10 1.242
+++ rs6000.c 2001/11/27 17:45:39
@@ -883,6 +883,23 @@
|| GET_CODE (op) == LABEL_REF);
}
+/* Return 1 if the operand is a constant that is larger than can fit
+ into a 32-bit register. */
+int
+large_SI_int_operand (op, mode)
+ rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ if (GET_CODE (op) != CONST_INT)
+ return 0;
+
+ if ((INTVAL (op) <= ((HOST_WIDE_INT) 2147483647))
+ && (INTVAL (op) >= ((HOST_WIDE_INT) -2147483648LL)))
+ return 0;
+
+ return 1;
+}
+
/* Return 1 if the operand is a simple references that can be loaded via
the GOT (labels involving addition aren't allowed). */
Index: config/rs6000/rs6000.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/rs6000/rs6000.h,v
retrieving revision 1.143
diff -u -r1.143 rs6000.h
--- rs6000.h 2001/11/22 02:19:56 1.143
+++ rs6000.h 2001/11/27 17:45:52
@@ -2773,6 +2773,7 @@
{"reg_or_add_cint64_operand", {SUBREG, REG, CONST_INT}}, \
{"reg_or_sub_cint64_operand", {SUBREG, REG, CONST_INT}}, \
{"reg_or_logical_cint_operand", {SUBREG, REG, CONST_INT, CONST_DOUBLE}}, \
+ {"large_SI_int_operand", {CONST_INT}}, \
{"got_operand", {SYMBOL_REF, CONST, LABEL_REF}}, \
{"got_no_const_operand", {SYMBOL_REF, LABEL_REF}}, \
{"easy_fp_constant", {CONST_DOUBLE}}, \
Index: config/rs6000/rs6000.md
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/rs6000/rs6000.md,v
retrieving revision 1.140
diff -u -r1.140 rs6000.md
--- rs6000.md 2001/11/24 06:44:11 1.140
+++ rs6000.md 2001/11/27 17:46:41
@@ -7487,6 +7487,33 @@
operands[3] = GEN_INT (INTVAL (operands[1]) & 0xffff);
}")
+;; This split is used to truncate constant integers that have overflowed
+;; during calculations
+
+(define_split
+ [(set (match_operand:SI 0 "gpc_reg_operand" "")
+ (match_operand:SI 1 "large_SI_int_operand" ""))]
+ ""
+ [(set (match_dup 0)
+ (match_dup 2))
+ (set (match_dup 0)
+ (ior:SI (match_dup 0)
+ (match_dup 3)))]
+ "
+{
+ HOST_WIDE_INT v = INTVAL (operands[1]);
+
+ operands[3] = GEN_INT (v & 0xffff);
+
+ /* If the constant is negative, then sign extend it, otherwise
+ chop it off. */
+ if (v & 0x80000000) {
+ v = v | (((HOST_WIDE_INT) -1) & (~ (HOST_WIDE_INT) 0xffffffff));
+ operands[2] = GEN_INT (v & (~ (HOST_WIDE_INT) 0xffff));
+ } else
+ operands[2] = GEN_INT (v & 0xffff0000);
+}")
+
(define_insn "*movsi_internal2"
[(set (match_operand:CC 2 "cc_reg_operand" "=x,?y")
(compare:CC (match_operand:SI 1 "gpc_reg_operand" "r,r")