From: Keith Whitwell Date: Sun, 10 Jul 2005 11:23:10 +0000 (+0000) Subject: Add runtime compiled x87 implementations for most of the remaining X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=9311bc253e0942af621b7efab0549ed75c0ce4a8;p=mesa.git Add runtime compiled x87 implementations for most of the remaining opcodes. When enabled via environment vars, gears runs and almost looks right but other apps are still quite buggy. --- diff --git a/src/mesa/tnl/t_vb_arbprogram.c b/src/mesa/tnl/t_vb_arbprogram.c index 508233cf316..13041949d17 100644 --- a/src/mesa/tnl/t_vb_arbprogram.c +++ b/src/mesa/tnl/t_vb_arbprogram.c @@ -110,6 +110,10 @@ static GLfloat ApproxPower(GLfloat x, GLfloat y) return (GLfloat) _mesa_pow(x, y); } +static GLfloat rough_approx_log2_0_1(GLfloat x) +{ + return LOG2(x); +} @@ -273,13 +277,11 @@ static void do_EXP( struct arb_vp_machine *m, union instruction op ) const GLfloat *arg0 = m->File[op.alu.file0][op.alu.idx0]; GLfloat tmp = arg0[0]; GLfloat flr_tmp = FLOORF(tmp); + GLfloat frac_tmp = tmp - flr_tmp; - /* KW: nvvertexec has an optimized version of this which is pretty - * hard to understand/validate, but avoids the RoughApproxExp2. - */ - result[0] = (GLfloat) (1 << (int)flr_tmp); - result[1] = tmp - flr_tmp; - result[2] = RoughApproxExp2(tmp); + result[0] = ldexpf(1.0, (int)flr_tmp); + result[1] = frac_tmp; + result[2] = ldexpf(rough_approx_log2_0_1(frac_tmp), (int)flr_tmp); result[3] = 1.0F; } @@ -322,18 +324,22 @@ static void do_LIT( struct arb_vp_machine *m, union instruction op ) { 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 */ 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)); + tmp[0] = 1.0; + tmp[1] = 0.0; + tmp[2] = 0.0; + tmp[3] = 1.0; - result[0] = 1.0; - result[1] = tmp[0]; - result[2] = (tmp[0] > 0.0) ? RoughApproxPower(tmp[1], tmp[3]) : 0.0F; - result[3] = 1.0; + if (arg0[0] > 0.0) { + tmp[1] = arg0[0]; + + if (arg0[1] > 0.0) { + tmp[2] = RoughApproxPower(arg0[1], arg0[3]); + } + } + + COPY_4V(result, tmp); } @@ -353,7 +359,7 @@ static void do_LOG( struct arb_vp_machine *m, union instruction op ) result[0] = (GLfloat) (exponent - 1); result[1] = 2.0 * mantissa; /* map [.5, 1) -> [1, 2) */ - result[2] = result[0] + LOG2(result[1]); + result[2] = exponent + LOG2(mantissa); result[3] = 1.0; } @@ -1201,9 +1207,6 @@ run_arb_vertex_program(GLcontext *ctx, struct tnl_pipeline_stage *stage) p = (struct tnl_compiled_program *)program->TnlData; assert(p); - /* Initialize regs where necessary: - */ - ASSIGN_4V(m->File[0][REG_ID], 0, 0, 0, 1); m->nr_inputs = m->nr_outputs = 0; @@ -1386,6 +1389,15 @@ static GLboolean init_vertex_program( GLcontext *ctx, m->File[0] = ALIGN_MALLOC(REG_MAX * sizeof(GLfloat) * 4, 16); + /* Initialize regs where necessary: + */ + ASSIGN_4V(m->File[0][REG_ID], 0, 0, 0, 1); + ASSIGN_4V(m->File[0][REG_ONES], 1, 1, 1, 1); + ASSIGN_4V(m->File[0][REG_SWZ], -1, 1, 0, 0); + ASSIGN_4V(m->File[0][REG_NEG], -1, -1, -1, -1); + ASSIGN_4V(m->File[0][REG_LIT], 1, 0, 0, 1); + ASSIGN_4V(m->File[0][REG_LIT2], 1, .5, .2, 1); /* debug value */ + if (_mesa_getenv("MESA_EXPERIMENTAL")) m->try_codegen = 1; @@ -1402,6 +1414,9 @@ static GLboolean init_vertex_program( GLcontext *ctx, if (ctx->_MaintainTnlProgram) _mesa_allow_light_in_model( ctx, GL_FALSE ); + m->fpucntl_rnd_neg = RND_NEG_FPU; /* const value */ + m->fpucntl_restore = RESTORE_FPU; /* const value */ + return GL_TRUE; } diff --git a/src/mesa/tnl/t_vb_arbprogram.h b/src/mesa/tnl/t_vb_arbprogram.h index 6279f098f6f..004269d60e9 100644 --- a/src/mesa/tnl/t_vb_arbprogram.h +++ b/src/mesa/tnl/t_vb_arbprogram.h @@ -60,6 +60,9 @@ #define REG_ONES 65 /* 1,1,1,1 */ #define REG_SWZ 66 /* -1,1,0,0 */ #define REG_NEG 67 /* -1,-1,-1,-1 */ +#define REG_LIT 68 /* 1,0,0,1 */ +#define REG_LIT2 69 /* 1,0,0,1 */ +#define REG_SCRATCH 70 /* internal temporary */ #define REG_UNDEF 127 /* special case - never used */ #define REG_MAX 128 #define REG_INVALID ~0 @@ -125,6 +128,13 @@ struct output { /*--------------------------------------------------------------------------- */ +#ifdef NO_FAST_MATH +#define RESTORE_FPU (DEFAULT_X86_FPU) +#define RND_NEG_FPU (DEFAULT_X86_FPU | 0x400) +#else +#define RESTORE_FPU (FAST_X86_FPU) +#define RND_NEG_FPU (FAST_X86_FPU | 0x400) +#endif /*! * Private storage for the vertex program pipeline stage. @@ -148,6 +158,9 @@ struct arb_vp_machine { struct vertex_buffer *VB; GLcontext *ctx; + GLshort fpucntl_rnd_neg; /* constant value */ + GLshort fpucntl_restore; /* constant value */ + GLboolean try_codegen; }; diff --git a/src/mesa/tnl/t_vb_arbprogram_sse.c b/src/mesa/tnl/t_vb_arbprogram_sse.c index bfe45912a77..330d30efb92 100644 --- a/src/mesa/tnl/t_vb_arbprogram_sse.c +++ b/src/mesa/tnl/t_vb_arbprogram_sse.c @@ -25,8 +25,13 @@ /** * \file t_vb_arb_program_sse.c * - * Translate simplified vertex_program representation to x86/SSE/SSE2 - * machine code using mesa's rtasm runtime assembler. + * Translate simplified vertex_program representation to + * x86/x87/SSE/SSE2 machine code using mesa's rtasm runtime assembler. + * + * This is very much a first attempt - build something that works. + * There are probably better approaches for applying SSE to vertex + * programs, and the whole thing is crying out for static analysis of + * the programs to avoid redundant operations. * * \author Keith Whitwell */ @@ -48,7 +53,6 @@ #include "x86/rtasm/x86sse.h" #include "x86/common_x86_asm.h" - #define X 0 #define Y 1 #define Z 2 @@ -56,11 +60,10 @@ /* Reg usage: * - * EAX - point to 'm->File[0]' + * EAX - temp + * EBX - point to 'm->File[0]' * ECX - point to 'm->File[3]' - * EDX, - * EBX, - * ESP, + * EDX - holds 'm' * EBP, * ESI, * EDI @@ -91,6 +94,7 @@ struct compilation { } file[4]; GLboolean have_sse2; + GLshort fpucntl; }; static INLINE GLboolean eq( struct x86_reg a, @@ -102,6 +106,10 @@ static INLINE GLboolean eq( struct x86_reg a, a.disp == b.disp); } +static GLint get_offset( const void *a, const void *b ) +{ + return (const char *)b - (const char *)a; +} static struct x86_reg get_reg_ptr(GLuint file, @@ -111,7 +119,7 @@ static struct x86_reg get_reg_ptr(GLuint file, switch (file) { case FILE_REG: - reg = x86_make_reg(file_REG32, reg_AX); + reg = x86_make_reg(file_REG32, reg_BX); assert(idx != REG_UNDEF); break; case FILE_STATE_PARAM: @@ -157,17 +165,12 @@ static struct x86_reg get_xmm_reg( struct compilation *cp ) return x86_make_reg(file_XMM, oldest); } - - - -static struct x86_reg get_dst_reg( struct compilation *cp, - GLuint file, GLuint idx ) +static void invalidate_xmm( 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. + /* Invalidate any old copy of this register in XMM0-7. */ for (i = 0; i < 8; i++) { if (cp->xmm[i].file == file && cp->xmm[i].idx == idx) { @@ -177,6 +180,20 @@ static struct x86_reg get_dst_reg( struct compilation *cp, break; } } +} + + +/* Return an XMM reg to receive the results of an operation. + */ +static struct x86_reg get_dst_xmm_reg( struct compilation *cp, + GLuint file, GLuint idx ) +{ + struct x86_reg reg; + + /* Invalidate any old copy of this register in XMM0-7. Don't reuse + * as this may be one of the arguments. + */ + invalidate_xmm( cp, file, idx ); reg = get_xmm_reg( cp ); cp->xmm[reg.idx].file = file; @@ -185,6 +202,21 @@ static struct x86_reg get_dst_reg( struct compilation *cp, return reg; } +/* As above, but return a pointer. Note - this pointer may alias + * those returned by get_arg_ptr(). + */ +static struct x86_reg get_dst_ptr( struct compilation *cp, + GLuint file, GLuint idx ) +{ + /* Invalidate any old copy of this register in XMM0-7. Don't reuse + * as this may be one of the arguments. + */ + invalidate_xmm( cp, file, idx ); + + return get_reg_ptr(file, idx); +} + + /* Return an XMM reg if the argument is resident, otherwise return a * base+offset pointer to the saved value. @@ -204,6 +236,27 @@ static struct x86_reg get_arg( struct compilation *cp, GLuint file, GLuint idx ) return get_reg_ptr(file, idx); } +/* As above, but always return a pointer: + */ +static struct x86_reg get_arg_ptr( struct compilation *cp, GLuint file, GLuint idx ) +{ + GLuint i; + + /* If there is a modified version of this register in one of the + * XMM regs, write it out to memory. + */ + for (i = 0; i < 8; i++) { + if (cp->xmm[i].file == file && + cp->xmm[i].idx == idx && + cp->xmm[i].dirty) + spill(cp, i); + } + + return get_reg_ptr(file, idx); +} + +/* Emulate pshufd insn in regular SSE, if necessary: + */ static void emit_pshufd( struct compilation *cp, struct x86_reg dst, struct x86_reg arg0, @@ -220,7 +273,18 @@ static void emit_pshufd( struct compilation *cp, sse_shufps(&cp->func, dst, dst, shuf); } } - + +static void set_fpu_round_neg_inf( struct compilation *cp ) +{ + if (cp->fpucntl != RND_NEG_FPU) { + struct x86_reg regEDX = x86_make_reg(file_REG32, reg_DX); + struct arb_vp_machine *m = NULL; + + cp->fpucntl = RND_NEG_FPU; + x87_fnclex(&cp->func); + x87_fldcw(&cp->func, x86_make_disp(regEDX, get_offset(m, &m->fpucntl_rnd_neg))); + } +} /* Perform a reduced swizzle. @@ -228,7 +292,7 @@ static void emit_pshufd( struct compilation *cp, 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); + struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.rsw.dst); GLuint swz = op.rsw.swz; GLuint neg = op.rsw.neg; @@ -252,114 +316,196 @@ static GLboolean emit_RSW( struct compilation *cp, union instruction op ) return GL_TRUE; } +/* Helper for writemask: + */ +static GLboolean emit_shuf_copy1( struct compilation *cp, + struct x86_reg dst, + struct x86_reg arg0, + struct x86_reg arg1, + GLubyte shuf ) +{ + struct x86_reg tmp = get_xmm_reg(cp); + sse_movups(&cp->func, dst, arg1); + emit_pshufd(cp, dst, dst, shuf); + emit_pshufd(cp, tmp, arg0, shuf); + + sse_movss(&cp->func, dst, tmp); + + emit_pshufd(cp, dst, dst, shuf); + return GL_TRUE; +} + + +/* Helper for writemask: + */ +static GLboolean emit_shuf_copy2( struct compilation *cp, + struct x86_reg dst, + struct x86_reg arg0, + struct x86_reg arg1, + GLubyte shuf ) +{ + struct x86_reg tmp = get_xmm_reg(cp); + emit_pshufd(cp, dst, arg1, shuf); + emit_pshufd(cp, tmp, arg0, shuf); + + sse_shufps(&cp->func, dst, tmp, SHUF(X, Y, Z, W)); + + emit_pshufd(cp, dst, dst, shuf); + return GL_TRUE; +} + + +static void emit_x87_ex2( struct compilation *cp ) +{ + struct x86_reg st0 = x86_make_reg(file_x87, 0); + struct x86_reg st1 = x86_make_reg(file_x87, 1); + struct x86_reg st3 = x86_make_reg(file_x87, 3); + + set_fpu_round_neg_inf( cp ); + + x87_fld(&cp->func, st0); /* a a */ + x87_fprndint( &cp->func ); /* int(a) a */ + x87_fld(&cp->func, st0); /* int(a) int(a) a */ + x87_fstp(&cp->func, st3); /* int(a) a int(a)*/ + x87_fsubp(&cp->func, st1); /* frac(a) int(a) */ + x87_f2xm1(&cp->func); /* (2^frac(a))-1 int(a)*/ + x87_fld1(&cp->func); /* 1 (2^frac(a))-1 int(a)*/ + x87_faddp(&cp->func, st1); /* 2^frac(a) int(a) */ + x87_fscale(&cp->func); /* 2^a */ +} + +#if 0 +static GLboolean emit_MSK2( struct compilation *cp, union instruction op ) +{ + struct x86_reg arg0 = get_arg(cp, op.msk.file, op.msk.arg); + struct x86_reg arg1 = get_arg(cp, FILE_REG, op.msk.dst); /* NOTE! */ + struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.msk.dst); + + /* make full width bitmask in tmp + * dst = ~tmp + * tmp &= arg0 + * dst &= arg1 + * dst |= tmp + */ + emit_pshufd(cp, tmp, get_arg(cp, FILE_REG, REG_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)); + sse2_pnot(&cp->func, dst, tmp); + sse2_pand(&cp->func, arg0, tmp); + sse2_pand(&cp->func, arg1, dst); + sse2_por(&cp->func, tmp, dst); + return GL_TRUE; +} +#endif + + /* 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); + struct x86_reg dst0 = get_arg(cp, FILE_REG, op.msk.dst); /* NOTE! */ + struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.msk.dst); - sse_movups(&cp->func, dst, dst0); + /* Note that dst and dst0 refer to the same program variable, but + * will definitely be different XMM registers. We're effectively + * treating this as a 2 argument SEL now, just one of which happens + * always to be the same register as the destination. + */ switch (op.msk.mask) { case 0: + sse_movups(&cp->func, dst, dst0); return GL_TRUE; case WRITEMASK_X: if (arg.file == file_XMM) { + sse_movups(&cp->func, dst, dst0); sse_movss(&cp->func, dst, arg); } else { struct x86_reg tmp = get_xmm_reg(cp); + sse_movups(&cp->func, dst, dst0); 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)); + case WRITEMASK_XY: + sse_movups(&cp->func, dst, dst0); + sse_shufps(&cp->func, dst, arg, SHUF(X, Y, 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)); + case WRITEMASK_ZW: + sse_movups(&cp->func, dst, arg); + sse_shufps(&cp->func, dst, dst0, SHUF(X, Y, Z, 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)); + case WRITEMASK_YZW: + if (dst0.file == file_XMM) { + sse_movups(&cp->func, dst, arg); + sse_movss(&cp->func, dst, dst0); + } + else { + struct x86_reg tmp = get_xmm_reg(cp); + sse_movups(&cp->func, dst, arg); + sse_movss(&cp->func, tmp, dst0); + sse_movss(&cp->func, dst, tmp); + } return GL_TRUE; - } - case WRITEMASK_XY: - sse_shufps(&cp->func, dst, arg, SHUF(X, Y, Z, W)); + case WRITEMASK_Y: + emit_shuf_copy1(cp, dst, arg, dst0, SHUF(Y,X,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)); + case WRITEMASK_Z: + emit_shuf_copy1(cp, dst, arg, dst0, SHUF(Z,Y,X,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); + case WRITEMASK_W: + emit_shuf_copy1(cp, dst, arg, dst0, SHUF(W,Y,Z,X)); + return GL_TRUE; + + case WRITEMASK_XZ: + emit_shuf_copy2(cp, dst, arg, dst0, SHUF(X,Z,Y,W)); + return GL_TRUE; + + case WRITEMASK_XW: + emit_shuf_copy2(cp, dst, arg, dst0, SHUF(X,W,Z,Y)); + + case WRITEMASK_YZ: + emit_shuf_copy2(cp, dst, arg, dst0, SHUF(Z,Y,X,W)); + return GL_TRUE; + + case WRITEMASK_YW: + emit_shuf_copy2(cp, dst, arg, dst0, SHUF(W,Y,Z,X)); + return GL_TRUE; + + case WRITEMASK_XZW: + emit_shuf_copy1(cp, dst, dst0, arg, SHUF(Y,X,Z,W)); + return GL_TRUE; + + case WRITEMASK_XYW: + emit_shuf_copy1(cp, dst, dst0, arg, SHUF(Z,Y,X,W)); + return GL_TRUE; + + case WRITEMASK_XYZ: + emit_shuf_copy1(cp, dst, dst0, arg, SHUF(W,Y,Z,X)); 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); + assert(0); + break; } - - return GL_TRUE; -#endif - FAIL; } @@ -378,7 +524,7 @@ static GLboolean emit_PRT( struct compilation *cp, union instruction op ) 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 dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst); struct x86_reg neg = get_reg_ptr(FILE_REG, REG_NEG); sse_movups(&cp->func, dst, arg0); @@ -391,7 +537,7 @@ 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); + struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst); sse_movups(&cp->func, dst, arg0); sse_addps(&cp->func, dst, arg1); @@ -405,7 +551,7 @@ 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 dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst); struct x86_reg tmp = get_xmm_reg(cp); sse_movups(&cp->func, dst, arg0); @@ -427,7 +573,7 @@ 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 dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst); struct x86_reg tmp = get_xmm_reg(cp); sse_movups(&cp->func, dst, arg0); @@ -445,135 +591,303 @@ static GLboolean emit_DP4( struct compilation *cp, union instruction op ) 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); + 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_xmm_reg(cp, FILE_REG, op.alu.dst); + struct x86_reg ones = get_reg_ptr(FILE_REG, REG_ONES); + struct x86_reg tmp = get_xmm_reg(cp); -/* dst[0] = (arg0[0] * arg1[0] + */ -/* arg0[1] * arg1[1] + */ -/* arg0[2] * arg1[2] + */ -/* 1.0 * arg1[3]); */ + emit_pshufd(cp, dst, arg0, SHUF(W,X,Y,Z)); + sse_movss(&cp->func, dst, ones); + emit_pshufd(cp, dst, dst, SHUF(W,X,Y,Z)); + sse_mulps(&cp->func, dst, arg1); + /* Now the hard bit: sum the values (from DP4): + */ + 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)); - FAIL; + return GL_TRUE; } +#if 0 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); */ + struct x86_reg arg0 = get_arg_ptr(cp, op.alu.file0, op.alu.idx0); + struct x86_reg arg1 = get_arg_ptr(cp, op.alu.file1, op.alu.idx1); + struct x86_reg dst = get_dst_ptr(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; + /* Would rather do some of this with integer regs, but: + * 1) No proper support for immediate values yet + * 2) I'd need to push/pop somewhere to get a free reg. + */ + x87_fld1(&cp->func); + x87_fstp(&cp->func, dst); /* would rather do an immediate store... */ + x87_fld(&cp->func, x86_make_disp(arg0, 4)); + x87_fmul(&cp->func, x86_make_disp(arg1, 4)); + x87_fstp(&cp->func, x86_make_disp(dst, 4)); + + if (!eq(arg0, dst)) { + x86_fld(&cp->func, x86_make_disp(arg0, 8)); + x86_stp(&cp->func, x86_make_disp(dst, 8)); + } + + if (!eq(arg1, dst)) { + x86_fld(&cp->func, x86_make_disp(arg0, 12)); + x86_stp(&cp->func, x86_make_disp(dst, 12)); + } + + return GL_TRUE; +} +#else +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_xmm_reg(cp, FILE_REG, op.alu.dst); + struct x86_reg tmp = get_xmm_reg(cp); + struct x86_reg ones = get_reg_ptr(FILE_REG, REG_ONES); + + emit_shuf_copy2(cp, dst, arg0, ones, SHUF(X,W,Z,Y)); + emit_shuf_copy2(cp, tmp, arg1, ones, SHUF(X,Z,Y,W)); + sse_mulps(&cp->func, dst, tmp); + +/* dst[0] = 1.0 * 1.0F; */ +/* dst[1] = arg0[1] * arg1[1]; */ +/* dst[2] = arg0[2] * 1.0; */ +/* dst[3] = 1.0 * arg1[3]; */ + + return GL_TRUE; +} +#endif + +static GLboolean emit_LG2( struct compilation *cp, union instruction op ) +{ + struct x86_reg arg0 = get_arg_ptr(cp, op.alu.file0, op.alu.idx0); + struct x86_reg dst = get_dst_ptr(cp, FILE_REG, op.alu.dst); + + x87_fld1(&cp->func); /* 1 */ + x87_fld(&cp->func, arg0); /* a0 1 */ + x87_fyl2x(&cp->func); /* log2(a0) */ + x87_fst(&cp->func, x86_make_disp(dst, 0)); + x87_fst(&cp->func, x86_make_disp(dst, 4)); + x87_fst(&cp->func, x86_make_disp(dst, 8)); + x87_fstp(&cp->func, x86_make_disp(dst, 12)); + + return GL_TRUE; } 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); + struct x86_reg arg0 = get_arg_ptr(cp, op.alu.file0, op.alu.idx0); + struct x86_reg dst = get_dst_ptr(cp, FILE_REG, op.alu.dst); -/* dst[0] = (GLfloat)RoughApproxPow2(arg0[0]); */ - sse_shufps(&cp->func, dst, dst, SHUF(X, X, X, X)); - FAIL; + /* CAUTION: dst may alias arg0! + */ + x87_fld(&cp->func, arg0); + + emit_x87_ex2(cp); + + x87_fst(&cp->func, x86_make_disp(dst, 0)); + x87_fst(&cp->func, x86_make_disp(dst, 4)); + x87_fst(&cp->func, x86_make_disp(dst, 8)); + x87_fst(&cp->func, x86_make_disp(dst, 12)); + return GL_TRUE; } 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); */ + struct x86_reg arg0 = get_arg_ptr(cp, op.alu.file0, op.alu.idx0); + struct x86_reg dst = get_dst_ptr(cp, FILE_REG, op.alu.dst); + struct x86_reg st0 = x86_make_reg(file_x87, 0); + struct x86_reg st1 = x86_make_reg(file_x87, 1); + struct x86_reg st3 = x86_make_reg(file_x87, 3); + + /* CAUTION: dst may alias arg0! + */ + x87_fld(&cp->func, arg0); /* arg0.x */ + x87_fld(&cp->func, st0); /* arg arg */ + + /* by default, fpu is setup to round-to-nearest. We want to + * change this now, and track the state through to the end of the + * generated function so that it isn't repeated unnecessarily. + * Alternately, could subtract .5 to get round to -inf behaviour. + */ + set_fpu_round_neg_inf( cp ); + x87_fprndint( &cp->func ); /* flr(a) a */ + x87_fld(&cp->func, st0); /* flr(a) flr(a) a */ + x87_fld1(&cp->func); /* 1 floor(a) floor(a) a */ + x87_fst(&cp->func, x86_make_disp(dst, 12)); /* stack unchanged */ + x87_fscale(&cp->func); /* 2^floor(a) floor(a) a */ + x87_fst(&cp->func, st3); /* 2^floor(a) floor(a) a 2^floor(a)*/ + x87_fstp(&cp->func, x86_make_disp(dst, 0)); /* flr(a) a 2^flr(a) */ + x87_fsubrp(&cp->func, st1); /* frac(a) 2^flr(a) */ + x87_fst(&cp->func, x86_make_disp(dst, 4)); /* frac(a) 2^flr(a) */ + x87_f2xm1(&cp->func); /* (2^frac(a))-1 2^flr(a)*/ + x87_fld1(&cp->func); /* 1 (2^frac(a))-1 2^flr(a)*/ + x87_faddp(&cp->func, st1); /* 2^frac(a) 2^flr(a) */ + x87_fmulp(&cp->func, st1); /* 2^a */ + x87_fst(&cp->func, x86_make_disp(dst, 8)); + + + +/* dst[0] = 2^floor(tmp); */ +/* dst[1] = frac(tmp); */ +/* dst[2] = 2^floor(tmp) * 2^frac(tmp); */ /* dst[3] = 1.0F; */ - FAIL; + return GL_TRUE; } -static GLboolean emit_FLR( struct compilation *cp, union instruction op ) +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); */ - -/* dst[0] = FLOORF(arg0[0]); */ -/* dst[1] = FLOORF(arg0[1]); */ -/* dst[2] = FLOORF(arg0[2]); */ -/* dst[3] = FLOORF(arg0[3]); */ - FAIL; + struct x86_reg arg0 = get_arg_ptr(cp, op.alu.file0, op.alu.idx0); + struct x86_reg dst = get_dst_ptr(cp, FILE_REG, op.alu.dst); + struct x86_reg st0 = x86_make_reg(file_x87, 0); + struct x86_reg st1 = x86_make_reg(file_x87, 1); + struct x86_reg st2 = x86_make_reg(file_x87, 2); + + /* CAUTION: dst may alias arg0! + */ + x87_fld(&cp->func, arg0); /* arg0.x */ + x87_fabs(&cp->func); /* |arg0.x| */ + x87_fxtract(&cp->func); /* mantissa(arg0.x), exponent(arg0.x) */ + x87_fst(&cp->func, st2); /* mantissa, exponent, mantissa */ + x87_fld1(&cp->func); /* 1, mantissa, exponent, mantissa */ + x87_fyl2x(&cp->func); /* log2(mantissa), exponent, mantissa */ + x87_fadd(&cp->func, st0, st1); /* e+l2(m), e, m */ + x87_fstp(&cp->func, x86_make_disp(dst, 8)); /* e, m */ + + x87_fld1(&cp->func); /* 1, e, m */ + x87_fsub(&cp->func, st1, st0); /* 1, e-1, m */ + x87_fstp(&cp->func, x86_make_disp(dst, 12)); /* e-1,m */ + x87_fstp(&cp->func, dst); /* m */ + + x87_fadd(&cp->func, st0, st0); /* 2m */ + x87_fstp(&cp->func, x86_make_disp(dst, 4)); + + return GL_TRUE; } -static GLboolean emit_FRC( struct compilation *cp, union instruction op ) +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); */ + struct x86_reg arg0 = get_arg_ptr(cp, op.alu.file0, op.alu.idx0); + struct x86_reg dst = get_dst_ptr(cp, FILE_REG, op.alu.dst); + int i; -/* 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; + set_fpu_round_neg_inf( cp ); + + for (i = 0; i < 4; i++) { + x87_fld(&cp->func, x86_make_disp(arg0, i*4)); + x87_fprndint( &cp->func ); + x87_fstp(&cp->func, x86_make_disp(dst, i*4)); + } + + + return GL_TRUE; } -static GLboolean emit_LG2( struct compilation *cp, union instruction op ) +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); */ + struct x86_reg arg0 = get_arg_ptr(cp, op.alu.file0, op.alu.idx0); + struct x86_reg dst = get_dst_ptr(cp, FILE_REG, op.alu.dst); + struct x86_reg st0 = x86_make_reg(file_x87, 0); + struct x86_reg st1 = x86_make_reg(file_x87, 1); + int i; -/* dst[0] = RoughApproxLog2(arg0[0]); */ + set_fpu_round_neg_inf( cp ); -/* sse_shufps(&cp->func, dst, dst, SHUF(X, X, X, X)); */ - FAIL; + /* Knowing liveness info or even just writemask would be useful + * here: + */ + for (i = 0; i < 4; i++) { + x87_fld(&cp->func, x86_make_disp(arg0, i*4)); + x87_fld(&cp->func, st0); /* a a */ + x87_fprndint( &cp->func ); /* flr(a) a */ + x87_fsubrp(&cp->func, st1); /* frc(a) */ + x87_fstp(&cp->func, x86_make_disp(dst, i*4)); + } + + return GL_TRUE; } 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); */ +#if 1 + struct x86_reg arg0 = get_arg_ptr(cp, op.alu.file0, op.alu.idx0); + struct x86_reg dst = get_dst_ptr(cp, FILE_REG, op.alu.dst); + struct x86_reg lit = get_arg(cp, FILE_REG, REG_LIT); + struct x86_reg tmp = get_xmm_reg(cp); + struct x86_reg st1 = x86_make_reg(file_x87, 1); + struct x86_reg regEAX = x86_make_reg(file_REG32, reg_AX); + GLubyte *fixup1, *fixup2; -/* 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)); */ + /* Load the interesting parts of arg0: + */ + x87_fld(&cp->func, x86_make_disp(arg0, 12)); /* a3 */ + x87_fld(&cp->func, x86_make_disp(arg0, 4)); /* a1 a3 */ + x87_fld(&cp->func, x86_make_disp(arg0, 0)); /* a0 a1 a3 */ + + /* Intialize dst: + */ + sse_movaps(&cp->func, tmp, lit); + sse_movaps(&cp->func, dst, tmp); + + /* Check arg0[0]: + */ + x87_fldz(&cp->func); /* 0 a0 a1 a3 */ + x87_fucomp(&cp->func, st1); /* a0 a1 a3 */ + x87_fnstsw(&cp->func, regEAX); + x86_sahf(&cp->func); + fixup1 = x86_jcc_forward(&cp->func, cc_AE); + + x87_fstp(&cp->func, x86_make_disp(dst, 4)); /* a1 a3 */ -/* 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; -} + /* Check arg0[1]: + */ + x87_fldz(&cp->func); /* 0 a1 a3 */ + x87_fucomp(&cp->func, st1); /* a1 a3 */ + x87_fnstsw(&cp->func, regEAX); + x86_sahf(&cp->func); + fixup2 = x86_jcc_forward(&cp->func, cc_AE); + /* Compute pow(a1, a3) + */ + x87_fyl2x(&cp->func); /* a3*log2(a1) */ -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); */ + emit_x87_ex2( cp ); /* 2^(a3*log2(a1)) */ -/* 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; + x87_fstp(&cp->func, x86_make_disp(dst, 8)); + + /* Land jumps: + */ + x86_fixup_fwd_jump(&cp->func, fixup1); + x86_fixup_fwd_jump(&cp->func, fixup2); +#else + struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst); + struct x86_reg ones = get_reg_ptr(FILE_REG, REG_LIT); + sse_movups(&cp->func, dst, ones); +#endif + return GL_TRUE; } + + 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); + struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst); sse_movups(&cp->func, dst, arg0); sse_maxps(&cp->func, dst, arg1); @@ -585,7 +899,7 @@ 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); + struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst); sse_movups(&cp->func, dst, arg0); sse_minps(&cp->func, dst, arg1); @@ -595,7 +909,7 @@ static GLboolean emit_MIN( struct compilation *cp, union instruction op ) 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); + struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst); sse_movups(&cp->func, dst, arg0); return GL_TRUE; @@ -605,7 +919,7 @@ 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); + struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst); sse_movups(&cp->func, dst, arg0); sse_mulps(&cp->func, dst, arg1); @@ -615,14 +929,22 @@ static GLboolean emit_MUL( struct compilation *cp, union instruction op ) 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); + struct x86_reg arg0 = get_arg_ptr(cp, op.alu.file0, op.alu.idx0); + struct x86_reg arg1 = get_arg_ptr(cp, op.alu.file1, op.alu.idx1); + struct x86_reg dst = get_dst_ptr(cp, FILE_REG, op.alu.dst); -/* dst[0] = (GLfloat)RoughApproxPower(arg0[0], arg1[0]); */ + x87_fld(&cp->func, arg1); /* a1 */ + x87_fld(&cp->func, arg0); /* a0 a1 */ + x87_fyl2x(&cp->func); /* a1*log2(a0) */ - sse_shufps(&cp->func, dst, dst, SHUF(X, X, X, X)); - FAIL; + emit_x87_ex2( cp ); /* 2^(a1*log2(a0)) */ + + x87_fst(&cp->func, x86_make_disp(dst, 0)); + x87_fst(&cp->func, x86_make_disp(dst, 4)); + x87_fst(&cp->func, x86_make_disp(dst, 8)); + x87_fstp(&cp->func, x86_make_disp(dst, 12)); + + return GL_TRUE; } static GLboolean emit_REL( struct compilation *cp, union instruction op ) @@ -630,7 +952,7 @@ 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); */ +/* struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst); */ /* dst[0] = arg0[0]; */ /* dst[1] = arg0[1]; */ @@ -643,7 +965,7 @@ static GLboolean emit_REL( struct compilation *cp, union instruction op ) 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); + struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst); if (cp->have_sse2) { sse2_rcpss(&cp->func, dst, arg0); @@ -661,7 +983,15 @@ static GLboolean emit_RCP( struct compilation *cp, union instruction op ) 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); + struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst); + + /* TODO: Calculate absolute value + */ +#if 0 + sse_movss(&cp->func, dst, arg0); + sse_mulss(&cp->func, dst, neg); + sse_maxss(&cp->func, dst, arg0); +#endif sse_rsqrtss(&cp->func, dst, arg0); sse_shufps(&cp->func, dst, dst, SHUF(X, X, X, X)); @@ -673,7 +1003,7 @@ 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 dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst); struct x86_reg ones = get_reg_ptr(FILE_REG, REG_ONES); sse_movups(&cp->func, dst, arg0); @@ -687,7 +1017,7 @@ 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 dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst); struct x86_reg ones = get_reg_ptr(FILE_REG, REG_ONES); sse_movups(&cp->func, dst, arg0); @@ -700,7 +1030,7 @@ 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); + struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst); sse_movups(&cp->func, dst, arg0); sse_subps(&cp->func, dst, arg1); @@ -712,7 +1042,7 @@ 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 dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst); struct x86_reg tmp0 = get_xmm_reg(cp); struct x86_reg tmp1 = get_xmm_reg(cp); @@ -779,10 +1109,6 @@ static GLboolean (* const emit_func[])(struct compilation *, union instruction) 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 ) @@ -790,14 +1116,15 @@ static GLboolean build_vertex_program( struct compilation *cp ) struct arb_vp_machine *m = NULL; GLuint j; - struct x86_reg regEAX = x86_make_reg(file_REG32, reg_AX); - struct x86_reg parmECX = x86_make_reg(file_REG32, reg_CX); + struct x86_reg regEBX = x86_make_reg(file_REG32, reg_BX); + struct x86_reg regECX = x86_make_reg(file_REG32, reg_CX); + struct x86_reg regEDX = x86_make_reg(file_REG32, reg_DX); - 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(m, m->File + FILE_REG))); - x86_mov(&cp->func, parmECX, x86_make_disp(parmECX, get_offset(m, m->File + FILE_STATE_PARAM))); + x86_push(&cp->func, regEBX); + + x86_mov(&cp->func, regEDX, x86_fn_arg(&cp->func, 1)); + x86_mov(&cp->func, regEBX, x86_make_disp(regEDX, get_offset(m, m->File + FILE_REG))); + x86_mov(&cp->func, regECX, x86_make_disp(regEDX, get_offset(m, m->File + FILE_STATE_PARAM))); for (j = 0; j < cp->p->nr_instructions; j++) { union instruction inst = cp->p->instructions[j]; @@ -827,6 +1154,14 @@ static GLboolean build_vertex_program( struct compilation *cp ) if (cp->func.need_emms) mmx_emms(&cp->func); + /* Restore FPU control word? + */ + if (cp->fpucntl != RESTORE_FPU) { + x87_fnclex(&cp->func); + x87_fldcw(&cp->func, x86_make_disp(regEDX, get_offset(m, &m->fpucntl_restore))); + } + + x86_pop(&cp->func, regEBX); x86_ret(&cp->func); return GL_TRUE; @@ -857,6 +1192,9 @@ _tnl_sse_codegen_vertex_program(struct tnl_compiled_program *p) x86_init_func(&cp.func); + cp.fpucntl = RESTORE_FPU; + + /* Note ctx state is not referenced in building the function, so it * depends only on the list of instructions: */ @@ -865,6 +1203,7 @@ _tnl_sse_codegen_vertex_program(struct tnl_compiled_program *p) return GL_FALSE; } + p->compiled_func = (void (*)(struct arb_vp_machine *))x86_get_func( &cp.func ); return GL_TRUE; }