This fixes DImode multiply of negative immediate constants. The issues
originate from the 32-bit encoding in assembler. The solution is to force the
constant into a register.
gcc/ChangeLog:
* config/gcn/gcn.md (<su>mulsi3_highpart): Use the _imm insn only
for constants that mean the same as SImode and as DImode, and force
other operands into a register.
(<su>mulsidi3): Likewise.
(<su>mulsi3_highpart_imm): Only accept such constants.
(<su>mulsidi3_imm): Likewise.
(muldi3): Load a negative low-part constant into the scratch
register before the umulsidi3.
---
gcc/config/gcn/gcn.md | 40 ++++++++++++++++++++++++++++++----------
1 file changed, 30 insertions(+), 10 deletions(-)
diff --git a/gcc/config/gcn/gcn.md b/gcc/config/gcn/gcn.md
index 3e6f132b97d..202a4a2251d 100644
--- a/gcc/config/gcn/gcn.md
+++ b/gcc/config/gcn/gcn.md
@@ -1473,12 +1473,15 @@ (define_expand "<su>mulsi3_highpart"
(const_int 32))))]
""
{
- if (REG_P (operands[2]))
- emit_insn (gen_<su>mulsi3_highpart_reg (operands[0], operands[1],
- operands[2]));
- else
+ /* The _imm insn reads the DImode constant as the extended SImode
+ value, so only use it where the two are the same. */
+ if (CONST_INT_P (operands[2])
+ && ('<su>' == 's' || INTVAL (operands[2]) >= 0))
emit_insn (gen_<su>mulsi3_highpart_imm (operands[0], operands[1],
operands[2]));
+ else
+ emit_insn (gen_<su>mulsi3_highpart_reg (operands[0], operands[1],
+ force_reg (SImode, operands[2])));
DONE;
})
@@ -1509,7 +1512,9 @@ (define_insn "<su>mulsi3_highpart_imm"
(match_operand:SI 1 "register_operand" "Sg,Sg,v"))
(match_operand:DI 2 "gcn_32bit_immediate_operand" "A, B,A"))
(const_int 32))))]
- ""
+ "CONST_INT_P (operands[2])
+ && INTVAL (operands[2]) == trunc_int_for_mode (INTVAL (operands[2]), SImode)
+ && ('<su>' == 's' || INTVAL (operands[2]) >= 0)"
"@
s_mul_hi<sgnsuffix>0\t%0, %1, %2
s_mul_hi<sgnsuffix>0\t%0, %1, %2
@@ -1525,10 +1530,14 @@ (define_expand "<su>mulsidi3"
(match_operand:SI 2 "nonmemory_operand" ""))))]
""
{
- if (REG_P (operands[2]))
- emit_insn (gen_<su>mulsidi3_reg (operands[0], operands[1], operands[2]));
- else
+ /* The _imm insn reads the DImode constant as the extended SImode
+ value, so only use it where the two are the same. */
+ if (CONST_INT_P (operands[2])
+ && ('<su>' == 's' || INTVAL (operands[2]) >= 0))
emit_insn (gen_<su>mulsidi3_imm (operands[0], operands[1], operands[2]));
+ else
+ emit_insn (gen_<su>mulsidi3_reg (operands[0], operands[1],
+ force_reg (SImode, operands[2])));
DONE;
})
@@ -1557,7 +1566,9 @@ (define_insn_and_split "<su>mulsidi3_imm"
(match_operand:SI 1 "register_operand" "Sg, Sg, v"))
(match_operand:DI 2 "gcn_32bit_immediate_operand"
"A, B, A")))]
- ""
+ "CONST_INT_P (operands[2])
+ && INTVAL (operands[2]) == trunc_int_for_mode (INTVAL (operands[2]), SImode)
+ && ('<su>' == 's' || INTVAL (operands[2]) >= 0)"
"#"
"&& reload_completed"
[(const_int 0)]
@@ -1586,7 +1597,16 @@ (define_insn_and_split "muldi3"
rtx op1hi = gcn_operand_part (DImode, operands[1], 1);
rtx op2lo = gcn_operand_part (DImode, operands[2], 0);
rtx op2hi = gcn_operand_part (DImode, operands[2], 1);
- emit_insn (gen_umulsidi3 (operands[0], op1lo, op2lo));
+ rtx op2lo_ext = op2lo;
+ /* The low part is unsigned, so the expander would force a negative
+ constant into a new register, which we can't do after reload.
+ Use TMP (which is not needed yet) instead. */
+ if (CONST_INT_P (op2lo) && INTVAL (op2lo) < 0)
+ {
+ emit_move_insn (tmp, op2lo);
+ op2lo_ext = tmp;
+ }
+ emit_insn (gen_umulsidi3 (operands[0], op1lo, op2lo_ext));
emit_insn (gen_mulsi3 (tmp, op1lo, op2hi));
rtx add = gen_rtx_SET (dsthi, gen_rtx_PLUS (SImode, dsthi, tmp));
rtx clob = gen_rtx_CLOBBER (VOIDmode, operands[4]);
--
2.54.0