+\f
+/* A subroutine of ix86_expand_vector_init. Store into TARGET a vector
+ with all elements equal to VAR. Return true if successful. */
+
+static bool
+ix86_expand_vector_init_duplicate (bool mmx_ok, enum machine_mode mode,
+ rtx target, rtx val)
+{
+ enum machine_mode smode, wsmode, wvmode;
+ rtx x;
+
+ switch (mode)
+ {
+ case V2SImode:
+ case V2SFmode:
+ if (!mmx_ok && !TARGET_SSE)
+ return false;
+ /* FALLTHRU */
+
+ case V2DFmode:
+ case V2DImode:
+ case V4SFmode:
+ case V4SImode:
+ val = force_reg (GET_MODE_INNER (mode), val);
+ x = gen_rtx_VEC_DUPLICATE (mode, val);
+ emit_insn (gen_rtx_SET (VOIDmode, target, x));
+ return true;
+
+ case V4HImode:
+ if (!mmx_ok)
+ return false;
+ if (TARGET_SSE || TARGET_3DNOW_A)
+ {
+ val = gen_lowpart (SImode, val);
+ x = gen_rtx_TRUNCATE (HImode, val);
+ x = gen_rtx_VEC_DUPLICATE (mode, x);
+ emit_insn (gen_rtx_SET (VOIDmode, target, x));
+ return true;
+ }
+ else
+ {
+ smode = HImode;
+ wsmode = SImode;
+ wvmode = V2SImode;
+ goto widen;
+ }
+
+ case V8QImode:
+ if (!mmx_ok)
+ return false;
+ smode = QImode;
+ wsmode = HImode;
+ wvmode = V4HImode;
+ goto widen;
+ case V8HImode:
+ smode = HImode;
+ wsmode = SImode;
+ wvmode = V4SImode;
+ goto widen;
+ case V16QImode:
+ smode = QImode;
+ wsmode = HImode;
+ wvmode = V8HImode;
+ goto widen;
+ widen:
+ /* Replicate the value once into the next wider mode and recurse. */
+ val = convert_modes (wsmode, smode, val, true);
+ x = expand_simple_binop (wsmode, ASHIFT, val,
+ GEN_INT (GET_MODE_BITSIZE (smode)),
+ NULL_RTX, 1, OPTAB_LIB_WIDEN);
+ val = expand_simple_binop (wsmode, IOR, val, x, x, 1, OPTAB_LIB_WIDEN);
+
+ x = gen_reg_rtx (wvmode);
+ if (!ix86_expand_vector_init_duplicate (mmx_ok, wvmode, x, val))
+ gcc_unreachable ();
+ emit_move_insn (target, gen_lowpart (mode, x));
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+/* A subroutine of ix86_expand_vector_init. Store into TARGET a vector
+ whose low element is VAR, and other elements are zero. Return true
+ if successful. */
+
+static bool
+ix86_expand_vector_init_low_nonzero (bool mmx_ok, enum machine_mode mode,
+ rtx target, rtx var)
+{
+ enum machine_mode vsimode;
+ rtx x;
+
+ switch (mode)
+ {
+ case V2SFmode:
+ case V2SImode:
+ if (!mmx_ok && !TARGET_SSE)
+ return false;
+ /* FALLTHRU */
+
+ case V2DFmode:
+ case V2DImode:
+ var = force_reg (GET_MODE_INNER (mode), var);
+ x = gen_rtx_VEC_CONCAT (mode, var, CONST0_RTX (GET_MODE_INNER (mode)));
+ emit_insn (gen_rtx_SET (VOIDmode, target, x));
+ return true;
+
+ case V4SFmode:
+ case V4SImode:
+ var = force_reg (GET_MODE_INNER (mode), var);
+ x = gen_rtx_VEC_DUPLICATE (mode, var);
+ x = gen_rtx_VEC_MERGE (mode, x, CONST0_RTX (mode), const1_rtx);
+ emit_insn (gen_rtx_SET (VOIDmode, target, x));
+ return true;
+
+ case V8HImode:
+ case V16QImode:
+ vsimode = V4SImode;
+ goto widen;
+ case V4HImode:
+ case V8QImode:
+ if (!mmx_ok)
+ return false;
+ vsimode = V2SImode;
+ goto widen;
+ widen:
+ /* Zero extend the variable element to SImode and recurse. */
+ var = convert_modes (SImode, GET_MODE_INNER (mode), var, true);
+
+ x = gen_reg_rtx (vsimode);
+ if (!ix86_expand_vector_init_low_nonzero (mmx_ok, vsimode, x, var))
+ gcc_unreachable ();
+
+ emit_move_insn (target, gen_lowpart (mode, x));
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+/* A subroutine of ix86_expand_vector_init. Store into TARGET a vector
+ consisting of the values in VALS. It is known that all elements
+ except ONE_VAR are constants. Return true if successful. */
+
+static bool
+ix86_expand_vector_init_one_var (bool mmx_ok, enum machine_mode mode,
+ rtx target, rtx vals, int one_var)
+{
+ rtx var = XVECEXP (vals, 0, one_var);
+ enum machine_mode wmode;
+ rtx const_vec, x;
+
+ XVECEXP (vals, 0, one_var) = CONST0_RTX (GET_MODE_INNER (mode));
+ const_vec = gen_rtx_CONST_VECTOR (mode, XVEC (vals, 0));
+
+ switch (mode)
+ {
+ case V2DFmode:
+ case V2DImode:
+ case V2SFmode:
+ case V2SImode:
+ /* For the two element vectors, it's just as easy to use
+ the general case. */
+ return false;
+
+ case V4SFmode:
+ case V4SImode:
+ case V8HImode:
+ case V4HImode:
+ break;
+
+ case V16QImode:
+ wmode = V8HImode;
+ goto widen;
+ case V8QImode:
+ wmode = V4HImode;
+ goto widen;
+ widen:
+ /* There's no way to set one QImode entry easily. Combine
+ the variable value with its adjacent constant value, and
+ promote to an HImode set. */
+ x = XVECEXP (vals, 0, one_var ^ 1);
+ if (one_var & 1)
+ {
+ var = convert_modes (HImode, QImode, var, true);
+ var = expand_simple_binop (HImode, ASHIFT, var, GEN_INT (8),
+ NULL_RTX, 1, OPTAB_LIB_WIDEN);
+ x = GEN_INT (INTVAL (x) & 0xff);
+ }
+ else
+ {
+ var = convert_modes (HImode, QImode, var, true);
+ x = gen_int_mode (INTVAL (x) << 8, HImode);
+ }
+ if (x != const0_rtx)
+ var = expand_simple_binop (HImode, IOR, var, x, var,
+ 1, OPTAB_LIB_WIDEN);
+
+ x = gen_reg_rtx (wmode);
+ emit_move_insn (x, gen_lowpart (wmode, const_vec));
+ ix86_expand_vector_set (mmx_ok, x, var, one_var >> 1);
+
+ emit_move_insn (target, gen_lowpart (mode, x));
+ return true;
+
+ default:
+ return false;
+ }
+
+ emit_move_insn (target, const_vec);
+ ix86_expand_vector_set (mmx_ok, target, var, one_var);
+ return true;
+}
+
+/* A subroutine of ix86_expand_vector_init. Handle the most general case:
+ all values variable, and none identical. */
+
+static void
+ix86_expand_vector_init_general (bool mmx_ok, enum machine_mode mode,
+ rtx target, rtx vals)
+{
+ enum machine_mode half_mode = GET_MODE_INNER (mode);
+ rtx op0 = NULL, op1 = NULL;
+ bool use_vec_concat = false;
+
+ switch (mode)
+ {
+ case V2SFmode:
+ case V2SImode:
+ if (!mmx_ok && !TARGET_SSE)
+ break;
+ /* FALLTHRU */
+
+ case V2DFmode:
+ case V2DImode:
+ /* For the two element vectors, we always implement VEC_CONCAT. */
+ op0 = XVECEXP (vals, 0, 0);
+ op1 = XVECEXP (vals, 0, 1);
+ use_vec_concat = true;
+ break;
+
+ case V4SFmode:
+ half_mode = V2SFmode;
+ goto half;
+ case V4SImode:
+ half_mode = V2SImode;
+ goto half;
+ half:
+ {
+ rtvec v;
+
+ /* For V4SF and V4SI, we implement a concat of two V2 vectors.
+ Recurse to load the two halves. */
+
+ op0 = gen_reg_rtx (half_mode);
+ v = gen_rtvec (2, XVECEXP (vals, 0, 0), XVECEXP (vals, 0, 1));
+ ix86_expand_vector_init (false, op0, gen_rtx_PARALLEL (half_mode, v));
+
+ op1 = gen_reg_rtx (half_mode);
+ v = gen_rtvec (2, XVECEXP (vals, 0, 2), XVECEXP (vals, 0, 3));
+ ix86_expand_vector_init (false, op1, gen_rtx_PARALLEL (half_mode, v));
+
+ use_vec_concat = true;
+ }
+ break;
+
+ case V8HImode:
+ case V16QImode:
+ case V4HImode:
+ case V8QImode:
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ if (use_vec_concat)
+ {
+ if (!register_operand (op0, half_mode))
+ op0 = force_reg (half_mode, op0);
+ if (!register_operand (op1, half_mode))
+ op1 = force_reg (half_mode, op1);
+
+ emit_insn (gen_rtx_SET (VOIDmode, target,
+ gen_rtx_VEC_CONCAT (mode, op0, op1)));
+ }
+ else
+ {
+ int i, j, n_elts, n_words, n_elt_per_word;
+ enum machine_mode inner_mode;
+ rtx words[4], shift;
+
+ inner_mode = GET_MODE_INNER (mode);
+ n_elts = GET_MODE_NUNITS (mode);
+ n_words = GET_MODE_SIZE (mode) / UNITS_PER_WORD;
+ n_elt_per_word = n_elts / n_words;
+ shift = GEN_INT (GET_MODE_BITSIZE (inner_mode));
+
+ for (i = 0; i < n_words; ++i)
+ {
+ rtx word = NULL_RTX;
+
+ for (j = 0; j < n_elt_per_word; ++j)
+ {
+ rtx elt = XVECEXP (vals, 0, (i+1)*n_elt_per_word - j - 1);
+ elt = convert_modes (word_mode, inner_mode, elt, true);
+
+ if (j == 0)
+ word = elt;
+ else
+ {
+ word = expand_simple_binop (word_mode, ASHIFT, word, shift,
+ word, 1, OPTAB_LIB_WIDEN);
+ word = expand_simple_binop (word_mode, IOR, word, elt,
+ word, 1, OPTAB_LIB_WIDEN);
+ }
+ }
+
+ words[i] = word;
+ }
+
+ if (n_words == 1)
+ emit_move_insn (target, gen_lowpart (mode, words[0]));
+ else if (n_words == 2)
+ {
+ rtx tmp = gen_reg_rtx (mode);
+ emit_insn (gen_rtx_CLOBBER (VOIDmode, tmp));
+ emit_move_insn (gen_lowpart (word_mode, tmp), words[0]);
+ emit_move_insn (gen_highpart (word_mode, tmp), words[1]);
+ emit_move_insn (target, tmp);
+ }
+ else if (n_words == 4)
+ {
+ rtx tmp = gen_reg_rtx (V4SImode);
+ vals = gen_rtx_PARALLEL (V4SImode, gen_rtvec_v (4, words));
+ ix86_expand_vector_init_general (false, V4SImode, tmp, vals);
+ emit_move_insn (target, gen_lowpart (mode, tmp));
+ }
+ else
+ gcc_unreachable ();
+ }
+}
+
+/* Initialize vector TARGET via VALS. Suppress the use of MMX
+ instructions unless MMX_OK is true. */