===================================================================
@@ -58,6 +58,7 @@ extern void rs6000_expand_vector_extract (rtx, rtx
extern bool altivec_expand_vec_perm_const (rtx op[4]);
extern void altivec_expand_vec_perm_le (rtx op[4]);
extern bool rs6000_expand_vec_perm_const (rtx op[4]);
+extern bool altivec_expand_mul_highpart (rtx op[3], bool);
extern void rs6000_expand_extract_even (rtx, rtx, rtx);
extern void rs6000_expand_interleave (rtx, rtx, rtx, bool);
extern void build_mask64_2_operands (rtx, rtx *);
===================================================================
@@ -29249,6 +29249,58 @@ rs6000_do_expand_vec_perm (rtx target, rtx op0, rt
emit_move_insn (target, x);
}
+/* Expand an Altivec multiply high-part. The logic matches the
+ general logic in optabs.c:expand_mult_highpart, but swaps the
+ inputs for little endian. Note that we will swap them again
+ during the permute; this is the one case where we don't want
+ the operands swapped, as if we do we get the even and odd values
+ reversed. */
+
+bool
+altivec_expand_mul_highpart (rtx operands[3], bool uns_p)
+{
+ struct expand_operand eops[3];
+ rtx m1, m2, perm, tmp;
+ int i;
+
+ optab tab1 = uns_p ? vec_widen_umult_even_optab : vec_widen_smult_even_optab;
+ optab tab2 = uns_p ? vec_widen_umult_odd_optab : vec_widen_umult_odd_optab;
+ enum machine_mode mode = GET_MODE (operands[0]);
+ enum insn_code icode = optab_handler (tab1, mode);
+ int nunits = GET_MODE_NUNITS (mode);
+ enum machine_mode wmode = insn_data[icode].operand[0].mode;
+ rtvec v = rtvec_alloc (nunits);
+
+ create_output_operand (&eops[0], gen_reg_rtx (wmode), wmode);
+ create_input_operand (&eops[1], operands[1], mode);
+ create_input_operand (&eops[2], operands[2], mode);
+ expand_insn (icode, 3, eops);
+ m1 = gen_lowpart (mode, eops[0].value);
+
+ create_output_operand (&eops[0], gen_reg_rtx (wmode), wmode);
+ create_input_operand (&eops[1], operands[1], mode);
+ create_input_operand (&eops[2], operands[2], mode);
+ expand_insn (optab_handler (tab2, mode), 3, eops);
+ m2 = gen_lowpart (mode, eops[0].value);
+
+ for (i = 0; i < nunits; ++i)
+ RTVEC_ELT (v, i) = GEN_INT (!BYTES_BIG_ENDIAN + (i & ~1)
+ + ((i & 1) ? nunits : 0));
+
+ perm = gen_rtx_CONST_VECTOR (mode, v);
+
+ if (!BYTES_BIG_ENDIAN) {
+ tmp = m1;
+ m1 = m2;
+ m2 = tmp;
+ }
+
+ perm = expand_vec_perm (mode, m1, m2, perm, NULL_RTX);
+ emit_move_insn (operands[0], perm);
+
+ return true;
+}
+
/* Expand an extract even operation. */
void
===================================================================
@@ -1401,6 +1401,30 @@
FAIL;
})
+(define_expand "umul<mode>3_highpart"
+ [(match_operand:VIshort 0 "register_operand" "")
+ (match_operand:VIshort 1 "register_operand" "")
+ (match_operand:VIshort 2 "register_operand" "")]
+ "TARGET_ALTIVEC"
+{
+ if (altivec_expand_mul_highpart (operands, /*uns_p*/true))
+ DONE;
+ else
+ FAIL;
+})
+
+(define_expand "smul<mode>3_highpart"
+ [(match_operand:VIshort 0 "register_operand" "")
+ (match_operand:VIshort 1 "register_operand" "")
+ (match_operand:VIshort 2 "register_operand" "")]
+ "TARGET_ALTIVEC"
+{
+ if (altivec_expand_mul_highpart (operands, /*uns_p*/false))
+ DONE;
+ else
+ FAIL;
+})
+
(define_insn "altivec_vrfip" ; ceil
[(set (match_operand:V4SF 0 "register_operand" "=v")
(unspec:V4SF [(match_operand:V4SF 1 "register_operand" "v")]