Runtime generate sse/sse2 code for some vertex programs. Experimental
authorKeith Whitwell <keith@tungstengraphics.com>
Wed, 8 Jun 2005 22:10:16 +0000 (22:10 +0000)
committerKeith Whitwell <keith@tungstengraphics.com>
Wed, 8 Jun 2005 22:10:16 +0000 (22:10 +0000)
and only active when _TNL_FIXED_FUNCTION_PROGRAM is defined *and*
the MESA_EXPERIMENTAL environment variable is set...
Works for the arbfplight demo at least.

src/mesa/tnl/t_vb_arbprogram.c
src/mesa/tnl/t_vb_arbprogram.h
src/mesa/tnl/t_vb_arbprogram_sse.c [new file with mode: 0644]

index eafd8d094b26138b9c177e63ea20bbec69c1484a..eeab0ed3ca34ea6a3fdfb6219463e92598253a57 100644 (file)
@@ -26,7 +26,7 @@
  * \file t_arb_program.c
  * Compile vertex programs to an intermediate representation.
  * Execute vertex programs over a buffer of vertices.
- * \author Keith Whitwell, Brian Paul
+ * \author Keith Whitwell
  */
 
 #include "glheader.h"
@@ -53,6 +53,12 @@ struct opcode_info {
    void (*print)( union instruction , const struct opcode_info * );
 };
 
+struct compilation {
+   GLuint reg_active;
+   union instruction *csr;
+   struct vertex_buffer *VB;   /* for input sizes! */
+};
+
 
 #define ARB_VP_MACHINE(stage) ((struct arb_vp_machine *)(stage->privatePtr))
 
@@ -117,7 +123,7 @@ static GLfloat RoughApproxPower(GLfloat x, GLfloat y)
  */
 static void do_RSW( struct arb_vp_machine *m, union instruction op ) 
 {
-   GLfloat *result = m->reg[op.rsw.dst];
+   GLfloat *result = m->File[0][op.rsw.dst];
    const GLfloat *arg0 = m->File[op.rsw.file0][op.rsw.idx0];
    GLuint swz = op.rsw.swz;
    GLuint neg = op.rsw.neg;
@@ -147,7 +153,7 @@ static void do_RSW( struct arb_vp_machine *m, union instruction op )
  */
 static void do_MSK( struct arb_vp_machine *m, union instruction op )
 {
-   GLfloat *dst = m->reg[op.msk.dst];
+   GLfloat *dst = m->File[0][op.msk.dst];
    const GLfloat *arg = m->File[op.msk.file][op.msk.idx];
  
    if (op.msk.mask & 0x1) dst[0] = arg[0];
@@ -173,7 +179,7 @@ static void do_PRT( struct arb_vp_machine *m, union instruction op )
 
 static void do_ABS( struct arb_vp_machine *m, union instruction op ) 
 {
-   GLfloat *result = m->reg[op.alu.dst];
+   GLfloat *result = m->File[0][op.alu.dst];
    const GLfloat *arg0 = m->File[op.alu.file0][op.alu.idx0];
 
    result[0] = (arg0[0] < 0.0) ? -arg0[0] : arg0[0];
@@ -184,7 +190,7 @@ static void do_ABS( struct arb_vp_machine *m, union instruction op )
 
 static void do_ADD( struct arb_vp_machine *m, union instruction op )
 {
-   GLfloat *result = m->reg[op.alu.dst];
+   GLfloat *result = m->File[0][op.alu.dst];
    const GLfloat *arg0 = m->File[op.alu.file0][op.alu.idx0];
    const GLfloat *arg1 = m->File[op.alu.file1][op.alu.idx1];
 
@@ -197,7 +203,7 @@ static void do_ADD( struct arb_vp_machine *m, union instruction op )
 
 static void do_DP3( struct arb_vp_machine *m, union instruction op )
 {
-   GLfloat *result = m->reg[op.alu.dst];
+   GLfloat *result = m->File[0][op.alu.dst];
    const GLfloat *arg0 = m->File[op.alu.file0][op.alu.idx0];
    const GLfloat *arg1 = m->File[op.alu.file1][op.alu.idx1];
 
@@ -212,7 +218,7 @@ static void do_DP3( struct arb_vp_machine *m, union instruction op )
 
 static void do_DP4( struct arb_vp_machine *m, union instruction op )
 {
-   GLfloat *result = m->reg[op.alu.dst];
+   GLfloat *result = m->File[0][op.alu.dst];
    const GLfloat *arg0 = m->File[op.alu.file0][op.alu.idx0];
    const GLfloat *arg1 = m->File[op.alu.file1][op.alu.idx1];
 
@@ -226,7 +232,7 @@ static void do_DP4( struct arb_vp_machine *m, union instruction op )
 
 static void do_DPH( struct arb_vp_machine *m, union instruction op )
 {
-   GLfloat *result = m->reg[op.alu.dst];
+   GLfloat *result = m->File[0][op.alu.dst];
    const GLfloat *arg0 = m->File[op.alu.file0][op.alu.idx0];
    const GLfloat *arg1 = m->File[op.alu.file1][op.alu.idx1];
 
@@ -240,7 +246,7 @@ static void do_DPH( struct arb_vp_machine *m, union instruction op )
 
 static void do_DST( struct arb_vp_machine *m, union instruction op )
 {
-   GLfloat *result = m->reg[op.alu.dst];
+   GLfloat *result = m->File[0][op.alu.dst];
    const GLfloat *arg0 = m->File[op.alu.file0][op.alu.idx0];
    const GLfloat *arg1 = m->File[op.alu.file1][op.alu.idx1];
 
@@ -253,7 +259,7 @@ static void do_DST( struct arb_vp_machine *m, union instruction op )
 
 static void do_EX2( struct arb_vp_machine *m, union instruction op ) 
 {
-   GLfloat *result = m->reg[op.alu.dst];
+   GLfloat *result = m->File[0][op.alu.dst];
    const GLfloat *arg0 = m->File[op.alu.file0][op.alu.idx0];
 
    result[0] = (GLfloat)RoughApproxPow2(arg0[0]);
@@ -262,7 +268,7 @@ static void do_EX2( struct arb_vp_machine *m, union instruction op )
 
 static void do_EXP( struct arb_vp_machine *m, union instruction op )
 {
-   GLfloat *result = m->reg[op.alu.dst];
+   GLfloat *result = m->File[0][op.alu.dst];
    const GLfloat *arg0 = m->File[op.alu.file0][op.alu.idx0];
    GLfloat tmp = arg0[0];
    GLfloat flr_tmp = FLOORF(tmp);
@@ -278,7 +284,7 @@ static void do_EXP( struct arb_vp_machine *m, union instruction op )
 
 static void do_FLR( struct arb_vp_machine *m, union instruction op ) 
 {
-   GLfloat *result = m->reg[op.alu.dst];
+   GLfloat *result = m->File[0][op.alu.dst];
    const GLfloat *arg0 = m->File[op.alu.file0][op.alu.idx0];
 
    result[0] = FLOORF(arg0[0]);
@@ -289,7 +295,7 @@ static void do_FLR( struct arb_vp_machine *m, union instruction op )
 
 static void do_FRC( struct arb_vp_machine *m, union instruction op ) 
 {
-   GLfloat *result = m->reg[op.alu.dst];
+   GLfloat *result = m->File[0][op.alu.dst];
    const GLfloat *arg0 = m->File[op.alu.file0][op.alu.idx0];
 
    result[0] = arg0[0] - FLOORF(arg0[0]);
@@ -300,7 +306,7 @@ static void do_FRC( struct arb_vp_machine *m, union instruction op )
 
 static void do_LG2( struct arb_vp_machine *m, union instruction op ) 
 {
-   GLfloat *result = m->reg[op.alu.dst];
+   GLfloat *result = m->File[0][op.alu.dst];
    const GLfloat *arg0 = m->File[op.alu.file0][op.alu.idx0];
 
    result[0] = RoughApproxLog2(arg0[0]);
@@ -311,7 +317,7 @@ static void do_LG2( struct arb_vp_machine *m, union instruction op )
 
 static void do_LIT( struct arb_vp_machine *m, union instruction op )
 {
-   GLfloat *result = m->reg[op.alu.dst];
+   GLfloat *result = m->File[0][op.alu.dst];
    const GLfloat *arg0 = m->File[op.alu.file0][op.alu.idx0];
 
    const GLfloat epsilon = 1.0F / 256.0F; /* per NV spec */
@@ -330,7 +336,7 @@ static void do_LIT( struct arb_vp_machine *m, union instruction op )
 
 static void do_LOG( struct arb_vp_machine *m, union instruction op )
 {
-   GLfloat *result = m->reg[op.alu.dst];
+   GLfloat *result = m->File[0][op.alu.dst];
    const GLfloat *arg0 = m->File[op.alu.file0][op.alu.idx0];
    GLfloat tmp = FABSF(arg0[0]);
    int exponent;
@@ -344,7 +350,7 @@ static void do_LOG( struct arb_vp_machine *m, union instruction op )
 
 static void do_MAX( struct arb_vp_machine *m, union instruction op )
 {
-   GLfloat *result = m->reg[op.alu.dst];
+   GLfloat *result = m->File[0][op.alu.dst];
    const GLfloat *arg0 = m->File[op.alu.file0][op.alu.idx0];
    const GLfloat *arg1 = m->File[op.alu.file1][op.alu.idx1];
 
@@ -357,7 +363,7 @@ static void do_MAX( struct arb_vp_machine *m, union instruction op )
 
 static void do_MIN( struct arb_vp_machine *m, union instruction op )
 {
-   GLfloat *result = m->reg[op.alu.dst];
+   GLfloat *result = m->File[0][op.alu.dst];
    const GLfloat *arg0 = m->File[op.alu.file0][op.alu.idx0];
    const GLfloat *arg1 = m->File[op.alu.file1][op.alu.idx1];
 
@@ -369,7 +375,7 @@ static void do_MIN( struct arb_vp_machine *m, union instruction op )
 
 static void do_MOV( struct arb_vp_machine *m, union instruction op )
 {
-   GLfloat *result = m->reg[op.alu.dst];
+   GLfloat *result = m->File[0][op.alu.dst];
    const GLfloat *arg0 = m->File[op.alu.file0][op.alu.idx0];
 
    result[0] = arg0[0];
@@ -380,7 +386,7 @@ static void do_MOV( struct arb_vp_machine *m, union instruction op )
 
 static void do_MUL( struct arb_vp_machine *m, union instruction op )
 {
-   GLfloat *result = m->reg[op.alu.dst];
+   GLfloat *result = m->File[0][op.alu.dst];
    const GLfloat *arg0 = m->File[op.alu.file0][op.alu.idx0];
    const GLfloat *arg1 = m->File[op.alu.file1][op.alu.idx1];
 
@@ -393,7 +399,7 @@ static void do_MUL( struct arb_vp_machine *m, union instruction op )
 
 static void do_POW( struct arb_vp_machine *m, union instruction op ) 
 {
-   GLfloat *result = m->reg[op.alu.dst];
+   GLfloat *result = m->File[0][op.alu.dst];
    const GLfloat *arg0 = m->File[op.alu.file0][op.alu.idx0];
    const GLfloat *arg1 = m->File[op.alu.file1][op.alu.idx1];
 
@@ -403,8 +409,8 @@ static void do_POW( struct arb_vp_machine *m, union instruction op )
 
 static void do_REL( struct arb_vp_machine *m, union instruction op )
 {
-   GLfloat *result = m->reg[op.alu.dst];
-   GLuint idx = (op.alu.idx0 + (GLint)m->reg[REG_ADDR][0]) & (MAX_NV_VERTEX_PROGRAM_PARAMS-1);
+   GLfloat *result = m->File[0][op.alu.dst];
+   GLuint idx = (op.alu.idx0 + (GLint)m->File[0][REG_ADDR][0]) & (MAX_NV_VERTEX_PROGRAM_PARAMS-1);
    const GLfloat *arg0 = m->File[op.alu.file0][idx];
 
    result[0] = arg0[0];
@@ -415,7 +421,7 @@ static void do_REL( struct arb_vp_machine *m, union instruction op )
 
 static void do_RCP( struct arb_vp_machine *m, union instruction op )
 {
-   GLfloat *result = m->reg[op.alu.dst];
+   GLfloat *result = m->File[0][op.alu.dst];
    const GLfloat *arg0 = m->File[op.alu.file0][op.alu.idx0];
 
    result[0] = 1.0F / arg0[0];  
@@ -424,7 +430,7 @@ static void do_RCP( struct arb_vp_machine *m, union instruction op )
 
 static void do_RSQ( struct arb_vp_machine *m, union instruction op )
 {
-   GLfloat *result = m->reg[op.alu.dst];
+   GLfloat *result = m->File[0][op.alu.dst];
    const GLfloat *arg0 = m->File[op.alu.file0][op.alu.idx0];
 
    result[0] = INV_SQRTF(FABSF(arg0[0]));
@@ -434,7 +440,7 @@ static void do_RSQ( struct arb_vp_machine *m, union instruction op )
 
 static void do_SGE( struct arb_vp_machine *m, union instruction op )
 {
-   GLfloat *result = m->reg[op.alu.dst];
+   GLfloat *result = m->File[0][op.alu.dst];
    const GLfloat *arg0 = m->File[op.alu.file0][op.alu.idx0];
    const GLfloat *arg1 = m->File[op.alu.file1][op.alu.idx1];
 
@@ -447,7 +453,7 @@ static void do_SGE( struct arb_vp_machine *m, union instruction op )
 
 static void do_SLT( struct arb_vp_machine *m, union instruction op )
 {
-   GLfloat *result = m->reg[op.alu.dst];
+   GLfloat *result = m->File[0][op.alu.dst];
    const GLfloat *arg0 = m->File[op.alu.file0][op.alu.idx0];
    const GLfloat *arg1 = m->File[op.alu.file1][op.alu.idx1];
 
@@ -459,7 +465,7 @@ static void do_SLT( struct arb_vp_machine *m, union instruction op )
 
 static void do_SUB( struct arb_vp_machine *m, union instruction op ) 
 {
-   GLfloat *result = m->reg[op.alu.dst];
+   GLfloat *result = m->File[0][op.alu.dst];
    const GLfloat *arg0 = m->File[op.alu.file0][op.alu.idx0];
    const GLfloat *arg1 = m->File[op.alu.file1][op.alu.idx1];
 
@@ -472,7 +478,7 @@ static void do_SUB( struct arb_vp_machine *m, union instruction op )
 
 static void do_XPD( struct arb_vp_machine *m, union instruction op ) 
 {
-   GLfloat *result = m->reg[op.alu.dst];
+   GLfloat *result = m->File[0][op.alu.dst];
    const GLfloat *arg0 = m->File[op.alu.file0][op.alu.idx0];
    const GLfloat *arg1 = m->File[op.alu.file1][op.alu.idx1];
 
@@ -618,6 +624,12 @@ static const struct opcode_info opcode_info[] =
    { 1, "REL", print_ALU },
 };
 
+void _tnl_disassem_vba_insn( union instruction op )
+{
+   const struct opcode_info *info = &opcode_info[op.alu.opcode];
+   info->print( op, info );
+}
+
 
 static void (* const opcode_func[])(struct arb_vp_machine *, union instruction) = 
 {
@@ -1006,12 +1018,18 @@ static void compile_vertex_program( struct arb_vp_machine *m,
     */
    if (DISASSEM) {
       for (i = 0; i < m->nr_instructions; i++) {
-        union instruction insn = m->instructions[i];
-        const struct opcode_info *info = &opcode_info[insn.alu.opcode];
-        info->print( insn, info );
+        _tnl_disassem_vba_insn(m->instructions[i]);
       }
       _mesa_printf("\n\n");
    }
+   
+#ifdef USE_SSE_ASM
+   /* TODO: check if anything changed...
+    */
+   if (m->try_codegen)
+      _tnl_sse_codegen_vertex_program(m);
+#endif
+
 }
 
 
@@ -1120,7 +1138,10 @@ static GLboolean do_ndc_cliptest( struct arb_vp_machine *m )
 }
 
 
-
+static void call_func( struct arb_vp_machine *m )
+{
+   m->func(m);
+}
 
 /**
  * Execute the given vertex program.  
@@ -1148,7 +1169,7 @@ run_arb_vertex_program(GLcontext *ctx, struct tnl_pipeline_stage *stage)
 
    /* Initialize regs where necessary:
     */
-   ASSIGN_4V(m->reg[REG_ID], 0, 0, 0, 1);
+   ASSIGN_4V(m->File[0][REG_ID], 0, 0, 0, 1);
 
    m->nr_inputs = m->nr_outputs = 0;
 
@@ -1159,7 +1180,7 @@ run_arb_vertex_program(GLcontext *ctx, struct tnl_pipeline_stage *stage)
         m->input[j].data = m->VB->AttribPtr[i]->data;
         m->input[j].stride = m->VB->AttribPtr[i]->stride;
         m->input[j].size = m->VB->AttribPtr[i]->size;
-        ASSIGN_4V(m->reg[REG_IN0 + i], 0, 0, 0, 1);
+        ASSIGN_4V(m->File[0][REG_IN0 + i], 0, 0, 0, 1);
       }
    }     
 
@@ -1178,26 +1199,31 @@ run_arb_vertex_program(GLcontext *ctx, struct tnl_pipeline_stage *stage)
       for (j = 0; j < m->nr_inputs; j++) {
         GLuint idx = REG_IN0 + m->input[j].idx;
         switch (m->input[j].size) {
-        case 4: m->reg[idx][3] = m->input[j].data[3];
-        case 3: m->reg[idx][2] = m->input[j].data[2];
-        case 2: m->reg[idx][1] = m->input[j].data[1];
-        case 1: m->reg[idx][0] = m->input[j].data[0];
+        case 4: m->File[0][idx][3] = m->input[j].data[3];
+        case 3: m->File[0][idx][2] = m->input[j].data[2];
+        case 2: m->File[0][idx][1] = m->input[j].data[1];
+        case 1: m->File[0][idx][0] = m->input[j].data[0];
         }
 
         STRIDE_F(m->input[j].data, m->input[j].stride);
       }
 
-      for (j = 0; j < m->nr_instructions; j++) {
-        union instruction inst = m->instructions[j];    
-        opcode_func[inst.alu.opcode]( m, inst );
+      if (m->func) {
+        call_func( m );
+      }
+      else {
+        for (j = 0; j < m->nr_instructions; j++) {
+           union instruction inst = m->instructions[j];         
+           opcode_func[inst.alu.opcode]( m, inst );
+        }
       }
 
       for (j = 0; j < m->nr_outputs; j++) {
         GLuint idx = REG_OUT0 + m->output[j].idx;
-        m->output[j].data[0] = m->reg[idx][0];
-        m->output[j].data[1] = m->reg[idx][1];
-        m->output[j].data[2] = m->reg[idx][2];
-        m->output[j].data[3] = m->reg[idx][3];
+        m->output[j].data[0] = m->File[0][idx][0];
+        m->output[j].data[1] = m->File[0][idx][1];
+        m->output[j].data[2] = m->File[0][idx][2];
+        m->output[j].data[3] = m->File[0][idx][3];
         m->output[j].data += 4;
       }
    }
@@ -1250,17 +1276,17 @@ run_arb_vertex_program(GLcontext *ctx, struct tnl_pipeline_stage *stage)
       }
    }
 
-#if 0
+#if 1
    for (i = 0; i < VB->Count; i++) {
       printf("Out %d: %f %f %f %f %f %f %f %f\n", i,
             VEC_ELT(VB->ClipPtr, GLfloat, i)[0],
             VEC_ELT(VB->ClipPtr, GLfloat, i)[1],
             VEC_ELT(VB->ClipPtr, GLfloat, i)[2],
             VEC_ELT(VB->ClipPtr, GLfloat, i)[3],
-            VEC_ELT(VB->ColorPtr[0], GLfloat, i)[0],
-            VEC_ELT(VB->ColorPtr[0], GLfloat, i)[1],
-            VEC_ELT(VB->ColorPtr[0], GLfloat, i)[2],
-            VEC_ELT(VB->ColorPtr[0], GLfloat, i)[3]);
+            VEC_ELT(VB->TexCoordPtr[0], GLfloat, i)[0],
+            VEC_ELT(VB->TexCoordPtr[0], GLfloat, i)[1],
+            VEC_ELT(VB->TexCoordPtr[0], GLfloat, i)[2],
+            VEC_ELT(VB->TexCoordPtr[0], GLfloat, i)[3]);
    }
 #endif
 
@@ -1288,7 +1314,6 @@ validate_vertex_program( GLcontext *ctx, struct tnl_pipeline_stage *stage )
       
       /* Grab the state GL state and put into registers:
        */
-      m->File[FILE_REG] = m->reg;
       m->File[FILE_LOCAL_PARAM] = program->Base.LocalParams;
       m->File[FILE_ENV_PARAM] = ctx->VertexProgram.Parameters;
       m->File[FILE_STATE_PARAM] = program->Parameters->ParameterValues;
@@ -1324,6 +1349,13 @@ static GLboolean init_vertex_program( GLcontext *ctx,
    m->VB = VB;
    m->ctx = ctx;
 
+   m->File[0] = ALIGN_MALLOC(REG_MAX * sizeof(GLfloat) * 4, 16);
+
+   if (_mesa_getenv("MESA_EXPERIMENTAL"))
+      m->try_codegen = 1;
+
+   _mesa_printf("try_codegen %d\n", m->try_codegen);
+
    /* Allocate arrays of vertex output values */
    for (i = 0; i < VERT_RESULT_MAX; i++) {
       _mesa_vector4f_alloc( &m->attribs[i], 0, size, 32 );
@@ -1363,6 +1395,7 @@ static void dtr( struct tnl_pipeline_stage *stage )
       /* free misc arrays */
       _mesa_vector4f_free( &m->ndcCoords );
       ALIGN_FREE( m->clipmask );
+      ALIGN_FREE( m->File[0] );
 
       FREE( m );
       stage->privatePtr = NULL;
index 618a96c74ead50fe35c5c500adb4005a1a974224..fd2f09f1da6d59d312b9ce06243c5f65a8a05e19 100644 (file)
 #define MSK        (VP_MAX_OPCODE+1)
 #define REL        (VP_MAX_OPCODE+2)
 
-
-/* Layout of register file:
-
-  0 -- Scratch (Arg0)
-  1 -- Scratch (Arg1)
-  2 -- Scratch (Result)
-  4 -- Program Temporary 0
-  16 -- Program Temporary 12 (max for NV_VERTEX_PROGRAM)
-  17 -- Output 0
-  31 -- Output 15 (max for NV_VERTEX_PROGRAM) (Last writeable register)
-  32 -- Parameter 0
-  ..
-  127 -- Parameter 63 (max for NV_VERTEX_PROGRAM)
-
-*/
-
 #define FILE_REG         0
 #define FILE_LOCAL_PARAM 1
 #define FILE_ENV_PARAM   2
 #define FILE_STATE_PARAM 3
 
-
 #define REG_ARG0   0
 #define REG_ARG1   1
 #define REG_ARG2   2
 #define REG_IN0    32
 #define REG_IN15   47
 #define REG_ID     48          /* 0,0,0,1 */
+#define REG_ONES   49          /* 1,1,1,1 */
+#define REG_SWZ    50          /* -1,1,0,0 */
+#define REG_NEG    51          /* -1,-1,-1,-1 */
+#define REG_UNDEF  127         /* special case - never used */
 #define REG_MAX    128
 #define REG_INVALID ~0
 
@@ -81,7 +68,6 @@
  * following micro-instructions, each representable in a 32 bit packed
  * structure.
  */
-
 struct reg {
    GLuint file:2;
    GLuint idx:7;
@@ -124,13 +110,6 @@ union instruction {
 #define GET_RSW(swz, idx)      (((swz) >> ((idx)*2)) & 0x3)
 
 
-
-struct compilation {
-   GLuint reg_active;
-   union instruction *csr;
-   struct vertex_buffer *VB;   /* for input sizes! */
-};
-
 struct input {
    GLuint idx;
    GLfloat *data;
@@ -149,8 +128,7 @@ struct output {
  * Private storage for the vertex program pipeline stage.
  */
 struct arb_vp_machine {
-   GLfloat reg[REG_MAX][4];    /* Program temporaries, inputs and outputs */
-   GLfloat (*File[4])[4];      /* All values reference-able from the program. */
+   GLfloat (*File[4])[4];      /* All values referencable from the program. */
    GLint AddressReg;
 
    struct input input[16];
@@ -170,9 +148,14 @@ struct arb_vp_machine {
 
    GLuint vtx_nr;              /**< loop counter */
 
+   void (*func)( struct arb_vp_machine * ); /**< codegen'd program? */
+
    struct vertex_buffer *VB;
    GLcontext *ctx;
+
+   GLboolean try_codegen;
 };
 
+void _tnl_disassem_vba_insn( union instruction op );
 
 #endif
diff --git a/src/mesa/tnl/t_vb_arbprogram_sse.c b/src/mesa/tnl/t_vb_arbprogram_sse.c
new file mode 100644 (file)
index 0000000..6b85e23
--- /dev/null
@@ -0,0 +1,878 @@
+/*
+ * Mesa 3-D graphics library
+ * Version:  6.3
+ *
+ * Copyright (C) 1999-2004  Brian Paul   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
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * 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 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 NONINFRINGEMENT.  IN NO EVENT SHALL
+ * BRIAN PAUL 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.
+ */
+
+/**
+ * \file t_vb_arb_program_sse.c
+ *
+ * Translate simplified vertex_program representation to x86/SSE/SSE2
+ * machine code using mesa's rtasm runtime assembler.
+ *
+ * \author Keith Whitwell
+ */
+
+#include "glheader.h"
+#include "context.h"
+#include "imports.h"
+#include "macros.h"
+#include "mtypes.h"
+#include "arbprogparse.h"
+#include "program.h"
+#include "math/m_matrix.h"
+#include "math/m_translate.h"
+#include "t_context.h"
+#include "t_vb_arbprogram.h"
+
+#if defined(USE_SSE_ASM)
+
+#include "x86/rtasm/x86sse.h"
+#include "x86/common_x86_asm.h"
+
+
+#define X    0
+#define Y    1
+#define Z    2
+#define W    3
+
+/* Reg usage:
+ *
+ * EAX - point to 'm->File[0]'
+ * ECX - point to 'm->File[3]'
+ * EDX,
+ * EBX,
+ * ESP,
+ * EBP,
+ * ESI,
+ * EDI
+ */
+
+
+
+#define FAIL                                                           \
+do {                                                                   \
+   _mesa_printf("x86 translation failed in %s\n", __FUNCTION__);       \
+   return GL_FALSE;                                                    \
+} while (0)
+
+struct compilation {
+   struct x86_function func;
+   struct arb_vp_machine *m;
+
+   GLuint insn_counter;
+
+   struct {
+      GLuint file:2;
+      GLuint idx:7;
+      GLuint dirty:1;
+      GLuint last_used:10;
+   } xmm[8];
+
+   struct {
+      struct x86_reg base;
+   } file[4];
+
+   GLboolean have_sse2;
+};
+
+static INLINE GLboolean eq( struct x86_reg a,
+                           struct x86_reg b )
+{
+   return (a.file == b.file &&
+          a.idx == b.idx &&
+          a.mod == b.mod &&
+          a.disp == b.disp);
+}
+      
+
+
+static struct x86_reg get_reg_ptr(GLuint file,
+                                 GLuint idx )
+{
+   struct x86_reg reg;
+
+   switch (file) {
+   case FILE_REG:
+      reg = x86_make_reg(file_REG32, reg_AX);
+      assert(idx != REG_UNDEF);
+      break;
+   case FILE_STATE_PARAM:
+      reg = x86_make_reg(file_REG32, reg_CX);
+      break;
+   default:
+      assert(0);
+   }
+
+   return x86_make_disp(reg, 16 * idx);
+}
+                         
+
+static void spill( struct compilation *cp, GLuint idx )
+{
+   struct x86_reg oldval = get_reg_ptr(cp->xmm[idx].file,
+                                      cp->xmm[idx].idx);
+
+   assert(cp->xmm[idx].dirty);
+   sse_movups(&cp->func, oldval, x86_make_reg(file_XMM, idx));
+   cp->xmm[idx].dirty = 0;
+}
+
+static struct x86_reg get_xmm_reg( struct compilation *cp )
+{
+   GLuint i;
+   GLuint oldest = 0;
+
+   for (i = 0; i < 8; i++) 
+      if (cp->xmm[i].last_used < cp->xmm[oldest].last_used)
+        oldest = i;
+
+   /* Need to write out the old value?
+    */
+   if (cp->xmm[oldest].dirty) 
+      spill(cp, oldest);
+
+   assert(cp->xmm[oldest].last_used != cp->insn_counter);
+
+   cp->xmm[oldest].file = FILE_REG;
+   cp->xmm[oldest].idx = REG_UNDEF;
+   cp->xmm[oldest].last_used = cp->insn_counter;
+   return x86_make_reg(file_XMM, oldest);
+}
+
+      
+
+
+static struct x86_reg get_dst_reg( struct compilation *cp, 
+                                  GLuint file, GLuint idx )
+{
+   struct x86_reg reg;
+   GLuint i;
+
+   /* Invalidate any old copy of this register in XMM0-7.  Don't reuse
+    * as this may be one of the arguments.
+    */
+   for (i = 0; i < 8; i++) {
+      if (cp->xmm[i].file == file && cp->xmm[i].idx == idx) {
+        cp->xmm[i].file = FILE_REG;
+        cp->xmm[i].idx = REG_UNDEF;
+        cp->xmm[i].dirty = 0;
+        break;
+      }
+   }
+
+   reg = get_xmm_reg( cp );
+   cp->xmm[reg.idx].file = file;
+   cp->xmm[reg.idx].idx = idx;
+   cp->xmm[reg.idx].dirty = 1;
+   return reg;   
+}
+
+
+/* Return an XMM reg if the argument is resident, otherwise return a
+ * base+offset pointer to the saved value.
+ */
+static struct x86_reg get_arg( struct compilation *cp, GLuint file, GLuint idx )
+{
+   GLuint i;
+
+   for (i = 0; i < 8; i++) {
+      if (cp->xmm[i].file == file &&
+         cp->xmm[i].idx == idx) {
+        cp->xmm[i].last_used = cp->insn_counter;
+        return x86_make_reg(file_XMM, i);
+      }
+   }
+
+   return get_reg_ptr(file, idx);
+}
+
+static void emit_pshufd( struct compilation *cp,
+                        struct x86_reg dst,
+                        struct x86_reg arg0,
+                        GLubyte shuf )
+{
+   if (cp->have_sse2) {
+      sse2_pshufd(&cp->func, dst, arg0, shuf);
+      cp->func.fn = 0;
+   }
+   else {
+      if (!eq(dst, arg0)) 
+        sse_movups(&cp->func, dst, arg0);
+
+      sse_shufps(&cp->func, dst, dst, shuf);
+   }
+}
+                        
+
+
+/* Perform a reduced swizzle.  
+ */
+static GLboolean emit_RSW( struct compilation *cp, union instruction op ) 
+{
+   struct x86_reg arg0 = get_arg(cp, op.rsw.file0, op.rsw.idx0);
+   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.rsw.dst);
+   GLuint swz = op.rsw.swz;
+   GLuint neg = op.rsw.neg;
+
+   emit_pshufd(cp, dst, arg0, swz);
+   
+   if (neg) {
+      struct x86_reg negs = get_arg(cp, FILE_REG, REG_SWZ);
+      struct x86_reg tmp = get_xmm_reg(cp);
+      /* Load 1,-1,0,0
+       * Use neg as arg to pshufd
+       * Multiply
+       */
+      emit_pshufd(cp, tmp, negs, 
+                 SHUF((neg & 1) ? 1 : 0,
+                      (neg & 2) ? 1 : 0,
+                      (neg & 4) ? 1 : 0,
+                      (neg & 8) ? 1 : 0));
+      sse_mulps(&cp->func, dst, tmp);
+   }
+
+   return GL_TRUE;
+}
+
+/* Used to implement write masking.  This and most of the other instructions
+ * here would be easier to implement if there had been a translation
+ * to a 2 argument format (dst/arg0, arg1) at the shader level before
+ * attempting to translate to x86/sse code.
+ */
+/* Hmm.  I went back to MSK from SEL to make things easier -- was that just BS?
+ */
+static GLboolean emit_MSK( struct compilation *cp, union instruction op )
+{
+   struct x86_reg arg = get_arg(cp, op.msk.file, op.msk.idx);
+   struct x86_reg dst0 = get_arg(cp, FILE_REG, op.msk.dst);
+   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.msk.dst);
+   
+   sse_movups(&cp->func, dst, dst0);
+
+   switch (op.msk.mask) {
+   case 0:
+      return GL_TRUE;
+
+   case WRITEMASK_X:
+      if (arg.file == file_XMM) {
+        sse_movss(&cp->func, dst, arg);
+      }
+      else {
+        struct x86_reg tmp = get_xmm_reg(cp);
+        sse_movss(&cp->func, tmp, arg);
+        sse_movss(&cp->func, dst, tmp);
+      }
+      return GL_TRUE;
+
+   case WRITEMASK_Y: {
+      struct x86_reg tmp = get_xmm_reg(cp);
+      emit_pshufd(cp, dst, dst, SHUF(Y, X, Z, W));
+      emit_pshufd(cp, tmp, arg, SHUF(Y, X, Z, W));
+      sse_movss(&cp->func, dst, tmp);
+      emit_pshufd(cp, dst, dst, SHUF(Y, X, Z, W));
+      return GL_TRUE;
+   }
+
+   case WRITEMASK_Z: {
+      struct x86_reg tmp = get_xmm_reg(cp);
+      emit_pshufd(cp, dst, dst, SHUF(Z, Y, X, W));
+      emit_pshufd(cp, tmp, arg, SHUF(Z, Y, X, W));
+      sse_movss(&cp->func, dst, tmp);
+      emit_pshufd(cp, dst, dst, SHUF(Z, Y, X, W));
+      return GL_TRUE;
+   }
+
+   case WRITEMASK_W: {
+      struct x86_reg tmp = get_xmm_reg(cp);
+      emit_pshufd(cp, dst, dst, SHUF(W, Y, Z, X));
+      emit_pshufd(cp, tmp, arg, SHUF(W, Y, Z, X));
+      sse_movss(&cp->func, dst, tmp);
+      emit_pshufd(cp, dst, dst, SHUF(W, Y, Z, X));
+      return GL_TRUE;
+   }
+
+   case WRITEMASK_XY:
+      sse_shufps(&cp->func, dst, arg, SHUF(X, Y, Z, W));
+      return GL_TRUE;
+
+   case WRITEMASK_ZW: {
+      struct x86_reg tmp = get_xmm_reg(cp);      
+      sse_movups(&cp->func, tmp, dst);
+      sse_movups(&cp->func, dst, arg);
+      sse_shufps(&cp->func, dst, tmp, SHUF(X, Y, Z, W));
+      return GL_TRUE;
+   }
+
+   case WRITEMASK_YZW: {
+      struct x86_reg tmp = get_xmm_reg(cp);      
+      sse_movss(&cp->func, tmp, dst);
+      sse_movups(&cp->func, dst, arg);
+      sse_movss(&cp->func, dst, tmp);
+      return GL_TRUE;
+   }
+
+   case WRITEMASK_XYZW:
+      sse_movups(&cp->func, dst, arg);
+      return GL_TRUE;      
+
+   default:
+      FAIL;
+   }
+
+#if 0
+   /* The catchall implementation:
+    */
+
+   /* make full width bitmask in tmp 
+    * dst = ~tmp
+    * tmp &= arg0
+    * dst &= arg1
+    * dst |= tmp
+    */
+   {
+      struct x86_reg negs = get_arg(cp, FILE_REG, REG_NEGS);
+      emit_pshufd(cp, tmp, negs, 
+                 SHUF((op.msk.mask & 1) ? 2 : 0,
+                      (op.msk.mask & 2) ? 2 : 0,
+                      (op.msk.mask & 4) ? 2 : 0,
+                      (op.msk.mask & 8) ? 2 : 0));
+      sse_mulps(&cp->func, dst, tmp);
+   }
+
+   return GL_TRUE;
+#endif
+   FAIL;
+}
+
+
+
+static GLboolean emit_PRT( struct compilation *cp, union instruction op )
+{
+   FAIL;
+}
+
+
+/**
+ * The traditional instructions.  All operate on internal registers
+ * and ignore write masks and swizzling issues.
+ */
+
+static GLboolean emit_ABS( struct compilation *cp, union instruction op ) 
+{
+   struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0);
+   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+   struct x86_reg neg = get_reg_ptr(FILE_REG, REG_NEG);
+
+   sse_movups(&cp->func, dst, arg0);
+   sse_mulps(&cp->func, dst, neg);
+   sse_maxps(&cp->func, dst, arg0);
+   return GL_TRUE;
+}
+
+static GLboolean emit_ADD( struct compilation *cp, union instruction op )
+{
+   struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0);
+   struct x86_reg arg1 = get_arg(cp, op.alu.file1, op.alu.idx1);
+   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+
+   sse_movups(&cp->func, dst, arg0);
+   sse_addps(&cp->func, dst, arg1);
+   return GL_TRUE;
+}
+
+
+/* The dotproduct instructions don't really do that well in sse:
+ */
+static GLboolean emit_DP3( struct compilation *cp, union instruction op )
+{
+   struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0);
+   struct x86_reg arg1 = get_arg(cp, op.alu.file1, op.alu.idx1);
+   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+   struct x86_reg tmp = get_xmm_reg(cp); 
+
+   sse_movups(&cp->func, dst, arg0);
+   sse_mulps(&cp->func, dst, arg1);
+   
+   /* Now the hard bit: sum the first 3 values:
+    */ 
+   sse_movhlps(&cp->func, tmp, dst);
+   sse_addss(&cp->func, dst, tmp); /* a*x+c*z, b*y, ?, ? */
+   emit_pshufd(cp, tmp, dst, SHUF(Y,X,W,Z));
+   sse_addss(&cp->func, dst, tmp);
+   sse_shufps(&cp->func, dst, dst, SHUF(X, X, X, X));
+   return GL_TRUE;
+}
+
+
+
+static GLboolean emit_DP4( struct compilation *cp, union instruction op )
+{
+   struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0);
+   struct x86_reg arg1 = get_arg(cp, op.alu.file1, op.alu.idx1);
+   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+   struct x86_reg tmp = get_xmm_reg(cp);      
+
+   sse_movups(&cp->func, dst, arg0);
+   sse_mulps(&cp->func, dst, arg1);
+   
+   /* Now the hard bit: sum the values:
+    */ 
+   sse_movhlps(&cp->func, tmp, dst);
+   sse_addps(&cp->func, dst, tmp); /* a*x+c*z, b*y+d*w, a*x+c*z, b*y+d*w */
+   emit_pshufd(cp, tmp, dst, SHUF(Y,X,W,Z));
+   sse_addss(&cp->func, dst, tmp);
+   sse_shufps(&cp->func, dst, dst, SHUF(X, X, X, X));
+   return GL_TRUE;
+}
+
+static GLboolean emit_DPH( struct compilation *cp, union instruction op )
+{
+/*    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0); */
+/*    struct x86_reg arg1 = get_arg(cp, op.alu.file1, op.alu.idx1); */
+   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+
+/*    dst[0] = (arg0[0] * arg1[0] +  */
+/*          arg0[1] * arg1[1] +  */
+/*          arg0[2] * arg1[2] +  */
+/*          1.0     * arg1[3]); */
+   
+   sse_shufps(&cp->func, dst, dst, SHUF(X, X, X, X));
+   FAIL;
+}
+
+static GLboolean emit_DST( struct compilation *cp, union instruction op )
+{
+/*    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0); */
+/*    struct x86_reg arg1 = get_arg(cp, op.alu.file1, op.alu.idx1); */
+/*    struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst); */
+
+/*    dst[0] = 1.0     * 1.0F; */
+/*    dst[1] = arg0[1] * arg1[1]; */
+/*    dst[2] = arg0[2] * 1.0; */
+/*    dst[3] = 1.0     * arg1[3]; */
+
+   FAIL;
+}
+
+
+static GLboolean emit_EX2( struct compilation *cp, union instruction op ) 
+{
+/*    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0); */
+   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+
+/*    dst[0] = (GLfloat)RoughApproxPow2(arg0[0]); */
+   sse_shufps(&cp->func, dst, dst, SHUF(X, X, X, X));
+   FAIL;
+}
+
+static GLboolean emit_EXP( struct compilation *cp, union instruction op )
+{
+/*    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0); */
+/*    struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst); */
+
+/*    GLfloat tmp = arg0[0]; */
+/*    GLfloat flr_tmp = FLOORF(tmp); */
+/*    dst[0] = (GLfloat) (1 << (int)flr_tmp); */
+/*    dst[1] = tmp - flr_tmp; */
+/*    dst[2] = RoughApproxPow2(tmp); */
+/*    dst[3] = 1.0F; */
+   FAIL;
+}
+
+static GLboolean emit_FLR( struct compilation *cp, union instruction op ) 
+{
+/*    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0); */
+/*    struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst); */
+
+/*    dst[0] = FLOORF(arg0[0]); */
+/*    dst[1] = FLOORF(arg0[1]); */
+/*    dst[2] = FLOORF(arg0[2]); */
+/*    dst[3] = FLOORF(arg0[3]); */
+   FAIL;
+}
+
+static GLboolean emit_FRC( struct compilation *cp, union instruction op ) 
+{
+/*    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0); */
+/*    struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst); */
+
+/*    dst[0] = arg0[0] - FLOORF(arg0[0]); */
+/*    dst[1] = arg0[1] - FLOORF(arg0[1]); */
+/*    dst[2] = arg0[2] - FLOORF(arg0[2]); */
+/*    dst[3] = arg0[3] - FLOORF(arg0[3]); */
+   FAIL;
+}
+
+static GLboolean emit_LG2( struct compilation *cp, union instruction op ) 
+{
+/*    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0); */
+/*    struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst); */
+
+/*    dst[0] = RoughApproxLog2(arg0[0]); */
+
+/*    sse_shufps(&cp->func, dst, dst, SHUF(X, X, X, X)); */
+   FAIL;
+}
+
+
+
+static GLboolean emit_LIT( struct compilation *cp, union instruction op )
+{
+/*    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0); */
+/*    struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst); */
+
+/*    const GLfloat epsilon = 1.0F / 256.0F; */
+/*    GLfloat tmp[4]; */
+
+/*    tmp[0] = MAX2(arg0[0], 0.0F); */
+/*    tmp[1] = MAX2(arg0[1], 0.0F); */
+/*    tmp[3] = CLAMP(arg0[3], -(128.0F - epsilon), (128.0F - epsilon)); */
+
+/*    dst[0] = 1.0; */
+/*    dst[1] = tmp[0]; */
+/*    dst[2] = (tmp[0] > 0.0) ? RoughApproxPower(tmp[1], tmp[3]) : 0.0F; */
+/*    dst[3] = 1.0; */
+   FAIL;
+}
+
+
+static GLboolean emit_LOG( struct compilation *cp, union instruction op )
+{
+/*    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0); */
+/*    struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst); */
+
+/*    GLfloat tmp = FABSF(arg0[0]); */
+/*    int exponent; */
+/*    GLfloat mantissa = FREXPF(tmp, &exponent); */
+/*    dst[0] = (GLfloat) (exponent - 1); */
+/*    dst[1] = 2.0 * mantissa; // map [.5, 1) -> [1, 2)  */
+/*    dst[2] = dst[0] + LOG2(dst[1]); */
+/*    dst[3] = 1.0; */
+   FAIL;
+}
+
+static GLboolean emit_MAX( struct compilation *cp, union instruction op )
+{
+   struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0);
+   struct x86_reg arg1 = get_arg(cp, op.alu.file1, op.alu.idx1);
+   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+
+   sse_movups(&cp->func, dst, arg0);
+   sse_maxps(&cp->func, dst, arg1);
+   return GL_TRUE;
+}
+
+
+static GLboolean emit_MIN( struct compilation *cp, union instruction op )
+{
+   struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0);
+   struct x86_reg arg1 = get_arg(cp, op.alu.file1, op.alu.idx1);
+   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+
+   sse_movups(&cp->func, dst, arg0);
+   sse_minps(&cp->func, dst, arg1);
+   return GL_TRUE;
+}
+
+static GLboolean emit_MOV( struct compilation *cp, union instruction op )
+{
+   struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0);
+   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+
+   sse_movups(&cp->func, dst, arg0);
+   return GL_TRUE;
+}
+
+static GLboolean emit_MUL( struct compilation *cp, union instruction op )
+{
+   struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0);
+   struct x86_reg arg1 = get_arg(cp, op.alu.file1, op.alu.idx1);
+   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+
+   sse_movups(&cp->func, dst, arg0);
+   sse_mulps(&cp->func, dst, arg1);
+   return GL_TRUE;
+}
+
+
+static GLboolean emit_POW( struct compilation *cp, union instruction op ) 
+{
+/*    struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0); */
+/*    struct x86_reg arg1 = get_arg(cp, op.alu.file1, op.alu.idx1); */
+   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+
+/*    dst[0] = (GLfloat)RoughApproxPower(arg0[0], arg1[0]); */
+
+   sse_shufps(&cp->func, dst, dst, SHUF(X, X, X, X));
+   FAIL;
+}
+
+static GLboolean emit_REL( struct compilation *cp, union instruction op )
+{
+/*    GLuint idx = (op.alu.idx0 + (GLint)cp->File[0][REG_ADDR][0]) & (MAX_NV_VERTEX_PROGRAM_PARAMS-1); */
+/*    GLuint idx = 0; */
+/*    struct x86_reg arg0 = get_arg(cp, op.alu.file0, idx); */
+/*    struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst); */
+
+/*    dst[0] = arg0[0]; */
+/*    dst[1] = arg0[1]; */
+/*    dst[2] = arg0[2]; */
+/*    dst[3] = arg0[3]; */
+
+   FAIL;
+}
+
+static GLboolean emit_RCP( struct compilation *cp, union instruction op )
+{
+   struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0);
+   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+
+   if (cp->have_sse2) {
+      sse2_rcpss(&cp->func, dst, arg0);
+   }
+   else {
+      struct x86_reg ones = get_reg_ptr(FILE_REG, REG_ONES);
+      sse_movss(&cp->func, dst, ones);
+      sse_divss(&cp->func, dst, arg0);
+   }
+
+   sse_shufps(&cp->func, dst, dst, SHUF(X, X, X, X));
+   return GL_TRUE;
+}
+
+static GLboolean emit_RSQ( struct compilation *cp, union instruction op )
+{
+   struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0);
+   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+
+   sse_rsqrtss(&cp->func, dst, arg0);
+   sse_shufps(&cp->func, dst, dst, SHUF(X, X, X, X));
+   return GL_TRUE;
+}
+
+
+static GLboolean emit_SGE( struct compilation *cp, union instruction op )
+{
+   struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0);
+   struct x86_reg arg1 = get_arg(cp, op.alu.file1, op.alu.idx1);
+   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+   struct x86_reg ones = get_reg_ptr(FILE_REG, REG_ONES);
+
+   sse_movups(&cp->func, dst, arg0);
+   sse_cmpps(&cp->func, dst, arg1, cc_NotLessThan);
+   sse_andps(&cp->func, dst, ones);
+   return GL_TRUE;
+}
+
+
+static GLboolean emit_SLT( struct compilation *cp, union instruction op )
+{
+   struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0);
+   struct x86_reg arg1 = get_arg(cp, op.alu.file1, op.alu.idx1);
+   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+   struct x86_reg ones = get_reg_ptr(FILE_REG, REG_ONES);
+   
+   sse_movups(&cp->func, dst, arg0);
+   sse_cmpps(&cp->func, dst, arg1, cc_LessThan);
+   sse_andps(&cp->func, dst, ones);
+   return GL_TRUE;
+}
+
+static GLboolean emit_SUB( struct compilation *cp, union instruction op ) 
+{
+   struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0);
+   struct x86_reg arg1 = get_arg(cp, op.alu.file1, op.alu.idx1);
+   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+
+   sse_movups(&cp->func, dst, arg0);
+   sse_subps(&cp->func, dst, arg1);
+   return GL_TRUE;
+}
+
+
+static GLboolean emit_XPD( struct compilation *cp, union instruction op ) 
+{
+   struct x86_reg arg0 = get_arg(cp, op.alu.file0, op.alu.idx0);
+   struct x86_reg arg1 = get_arg(cp, op.alu.file1, op.alu.idx1);
+   struct x86_reg dst = get_dst_reg(cp, FILE_REG, op.alu.dst);
+   struct x86_reg tmp0 = get_xmm_reg(cp);
+   struct x86_reg tmp1 = get_xmm_reg(cp);
+
+   /* Could avoid tmp0, tmp1 if we overwrote arg0, arg1.  Need a way
+    * to invalidate registers.  This will come with better analysis
+    * (liveness analysis) of the incoming program.
+    */
+   emit_pshufd(cp, dst, arg0, SHUF(Y, Z, X, W));
+   emit_pshufd(cp, tmp1, arg1, SHUF(Z, X, Y, W));
+   sse_mulps(&cp->func, dst, tmp1);
+   emit_pshufd(cp, tmp0, arg0, SHUF(Z, X, Y, W));
+   emit_pshufd(cp, tmp1, arg1, SHUF(Y, Z, X, W));
+   sse_mulps(&cp->func, tmp0, tmp1);
+   sse_subps(&cp->func, dst, tmp0);
+
+/*    dst[0] = arg0[1] * arg1[2] - arg0[2] * arg1[1]; */
+/*    dst[1] = arg0[2] * arg1[0] - arg0[0] * arg1[2]; */
+/*    dst[2] = arg0[0] * arg1[1] - arg0[1] * arg1[0]; */
+/*    dst[3] is undef */
+
+   return GL_TRUE;
+}
+
+static GLboolean emit_NOP( struct compilation *cp, union instruction op ) 
+{
+   return GL_TRUE;
+}
+
+
+static GLboolean (* const emit_func[])(struct compilation *, union instruction) = 
+{
+   emit_ABS,
+   emit_ADD,
+   emit_NOP,
+   emit_DP3,
+   emit_DP4,
+   emit_DPH,
+   emit_DST,
+   emit_NOP,
+   emit_EX2,
+   emit_EXP,
+   emit_FLR,
+   emit_FRC,
+   emit_LG2,
+   emit_LIT,
+   emit_LOG,
+   emit_NOP,
+   emit_MAX,
+   emit_MIN,
+   emit_MOV,
+   emit_MUL,
+   emit_POW,
+   emit_PRT,
+   emit_NOP,
+   emit_RCP,
+   emit_RSQ,
+   emit_SGE,
+   emit_SLT,
+   emit_SUB,
+   emit_RSW,
+   emit_XPD,
+   emit_RSW,
+   emit_MSK,
+   emit_REL,
+};
+
+static GLint get_offset( const void *a, const void *b )
+{
+   return (const char *)b - (const char *)a;
+}
+
+
+static GLboolean build_vertex_program( struct compilation *cp )
+{
+   GLuint j;
+
+   struct x86_reg regEAX = x86_make_reg(file_REG32, reg_AX);
+   struct x86_reg parmECX = x86_make_reg(file_REG32, reg_CX);
+
+   x86_mov(&cp->func, regEAX, x86_fn_arg(&cp->func, 1));
+   x86_mov(&cp->func, parmECX, regEAX);
+   
+   x86_mov(&cp->func, regEAX, x86_make_disp(regEAX, get_offset(cp->m, cp->m->File + FILE_REG)));
+   x86_mov(&cp->func, parmECX, x86_make_disp(parmECX, get_offset(cp->m, cp->m->File + FILE_STATE_PARAM)));
+
+   for (j = 0; j < cp->m->nr_instructions; j++) {
+      union instruction inst = cp->m->instructions[j];  
+      cp->insn_counter = j+1;  /* avoid zero */
+      
+      _mesa_printf("%p: ", cp->func.csr); 
+      _tnl_disassem_vba_insn( inst );
+      cp->func.fn = NULL;
+
+      if (!emit_func[inst.alu.opcode]( cp, inst )) {
+        return GL_FALSE;
+      }
+   }
+
+   /* TODO: only for outputs:
+    */
+   for (j = 0; j < 8; j++) {
+      if (cp->xmm[j].dirty) 
+        spill(cp, j);
+   }
+      
+
+   /* Exit mmx state?
+    */
+   if (cp->func.need_emms)
+      mmx_emms(&cp->func);
+
+   x86_ret(&cp->func);
+
+   return GL_TRUE;
+}
+
+/**
+ * Execute the given vertex program.  
+ * 
+ * TODO: Integrate the t_vertex.c code here, to build machine vertices
+ * directly at this point.
+ *
+ * TODO: Eliminate the VB struct entirely and just use
+ * struct arb_vertex_machine.
+ */
+GLboolean
+_tnl_sse_codegen_vertex_program(struct arb_vp_machine *m)
+{
+   struct compilation cp;
+   
+   memset(&cp, 0, sizeof(cp));
+   cp.m = m;
+   cp.have_sse2 = 1;
+
+   if (m->func) {
+      free((void *)m->func);
+      m->func = NULL;
+   }
+
+   x86_init_func(&cp.func);
+
+   if (!build_vertex_program(&cp)) {
+      x86_release_func( &cp.func );
+      return GL_FALSE;
+   }
+
+   m->func = (void (*)(struct arb_vp_machine *))x86_get_func( &cp.func );
+   return GL_TRUE;
+}
+
+
+
+#else
+
+GLboolean
+_tnl_sse_codegen_vertex_program( GLcontext *ctx )
+{
+   /* Dummy version for when USE_SSE_ASM not defined */
+   return GL_FALSE;
+}
+
+#endif