I experienced the following ICE when working on a downstream patch for msp430:

void
foo (unsigned int r, unsigned int y)
{
  __builtin_umul_overflow ((unsigned int) (-1), y, &r);
}

> msp430-elf-gcc -S tester.c -O0

tester.c: In function 'foo':
tester.c:4:1: error: unrecognizable insn:
    4 | }
      | ^
(insn 16 15 17 2 (set (reg:HI 32)
        (const_int 65535 [0xffff])) "tester.c":3:3 -1
     (nil))
during RTL pass: vregs
dump file: tester.c.234r.vregs
tester.c:4:1: internal compiler error: in extract_insn, at recog.c:2311

Following discussions on ml/gcc
(https://gcc.gnu.org/ml/gcc/2019-10/msg00083.html), I narrowed this down to a
call to expand_mult_highpart_adjust in expand_expr_real_2.

If one of the operands is a constant, its mode had been converted to the wide
mode of our multiplication to generate some RTL, but not converted back to the
narrow mode before expanding what will be the high part of the result of the
multiplication.

If we look at the other two uses of expand_mult_highpart_adjust in the sources,
(both in expmed.c (expmed_mult_highpart_optab)) we can see that the narrow
version of the constant is always used:
      if (tem)
        /* We used the wrong signedness.  Adjust the result.  */
        return expand_mult_highpart_adjust (mode, tem, op0, narrow_op1,
                                            tem, unsignedp);

So the attached patch updates the use in expand_expr_real_2 to also use the
narrow version of the constant operand.
This fixes the aforementioned ICE.

Successfully bootstrapped and regtested for x86_64-pc-linux-gnu.
Successfully regtested for msp430-elf.

Ok for trunk?
>From b430cddbd257353f162fe3968a447b63cbcaa964 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <joze...@mittosystems.com>
Date: Thu, 17 Oct 2019 18:22:01 +0100
Subject: [PATCH] Use narrow mode of constant when expanding widening
 multiplication

gcc/ChangeLog:

2019-10-18  Jozef Lawrynowicz  <joze...@mittosystems.com>

	* expr.c (expand_expr_real_2): Use op1 in its original narrow mode when
	calling expand_mult_highpart_adjust. 

---
 gcc/expr.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/gcc/expr.c b/gcc/expr.c
index b54bf1d3dc5..0a571d3f7e3 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -8947,9 +8947,12 @@ expand_expr_real_2 (sepops ops, rtx target, machine_mode tmode,
 		  != CODE_FOR_nothing
 		  && innermode == word_mode)
 		{
-		  rtx htem, hipart;
+		  rtx htem, hipart, narrow_op1;
 		  op0 = expand_normal (treeop0);
 		  op1 = expand_normal (treeop1);
+		  /* Save op1 in the narrower mode WORD_MODE for when we expand
+		     the high part.  */
+		  narrow_op1 = op1;
 		  /* op0 and op1 might be constants, despite the above
 		     != INTEGER_CST check.  Handle it.  */
 		  if (GET_MODE (op0) == VOIDmode && GET_MODE (op1) == VOIDmode)
@@ -8961,7 +8964,7 @@ expand_expr_real_2 (sepops ops, rtx target, machine_mode tmode,
 				       unsignedp, OPTAB_LIB_WIDEN);
 		  hipart = gen_highpart (word_mode, temp);
 		  htem = expand_mult_highpart_adjust (word_mode, hipart,
-						      op0, op1, hipart,
+						      op0, narrow_op1, hipart,
 						      zextend_p);
 		  if (htem != hipart)
 		    emit_move_insn (hipart, htem);
-- 
2.17.1

Reply via email to