#include "glheader.h"
#include "context.h"
#include "colormac.h"
#include "t_context.h"
#include "t_vertex.h"
#include "simple_list.h"
#include "enums.h"
#if defined(USE_SSE_ASM)
#include "x86/rtasm/x86sse.h"
#include "x86/common_x86_asm.h"
#define MAX_SSE_CODE_SIZE 1024
#define X 0
#define Y 1
#define Z 2
#define W 3
struct x86_program {
struct x86_function func;
GLcontext *ctx;
GLboolean inputs_safe;
GLboolean outputs_safe;
GLboolean have_sse2;
struct x86_reg identity;
struct x86_reg chan0;
};
static struct x86_reg get_identity( struct x86_program *p )
{
return p->identity;
}
static void emit_load4f_4( struct x86_program *p,
struct x86_reg dest,
struct x86_reg arg0 )
{
sse_movups(&p->func, dest, arg0);
}
static void emit_load4f_3( struct x86_program *p,
struct x86_reg dest,
struct x86_reg arg0 )
{
sse_movss(&p->func, dest, x86_make_disp(arg0, 8));
sse_shufps(&p->func, dest, get_identity(p), SHUF(X,Y,Z,W) );
sse_shufps(&p->func, dest, dest, SHUF(Y,Z,X,W) );
sse_movlps(&p->func, dest, arg0);
}
static void emit_load4f_2( struct x86_program *p,
struct x86_reg dest,
struct x86_reg arg0 )
{
sse_movups(&p->func, dest, get_identity(p));
sse_movlps(&p->func, dest, arg0);
}
static void emit_load4f_1( struct x86_program *p,
struct x86_reg dest,
struct x86_reg arg0 )
{
sse_movss(&p->func, dest, arg0);
sse_shufps(&p->func, dest, get_identity(p), SHUF(X,Y,Z,W) );
}
static void emit_load3f_3( struct x86_program *p,
struct x86_reg dest,
struct x86_reg arg0 )
{
if (p->inputs_safe) {
sse_movups(&p->func, dest, arg0);
}
else {
sse_movss(&p->func, dest, x86_make_disp(arg0, 8));
sse_shufps(&p->func, dest, dest, SHUF(X,X,X,X));
sse_movlps(&p->func, dest, arg0);
}
}
static void emit_load3f_2( struct x86_program *p,
struct x86_reg dest,
struct x86_reg arg0 )
{
emit_load4f_2(p, dest, arg0);
}
static void emit_load3f_1( struct x86_program *p,
struct x86_reg dest,
struct x86_reg arg0 )
{
emit_load4f_1(p, dest, arg0);
}
static void emit_load2f_2( struct x86_program *p,
struct x86_reg dest,
struct x86_reg arg0 )
{
sse_movlps(&p->func, dest, arg0);
}
static void emit_load2f_1( struct x86_program *p,
struct x86_reg dest,
struct x86_reg arg0 )
{
emit_load4f_1(p, dest, arg0);
}
static void emit_load1f_1( struct x86_program *p,
struct x86_reg dest,
struct x86_reg arg0 )
{
sse_movss(&p->func, dest, arg0);
}
static void (*load[4][4])( struct x86_program *p,
struct x86_reg dest,
struct x86_reg arg0 ) = {
{ emit_load1f_1,
emit_load1f_1,
emit_load1f_1,
emit_load1f_1 },
{ emit_load2f_1,
emit_load2f_2,
emit_load2f_2,
emit_load2f_2 },
{ emit_load3f_1,
emit_load3f_2,
emit_load3f_3,
emit_load3f_3 },
{ emit_load4f_1,
emit_load4f_2,
emit_load4f_3,
emit_load4f_4 }
};
static void emit_load( struct x86_program *p,
struct x86_reg dest,
GLuint sz,
struct x86_reg src,
GLuint src_sz)
{
load[sz-1][src_sz-1](p, dest, src);
}
static void emit_store4f( struct x86_program *p,
struct x86_reg dest,
struct x86_reg arg0 )
{
sse_movups(&p->func, dest, arg0);
}
static void emit_store3f( struct x86_program *p,
struct x86_reg dest,
struct x86_reg arg0 )
{
if (p->outputs_safe) {
sse_movups(&p->func, dest, arg0);
}
else {
sse_movlps(&p->func, dest, arg0);
sse_shufps(&p->func, arg0, arg0, SHUF(Z,Z,Z,Z) );
sse_movss(&p->func, x86_make_disp(dest,8), arg0);
}
}
static void emit_store2f( struct x86_program *p,
struct x86_reg dest,
struct x86_reg arg0 )
{
sse_movlps(&p->func, dest, arg0);
}
static void emit_store1f( struct x86_program *p,
struct x86_reg dest,
struct x86_reg arg0 )
{
sse_movss(&p->func, dest, arg0);
}
static void (*store[4])( struct x86_program *p,
struct x86_reg dest,
struct x86_reg arg0 ) =
{
emit_store1f,
emit_store2f,
emit_store3f,
emit_store4f
};
static void emit_store( struct x86_program *p,
struct x86_reg dest,
GLuint sz,
struct x86_reg temp )
{
store[sz-1](p, dest, temp);
}
static void emit_pack_store_4ub( struct x86_program *p,
struct x86_reg dest,
struct x86_reg temp )
{
sse_mulps(&p->func, temp, p->chan0);
if (p->have_sse2) {
sse2_cvtps2dq(&p->func, temp, temp);
sse2_packssdw(&p->func, temp, temp);
sse2_packuswb(&p->func, temp, temp);
sse_movss(&p->func, dest, temp);
}
else {
struct x86_reg mmx0 = x86_make_reg(file_MMX, 0);
struct x86_reg mmx1 = x86_make_reg(file_MMX, 1);
sse_cvtps2pi(&p->func, mmx0, temp);
sse_movhlps(&p->func, temp, temp);
sse_cvtps2pi(&p->func, mmx1, temp);
mmx_packssdw(&p->func, mmx0, mmx1);
mmx_packuswb(&p->func, mmx0, mmx0);
mmx_movd(&p->func, dest, mmx0);
}
}
static GLint get_offset( const void *a, const void *b )
{
return (const char *)b - (const char *)a;
}
static void get_src_ptr( struct x86_program *p,
struct x86_reg srcREG,
struct x86_reg vtxREG,
struct tnl_clipspace_attr *a )
{
struct tnl_clipspace *vtx = GET_VERTEX_STATE(p->ctx);
struct x86_reg ptr_to_src = x86_make_disp(vtxREG, get_offset(vtx, &a->inputptr));
x86_mov(&p->func, srcREG, ptr_to_src);
}
static void update_src_ptr( struct x86_program *p,
struct x86_reg srcREG,
struct x86_reg vtxREG,
struct tnl_clipspace_attr *a )
{
if (a->inputstride) {
struct tnl_clipspace *vtx = GET_VERTEX_STATE(p->ctx);
struct x86_reg ptr_to_src = x86_make_disp(vtxREG, get_offset(vtx, &a->inputptr));
x86_lea(&p->func, srcREG, x86_make_disp(srcREG, a->inputstride));
x86_mov(&p->func, ptr_to_src, srcREG);
}
}
static GLboolean build_vertex_emit( struct x86_program *p )
{
GLcontext *ctx = p->ctx;
TNLcontext *tnl = TNL_CONTEXT(ctx);
struct tnl_clipspace *vtx = GET_VERTEX_STATE(ctx);
GLuint j = 0;
struct x86_reg vertexEAX = x86_make_reg(file_REG32, reg_AX);
struct x86_reg srcECX = x86_make_reg(file_REG32, reg_CX);
struct x86_reg countEBP = x86_make_reg(file_REG32, reg_BP);
struct x86_reg vtxESI = x86_make_reg(file_REG32, reg_SI);
struct x86_reg temp = x86_make_reg(file_XMM, 0);
struct x86_reg vp0 = x86_make_reg(file_XMM, 1);
struct x86_reg vp1 = x86_make_reg(file_XMM, 2);
GLubyte *fixup, *label;
x86_push(&p->func, countEBP);
x86_push(&p->func, vtxESI);
x86_xor(&p->func, srcECX, srcECX);
x86_mov(&p->func, countEBP, x86_fn_arg(&p->func, 2));
x86_cmp(&p->func, countEBP, srcECX);
fixup = x86_jcc_forward(&p->func, cc_E);
x86_mov(&p->func, vertexEAX, x86_fn_arg(&p->func, 3));
x86_mov(&p->func, vtxESI, x86_fn_arg(&p->func, 1));
x86_mov(&p->func, vtxESI, x86_make_disp(vtxESI, get_offset(ctx, &ctx->swtnl_context)));
vtxESI = x86_make_disp(vtxESI, get_offset(tnl, &tnl->clipspace));
if (vtx->need_viewport) {
sse_movups(&p->func, vp0, x86_make_disp(vtxESI, get_offset(vtx, &vtx->vp_scale[0])));
sse_movups(&p->func, vp1, x86_make_disp(vtxESI, get_offset(vtx, &vtx->vp_xlate[0])));
}
sse_movups(&p->func, p->chan0, x86_make_disp(vtxESI, get_offset(vtx, &vtx->chan_scale[0])));
sse_movups(&p->func, p->identity, x86_make_disp(vtxESI, get_offset(vtx, &vtx->identity[0])));
label = x86_get_label(&p->func);
while (j < vtx->attr_count) {
struct tnl_clipspace_attr *a = &vtx->attr[j];
struct x86_reg dest = x86_make_disp(vertexEAX, a->vertoffset);
switch (a->format) {
case EMIT_1F:
get_src_ptr(p, srcECX, vtxESI, a);
emit_load(p, temp, 1, x86_deref(srcECX), a->inputsize);
emit_store(p, dest, 1, temp);
update_src_ptr(p, srcECX, vtxESI, a);
break;
case EMIT_2F:
get_src_ptr(p, srcECX, vtxESI, a);
emit_load(p, temp, 2, x86_deref(srcECX), a->inputsize);
emit_store(p, dest, 2, temp);
update_src_ptr(p, srcECX, vtxESI, a);
break;
case EMIT_3F:
if (0) {
get_src_ptr(p, srcECX, vtxESI, a);
emit_load(p, temp, 3, x86_deref(srcECX), a->inputsize);
emit_store(p, dest, 3, temp);
update_src_ptr(p, srcECX, vtxESI, a);
}
else {
get_src_ptr(p, srcECX, vtxESI, a);
emit_load(p, temp, 2, x86_deref(srcECX), a->inputsize);
emit_store(p, dest, 2, temp);
if (a->inputsize > 2) {
emit_load(p, temp, 1, x86_make_disp(srcECX, 8), 1);
emit_store(p, x86_make_disp(dest,8), 1, temp);
}
else {
sse_movss(&p->func, x86_make_disp(dest,8), get_identity(p));
}
update_src_ptr(p, srcECX, vtxESI, a);
}
break;
case EMIT_4F:
get_src_ptr(p, srcECX, vtxESI, a);
emit_load(p, temp, 4, x86_deref(srcECX), a->inputsize);
emit_store(p, dest, 4, temp);
update_src_ptr(p, srcECX, vtxESI, a);
break;
case EMIT_2F_VIEWPORT:
get_src_ptr(p, srcECX, vtxESI, a);
emit_load(p, temp, 2, x86_deref(srcECX), a->inputsize);
sse_mulps(&p->func, temp, vp0);
sse_addps(&p->func, temp, vp1);
emit_store(p, dest, 2, temp);
update_src_ptr(p, srcECX, vtxESI, a);
break;
case EMIT_3F_VIEWPORT:
get_src_ptr(p, srcECX, vtxESI, a);
emit_load(p, temp, 3, x86_deref(srcECX), a->inputsize);
sse_mulps(&p->func, temp, vp0);
sse_addps(&p->func, temp, vp1);
emit_store(p, dest, 3, temp);
update_src_ptr(p, srcECX, vtxESI, a);
break;
case EMIT_4F_VIEWPORT:
get_src_ptr(p, srcECX, vtxESI, a);
emit_load(p, temp, 4, x86_deref(srcECX), a->inputsize);
sse_mulps(&p->func, temp, vp0);
sse_addps(&p->func, temp, vp1);
emit_store(p, dest, 4, temp);
update_src_ptr(p, srcECX, vtxESI, a);
break;
case EMIT_3F_XYW:
get_src_ptr(p, srcECX, vtxESI, a);
emit_load(p, temp, 4, x86_deref(srcECX), a->inputsize);
sse_shufps(&p->func, temp, temp, SHUF(X,Y,W,Z));
emit_store(p, dest, 3, temp);
update_src_ptr(p, srcECX, vtxESI, a);
break;
case EMIT_1UB_1F:
if (j > 0 &&
a[-1].vertoffset + a[-1].vertattrsize <= a->vertoffset - 3)
{
get_src_ptr(p, srcECX, vtxESI, a);
emit_load(p, temp, 1, x86_deref(srcECX), a->inputsize);
sse_shufps(&p->func, temp, temp, SHUF(X,X,X,X));
emit_pack_store_4ub(p, x86_make_disp(dest, -3), temp);
update_src_ptr(p, srcECX, vtxESI, a);
}
else {
_mesa_printf("Can't emit 1ub %x %x %d\n", a->vertoffset, a[-1].vertoffset, a[-1].vertattrsize );
return GL_FALSE;
}
break;
case EMIT_3UB_3F_RGB:
case EMIT_3UB_3F_BGR:
if (j == vtx->attr_count - 1 ||
a[1].vertoffset >= a->vertoffset + 4) {
get_src_ptr(p, srcECX, vtxESI, a);
emit_load(p, temp, 3, x86_deref(srcECX), a->inputsize);
if (a->format == EMIT_3UB_3F_BGR)
sse_shufps(&p->func, temp, temp, SHUF(Z,Y,X,W));
emit_pack_store_4ub(p, dest, temp);
update_src_ptr(p, srcECX, vtxESI, a);
}
else if (j < vtx->attr_count - 1 &&
a[1].format == EMIT_1UB_1F &&
a[1].vertoffset == a->vertoffset + 3) {
get_src_ptr(p, srcECX, vtxESI, a);
emit_load(p, temp, 3, x86_deref(srcECX), a->inputsize);
update_src_ptr(p, srcECX, vtxESI, a);
sse_shufps(&p->func, temp, temp, SHUF(W,X,Y,Z));
get_src_ptr(p, srcECX, vtxESI, &a[1]);
emit_load(p, temp, 1, x86_deref(srcECX), a[1].inputsize);
update_src_ptr(p, srcECX, vtxESI, &a[1]);
if (a->format == EMIT_3UB_3F_BGR)
sse_shufps(&p->func, temp, temp, SHUF(W,Z,Y,X));
else
sse_shufps(&p->func, temp, temp, SHUF(Y,Z,W,X));
emit_pack_store_4ub(p, dest, temp);
j++;
}
else {
_mesa_printf("Can't emit 3ub\n");
}
return GL_FALSE;
break;
case EMIT_4UB_4F_RGBA:
get_src_ptr(p, srcECX, vtxESI, a);
emit_load(p, temp, 4, x86_deref(srcECX), a->inputsize);
emit_pack_store_4ub(p, dest, temp);
update_src_ptr(p, srcECX, vtxESI, a);
break;
case EMIT_4UB_4F_BGRA:
get_src_ptr(p, srcECX, vtxESI, a);
emit_load(p, temp, 4, x86_deref(srcECX), a->inputsize);
sse_shufps(&p->func, temp, temp, SHUF(Z,Y,X,W));
emit_pack_store_4ub(p, dest, temp);
update_src_ptr(p, srcECX, vtxESI, a);
break;
case EMIT_4UB_4F_ARGB:
get_src_ptr(p, srcECX, vtxESI, a);
emit_load(p, temp, 4, x86_deref(srcECX), a->inputsize);
sse_shufps(&p->func, temp, temp, SHUF(W,X,Y,Z));
emit_pack_store_4ub(p, dest, temp);
update_src_ptr(p, srcECX, vtxESI, a);
break;
case EMIT_4UB_4F_ABGR:
get_src_ptr(p, srcECX, vtxESI, a);
emit_load(p, temp, 4, x86_deref(srcECX), a->inputsize);
sse_shufps(&p->func, temp, temp, SHUF(W,Z,Y,X));
emit_pack_store_4ub(p, dest, temp);
update_src_ptr(p, srcECX, vtxESI, a);
break;
case EMIT_4CHAN_4F_RGBA:
switch (CHAN_TYPE) {
case GL_UNSIGNED_BYTE:
get_src_ptr(p, srcECX, vtxESI, a);
emit_load(p, temp, 4, x86_deref(srcECX), a->inputsize);
emit_pack_store_4ub(p, dest, temp);
update_src_ptr(p, srcECX, vtxESI, a);
break;
case GL_FLOAT:
get_src_ptr(p, srcECX, vtxESI, a);
emit_load(p, temp, 4, x86_deref(srcECX), a->inputsize);
emit_store(p, dest, 4, temp);
update_src_ptr(p, srcECX, vtxESI, a);
break;
case GL_UNSIGNED_SHORT:
default:
_mesa_printf("unknown CHAN_TYPE %s\n", _mesa_lookup_enum_by_nr(CHAN_TYPE));
return GL_FALSE;
}
break;
default:
_mesa_printf("unknown a[%d].format %d\n", j, a->format);
return GL_FALSE;
}
j++;
}
x86_lea(&p->func, vertexEAX, x86_make_disp(vertexEAX, vtx->vertex_size));
x86_dec(&p->func, countEBP);
x86_test(&p->func, countEBP, countEBP);
x86_jcc(&p->func, cc_NZ, label);
if (p->func.need_emms)
mmx_emms(&p->func);
x86_fixup_fwd_jump(&p->func, fixup);
x86_pop(&p->func, x86_get_base_reg(vtxESI));
x86_pop(&p->func, countEBP);
x86_ret(&p->func);
assert(!vtx->emit);
vtx->emit = (tnl_emit_func)x86_get_func(&p->func);
assert( (char *) p->func.csr - (char *) p->func.store <= MAX_SSE_CODE_SIZE );
return GL_TRUE;
}
void _tnl_generate_sse_emit( GLcontext *ctx )
{
struct tnl_clipspace *vtx = GET_VERTEX_STATE(ctx);
struct x86_program p;
if (!cpu_has_xmm) {
vtx->codegen_emit = NULL;
return;
}
_mesa_memset(&p, 0, sizeof(p));
p.ctx = ctx;
p.inputs_safe = 0;
p.outputs_safe = 0;
p.have_sse2 = cpu_has_xmm2;
p.identity = x86_make_reg(file_XMM, 6);
p.chan0 = x86_make_reg(file_XMM, 7);
if (!x86_init_func_size(&p.func, MAX_SSE_CODE_SIZE)) {
vtx->emit = NULL;
return;
}
if (build_vertex_emit(&p)) {
_tnl_register_fastpath( vtx, GL_TRUE );
}
else {
_tnl_register_fastpath( vtx, GL_FALSE );
x86_release_func(&p.func);
}
}
#else
void _tnl_generate_sse_emit( GLcontext *ctx )
{
}
#endif