From ea93b2986b7e8c4c278b408d6a546442d40351f1 Mon Sep 17 00:00:00 2001 From: Keith Whitwell Date: Wed, 8 Jun 2005 22:10:16 +0000 Subject: [PATCH] Runtime generate sse/sse2 code for some vertex programs. Experimental 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 | 137 +++-- src/mesa/tnl/t_vb_arbprogram.h | 37 +- src/mesa/tnl/t_vb_arbprogram_sse.c | 878 +++++++++++++++++++++++++++++ 3 files changed, 973 insertions(+), 79 deletions(-) create mode 100644 src/mesa/tnl/t_vb_arbprogram_sse.c diff --git a/src/mesa/tnl/t_vb_arbprogram.c b/src/mesa/tnl/t_vb_arbprogram.c index eafd8d094b2..eeab0ed3ca3 100644 --- a/src/mesa/tnl/t_vb_arbprogram.c +++ b/src/mesa/tnl/t_vb_arbprogram.c @@ -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; diff --git a/src/mesa/tnl/t_vb_arbprogram.h b/src/mesa/tnl/t_vb_arbprogram.h index 618a96c74ea..fd2f09f1da6 100644 --- a/src/mesa/tnl/t_vb_arbprogram.h +++ b/src/mesa/tnl/t_vb_arbprogram.h @@ -40,28 +40,11 @@ #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 @@ -74,6 +57,10 @@ #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 index 00000000000..6b85e232158 --- /dev/null +++ b/src/mesa/tnl/t_vb_arbprogram_sse.c @@ -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 -- 2.30.2