/*
 * Copyright 2003 Tungsten Graphics, inc.
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * on the rights to use, copy, modify, merge, publish, distribute, sub
 * license, and/or sell copies of the Software, and to permit persons to whom
 * the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
 * TUNGSTEN GRAPHICS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 * USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * Authors:
 *    Keith Whitwell <keithw@tungstengraphics.com>
 */


#include "pipe/p_config.h"
#include "pipe/p_compiler.h"
#include "util/u_memory.h"
#include "util/u_math.h"
#include "util/u_format.h"

#include "translate.h"


#if (defined(PIPE_ARCH_X86) || (defined(PIPE_ARCH_X86_64) && !defined(__MINGW32__))) && !defined(PIPE_SUBSYSTEM_EMBEDDED)

#include "rtasm/rtasm_cpu.h"
#include "rtasm/rtasm_x86sse.h"


#define X    0
#define Y    1
#define Z    2
#define W    3


struct translate_buffer {
   const void *base_ptr;
   uintptr_t stride;
   unsigned max_index;
};

struct translate_buffer_variant {
   unsigned buffer_index;
   unsigned instance_divisor;
   void *ptr;                    /* updated either per vertex or per instance */
};


#define ELEMENT_BUFFER_INSTANCE_ID  1001

#define NUM_CONSTS 7

enum
{
   CONST_IDENTITY,
   CONST_INV_127,
   CONST_INV_255,
   CONST_INV_32767,
   CONST_INV_65535,
   CONST_INV_2147483647,
   CONST_255
};

#define C(v) {(float)(v), (float)(v), (float)(v), (float)(v)}
static float consts[NUM_CONSTS][4] = {
      {0, 0, 0, 1},
      C(1.0 / 127.0),
      C(1.0 / 255.0),
      C(1.0 / 32767.0),
      C(1.0 / 65535.0),
      C(1.0 / 2147483647.0),
      C(255.0)
};
#undef C

struct translate_sse {
   struct translate translate;

   struct x86_function linear_func;
   struct x86_function elt_func;
   struct x86_function elt16_func;
   struct x86_function elt8_func;
   struct x86_function *func;

   PIPE_ALIGN_VAR(16) float consts[NUM_CONSTS][4];
   int8_t reg_to_const[16];
   int8_t const_to_reg[NUM_CONSTS];

   struct translate_buffer buffer[PIPE_MAX_ATTRIBS];
   unsigned nr_buffers;

   /* Multiple buffer variants can map to a single buffer. */
   struct translate_buffer_variant buffer_variant[PIPE_MAX_ATTRIBS];
   unsigned nr_buffer_variants;

   /* Multiple elements can map to a single buffer variant. */
   unsigned element_to_buffer_variant[PIPE_MAX_ATTRIBS];

   boolean use_instancing;
   unsigned instance_id;

   /* these are actually known values, but putting them in a struct
    * like this is helpful to keep them in sync across the file.
    */
   struct x86_reg tmp_EAX;
   struct x86_reg tmp2_EDX;
   struct x86_reg src_ECX;
   struct x86_reg idx_ESI;     /* either start+i or &elt[i] */
   struct x86_reg machine_EDI;
   struct x86_reg outbuf_EBX;
   struct x86_reg count_EBP;    /* decrements to zero */
};

static int get_offset( const void *a, const void *b )
{
   return (const char *)b - (const char *)a;
}

static struct x86_reg get_const( struct translate_sse *p, unsigned id)
{
   struct x86_reg reg;
   unsigned i;

   if(p->const_to_reg[id] >= 0)
      return x86_make_reg(file_XMM, p->const_to_reg[id]);

   for(i = 2; i < 8; ++i)
   {
      if(p->reg_to_const[i] < 0)
         break;
   }

   /* TODO: be smarter here */
   if(i == 8)
      --i;

   reg = x86_make_reg(file_XMM, i);

   if(p->reg_to_const[i] >= 0)
      p->const_to_reg[p->reg_to_const[i]] = -1;

   p->reg_to_const[i] = id;
   p->const_to_reg[id] = i;

   /* TODO: this should happen outside the loop, if possible */
   sse_movaps(p->func, reg,
         x86_make_disp(p->machine_EDI,
               get_offset(p, &p->consts[id][0])));

   return reg;
}

/* load the data in a SSE2 register, padding with zeros */
static boolean emit_load_sse2( struct translate_sse *p,
				       struct x86_reg data,
				       struct x86_reg src,
				       unsigned size)
{
   struct x86_reg tmpXMM = x86_make_reg(file_XMM, 1);
   struct x86_reg tmp = p->tmp_EAX;
   switch(size)
   {
   case 1:
      x86_movzx8(p->func, tmp, src);
      sse2_movd(p->func, data, tmp);
      break;
   case 2:
      x86_movzx16(p->func, tmp, src);
      sse2_movd(p->func, data, tmp);
      break;
   case 3:
      x86_movzx8(p->func, tmp, x86_make_disp(src, 2));
      x86_shl_imm(p->func, tmp, 16);
      x86_mov16(p->func, tmp, src);
      sse2_movd(p->func, data, tmp);
      break;
   case 4:
      sse2_movd(p->func, data, src);
      break;
   case 6:
      sse2_movd(p->func, data, src);
      x86_movzx16(p->func, tmp, x86_make_disp(src, 4));
      sse2_movd(p->func, tmpXMM, tmp);
      sse2_punpckldq(p->func, data, tmpXMM);
      break;
   case 8:
      sse2_movq(p->func, data, src);
      break;
   case 12:
      sse2_movq(p->func, data, src);
      sse2_movd(p->func, tmpXMM, x86_make_disp(src, 8));
      sse2_punpcklqdq(p->func, data, tmpXMM);
      break;
   case 16:
      sse2_movdqu(p->func, data, src);
      break;
   default:
      return FALSE;
   }
   return TRUE;
}

/* this value can be passed for the out_chans argument */
#define CHANNELS_0001 5

/* this function will load #chans float values, and will
 * pad the register with zeroes at least up to out_chans.
 *
 * If out_chans is set to CHANNELS_0001, then the fourth
 * value will be padded with 1. Only pass this value if
 * chans < 4 or results are undefined.
 */
static void emit_load_float32( struct translate_sse *p,
                                       struct x86_reg data,
                                       struct x86_reg arg0,
                                       unsigned out_chans,
                                       unsigned chans)
{
   switch(chans)
   {
   case 1:
      /* a 0 0 0
       * a 0 0 1
       */
      sse_movss(p->func, data, arg0);
      if(out_chans == CHANNELS_0001)
         sse_orps(p->func, data, get_const(p, CONST_IDENTITY) );
      break;
   case 2:
      /* 0 0 0 1
       * a b 0 1
       */
      if(out_chans == CHANNELS_0001)
         sse_shufps(p->func, data, get_const(p, CONST_IDENTITY), SHUF(X, Y, Z, W) );
      else if(out_chans > 2)
         sse_movlhps(p->func, data, get_const(p, CONST_IDENTITY) );
      sse_movlps(p->func, data, arg0);
      break;
   case 3:
      /* Have to jump through some hoops:
       *
       * c 0 0 0
       * c 0 0 1 if out_chans == CHANNELS_0001
       * 0 0 c 0/1
       * a b c 0/1
       */
      sse_movss(p->func, data, x86_make_disp(arg0, 8));
      if(out_chans == CHANNELS_0001)
         sse_shufps(p->func, data, get_const(p, CONST_IDENTITY), SHUF(X,Y,Z,W) );
      sse_shufps(p->func, data, data, SHUF(Y,Z,X,W) );
      sse_movlps(p->func, data, arg0);
      break;
   case 4:
      sse_movups(p->func, data, arg0);
      break;
   }
}

/* this function behaves like emit_load_float32, but loads
   64-bit floating point numbers, converting them to 32-bit
  ones */
static void emit_load_float64to32( struct translate_sse *p,
                                       struct x86_reg data,
                                       struct x86_reg arg0,
                                       unsigned out_chans,
                                       unsigned chans)
{
   struct x86_reg tmpXMM = x86_make_reg(file_XMM, 1);
   switch(chans)
   {
   case 1:
      sse2_movsd(p->func, data, arg0);
      if(out_chans > 1)
         sse2_cvtpd2ps(p->func, data, data);
      else
         sse2_cvtsd2ss(p->func, data, data);
      if(out_chans == CHANNELS_0001)
         sse_shufps(p->func, data, get_const(p, CONST_IDENTITY), SHUF(X, Y, Z, W)  );
      break;
   case 2:
      sse2_movupd(p->func, data, arg0);
      sse2_cvtpd2ps(p->func, data, data);
      if(out_chans == CHANNELS_0001)
         sse_shufps(p->func, data, get_const(p, CONST_IDENTITY), SHUF(X, Y, Z, W) );
      else if(out_chans > 2)
         sse_movlhps(p->func, data, get_const(p, CONST_IDENTITY) );
       break;
   case 3:
      sse2_movupd(p->func, data, arg0);
      sse2_cvtpd2ps(p->func, data, data);
      sse2_movsd(p->func, tmpXMM, x86_make_disp(arg0, 16));
      if(out_chans > 3)
         sse2_cvtpd2ps(p->func, tmpXMM, tmpXMM);
      else
         sse2_cvtsd2ss(p->func, tmpXMM, tmpXMM);
      sse_movlhps(p->func, data, tmpXMM);
      if(out_chans == CHANNELS_0001)
         sse_orps(p->func, data, get_const(p, CONST_IDENTITY) );
      break;
   case 4:
      sse2_movupd(p->func, data, arg0);
      sse2_cvtpd2ps(p->func, data, data);
      sse2_movupd(p->func, tmpXMM, x86_make_disp(arg0, 16));
      sse2_cvtpd2ps(p->func, tmpXMM, tmpXMM);
      sse_movlhps(p->func, data, tmpXMM);
      break;
   }
}

static void emit_mov64(struct translate_sse *p, struct x86_reg dst_gpr, struct x86_reg dst_xmm, struct x86_reg src_gpr,  struct x86_reg src_xmm)
{
   if(x86_target(p->func) != X86_32)
      x64_mov64(p->func, dst_gpr, src_gpr);
   else
   {
      /* TODO: when/on which CPUs is SSE2 actually better than SSE? */
      if(x86_target_caps(p->func) & X86_SSE2)
         sse2_movq(p->func, dst_xmm, src_xmm);
      else
         sse_movlps(p->func, dst_xmm, src_xmm);
   }
}

static void emit_load64(struct translate_sse *p, struct x86_reg dst_gpr, struct x86_reg dst_xmm, struct x86_reg src)
{
   emit_mov64(p, dst_gpr, dst_xmm, src, src);
}

static void emit_store64(struct translate_sse *p, struct x86_reg dst, struct x86_reg src_gpr, struct x86_reg src_xmm)
{
   emit_mov64(p, dst, dst, src_gpr, src_xmm);
}

static void emit_mov128(struct translate_sse *p, struct x86_reg dst, struct x86_reg src)
{
   if(x86_target_caps(p->func) & X86_SSE2)
      sse2_movdqu(p->func, dst, src);
   else
      sse_movups(p->func, dst, src);
}

/* TODO: this uses unaligned accesses liberally, which is great on Nehalem,
 * but may or may not be good on older processors
 * TODO: may perhaps want to use non-temporal stores here if possible
 */
static void emit_memcpy(struct translate_sse *p, struct x86_reg dst, struct x86_reg src, unsigned size)
{
   struct x86_reg dataXMM = x86_make_reg(file_XMM, 0);
   struct x86_reg dataXMM2 = x86_make_reg(file_XMM, 1);
   struct x86_reg dataGPR = p->tmp_EAX;
   struct x86_reg dataGPR2 = p->tmp2_EDX;

   if(size < 8)
   {
      switch (size)
      {
      case 1:
         x86_mov8(p->func, dataGPR, src);
         x86_mov8(p->func, dst, dataGPR);
         break;
      case 2:
         x86_mov16(p->func, dataGPR, src);
         x86_mov16(p->func, dst, dataGPR);
         break;
      case 3:
         x86_mov16(p->func, dataGPR, src);
         x86_mov8(p->func, dataGPR2, x86_make_disp(src, 2));
         x86_mov16(p->func, dst, dataGPR);
         x86_mov8(p->func, x86_make_disp(dst, 2), dataGPR2);
         break;
      case 4:
         x86_mov(p->func, dataGPR, src);
         x86_mov(p->func, dst, dataGPR);
         break;
      case 6:
         x86_mov(p->func, dataGPR, src);
         x86_mov16(p->func, dataGPR2, x86_make_disp(src, 4));
         x86_mov(p->func, dst, dataGPR);
         x86_mov16(p->func, x86_make_disp(dst, 4), dataGPR2);
         break;
      }
   }
   else if(!(x86_target_caps(p->func) & X86_SSE))
   {
      unsigned i = 0;
      assert((size & 3) == 0);
      for(i = 0; i < size; i += 4)
      {
         x86_mov(p->func, dataGPR, x86_make_disp(src, i));
         x86_mov(p->func, x86_make_disp(dst, i), dataGPR);
      }
   }
   else
   {
      switch(size)
      {
      case 8:
         emit_load64(p, dataGPR, dataXMM, src);
         emit_store64(p, dst, dataGPR, dataXMM);
         break;
      case 12:
         emit_load64(p, dataGPR2, dataXMM, src);
         x86_mov(p->func, dataGPR, x86_make_disp(src, 8));
         emit_store64(p, dst, dataGPR2, dataXMM);
         x86_mov(p->func, x86_make_disp(dst, 8), dataGPR);
         break;
      case 16:
         emit_mov128(p, dataXMM, src);
         emit_mov128(p, dst, dataXMM);
         break;
      case 24:
         emit_mov128(p, dataXMM, src);
         emit_load64(p, dataGPR, dataXMM2, x86_make_disp(src, 16));
         emit_mov128(p, dst, dataXMM);
         emit_store64(p, x86_make_disp(dst, 16), dataGPR, dataXMM2);
         break;
      case 32:
         emit_mov128(p, dataXMM, src);
         emit_mov128(p, dataXMM2, x86_make_disp(src, 16));
         emit_mov128(p, dst, dataXMM);
         emit_mov128(p, x86_make_disp(dst, 16), dataXMM2);
         break;
      default:
         assert(0);
      }
   }
}

static boolean translate_attr_convert( struct translate_sse *p,
                               const struct translate_element *a,
                               struct x86_reg src,
                               struct x86_reg dst)

{
   const struct util_format_description* input_desc = util_format_description(a->input_format);
   const struct util_format_description* output_desc = util_format_description(a->output_format);
   unsigned i;
   boolean id_swizzle = TRUE;
   unsigned swizzle[4] = {UTIL_FORMAT_SWIZZLE_NONE, UTIL_FORMAT_SWIZZLE_NONE, UTIL_FORMAT_SWIZZLE_NONE, UTIL_FORMAT_SWIZZLE_NONE};
   unsigned needed_chans = 0;
   unsigned imms[2] = {0, 0x3f800000};

   if(a->output_format == PIPE_FORMAT_NONE || a->input_format == PIPE_FORMAT_NONE)
      return FALSE;

   if(input_desc->channel[0].size & 7)
      return FALSE;

   if(input_desc->colorspace != output_desc->colorspace)
      return FALSE;

   for(i = 1; i < input_desc->nr_channels; ++i)
   {
      if(memcmp(&input_desc->channel[i], &input_desc->channel[0], sizeof(input_desc->channel[0])))
         return FALSE;
   }

   for(i = 1; i < output_desc->nr_channels; ++i)
   {
      if(memcmp(&output_desc->channel[i], &output_desc->channel[0], sizeof(output_desc->channel[0])))
         return FALSE;
   }

   for(i = 0; i < output_desc->nr_channels; ++i)
   {
      if(output_desc->swizzle[i] < 4)
         swizzle[output_desc->swizzle[i]] = input_desc->swizzle[i];
   }

   if((x86_target_caps(p->func) & X86_SSE) && (0
         || a->output_format == PIPE_FORMAT_R32_FLOAT
         || a->output_format == PIPE_FORMAT_R32G32_FLOAT
         || a->output_format == PIPE_FORMAT_R32G32B32_FLOAT
         || a->output_format == PIPE_FORMAT_R32G32B32A32_FLOAT))
   {
      struct x86_reg dataXMM = x86_make_reg(file_XMM, 0);

      for(i = 0; i < output_desc->nr_channels; ++i)
      {
         if(swizzle[i] == UTIL_FORMAT_SWIZZLE_0 && i >= input_desc->nr_channels)
            swizzle[i] = i;
      }

      for(i = 0; i < output_desc->nr_channels; ++i)
      {
         if(swizzle[i] < 4)
            needed_chans = MAX2(needed_chans, swizzle[i] + 1);
         if(swizzle[i] < UTIL_FORMAT_SWIZZLE_0 && swizzle[i] != i)
            id_swizzle = FALSE;
      }

      if(needed_chans > 0)
      {
         switch(input_desc->channel[0].type)
         {
         case UTIL_FORMAT_TYPE_UNSIGNED:
            if(!(x86_target_caps(p->func) & X86_SSE2))
               return FALSE;
            emit_load_sse2(p, dataXMM, src, input_desc->channel[0].size * input_desc->nr_channels >> 3);

            /* TODO: add support for SSE4.1 pmovzx */
            switch(input_desc->channel[0].size)
            {
            case 8:
               /* TODO: this may be inefficient due to get_identity() being used both as a float and integer register */
               sse2_punpcklbw(p->func, dataXMM, get_const(p, CONST_IDENTITY));
               sse2_punpcklbw(p->func, dataXMM, get_const(p, CONST_IDENTITY));
               break;
            case 16:
               sse2_punpcklwd(p->func, dataXMM, get_const(p, CONST_IDENTITY));
               break;
            case 32: /* we lose precision here */
               sse2_psrld_imm(p->func, dataXMM, 1);
               break;
            default:
               return FALSE;
            }
            sse2_cvtdq2ps(p->func, dataXMM, dataXMM);
            if(input_desc->channel[0].normalized)
            {
               struct x86_reg factor;
               switch(input_desc->channel[0].size)
               {
               case 8:
                  factor = get_const(p, CONST_INV_255);
                  break;
               case 16:
                  factor = get_const(p, CONST_INV_65535);
                  break;
               case 32:
                  factor = get_const(p, CONST_INV_2147483647);
                  break;
               default:
                  assert(0);
                  factor.disp = 0;
                  factor.file = 0;
                  factor.idx = 0;
                  factor.mod = 0;
                  break;
               }
               sse_mulps(p->func, dataXMM, factor);
            }
            else if(input_desc->channel[0].size == 32)
               sse_addps(p->func, dataXMM, dataXMM); /* compensate for the bit we threw away to fit u32 into s32 */
            break;
         case UTIL_FORMAT_TYPE_SIGNED:
            if(!(x86_target_caps(p->func) & X86_SSE2))
               return FALSE;
            emit_load_sse2(p, dataXMM, src, input_desc->channel[0].size * input_desc->nr_channels >> 3);

            /* TODO: add support for SSE4.1 pmovsx */
            switch(input_desc->channel[0].size)
            {
            case 8:
               sse2_punpcklbw(p->func, dataXMM, dataXMM);
               sse2_punpcklbw(p->func, dataXMM, dataXMM);
               sse2_psrad_imm(p->func, dataXMM, 24);
               break;
            case 16:
               sse2_punpcklwd(p->func, dataXMM, dataXMM);
               sse2_psrad_imm(p->func, dataXMM, 16);
               break;
            case 32: /* we lose precision here */
               break;
            default:
               return FALSE;
            }
            sse2_cvtdq2ps(p->func, dataXMM, dataXMM);
            if(input_desc->channel[0].normalized)
            {
               struct x86_reg factor;
               switch(input_desc->channel[0].size)
               {
               case 8:
                  factor = get_const(p, CONST_INV_127);
                  break;
               case 16:
                  factor = get_const(p, CONST_INV_32767);
                  break;
               case 32:
                  factor = get_const(p, CONST_INV_2147483647);
                  break;
               default:
                  assert(0);
                  factor.disp = 0;
                  factor.file = 0;
                  factor.idx = 0;
                  factor.mod = 0;
                  break;
               }
               sse_mulps(p->func, dataXMM, factor);
            }
            break;

            break;
         case UTIL_FORMAT_TYPE_FLOAT:
            if(input_desc->channel[0].size != 32 && input_desc->channel[0].size != 64)
               return FALSE;
            if(swizzle[3] == UTIL_FORMAT_SWIZZLE_1 && input_desc->nr_channels <= 3)
            {
               swizzle[3] = UTIL_FORMAT_SWIZZLE_W;
               needed_chans = CHANNELS_0001;
            }
            switch(input_desc->channel[0].size)
            {
            case 32:
               emit_load_float32(p, dataXMM, src, needed_chans, input_desc->nr_channels);
               break;
            case 64: /* we lose precision here */
               if(!(x86_target_caps(p->func) & X86_SSE2))
                  return FALSE;
               emit_load_float64to32(p, dataXMM, src, needed_chans, input_desc->nr_channels);
               break;
            default:
               return FALSE;
            }
            break;
         default:
            return FALSE;
         }

         if(!id_swizzle)
            sse_shufps(p->func, dataXMM, dataXMM, SHUF(swizzle[0], swizzle[1], swizzle[2], swizzle[3]) );
      }

      if(output_desc->nr_channels >= 4
            && swizzle[0] < UTIL_FORMAT_SWIZZLE_0
            && swizzle[1] < UTIL_FORMAT_SWIZZLE_0
            && swizzle[2] < UTIL_FORMAT_SWIZZLE_0
            && swizzle[3] < UTIL_FORMAT_SWIZZLE_0
            )
         sse_movups(p->func, dst, dataXMM);
      else
      {
         if(output_desc->nr_channels >= 2
               && swizzle[0] < UTIL_FORMAT_SWIZZLE_0
               && swizzle[1] < UTIL_FORMAT_SWIZZLE_0)
            sse_movlps(p->func, dst, dataXMM);
         else
         {
            if(swizzle[0] < UTIL_FORMAT_SWIZZLE_0)
               sse_movss(p->func, dst, dataXMM);
            else
               x86_mov_imm(p->func, dst, imms[swizzle[0] - UTIL_FORMAT_SWIZZLE_0]);

            if(output_desc->nr_channels >= 2)
            {
               if(swizzle[1] < UTIL_FORMAT_SWIZZLE_0)
               {
                  sse_shufps(p->func, dataXMM, dataXMM, SHUF(1, 1, 2, 3));
                  sse_movss(p->func, x86_make_disp(dst, 4), dataXMM);
               }
               else
                  x86_mov_imm(p->func, x86_make_disp(dst, 4), imms[swizzle[1] - UTIL_FORMAT_SWIZZLE_0]);
            }
         }

         if(output_desc->nr_channels >= 3)
         {
            if(output_desc->nr_channels >= 4
                  && swizzle[2] < UTIL_FORMAT_SWIZZLE_0
                  && swizzle[3] < UTIL_FORMAT_SWIZZLE_0)
               sse_movhps(p->func, x86_make_disp(dst, 8), dataXMM);
            else
            {
               if(swizzle[2] < UTIL_FORMAT_SWIZZLE_0)
               {
                  sse_shufps(p->func, dataXMM, dataXMM, SHUF(2, 2, 2, 3));
                  sse_movss(p->func, x86_make_disp(dst, 8), dataXMM);
               }
               else
                  x86_mov_imm(p->func, x86_make_disp(dst, 8), imms[swizzle[2] - UTIL_FORMAT_SWIZZLE_0]);

               if(output_desc->nr_channels >= 4)
               {
                  if(swizzle[3] < UTIL_FORMAT_SWIZZLE_0)
                  {
                     sse_shufps(p->func, dataXMM, dataXMM, SHUF(3, 3, 3, 3));
                     sse_movss(p->func, x86_make_disp(dst, 12), dataXMM);
                  }
                  else
                     x86_mov_imm(p->func, x86_make_disp(dst, 12), imms[swizzle[3] - UTIL_FORMAT_SWIZZLE_0]);
               }
            }
         }
      }
      return TRUE;
   }
   else if((x86_target_caps(p->func) & X86_SSE2) && input_desc->channel[0].size == 8 && output_desc->channel[0].size == 16
         && output_desc->channel[0].normalized == input_desc->channel[0].normalized
         && (0
               || (input_desc->channel[0].type == UTIL_FORMAT_TYPE_UNSIGNED && output_desc->channel[0].type == UTIL_FORMAT_TYPE_UNSIGNED)
               || (input_desc->channel[0].type == UTIL_FORMAT_TYPE_UNSIGNED && output_desc->channel[0].type == UTIL_FORMAT_TYPE_SIGNED)
               || (input_desc->channel[0].type == UTIL_FORMAT_TYPE_SIGNED && output_desc->channel[0].type == UTIL_FORMAT_TYPE_SIGNED)
               ))
   {
      struct x86_reg dataXMM = x86_make_reg(file_XMM, 0);
      struct x86_reg tmpXMM = x86_make_reg(file_XMM, 1);
      struct x86_reg tmp = p->tmp_EAX;
      unsigned imms[2] = {0, 1};

      for(i = 0; i < output_desc->nr_channels; ++i)
      {
         if(swizzle[i] == UTIL_FORMAT_SWIZZLE_0 && i >= input_desc->nr_channels)
            swizzle[i] = i;
      }

      for(i = 0; i < output_desc->nr_channels; ++i)
      {
         if(swizzle[i] < 4)
            needed_chans = MAX2(needed_chans, swizzle[i] + 1);
         if(swizzle[i] < UTIL_FORMAT_SWIZZLE_0 && swizzle[i] != i)
            id_swizzle = FALSE;
      }

      if(needed_chans > 0)
      {
         emit_load_sse2(p, dataXMM, src, input_desc->channel[0].size * input_desc->nr_channels >> 3);

         switch(input_desc->channel[0].type)
         {
         case UTIL_FORMAT_TYPE_UNSIGNED:
            if(input_desc->channel[0].normalized)
            {
               sse2_punpcklbw(p->func, dataXMM, dataXMM);
               if(output_desc->channel[0].type == UTIL_FORMAT_TYPE_SIGNED)
        	       sse2_psrlw_imm(p->func, dataXMM, 1);
            }
            else
               sse2_punpcklbw(p->func, dataXMM, get_const(p, CONST_IDENTITY));
            break;
         case UTIL_FORMAT_TYPE_SIGNED:
            if(input_desc->channel[0].normalized)
            {
               sse2_movq(p->func, tmpXMM, get_const(p, CONST_IDENTITY));
               sse2_punpcklbw(p->func, tmpXMM, dataXMM);
               sse2_psllw_imm(p->func, dataXMM, 9);
               sse2_psrlw_imm(p->func, dataXMM, 8);
               sse2_por(p->func, tmpXMM, dataXMM);
               sse2_psrlw_imm(p->func, dataXMM, 7);
               sse2_por(p->func, tmpXMM, dataXMM);
               {
                  struct x86_reg t = dataXMM;
                  dataXMM = tmpXMM;
                  tmpXMM = t;
               }
            }
            else
            {
               sse2_punpcklbw(p->func, dataXMM, dataXMM);
               sse2_psraw_imm(p->func, dataXMM, 8);
            }
            break;
         default:
            assert(0);
         }

         if(output_desc->channel[0].normalized)
            imms[1] = (output_desc->channel[0].type == UTIL_FORMAT_TYPE_UNSIGNED) ? 0xffff : 0x7ffff;

         if(!id_swizzle)
            sse2_pshuflw(p->func, dataXMM, dataXMM, (swizzle[0] & 3) | ((swizzle[1] & 3) << 2) | ((swizzle[2] & 3) << 4) | ((swizzle[3] & 3) << 6));
      }

      if(output_desc->nr_channels >= 4
            && swizzle[0] < UTIL_FORMAT_SWIZZLE_0
            && swizzle[1] < UTIL_FORMAT_SWIZZLE_0
            && swizzle[2] < UTIL_FORMAT_SWIZZLE_0
            && swizzle[3] < UTIL_FORMAT_SWIZZLE_0
            )
         sse2_movq(p->func, dst, dataXMM);
      else
      {
         if(swizzle[0] < UTIL_FORMAT_SWIZZLE_0)
         {
            if(output_desc->nr_channels >= 2 && swizzle[1] < UTIL_FORMAT_SWIZZLE_0)
               sse2_movd(p->func, dst, dataXMM);
            else
            {
               sse2_movd(p->func, tmp, dataXMM);
               x86_mov16(p->func, dst, tmp);
               if(output_desc->nr_channels >= 2)
                  x86_mov16_imm(p->func, x86_make_disp(dst, 2), imms[swizzle[1] - UTIL_FORMAT_SWIZZLE_0]);
            }
         }
         else
         {
            if(output_desc->nr_channels >= 2 && swizzle[1] >= UTIL_FORMAT_SWIZZLE_0)
               x86_mov_imm(p->func, dst, (imms[swizzle[1] - UTIL_FORMAT_SWIZZLE_0] << 16) | imms[swizzle[0] - UTIL_FORMAT_SWIZZLE_0]);
            else
            {
               x86_mov16_imm(p->func, dst, imms[swizzle[0] - UTIL_FORMAT_SWIZZLE_0]);
               if(output_desc->nr_channels >= 2)
               {
                  sse2_movd(p->func, tmp, dataXMM);
                  x86_shr_imm(p->func, tmp, 16);
                  x86_mov16(p->func, x86_make_disp(dst, 2), tmp);
               }
            }
         }

         if(output_desc->nr_channels >= 3)
         {
            if(swizzle[2] < UTIL_FORMAT_SWIZZLE_0)
            {
               if(output_desc->nr_channels >= 4 && swizzle[3] < UTIL_FORMAT_SWIZZLE_0)
               {
                  sse2_psrlq_imm(p->func, dataXMM, 32);
                  sse2_movd(p->func, x86_make_disp(dst, 4), dataXMM);
               }
               else
               {
                  sse2_psrlq_imm(p->func, dataXMM, 32);
                  sse2_movd(p->func, tmp, dataXMM);
                  x86_mov16(p->func, x86_make_disp(dst, 4), tmp);
                  if(output_desc->nr_channels >= 4)
                  {
                     x86_mov16_imm(p->func, x86_make_disp(dst, 6), imms[swizzle[3] - UTIL_FORMAT_SWIZZLE_0]);
                  }
               }
            }
            else
            {
               if(output_desc->nr_channels >= 4 && swizzle[3] >= UTIL_FORMAT_SWIZZLE_0)
                  x86_mov_imm(p->func, x86_make_disp(dst, 4), (imms[swizzle[3] - UTIL_FORMAT_SWIZZLE_0] << 16) | imms[swizzle[2] - UTIL_FORMAT_SWIZZLE_0]);
               else
               {
                  x86_mov16_imm(p->func, x86_make_disp(dst, 4), imms[swizzle[2] - UTIL_FORMAT_SWIZZLE_0]);

                  if(output_desc->nr_channels >= 4)
                  {
                     sse2_psrlq_imm(p->func, dataXMM, 48);
                     sse2_movd(p->func, tmp, dataXMM);
                     x86_mov16(p->func, x86_make_disp(dst, 6), tmp);
                  }
               }
            }
         }
      }
      return TRUE;
   }
   else if(!memcmp(&output_desc->channel[0], &input_desc->channel[0], sizeof(output_desc->channel[0])))
   {
      struct x86_reg tmp = p->tmp_EAX;
      unsigned i;
      if(input_desc->channel[0].size == 8 && input_desc->nr_channels == 4 && output_desc->nr_channels == 4
                     && swizzle[0] == UTIL_FORMAT_SWIZZLE_W
                     && swizzle[1] == UTIL_FORMAT_SWIZZLE_Z
                     && swizzle[2] == UTIL_FORMAT_SWIZZLE_Y
                     && swizzle[3] == UTIL_FORMAT_SWIZZLE_X)
      {
         /* TODO: support movbe */
         x86_mov(p->func, tmp, src);
         x86_bswap(p->func, tmp);
         x86_mov(p->func, dst, tmp);
         return TRUE;
      }

      for(i = 0; i < output_desc->nr_channels; ++i)
      {
         switch(output_desc->channel[0].size)
         {
         case 8:
            if(swizzle[i] >= UTIL_FORMAT_SWIZZLE_0)
            {
               unsigned v = 0;
               if(swizzle[i] == UTIL_FORMAT_SWIZZLE_1)
               {
                  switch(output_desc->channel[0].type)
                  {
                  case UTIL_FORMAT_TYPE_UNSIGNED:
                     v = output_desc->channel[0].normalized ? 0xff : 1;
                     break;
                  case UTIL_FORMAT_TYPE_SIGNED:
                     v = output_desc->channel[0].normalized ? 0x7f : 1;
                     break;
                  default:
                     return FALSE;
                  }
               }
               x86_mov8_imm(p->func, x86_make_disp(dst, i * 1), v);
            }
            else
            {
               x86_mov8(p->func, tmp, x86_make_disp(src, swizzle[i] * 1));
               x86_mov8(p->func, x86_make_disp(dst, i * 1), tmp);
            }
            break;
         case 16:
            if(swizzle[i] >= UTIL_FORMAT_SWIZZLE_0)
            {
               unsigned v = 0;
               if(swizzle[i] == UTIL_FORMAT_SWIZZLE_1)
               {
                  switch(output_desc->channel[1].type)
                  {
                  case UTIL_FORMAT_TYPE_UNSIGNED:
                     v = output_desc->channel[1].normalized ? 0xffff : 1;
                     break;
                  case UTIL_FORMAT_TYPE_SIGNED:
                     v = output_desc->channel[1].normalized ? 0x7fff : 1;
                     break;
                  case UTIL_FORMAT_TYPE_FLOAT:
                     v = 0x3c00;
                     break;
                  default:
                     return FALSE;
                  }
               }
               x86_mov16_imm(p->func, x86_make_disp(dst, i * 2), v);
            }
            else if(swizzle[i] == UTIL_FORMAT_SWIZZLE_0)
               x86_mov16_imm(p->func, x86_make_disp(dst, i * 2), 0);
            else
            {
               x86_mov16(p->func, tmp, x86_make_disp(src, swizzle[i] * 2));
               x86_mov16(p->func, x86_make_disp(dst, i * 2), tmp);
            }
            break;
         case 32:
            if(swizzle[i] >= UTIL_FORMAT_SWIZZLE_0)
            {
               unsigned v = 0;
               if(swizzle[i] == UTIL_FORMAT_SWIZZLE_1)
               {
                  switch(output_desc->channel[1].type)
                  {
                  case UTIL_FORMAT_TYPE_UNSIGNED:
                     v = output_desc->channel[1].normalized ? 0xffffffff : 1;
                     break;
                  case UTIL_FORMAT_TYPE_SIGNED:
                     v = output_desc->channel[1].normalized ? 0x7fffffff : 1;
                     break;
                  case UTIL_FORMAT_TYPE_FLOAT:
                     v = 0x3f800000;
                     break;
                  default:
                     return FALSE;
                  }
               }
               x86_mov_imm(p->func, x86_make_disp(dst, i * 4), v);
            }
            else
            {
               x86_mov(p->func, tmp, x86_make_disp(src, swizzle[i] * 4));
               x86_mov(p->func, x86_make_disp(dst, i * 4), tmp);
            }
            break;
         case 64:
            if(swizzle[i] >= UTIL_FORMAT_SWIZZLE_0)
            {
               unsigned l = 0;
               unsigned h = 0;
               if(swizzle[i] == UTIL_FORMAT_SWIZZLE_1)
               {
                  switch(output_desc->channel[1].type)
                  {
                  case UTIL_FORMAT_TYPE_UNSIGNED:
                     h = output_desc->channel[1].normalized ? 0xffffffff : 0;
                     l = output_desc->channel[1].normalized ? 0xffffffff : 1;
                     break;
                  case UTIL_FORMAT_TYPE_SIGNED:
                     h = output_desc->channel[1].normalized ? 0x7fffffff : 0;
                     l = output_desc->channel[1].normalized ? 0xffffffff : 1;
                     break;
                  case UTIL_FORMAT_TYPE_FLOAT:
                     h = 0x3ff00000;
                     l = 0;
                     break;
                  default:
                     return FALSE;
                  }
               }
               x86_mov_imm(p->func, x86_make_disp(dst, i * 8), l);
               x86_mov_imm(p->func, x86_make_disp(dst, i * 8 + 4), h);
            }
            else
            {
               if(x86_target_caps(p->func) & X86_SSE)
               {
                  struct x86_reg tmpXMM = x86_make_reg(file_XMM, 0);
                  emit_load64(p, tmp, tmpXMM, x86_make_disp(src, swizzle[i] * 8));
                  emit_store64(p, x86_make_disp(dst, i * 8), tmp, tmpXMM);
               }
               else
               {
                  x86_mov(p->func, tmp, x86_make_disp(src, swizzle[i] * 8));
                  x86_mov(p->func, x86_make_disp(dst, i * 8), tmp);
                  x86_mov(p->func, tmp, x86_make_disp(src, swizzle[i] * 8 + 4));
                  x86_mov(p->func, x86_make_disp(dst, i * 8 + 4), tmp);
               }
            }
            break;
         default:
            return FALSE;
         }
      }
      return TRUE;
   }
   /* special case for draw's EMIT_4UB (RGBA) and EMIT_4UB_BGRA */
   else if((x86_target_caps(p->func) & X86_SSE2) &&
         a->input_format == PIPE_FORMAT_R32G32B32A32_FLOAT && (0
               || a->output_format == PIPE_FORMAT_B8G8R8A8_UNORM
               || a->output_format == PIPE_FORMAT_R8G8B8A8_UNORM
         ))
   {
      struct x86_reg dataXMM = x86_make_reg(file_XMM, 0);

      /* load */
      sse_movups(p->func, dataXMM, src);

      if (a->output_format == PIPE_FORMAT_B8G8R8A8_UNORM)
         sse_shufps(p->func, dataXMM, dataXMM, SHUF(2,1,0,3));

      /* scale by 255.0 */
      sse_mulps(p->func, dataXMM, get_const(p, CONST_255));

      /* pack and emit */
      sse2_cvtps2dq(p->func, dataXMM, dataXMM);
      sse2_packssdw(p->func, dataXMM, dataXMM);
      sse2_packuswb(p->func, dataXMM, dataXMM);
      sse2_movd(p->func, dst, dataXMM);

      return TRUE;
   }

   return FALSE;
}

static boolean translate_attr( struct translate_sse *p,
			       const struct translate_element *a,
			       struct x86_reg src,
			       struct x86_reg dst)
{
   if(a->input_format == a->output_format)
   {
      emit_memcpy(p, dst, src, util_format_get_stride(a->input_format, 1));
      return TRUE;
   }

   return translate_attr_convert(p, a, src, dst);
}

static boolean init_inputs( struct translate_sse *p,
                            unsigned index_size )
{
   unsigned i;
   struct x86_reg instance_id = x86_make_disp(p->machine_EDI,
                                              get_offset(p, &p->instance_id));

   for (i = 0; i < p->nr_buffer_variants; i++) {
      struct translate_buffer_variant *variant = &p->buffer_variant[i];
      struct translate_buffer *buffer = &p->buffer[variant->buffer_index];

      if (!index_size || variant->instance_divisor) {
         struct x86_reg buf_max_index = x86_make_disp(p->machine_EDI,
                                                     get_offset(p, &buffer->max_index));
         struct x86_reg buf_stride   = x86_make_disp(p->machine_EDI,
                                                     get_offset(p, &buffer->stride));
         struct x86_reg buf_ptr      = x86_make_disp(p->machine_EDI,
                                                     get_offset(p, &variant->ptr));
         struct x86_reg buf_base_ptr = x86_make_disp(p->machine_EDI,
                                                     get_offset(p, &buffer->base_ptr));
         struct x86_reg elt = p->idx_ESI;
         struct x86_reg tmp_EAX = p->tmp_EAX;

         /* Calculate pointer to first attrib:
          *   base_ptr + stride * index, where index depends on instance divisor
          */
         if (variant->instance_divisor) {
            /* Our index is instance ID divided by instance divisor.
             */
            x86_mov(p->func, tmp_EAX, instance_id);

            if (variant->instance_divisor != 1) {
               struct x86_reg tmp_EDX = p->tmp2_EDX;
               struct x86_reg tmp_ECX = p->src_ECX;

               /* TODO: Add x86_shr() to rtasm and use it whenever
                *       instance divisor is power of two.
                */

               x86_xor(p->func, tmp_EDX, tmp_EDX);
               x86_mov_reg_imm(p->func, tmp_ECX, variant->instance_divisor);
               x86_div(p->func, tmp_ECX);    /* EAX = EDX:EAX / ECX */
            }

            /* XXX we need to clamp the index here too, but to a
             * per-array max value, not the draw->pt.max_index value
             * that's being given to us via translate->set_buffer().
             */
         } else {
            x86_mov(p->func, tmp_EAX, elt);

            /* Clamp to max_index
             */
            x86_cmp(p->func, tmp_EAX, buf_max_index);
            x86_cmovcc(p->func, tmp_EAX, buf_max_index, cc_AE);
         }

         x86_imul(p->func, tmp_EAX, buf_stride);
         x64_rexw(p->func);
         x86_add(p->func, tmp_EAX, buf_base_ptr);

         x86_cmp(p->func, p->count_EBP, p->tmp_EAX);

         /* In the linear case, keep the buffer pointer instead of the
          * index number.
          */
         if (!index_size && p->nr_buffer_variants == 1)
         {
            x64_rexw(p->func);
            x86_mov(p->func, elt, tmp_EAX);
         }
         else
         {
            x64_rexw(p->func);
            x86_mov(p->func, buf_ptr, tmp_EAX);
         }
      }
   }

   return TRUE;
}


static struct x86_reg get_buffer_ptr( struct translate_sse *p,
                                      unsigned index_size,
                                      unsigned var_idx,
                                      struct x86_reg elt )
{
   if (var_idx == ELEMENT_BUFFER_INSTANCE_ID) {
      return x86_make_disp(p->machine_EDI,
                           get_offset(p, &p->instance_id));
   }
   if (!index_size && p->nr_buffer_variants == 1) {
      return p->idx_ESI;
   }
   else if (!index_size || p->buffer_variant[var_idx].instance_divisor) {
      struct x86_reg ptr = p->src_ECX;
      struct x86_reg buf_ptr = 
         x86_make_disp(p->machine_EDI,
                       get_offset(p, &p->buffer_variant[var_idx].ptr));
      
      x64_rexw(p->func);
      x86_mov(p->func, ptr, buf_ptr);
      return ptr;
   }
   else {
      struct x86_reg ptr = p->src_ECX;
      const struct translate_buffer_variant *variant = &p->buffer_variant[var_idx];

      struct x86_reg buf_stride = 
         x86_make_disp(p->machine_EDI,
                       get_offset(p, &p->buffer[variant->buffer_index].stride));

      struct x86_reg buf_base_ptr = 
         x86_make_disp(p->machine_EDI,
                       get_offset(p, &p->buffer[variant->buffer_index].base_ptr));

      struct x86_reg buf_max_index =
         x86_make_disp(p->machine_EDI,
                       get_offset(p, &p->buffer[variant->buffer_index].max_index));



      /* Calculate pointer to current attrib:
       */
      switch(index_size)
      {
      case 1:
         x86_movzx8(p->func, ptr, elt);
         break;
      case 2:
         x86_movzx16(p->func, ptr, elt);
         break;
      case 4:
         x86_mov(p->func, ptr, elt);
         break;
      }

      /* Clamp to max_index
       */
      x86_cmp(p->func, ptr, buf_max_index);
      x86_cmovcc(p->func, ptr, buf_max_index, cc_AE);

      x86_imul(p->func, ptr, buf_stride);
      x64_rexw(p->func);
      x86_add(p->func, ptr, buf_base_ptr);
      return ptr;
   }
}



static boolean incr_inputs( struct translate_sse *p, 
                            unsigned index_size )
{
   if (!index_size && p->nr_buffer_variants == 1) {
      struct x86_reg stride = x86_make_disp(p->machine_EDI,
                                            get_offset(p, &p->buffer[0].stride));

      if (p->buffer_variant[0].instance_divisor == 0) {
         x64_rexw(p->func);
         x86_add(p->func, p->idx_ESI, stride);
         sse_prefetchnta(p->func, x86_make_disp(p->idx_ESI, 192));
      }
   }
   else if (!index_size) {
      unsigned i;

      /* Is this worthwhile??
       */
      for (i = 0; i < p->nr_buffer_variants; i++) {
         struct translate_buffer_variant *variant = &p->buffer_variant[i];
         struct x86_reg buf_ptr = x86_make_disp(p->machine_EDI,
                                                get_offset(p, &variant->ptr));
         struct x86_reg buf_stride = x86_make_disp(p->machine_EDI,
                                                   get_offset(p, &p->buffer[variant->buffer_index].stride));

         if (variant->instance_divisor == 0) {
            x86_mov(p->func, p->tmp_EAX, buf_stride);
            x64_rexw(p->func);
            x86_add(p->func, p->tmp_EAX, buf_ptr);
            if (i == 0) sse_prefetchnta(p->func, x86_make_disp(p->tmp_EAX, 192));
            x64_rexw(p->func);
            x86_mov(p->func, buf_ptr, p->tmp_EAX);
         }
      }
   } 
   else {
      x64_rexw(p->func);
      x86_lea(p->func, p->idx_ESI, x86_make_disp(p->idx_ESI, index_size));
   }
   
   return TRUE;
}


/* Build run( struct translate *machine,
 *            unsigned start,
 *            unsigned count,
 *            void *output_buffer )
 * or
 *  run_elts( struct translate *machine,
 *            unsigned *elts,
 *            unsigned count,
 *            void *output_buffer )
 *
 *  Lots of hardcoding
 *
 * EAX -- pointer to current output vertex
 * ECX -- pointer to current attribute 
 * 
 */
static boolean build_vertex_emit( struct translate_sse *p,
				  struct x86_function *func,
				  unsigned index_size )
{
   int fixup, label;
   unsigned j;

   memset(p->reg_to_const, 0xff, sizeof(p->reg_to_const));
   memset(p->const_to_reg, 0xff, sizeof(p->const_to_reg));

   p->tmp_EAX       = x86_make_reg(file_REG32, reg_AX);
   p->idx_ESI       = x86_make_reg(file_REG32, reg_SI);
   p->outbuf_EBX    = x86_make_reg(file_REG32, reg_BX);
   p->machine_EDI   = x86_make_reg(file_REG32, reg_DI);
   p->count_EBP     = x86_make_reg(file_REG32, reg_BP);
   p->tmp2_EDX     = x86_make_reg(file_REG32, reg_DX);
   p->src_ECX     = x86_make_reg(file_REG32, reg_CX);

   p->func = func;

   x86_init_func(p->func);

   if(x86_target(p->func) == X86_64_WIN64_ABI)
   {
	   /* the ABI guarantees a 16-byte aligned 32-byte "shadow space" above the return address */
	   sse2_movdqa(p->func, x86_make_disp(x86_make_reg(file_REG32, reg_SP), 8), x86_make_reg(file_XMM, 6));
	   sse2_movdqa(p->func, x86_make_disp(x86_make_reg(file_REG32, reg_SP), 24), x86_make_reg(file_XMM, 7));
   }

   x86_push(p->func, p->outbuf_EBX);
   x86_push(p->func, p->count_EBP);

/* on non-Win64 x86-64, these are already in the right registers */
   if(x86_target(p->func) != X86_64_STD_ABI)
   {
      x86_push(p->func, p->machine_EDI);
      x86_push(p->func, p->idx_ESI);

      x86_mov(p->func, p->machine_EDI, x86_fn_arg(p->func, 1));
      x86_mov(p->func, p->idx_ESI, x86_fn_arg(p->func, 2));
   }

   x86_mov(p->func, p->count_EBP, x86_fn_arg(p->func, 3));

   if(x86_target(p->func) != X86_32)
      x64_mov64(p->func, p->outbuf_EBX, x86_fn_arg(p->func, 5));
   else
      x86_mov(p->func, p->outbuf_EBX, x86_fn_arg(p->func, 5));

   /* Load instance ID.
    */
   if (p->use_instancing) {
      x86_mov(p->func,
              p->tmp_EAX,
              x86_fn_arg(p->func, 4));
      x86_mov(p->func,
              x86_make_disp(p->machine_EDI, get_offset(p, &p->instance_id)),
              p->tmp_EAX);
   }

   /* Get vertex count, compare to zero
    */
   x86_xor(p->func, p->tmp_EAX, p->tmp_EAX);
   x86_cmp(p->func, p->count_EBP, p->tmp_EAX);
   fixup = x86_jcc_forward(p->func, cc_E);

   /* always load, needed or not:
    */
   init_inputs(p, index_size);

   /* Note address for loop jump
    */
   label = x86_get_label(p->func);
   {
      struct x86_reg elt = !index_size ? p->idx_ESI : x86_deref(p->idx_ESI);
      int last_variant = -1;
      struct x86_reg vb;

      for (j = 0; j < p->translate.key.nr_elements; j++) {
         const struct translate_element *a = &p->translate.key.element[j];
         unsigned variant = p->element_to_buffer_variant[j];

         /* Figure out source pointer address:
          */
         if (variant != last_variant) {
            last_variant = variant;
            vb = get_buffer_ptr(p, index_size, variant, elt);
         }
         
         if (!translate_attr( p, a, 
                              x86_make_disp(vb, a->input_offset), 
                              x86_make_disp(p->outbuf_EBX, a->output_offset)))
            return FALSE;
      }

      /* Next output vertex:
       */
      x64_rexw(p->func);
      x86_lea(p->func, 
              p->outbuf_EBX,
              x86_make_disp(p->outbuf_EBX,
                            p->translate.key.output_stride));

      /* Incr index
       */ 
      incr_inputs( p, index_size );
   }

   /* decr count, loop if not zero
    */
   x86_dec(p->func, p->count_EBP);
   x86_jcc(p->func, cc_NZ, label);

   /* Exit mmx state?
    */
   if (p->func->need_emms)
      mmx_emms(p->func);

   /* Land forward jump here:
    */
   x86_fixup_fwd_jump(p->func, fixup);

   /* Pop regs and return
    */
   
   if(x86_target(p->func) != X86_64_STD_ABI)
   {
      x86_pop(p->func, p->idx_ESI);
      x86_pop(p->func, p->machine_EDI);
   }

   x86_pop(p->func, p->count_EBP);
   x86_pop(p->func, p->outbuf_EBX);

   if(x86_target(p->func) == X86_64_WIN64_ABI)
   {
	   sse2_movdqa(p->func, x86_make_reg(file_XMM, 6), x86_make_disp(x86_make_reg(file_REG32, reg_SP), 8));
	   sse2_movdqa(p->func, x86_make_reg(file_XMM, 7), x86_make_disp(x86_make_reg(file_REG32, reg_SP), 24));
   }
   x86_ret(p->func);

   return TRUE;
}






			       
static void translate_sse_set_buffer( struct translate *translate,
				unsigned buf,
				const void *ptr,
				unsigned stride,
				unsigned max_index )
{
   struct translate_sse *p = (struct translate_sse *)translate;

   if (buf < p->nr_buffers) {
      p->buffer[buf].base_ptr = (char *)ptr;
      p->buffer[buf].stride = stride;
      p->buffer[buf].max_index = max_index;
   }

   if (0) debug_printf("%s %d/%d: %p %d\n", 
                       __FUNCTION__, buf, 
                       p->nr_buffers, 
                       ptr, stride);
}


static void translate_sse_release( struct translate *translate )
{
   struct translate_sse *p = (struct translate_sse *)translate;

   x86_release_func( &p->elt8_func );
   x86_release_func( &p->elt16_func );
   x86_release_func( &p->elt_func );
   x86_release_func( &p->linear_func );

   os_free_aligned(p);
}


struct translate *translate_sse2_create( const struct translate_key *key )
{
   struct translate_sse *p = NULL;
   unsigned i;

   /* this is misnamed, it actually refers to whether rtasm is enabled or not */
   if (!rtasm_cpu_has_sse())
      goto fail;

   p = os_malloc_aligned(sizeof(struct translate_sse), 16);
   if (p == NULL) 
      goto fail;
   memset(p, 0, sizeof(*p));
   memcpy(p->consts, consts, sizeof(consts));

   p->translate.key = *key;
   p->translate.release = translate_sse_release;
   p->translate.set_buffer = translate_sse_set_buffer;

   for (i = 0; i < key->nr_elements; i++) {
      if (key->element[i].type == TRANSLATE_ELEMENT_NORMAL) {
         unsigned j;

         p->nr_buffers = MAX2(p->nr_buffers, key->element[i].input_buffer + 1);

         if (key->element[i].instance_divisor) {
            p->use_instancing = TRUE;
         }

         /*
          * Map vertex element to vertex buffer variant.
          */
         for (j = 0; j < p->nr_buffer_variants; j++) {
            if (p->buffer_variant[j].buffer_index == key->element[i].input_buffer &&
                p->buffer_variant[j].instance_divisor == key->element[i].instance_divisor) {
               break;
            }
         }
         if (j == p->nr_buffer_variants) {
            p->buffer_variant[j].buffer_index = key->element[i].input_buffer;
            p->buffer_variant[j].instance_divisor = key->element[i].instance_divisor;
            p->nr_buffer_variants++;
         }
         p->element_to_buffer_variant[i] = j;
      } else {
         assert(key->element[i].type == TRANSLATE_ELEMENT_INSTANCE_ID);

         p->element_to_buffer_variant[i] = ELEMENT_BUFFER_INSTANCE_ID;
      }
   }

   if (0) debug_printf("nr_buffers: %d\n", p->nr_buffers);

   if (!build_vertex_emit(p, &p->linear_func, 0))
      goto fail;

   if (!build_vertex_emit(p, &p->elt_func, 4))
      goto fail;

   if (!build_vertex_emit(p, &p->elt16_func, 2))
      goto fail;

   if (!build_vertex_emit(p, &p->elt8_func, 1))
      goto fail;

   p->translate.run = (run_func) x86_get_func(&p->linear_func);
   if (p->translate.run == NULL)
      goto fail;

   p->translate.run_elts = (run_elts_func) x86_get_func(&p->elt_func);
   if (p->translate.run_elts == NULL)
      goto fail;

   p->translate.run_elts16 = (run_elts16_func) x86_get_func(&p->elt16_func);
   if (p->translate.run_elts16 == NULL)
      goto fail;

   p->translate.run_elts8 = (run_elts8_func) x86_get_func(&p->elt8_func);
   if (p->translate.run_elts8 == NULL)
      goto fail;

   return &p->translate;

 fail:
   if (p)
      translate_sse_release( &p->translate );

   return NULL;
}



#else

struct translate *translate_sse2_create( const struct translate_key *key )
{
   return NULL;
}

#endif
