2 * Mesa 3-D graphics library
5 * Copyright (C) 1999-2004 Brian Paul All Rights Reserved.
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 * \file t_vb_arb_program_sse.c
28 * Translate simplified vertex_program representation to
29 * x86/x87/SSE/SSE2 machine code using mesa's rtasm runtime assembler.
31 * This is very much a first attempt - build something that works.
32 * There are probably better approaches for applying SSE to vertex
33 * programs, and the whole thing is crying out for static analysis of
34 * the programs to avoid redundant operations.
36 * \author Keith Whitwell
44 #include "arbprogparse.h"
46 #include "program_instruction.h"
47 #include "math/m_matrix.h"
48 #include "math/m_translate.h"
49 #include "t_context.h"
50 #include "t_vb_arbprogram.h"
52 #if defined(USE_SSE_ASM)
54 #include "x86/rtasm/x86sse.h"
55 #include "x86/common_x86_asm.h"
65 * EBX - point to 'm->File[0]'
66 * ECX - point to 'm->File[3]'
77 _mesa_printf("x86 translation failed in %s\n", __FUNCTION__); \
82 struct x86_function func
;
83 struct tnl_compiled_program
*p
;
101 static INLINE GLboolean
eq( struct x86_reg a
,
104 return (a
.file
== b
.file
&&
110 static GLint
get_offset( const void *a
, const void *b
)
112 return (const char *)b
- (const char *)a
;
116 static struct x86_reg
get_reg_ptr(GLuint file
,
123 reg
= x86_make_reg(file_REG32
, reg_BX
);
124 assert(idx
!= REG_UNDEF
);
126 case FILE_STATE_PARAM
:
127 reg
= x86_make_reg(file_REG32
, reg_CX
);
133 return x86_make_disp(reg
, 16 * idx
);
137 static void spill( struct compilation
*cp
, GLuint idx
)
139 struct x86_reg oldval
= get_reg_ptr(cp
->xmm
[idx
].file
,
142 assert(cp
->xmm
[idx
].dirty
);
143 sse_movups(&cp
->func
, oldval
, x86_make_reg(file_XMM
, idx
));
144 cp
->xmm
[idx
].dirty
= 0;
147 static struct x86_reg
get_xmm_reg( struct compilation
*cp
)
152 for (i
= 0; i
< 8; i
++)
153 if (cp
->xmm
[i
].last_used
< cp
->xmm
[oldest
].last_used
)
156 /* Need to write out the old value?
158 if (cp
->xmm
[oldest
].dirty
)
161 assert(cp
->xmm
[oldest
].last_used
!= cp
->insn_counter
);
163 cp
->xmm
[oldest
].file
= FILE_REG
;
164 cp
->xmm
[oldest
].idx
= REG_UNDEF
;
165 cp
->xmm
[oldest
].last_used
= cp
->insn_counter
;
166 return x86_make_reg(file_XMM
, oldest
);
169 static void invalidate_xmm( struct compilation
*cp
,
170 GLuint file
, GLuint idx
)
174 /* Invalidate any old copy of this register in XMM0-7.
176 for (i
= 0; i
< 8; i
++) {
177 if (cp
->xmm
[i
].file
== file
&& cp
->xmm
[i
].idx
== idx
) {
178 cp
->xmm
[i
].file
= FILE_REG
;
179 cp
->xmm
[i
].idx
= REG_UNDEF
;
180 cp
->xmm
[i
].dirty
= 0;
187 /* Return an XMM reg to receive the results of an operation.
189 static struct x86_reg
get_dst_xmm_reg( struct compilation
*cp
,
190 GLuint file
, GLuint idx
)
194 /* Invalidate any old copy of this register in XMM0-7. Don't reuse
195 * as this may be one of the arguments.
197 invalidate_xmm( cp
, file
, idx
);
199 reg
= get_xmm_reg( cp
);
200 cp
->xmm
[reg
.idx
].file
= file
;
201 cp
->xmm
[reg
.idx
].idx
= idx
;
202 cp
->xmm
[reg
.idx
].dirty
= 1;
206 /* As above, but return a pointer. Note - this pointer may alias
207 * those returned by get_arg_ptr().
209 static struct x86_reg
get_dst_ptr( struct compilation
*cp
,
210 GLuint file
, GLuint idx
)
212 /* Invalidate any old copy of this register in XMM0-7. Don't reuse
213 * as this may be one of the arguments.
215 invalidate_xmm( cp
, file
, idx
);
217 return get_reg_ptr(file
, idx
);
222 /* Return an XMM reg if the argument is resident, otherwise return a
223 * base+offset pointer to the saved value.
225 static struct x86_reg
get_arg( struct compilation
*cp
, GLuint file
, GLuint idx
)
229 for (i
= 0; i
< 8; i
++) {
230 if (cp
->xmm
[i
].file
== file
&&
231 cp
->xmm
[i
].idx
== idx
) {
232 cp
->xmm
[i
].last_used
= cp
->insn_counter
;
233 return x86_make_reg(file_XMM
, i
);
237 return get_reg_ptr(file
, idx
);
240 /* As above, but always return a pointer:
242 static struct x86_reg
get_arg_ptr( struct compilation
*cp
, GLuint file
, GLuint idx
)
246 /* If there is a modified version of this register in one of the
247 * XMM regs, write it out to memory.
249 for (i
= 0; i
< 8; i
++) {
250 if (cp
->xmm
[i
].file
== file
&&
251 cp
->xmm
[i
].idx
== idx
&&
256 return get_reg_ptr(file
, idx
);
259 /* Emulate pshufd insn in regular SSE, if necessary:
261 static void emit_pshufd( struct compilation
*cp
,
267 sse2_pshufd(&cp
->func
, dst
, arg0
, shuf
);
272 sse_movups(&cp
->func
, dst
, arg0
);
274 sse_shufps(&cp
->func
, dst
, dst
, shuf
);
278 static void set_fpu_round_neg_inf( struct compilation
*cp
)
280 if (cp
->fpucntl
!= RND_NEG_FPU
) {
281 struct x86_reg regEDX
= x86_make_reg(file_REG32
, reg_DX
);
282 struct arb_vp_machine
*m
= NULL
;
284 cp
->fpucntl
= RND_NEG_FPU
;
285 x87_fnclex(&cp
->func
);
286 x87_fldcw(&cp
->func
, x86_make_disp(regEDX
, get_offset(m
, &m
->fpucntl_rnd_neg
)));
291 /* Perform a reduced swizzle.
293 static GLboolean
emit_RSW( struct compilation
*cp
, union instruction op
)
295 struct x86_reg arg0
= get_arg(cp
, op
.rsw
.file0
, op
.rsw
.idx0
);
296 struct x86_reg dst
= get_dst_xmm_reg(cp
, FILE_REG
, op
.rsw
.dst
);
297 GLuint swz
= GET_SWZ(op
.rsw
.swz
, 0) | (GET_SWZ(op
.rsw
.swz
, 1) << 2) |
298 (GET_SWZ(op
.rsw
.swz
, 2) << 4| (GET_SWZ(op
.rsw
.swz
, 3) << 6));
299 GLuint neg
= op
.rsw
.neg
;
301 emit_pshufd(cp
, dst
, arg0
, swz
);
304 struct x86_reg negs
= get_arg(cp
, FILE_REG
, REG_SWZ
);
305 struct x86_reg tmp
= get_xmm_reg(cp
);
307 * Use neg as arg to pshufd
310 /* is the emit_pshufd necessary? only SWZ can negate individual components */
311 emit_pshufd(cp
, tmp
, negs
,
312 SHUF((neg
& 1) ? 1 : 0,
316 sse_mulps(&cp
->func
, dst
, tmp
);
322 /* Perform a full swizzle
324 static GLboolean
emit_SWZ( struct compilation
*cp
, union instruction op
)
326 struct x86_reg arg0
= get_arg(cp
, op
.rsw
.file0
, op
.rsw
.idx0
);
327 struct x86_reg dst
= get_dst_xmm_reg(cp
, FILE_REG
, op
.rsw
.dst
);
328 struct x86_reg negs
= get_arg(cp
, FILE_REG
, REG_SWZ
);
329 struct x86_reg tmp
= get_xmm_reg(cp
);
330 GLubyte neg
= op
.rsw
.neg
;
331 GLubyte shuf2
, swz
, savepos
, savemask
, swizzle
[4];
333 swizzle
[0] = GET_SWZ(op
.rsw
.swz
, 0);
334 swizzle
[1] = GET_SWZ(op
.rsw
.swz
, 1);
335 swizzle
[2] = GET_SWZ(op
.rsw
.swz
, 2);
336 swizzle
[3] = GET_SWZ(op
.rsw
.swz
, 3);
338 swz
= SHUF((swizzle
[0] & 3), (swizzle
[1] & 3),
339 (swizzle
[2] & 3), (swizzle
[3] & 3));
341 emit_pshufd(cp
, dst
, arg0
, swz
);
343 /* can handle negation and replace with zero with the same shuffle/mul */
344 shuf2
= SHUF(swizzle
[0] == 4 ? 2 : (neg
& 1),
345 swizzle
[1] == 4 ? 2 : ((neg
& 2) >> 1),
346 swizzle
[2] == 4 ? 2 : ((neg
& 4) >> 2),
347 swizzle
[3] == 4 ? 2 : ((neg
& 8) >> 3));
349 /* now the hard part is getting those 1's in there... */
352 if (swizzle
[0] == 5) savepos
= 1;
353 if (swizzle
[1] == 5) savepos
= 2;
354 else savemask
|= 1 << 2;
355 if (swizzle
[2] == 5) savepos
= 3;
356 else savemask
|= 2 << 4;
357 if (swizzle
[3] == 5) savepos
= 4;
358 else savemask
|= 3 << 6;
360 /* need a mov first as movss from memory will overwrite high bits of xmm reg */
361 sse_movups(&cp
->func
, tmp
, negs
);
362 /* can only replace lowest 32bits, thus move away that part first */
363 emit_pshufd(cp
, dst
, dst
, savemask
);
364 sse_movss(&cp
->func
, dst
, tmp
);
365 emit_pshufd(cp
, dst
, dst
, (savepos
- 1) | (savemask
& 0xfc));
370 * Use neg as arg to pshufd
373 emit_pshufd(cp
, tmp
, negs
, shuf2
);
374 sse_mulps(&cp
->func
, dst
, tmp
);
380 /* Helper for writemask:
382 static GLboolean
emit_shuf_copy1( struct compilation
*cp
,
388 struct x86_reg tmp
= get_xmm_reg(cp
);
389 sse_movups(&cp
->func
, dst
, arg1
);
390 emit_pshufd(cp
, dst
, dst
, shuf
);
391 emit_pshufd(cp
, tmp
, arg0
, shuf
);
393 sse_movss(&cp
->func
, dst
, tmp
);
395 emit_pshufd(cp
, dst
, dst
, shuf
);
400 /* Helper for writemask:
402 static GLboolean
emit_shuf_copy2( struct compilation
*cp
,
408 struct x86_reg tmp
= get_xmm_reg(cp
);
409 emit_pshufd(cp
, dst
, arg1
, shuf
);
410 emit_pshufd(cp
, tmp
, arg0
, shuf
);
412 sse_shufps(&cp
->func
, dst
, tmp
, SHUF(X
, Y
, Z
, W
));
414 emit_pshufd(cp
, dst
, dst
, shuf
);
419 static void emit_x87_ex2( struct compilation
*cp
)
421 struct x86_reg st0
= x86_make_reg(file_x87
, 0);
422 struct x86_reg st1
= x86_make_reg(file_x87
, 1);
423 struct x86_reg st3
= x86_make_reg(file_x87
, 3);
425 set_fpu_round_neg_inf( cp
);
427 x87_fld(&cp
->func
, st0
); /* a a */
428 x87_fprndint( &cp
->func
); /* int(a) a */
429 x87_fld(&cp
->func
, st0
); /* int(a) int(a) a */
430 x87_fstp(&cp
->func
, st3
); /* int(a) a int(a)*/
431 x87_fsubp(&cp
->func
, st1
); /* frac(a) int(a) */
432 x87_f2xm1(&cp
->func
); /* (2^frac(a))-1 int(a)*/
433 x87_fld1(&cp
->func
); /* 1 (2^frac(a))-1 int(a)*/
434 x87_faddp(&cp
->func
, st1
); /* 2^frac(a) int(a) */
435 x87_fscale(&cp
->func
); /* 2^a */
439 static GLboolean
emit_MSK2( struct compilation
*cp
, union instruction op
)
441 struct x86_reg arg0
= get_arg(cp
, op
.msk
.file
, op
.msk
.arg
);
442 struct x86_reg arg1
= get_arg(cp
, FILE_REG
, op
.msk
.dst
); /* NOTE! */
443 struct x86_reg dst
= get_dst_xmm_reg(cp
, FILE_REG
, op
.msk
.dst
);
445 /* make full width bitmask in tmp
451 emit_pshufd(cp
, tmp
, get_arg(cp
, FILE_REG
, REG_NEGS
),
452 SHUF((op
.msk
.mask
& 1) ? 2 : 0,
453 (op
.msk
.mask
& 2) ? 2 : 0,
454 (op
.msk
.mask
& 4) ? 2 : 0,
455 (op
.msk
.mask
& 8) ? 2 : 0));
456 sse2_pnot(&cp
->func
, dst
, tmp
);
457 sse2_pand(&cp
->func
, arg0
, tmp
);
458 sse2_pand(&cp
->func
, arg1
, dst
);
459 sse2_por(&cp
->func
, tmp
, dst
);
465 /* Used to implement write masking. This and most of the other instructions
466 * here would be easier to implement if there had been a translation
467 * to a 2 argument format (dst/arg0, arg1) at the shader level before
468 * attempting to translate to x86/sse code.
470 static GLboolean
emit_MSK( struct compilation
*cp
, union instruction op
)
472 struct x86_reg arg
= get_arg(cp
, op
.msk
.file
, op
.msk
.idx
);
473 struct x86_reg dst0
= get_arg(cp
, FILE_REG
, op
.msk
.dst
); /* NOTE! */
474 struct x86_reg dst
= get_dst_xmm_reg(cp
, FILE_REG
, op
.msk
.dst
);
476 /* Note that dst and dst0 refer to the same program variable, but
477 * will definitely be different XMM registers. We're effectively
478 * treating this as a 2 argument SEL now, just one of which happens
479 * always to be the same register as the destination.
482 switch (op
.msk
.mask
) {
484 sse_movups(&cp
->func
, dst
, dst0
);
488 if (arg
.file
== file_XMM
) {
489 sse_movups(&cp
->func
, dst
, dst0
);
490 sse_movss(&cp
->func
, dst
, arg
);
493 struct x86_reg tmp
= get_xmm_reg(cp
);
494 sse_movups(&cp
->func
, dst
, dst0
);
495 sse_movss(&cp
->func
, tmp
, arg
);
496 sse_movss(&cp
->func
, dst
, tmp
);
501 sse_movups(&cp
->func
, dst
, dst0
);
502 sse_shufps(&cp
->func
, dst
, arg
, SHUF(X
, Y
, Z
, W
));
506 sse_movups(&cp
->func
, dst
, arg
);
507 sse_shufps(&cp
->func
, dst
, dst0
, SHUF(X
, Y
, Z
, W
));
511 if (dst0
.file
== file_XMM
) {
512 sse_movups(&cp
->func
, dst
, arg
);
513 sse_movss(&cp
->func
, dst
, dst0
);
516 struct x86_reg tmp
= get_xmm_reg(cp
);
517 sse_movups(&cp
->func
, dst
, arg
);
518 sse_movss(&cp
->func
, tmp
, dst0
);
519 sse_movss(&cp
->func
, dst
, tmp
);
524 emit_shuf_copy1(cp
, dst
, arg
, dst0
, SHUF(Y
,X
,Z
,W
));
528 emit_shuf_copy1(cp
, dst
, arg
, dst0
, SHUF(Z
,Y
,X
,W
));
532 emit_shuf_copy1(cp
, dst
, arg
, dst0
, SHUF(W
,Y
,Z
,X
));
536 emit_shuf_copy2(cp
, dst
, arg
, dst0
, SHUF(X
,Z
,Y
,W
));
540 emit_shuf_copy2(cp
, dst
, arg
, dst0
, SHUF(X
,W
,Z
,Y
));
543 emit_shuf_copy2(cp
, dst
, arg
, dst0
, SHUF(Z
,Y
,X
,W
));
547 emit_shuf_copy2(cp
, dst
, arg
, dst0
, SHUF(W
,Y
,Z
,X
));
551 emit_shuf_copy1(cp
, dst
, dst0
, arg
, SHUF(Y
,X
,Z
,W
));
555 emit_shuf_copy1(cp
, dst
, dst0
, arg
, SHUF(Z
,Y
,X
,W
));
559 emit_shuf_copy1(cp
, dst
, dst0
, arg
, SHUF(W
,Y
,Z
,X
));
563 sse_movups(&cp
->func
, dst
, arg
);
574 static GLboolean
emit_PRT( struct compilation
*cp
, union instruction op
)
581 * The traditional instructions. All operate on internal registers
582 * and ignore write masks and swizzling issues.
585 static GLboolean
emit_ABS( struct compilation
*cp
, union instruction op
)
587 struct x86_reg arg0
= get_arg(cp
, op
.alu
.file0
, op
.alu
.idx0
);
588 struct x86_reg dst
= get_dst_xmm_reg(cp
, FILE_REG
, op
.alu
.dst
);
589 struct x86_reg neg
= get_reg_ptr(FILE_REG
, REG_NEG
);
591 sse_movups(&cp
->func
, dst
, arg0
);
592 sse_mulps(&cp
->func
, dst
, neg
);
593 sse_maxps(&cp
->func
, dst
, arg0
);
597 static GLboolean
emit_ADD( struct compilation
*cp
, union instruction op
)
599 struct x86_reg arg0
= get_arg(cp
, op
.alu
.file0
, op
.alu
.idx0
);
600 struct x86_reg arg1
= get_arg(cp
, op
.alu
.file1
, op
.alu
.idx1
);
601 struct x86_reg dst
= get_dst_xmm_reg(cp
, FILE_REG
, op
.alu
.dst
);
603 sse_movups(&cp
->func
, dst
, arg0
);
604 sse_addps(&cp
->func
, dst
, arg1
);
609 /* The dotproduct instructions don't really do that well in sse:
611 static GLboolean
emit_DP3( struct compilation
*cp
, union instruction op
)
613 struct x86_reg arg0
= get_arg(cp
, op
.alu
.file0
, op
.alu
.idx0
);
614 struct x86_reg arg1
= get_arg(cp
, op
.alu
.file1
, op
.alu
.idx1
);
615 struct x86_reg dst
= get_dst_xmm_reg(cp
, FILE_REG
, op
.alu
.dst
);
616 struct x86_reg tmp
= get_xmm_reg(cp
);
618 sse_movups(&cp
->func
, dst
, arg0
);
619 sse_mulps(&cp
->func
, dst
, arg1
);
621 /* Now the hard bit: sum the first 3 values:
623 sse_movhlps(&cp
->func
, tmp
, dst
);
624 sse_addss(&cp
->func
, dst
, tmp
); /* a*x+c*z, b*y, ?, ? */
625 emit_pshufd(cp
, tmp
, dst
, SHUF(Y
,X
,W
,Z
));
626 sse_addss(&cp
->func
, dst
, tmp
);
627 sse_shufps(&cp
->func
, dst
, dst
, SHUF(X
, X
, X
, X
));
633 static GLboolean
emit_DP4( struct compilation
*cp
, union instruction op
)
635 struct x86_reg arg0
= get_arg(cp
, op
.alu
.file0
, op
.alu
.idx0
);
636 struct x86_reg arg1
= get_arg(cp
, op
.alu
.file1
, op
.alu
.idx1
);
637 struct x86_reg dst
= get_dst_xmm_reg(cp
, FILE_REG
, op
.alu
.dst
);
638 struct x86_reg tmp
= get_xmm_reg(cp
);
640 sse_movups(&cp
->func
, dst
, arg0
);
641 sse_mulps(&cp
->func
, dst
, arg1
);
643 /* Now the hard bit: sum the values:
645 sse_movhlps(&cp
->func
, tmp
, dst
);
646 sse_addps(&cp
->func
, dst
, tmp
); /* a*x+c*z, b*y+d*w, a*x+c*z, b*y+d*w */
647 emit_pshufd(cp
, tmp
, dst
, SHUF(Y
,X
,W
,Z
));
648 sse_addss(&cp
->func
, dst
, tmp
);
649 sse_shufps(&cp
->func
, dst
, dst
, SHUF(X
, X
, X
, X
));
653 static GLboolean
emit_DPH( struct compilation
*cp
, union instruction op
)
655 struct x86_reg arg0
= get_arg(cp
, op
.alu
.file0
, op
.alu
.idx0
);
656 struct x86_reg arg1
= get_arg(cp
, op
.alu
.file1
, op
.alu
.idx1
);
657 struct x86_reg dst
= get_dst_xmm_reg(cp
, FILE_REG
, op
.alu
.dst
);
658 struct x86_reg tmp
= get_xmm_reg(cp
);
660 sse_movups(&cp
->func
, dst
, arg0
);
661 sse_mulps(&cp
->func
, dst
, arg1
);
663 /* Now the hard bit: sum the values (from DP3):
665 sse_movhlps(&cp
->func
, tmp
, dst
);
666 sse_addss(&cp
->func
, dst
, tmp
); /* a*x+c*z, b*y, ?, ? */
667 emit_pshufd(cp
, tmp
, dst
, SHUF(Y
,X
,W
,Z
));
668 sse_addss(&cp
->func
, dst
, tmp
);
669 emit_pshufd(cp
, tmp
, arg1
, SHUF(W
,W
,W
,W
));
670 sse_addss(&cp
->func
, dst
, tmp
);
671 sse_shufps(&cp
->func
, dst
, dst
, SHUF(X
, X
, X
, X
));
676 static GLboolean
emit_DST( struct compilation
*cp
, union instruction op
)
678 struct x86_reg arg0
= get_arg_ptr(cp
, op
.alu
.file0
, op
.alu
.idx0
);
679 struct x86_reg arg1
= get_arg_ptr(cp
, op
.alu
.file1
, op
.alu
.idx1
);
680 struct x86_reg dst
= get_dst_ptr(cp
, FILE_REG
, op
.alu
.dst
);
682 /* dst[0] = 1.0 * 1.0F; */
683 /* dst[1] = arg0[1] * arg1[1]; */
684 /* dst[2] = arg0[2] * 1.0; */
685 /* dst[3] = 1.0 * arg1[3]; */
687 /* Would rather do some of this with integer regs, but:
688 * 1) No proper support for immediate values yet
689 * 2) I'd need to push/pop somewhere to get a free reg.
692 x87_fstp(&cp
->func
, dst
); /* would rather do an immediate store... */
693 x87_fld(&cp
->func
, x86_make_disp(arg0
, 4));
694 x87_fmul(&cp
->func
, x86_make_disp(arg1
, 4));
695 x87_fstp(&cp
->func
, x86_make_disp(dst
, 4));
697 if (!eq(arg0
, dst
)) {
698 x86_fld(&cp
->func
, x86_make_disp(arg0
, 8));
699 x86_stp(&cp
->func
, x86_make_disp(dst
, 8));
702 if (!eq(arg1
, dst
)) {
703 x86_fld(&cp
->func
, x86_make_disp(arg0
, 12));
704 x86_stp(&cp
->func
, x86_make_disp(dst
, 12));
710 static GLboolean
emit_DST( struct compilation
*cp
, union instruction op
)
712 struct x86_reg arg0
= get_arg(cp
, op
.alu
.file0
, op
.alu
.idx0
);
713 struct x86_reg arg1
= get_arg(cp
, op
.alu
.file1
, op
.alu
.idx1
);
714 struct x86_reg dst
= get_dst_xmm_reg(cp
, FILE_REG
, op
.alu
.dst
);
715 struct x86_reg tmp
= get_xmm_reg(cp
);
716 struct x86_reg ones
= get_reg_ptr(FILE_REG
, REG_ONES
);
718 emit_shuf_copy2(cp
, dst
, arg0
, ones
, SHUF(X
,W
,Z
,Y
));
719 emit_shuf_copy2(cp
, tmp
, arg1
, ones
, SHUF(X
,Z
,Y
,W
));
720 sse_mulps(&cp
->func
, dst
, tmp
);
722 /* dst[0] = 1.0 * 1.0F; */
723 /* dst[1] = arg0[1] * arg1[1]; */
724 /* dst[2] = arg0[2] * 1.0; */
725 /* dst[3] = 1.0 * arg1[3]; */
731 static GLboolean
emit_LG2( struct compilation
*cp
, union instruction op
)
733 struct x86_reg arg0
= get_arg_ptr(cp
, op
.alu
.file0
, op
.alu
.idx0
);
734 struct x86_reg dst
= get_dst_ptr(cp
, FILE_REG
, op
.alu
.dst
);
736 x87_fld1(&cp
->func
); /* 1 */
737 x87_fld(&cp
->func
, arg0
); /* a0 1 */
738 x87_fyl2x(&cp
->func
); /* log2(a0) */
739 x87_fst(&cp
->func
, x86_make_disp(dst
, 0));
740 x87_fst(&cp
->func
, x86_make_disp(dst
, 4));
741 x87_fst(&cp
->func
, x86_make_disp(dst
, 8));
742 x87_fstp(&cp
->func
, x86_make_disp(dst
, 12));
748 static GLboolean
emit_EX2( struct compilation
*cp
, union instruction op
)
750 struct x86_reg arg0
= get_arg_ptr(cp
, op
.alu
.file0
, op
.alu
.idx0
);
751 struct x86_reg dst
= get_dst_ptr(cp
, FILE_REG
, op
.alu
.dst
);
753 /* CAUTION: dst may alias arg0!
755 x87_fld(&cp
->func
, arg0
);
759 x87_fst(&cp
->func
, x86_make_disp(dst
, 0));
760 x87_fst(&cp
->func
, x86_make_disp(dst
, 4));
761 x87_fst(&cp
->func
, x86_make_disp(dst
, 8));
762 x87_fst(&cp
->func
, x86_make_disp(dst
, 12));
766 static GLboolean
emit_EXP( struct compilation
*cp
, union instruction op
)
768 struct x86_reg arg0
= get_arg_ptr(cp
, op
.alu
.file0
, op
.alu
.idx0
);
769 struct x86_reg dst
= get_dst_ptr(cp
, FILE_REG
, op
.alu
.dst
);
770 struct x86_reg st0
= x86_make_reg(file_x87
, 0);
771 struct x86_reg st1
= x86_make_reg(file_x87
, 1);
772 struct x86_reg st3
= x86_make_reg(file_x87
, 3);
774 /* CAUTION: dst may alias arg0!
776 x87_fld(&cp
->func
, arg0
); /* arg0.x */
777 x87_fld(&cp
->func
, st0
); /* arg arg */
779 /* by default, fpu is setup to round-to-nearest. We want to
780 * change this now, and track the state through to the end of the
781 * generated function so that it isn't repeated unnecessarily.
782 * Alternately, could subtract .5 to get round to -inf behaviour.
784 set_fpu_round_neg_inf( cp
);
785 x87_fprndint( &cp
->func
); /* flr(a) a */
786 x87_fld(&cp
->func
, st0
); /* flr(a) flr(a) a */
787 x87_fld1(&cp
->func
); /* 1 floor(a) floor(a) a */
788 x87_fst(&cp
->func
, x86_make_disp(dst
, 12)); /* stack unchanged */
789 x87_fscale(&cp
->func
); /* 2^floor(a) floor(a) a */
790 x87_fst(&cp
->func
, st3
); /* 2^floor(a) floor(a) a 2^floor(a)*/
791 x87_fstp(&cp
->func
, x86_make_disp(dst
, 0)); /* flr(a) a 2^flr(a) */
792 x87_fsubrp(&cp
->func
, st1
); /* frac(a) 2^flr(a) */
793 x87_fst(&cp
->func
, x86_make_disp(dst
, 4)); /* frac(a) 2^flr(a) */
794 x87_f2xm1(&cp
->func
); /* (2^frac(a))-1 2^flr(a)*/
795 x87_fld1(&cp
->func
); /* 1 (2^frac(a))-1 2^flr(a)*/
796 x87_faddp(&cp
->func
, st1
); /* 2^frac(a) 2^flr(a) */
797 x87_fmulp(&cp
->func
, st1
); /* 2^a */
798 x87_fst(&cp
->func
, x86_make_disp(dst
, 8));
802 /* dst[0] = 2^floor(tmp); */
803 /* dst[1] = frac(tmp); */
804 /* dst[2] = 2^floor(tmp) * 2^frac(tmp); */
809 static GLboolean
emit_LOG( struct compilation
*cp
, union instruction op
)
811 struct x86_reg arg0
= get_arg_ptr(cp
, op
.alu
.file0
, op
.alu
.idx0
);
812 struct x86_reg dst
= get_dst_ptr(cp
, FILE_REG
, op
.alu
.dst
);
813 struct x86_reg st0
= x86_make_reg(file_x87
, 0);
814 struct x86_reg st1
= x86_make_reg(file_x87
, 1);
815 struct x86_reg st2
= x86_make_reg(file_x87
, 2);
817 /* CAUTION: dst may alias arg0!
819 x87_fld(&cp
->func
, arg0
); /* arg0.x */
820 x87_fabs(&cp
->func
); /* |arg0.x| */
821 x87_fxtract(&cp
->func
); /* mantissa(arg0.x), exponent(arg0.x) */
822 x87_fst(&cp
->func
, st2
); /* mantissa, exponent, mantissa */
823 x87_fld1(&cp
->func
); /* 1, mantissa, exponent, mantissa */
824 x87_fyl2x(&cp
->func
); /* log2(mantissa), exponent, mantissa */
825 x87_fadd(&cp
->func
, st0
, st1
); /* e+l2(m), e, m */
826 x87_fstp(&cp
->func
, x86_make_disp(dst
, 8)); /* e, m */
828 x87_fld1(&cp
->func
); /* 1, e, m */
829 x87_fsub(&cp
->func
, st1
, st0
); /* 1, e-1, m */
830 x87_fstp(&cp
->func
, x86_make_disp(dst
, 12)); /* e-1,m */
831 x87_fstp(&cp
->func
, dst
); /* m */
833 x87_fadd(&cp
->func
, st0
, st0
); /* 2m */
834 x87_fstp(&cp
->func
, x86_make_disp(dst
, 4));
839 static GLboolean
emit_FLR( struct compilation
*cp
, union instruction op
)
841 struct x86_reg arg0
= get_arg_ptr(cp
, op
.alu
.file0
, op
.alu
.idx0
);
842 struct x86_reg dst
= get_dst_ptr(cp
, FILE_REG
, op
.alu
.dst
);
845 set_fpu_round_neg_inf( cp
);
847 for (i
= 0; i
< 4; i
++) {
848 x87_fld(&cp
->func
, x86_make_disp(arg0
, i
*4));
849 x87_fprndint( &cp
->func
);
850 x87_fstp(&cp
->func
, x86_make_disp(dst
, i
*4));
857 static GLboolean
emit_FRC( struct compilation
*cp
, union instruction op
)
859 struct x86_reg arg0
= get_arg_ptr(cp
, op
.alu
.file0
, op
.alu
.idx0
);
860 struct x86_reg dst
= get_dst_ptr(cp
, FILE_REG
, op
.alu
.dst
);
861 struct x86_reg st0
= x86_make_reg(file_x87
, 0);
862 struct x86_reg st1
= x86_make_reg(file_x87
, 1);
865 set_fpu_round_neg_inf( cp
);
867 /* Knowing liveness info or even just writemask would be useful
870 for (i
= 0; i
< 4; i
++) {
871 x87_fld(&cp
->func
, x86_make_disp(arg0
, i
*4));
872 x87_fld(&cp
->func
, st0
); /* a a */
873 x87_fprndint( &cp
->func
); /* flr(a) a */
874 x87_fsubrp(&cp
->func
, st1
); /* frc(a) */
875 x87_fstp(&cp
->func
, x86_make_disp(dst
, i
*4));
883 static GLboolean
emit_LIT( struct compilation
*cp
, union instruction op
)
886 struct x86_reg arg0
= get_arg_ptr(cp
, op
.alu
.file0
, op
.alu
.idx0
);
887 struct x86_reg dst
= get_dst_ptr(cp
, FILE_REG
, op
.alu
.dst
);
888 struct x86_reg lit
= get_arg(cp
, FILE_REG
, REG_LIT
);
889 struct x86_reg tmp
= get_xmm_reg(cp
);
890 struct x86_reg st1
= x86_make_reg(file_x87
, 1);
891 struct x86_reg regEAX
= x86_make_reg(file_REG32
, reg_AX
);
892 GLubyte
*fixup1
, *fixup2
;
895 /* Load the interesting parts of arg0:
897 x87_fld(&cp
->func
, x86_make_disp(arg0
, 12)); /* a3 */
898 x87_fld(&cp
->func
, x86_make_disp(arg0
, 4)); /* a1 a3 */
899 x87_fld(&cp
->func
, x86_make_disp(arg0
, 0)); /* a0 a1 a3 */
903 sse_movaps(&cp
->func
, tmp
, lit
);
904 sse_movaps(&cp
->func
, dst
, tmp
);
908 x87_fldz(&cp
->func
); /* 0 a0 a1 a3 */
909 x87_fucomp(&cp
->func
, st1
); /* a0 a1 a3 */
910 x87_fnstsw(&cp
->func
, regEAX
);
912 fixup1
= x86_jcc_forward(&cp
->func
, cc_AE
);
914 x87_fstp(&cp
->func
, x86_make_disp(dst
, 4)); /* a1 a3 */
918 x87_fldz(&cp
->func
); /* 0 a1 a3 */
919 x87_fucomp(&cp
->func
, st1
); /* a1 a3 */
920 x87_fnstsw(&cp
->func
, regEAX
);
922 fixup2
= x86_jcc_forward(&cp
->func
, cc_AE
);
924 /* Compute pow(a1, a3)
926 x87_fyl2x(&cp
->func
); /* a3*log2(a1) */
928 emit_x87_ex2( cp
); /* 2^(a3*log2(a1)) */
930 x87_fstp(&cp
->func
, x86_make_disp(dst
, 8));
934 x86_fixup_fwd_jump(&cp
->func
, fixup1
);
935 x86_fixup_fwd_jump(&cp
->func
, fixup2
);
937 struct x86_reg dst
= get_dst_xmm_reg(cp
, FILE_REG
, op
.alu
.dst
);
938 struct x86_reg ones
= get_reg_ptr(FILE_REG
, REG_LIT
);
939 sse_movups(&cp
->func
, dst
, ones
);
946 static GLboolean
emit_MAX( struct compilation
*cp
, union instruction op
)
948 struct x86_reg arg0
= get_arg(cp
, op
.alu
.file0
, op
.alu
.idx0
);
949 struct x86_reg arg1
= get_arg(cp
, op
.alu
.file1
, op
.alu
.idx1
);
950 struct x86_reg dst
= get_dst_xmm_reg(cp
, FILE_REG
, op
.alu
.dst
);
952 sse_movups(&cp
->func
, dst
, arg0
);
953 sse_maxps(&cp
->func
, dst
, arg1
);
958 static GLboolean
emit_MIN( struct compilation
*cp
, union instruction op
)
960 struct x86_reg arg0
= get_arg(cp
, op
.alu
.file0
, op
.alu
.idx0
);
961 struct x86_reg arg1
= get_arg(cp
, op
.alu
.file1
, op
.alu
.idx1
);
962 struct x86_reg dst
= get_dst_xmm_reg(cp
, FILE_REG
, op
.alu
.dst
);
964 sse_movups(&cp
->func
, dst
, arg0
);
965 sse_minps(&cp
->func
, dst
, arg1
);
969 static GLboolean
emit_MOV( struct compilation
*cp
, union instruction op
)
971 struct x86_reg arg0
= get_arg(cp
, op
.alu
.file0
, op
.alu
.idx0
);
972 struct x86_reg dst
= get_dst_xmm_reg(cp
, FILE_REG
, op
.alu
.dst
);
974 sse_movups(&cp
->func
, dst
, arg0
);
978 static GLboolean
emit_MUL( struct compilation
*cp
, union instruction op
)
980 struct x86_reg arg0
= get_arg(cp
, op
.alu
.file0
, op
.alu
.idx0
);
981 struct x86_reg arg1
= get_arg(cp
, op
.alu
.file1
, op
.alu
.idx1
);
982 struct x86_reg dst
= get_dst_xmm_reg(cp
, FILE_REG
, op
.alu
.dst
);
984 sse_movups(&cp
->func
, dst
, arg0
);
985 sse_mulps(&cp
->func
, dst
, arg1
);
990 static GLboolean
emit_POW( struct compilation
*cp
, union instruction op
)
992 struct x86_reg arg0
= get_arg_ptr(cp
, op
.alu
.file0
, op
.alu
.idx0
);
993 struct x86_reg arg1
= get_arg_ptr(cp
, op
.alu
.file1
, op
.alu
.idx1
);
994 struct x86_reg dst
= get_dst_ptr(cp
, FILE_REG
, op
.alu
.dst
);
996 x87_fld(&cp
->func
, arg1
); /* a1 */
997 x87_fld(&cp
->func
, arg0
); /* a0 a1 */
998 x87_fyl2x(&cp
->func
); /* a1*log2(a0) */
1000 emit_x87_ex2( cp
); /* 2^(a1*log2(a0)) */
1002 x87_fst(&cp
->func
, x86_make_disp(dst
, 0));
1003 x87_fst(&cp
->func
, x86_make_disp(dst
, 4));
1004 x87_fst(&cp
->func
, x86_make_disp(dst
, 8));
1005 x87_fstp(&cp
->func
, x86_make_disp(dst
, 12));
1010 static GLboolean
emit_REL( struct compilation
*cp
, union instruction op
)
1012 /* GLuint idx = (op.alu.idx0 + (GLint)cp->File[0][REG_ADDR][0]) & (MAX_NV_VERTEX_PROGRAM_PARAMS-1); */
1013 /* GLuint idx = 0; */
1014 /* struct x86_reg arg0 = get_arg(cp, op.alu.file0, idx); */
1015 /* struct x86_reg dst = get_dst_xmm_reg(cp, FILE_REG, op.alu.dst); */
1017 /* dst[0] = arg0[0]; */
1018 /* dst[1] = arg0[1]; */
1019 /* dst[2] = arg0[2]; */
1020 /* dst[3] = arg0[3]; */
1025 static GLboolean
emit_RCP( struct compilation
*cp
, union instruction op
)
1027 struct x86_reg arg0
= get_arg(cp
, op
.alu
.file0
, op
.alu
.idx0
);
1028 struct x86_reg dst
= get_dst_xmm_reg(cp
, FILE_REG
, op
.alu
.dst
);
1030 if (cp
->have_sse2
) {
1031 sse2_rcpss(&cp
->func
, dst
, arg0
);
1034 struct x86_reg ones
= get_reg_ptr(FILE_REG
, REG_ONES
);
1035 sse_movss(&cp
->func
, dst
, ones
);
1036 sse_divss(&cp
->func
, dst
, arg0
);
1039 sse_shufps(&cp
->func
, dst
, dst
, SHUF(X
, X
, X
, X
));
1043 static GLboolean
emit_RSQ( struct compilation
*cp
, union instruction op
)
1045 struct x86_reg arg0
= get_arg(cp
, op
.alu
.file0
, op
.alu
.idx0
);
1046 struct x86_reg dst
= get_dst_xmm_reg(cp
, FILE_REG
, op
.alu
.dst
);
1048 struct x86_reg neg
= get_reg_ptr(FILE_REG
, REG_NEG
);
1050 /* get abs value first. This STILL doesn't work.
1051 Looks like we get bogus neg values ?
1053 sse_movss(&cp
->func
, dst
, arg0
);
1054 sse_mulss(&cp
->func
, dst
, neg
);
1055 sse_maxss(&cp
->func
, dst
, arg0
);
1057 sse_rsqrtss(&cp
->func
, dst
, dst
);
1059 sse_rsqrtss(&cp
->func
, dst
, arg0
);
1060 sse_shufps(&cp
->func
, dst
, dst
, SHUF(X
, X
, X
, X
));
1065 static GLboolean
emit_SGE( struct compilation
*cp
, union instruction op
)
1067 struct x86_reg arg0
= get_arg(cp
, op
.alu
.file0
, op
.alu
.idx0
);
1068 struct x86_reg arg1
= get_arg(cp
, op
.alu
.file1
, op
.alu
.idx1
);
1069 struct x86_reg dst
= get_dst_xmm_reg(cp
, FILE_REG
, op
.alu
.dst
);
1070 struct x86_reg ones
= get_reg_ptr(FILE_REG
, REG_ONES
);
1072 sse_movups(&cp
->func
, dst
, arg0
);
1073 sse_cmpps(&cp
->func
, dst
, arg1
, cc_NotLessThan
);
1074 sse_andps(&cp
->func
, dst
, ones
);
1079 static GLboolean
emit_SLT( struct compilation
*cp
, union instruction op
)
1081 struct x86_reg arg0
= get_arg(cp
, op
.alu
.file0
, op
.alu
.idx0
);
1082 struct x86_reg arg1
= get_arg(cp
, op
.alu
.file1
, op
.alu
.idx1
);
1083 struct x86_reg dst
= get_dst_xmm_reg(cp
, FILE_REG
, op
.alu
.dst
);
1084 struct x86_reg ones
= get_reg_ptr(FILE_REG
, REG_ONES
);
1086 sse_movups(&cp
->func
, dst
, arg0
);
1087 sse_cmpps(&cp
->func
, dst
, arg1
, cc_LessThan
);
1088 sse_andps(&cp
->func
, dst
, ones
);
1092 static GLboolean
emit_SUB( struct compilation
*cp
, union instruction op
)
1094 struct x86_reg arg0
= get_arg(cp
, op
.alu
.file0
, op
.alu
.idx0
);
1095 struct x86_reg arg1
= get_arg(cp
, op
.alu
.file1
, op
.alu
.idx1
);
1096 struct x86_reg dst
= get_dst_xmm_reg(cp
, FILE_REG
, op
.alu
.dst
);
1098 sse_movups(&cp
->func
, dst
, arg0
);
1099 sse_subps(&cp
->func
, dst
, arg1
);
1104 static GLboolean
emit_XPD( struct compilation
*cp
, union instruction op
)
1106 struct x86_reg arg0
= get_arg(cp
, op
.alu
.file0
, op
.alu
.idx0
);
1107 struct x86_reg arg1
= get_arg(cp
, op
.alu
.file1
, op
.alu
.idx1
);
1108 struct x86_reg dst
= get_dst_xmm_reg(cp
, FILE_REG
, op
.alu
.dst
);
1109 struct x86_reg tmp0
= get_xmm_reg(cp
);
1110 struct x86_reg tmp1
= get_xmm_reg(cp
);
1112 /* Could avoid tmp0, tmp1 if we overwrote arg0, arg1. Need a way
1113 * to invalidate registers. This will come with better analysis
1114 * (liveness analysis) of the incoming program.
1116 emit_pshufd(cp
, dst
, arg0
, SHUF(Y
, Z
, X
, W
));
1117 emit_pshufd(cp
, tmp1
, arg1
, SHUF(Z
, X
, Y
, W
));
1118 sse_mulps(&cp
->func
, dst
, tmp1
);
1119 emit_pshufd(cp
, tmp0
, arg0
, SHUF(Z
, X
, Y
, W
));
1120 emit_pshufd(cp
, tmp1
, arg1
, SHUF(Y
, Z
, X
, W
));
1121 sse_mulps(&cp
->func
, tmp0
, tmp1
);
1122 sse_subps(&cp
->func
, dst
, tmp0
);
1124 /* dst[0] = arg0[1] * arg1[2] - arg0[2] * arg1[1]; */
1125 /* dst[1] = arg0[2] * arg1[0] - arg0[0] * arg1[2]; */
1126 /* dst[2] = arg0[0] * arg1[1] - arg0[1] * arg1[0]; */
1127 /* dst[3] is undef */
1132 static GLboolean
emit_NOP( struct compilation
*cp
, union instruction op
)
1138 static GLboolean (* const emit_func
[])(struct compilation
*, union instruction
) =
1144 emit_NOP
, /* ARL_NV */
1162 emit_NOP
, /* KIL_NV */
1172 emit_NOP
, /* PK2H */
1173 emit_NOP
, /* PK2US */
1174 emit_NOP
, /* PK4B */
1175 emit_NOP
, /* PK4UB */
1177 emit_NOP
, /* POPA */
1179 emit_NOP
, /* PUSHA */
1203 emit_NOP
, /* TXP_NV */
1204 emit_NOP
, /* UP2H */
1205 emit_NOP
, /* UP2US */
1206 emit_NOP
, /* UP4B */
1207 emit_NOP
, /* UP4UB */
1217 static GLboolean
build_vertex_program( struct compilation
*cp
)
1219 struct arb_vp_machine
*m
= NULL
;
1222 struct x86_reg regEBX
= x86_make_reg(file_REG32
, reg_BX
);
1223 struct x86_reg regECX
= x86_make_reg(file_REG32
, reg_CX
);
1224 struct x86_reg regEDX
= x86_make_reg(file_REG32
, reg_DX
);
1226 x86_push(&cp
->func
, regEBX
);
1228 x86_mov(&cp
->func
, regEDX
, x86_fn_arg(&cp
->func
, 1));
1229 x86_mov(&cp
->func
, regEBX
, x86_make_disp(regEDX
, get_offset(m
, m
->File
+ FILE_REG
)));
1230 x86_mov(&cp
->func
, regECX
, x86_make_disp(regEDX
, get_offset(m
, m
->File
+ FILE_STATE_PARAM
)));
1232 for (j
= 0; j
< cp
->p
->nr_instructions
; j
++) {
1233 union instruction inst
= cp
->p
->instructions
[j
];
1234 cp
->insn_counter
= j
+1; /* avoid zero */
1237 _mesa_printf("%p: ", cp
->func
.csr
);
1238 _tnl_disassem_vba_insn( inst
);
1242 if (!emit_func
[inst
.alu
.opcode
]( cp
, inst
)) {
1247 /* TODO: only for outputs:
1249 for (j
= 0; j
< 8; j
++) {
1250 if (cp
->xmm
[j
].dirty
)
1257 if (cp
->func
.need_emms
)
1258 mmx_emms(&cp
->func
);
1260 /* Restore FPU control word?
1262 if (cp
->fpucntl
!= RESTORE_FPU
) {
1263 x87_fnclex(&cp
->func
);
1264 x87_fldcw(&cp
->func
, x86_make_disp(regEDX
, get_offset(m
, &m
->fpucntl_restore
)));
1267 x86_pop(&cp
->func
, regEBX
);
1274 * Execute the given vertex program.
1276 * TODO: Integrate the t_vertex.c code here, to build machine vertices
1277 * directly at this point.
1279 * TODO: Eliminate the VB struct entirely and just use
1280 * struct arb_vertex_machine.
1283 _tnl_sse_codegen_vertex_program(struct tnl_compiled_program
*p
)
1285 struct compilation cp
;
1288 assert(emit_func
[OPCODE_ABS
] == emit_ABS
);
1289 assert(emit_func
[OPCODE_MUL
] == emit_MUL
);
1290 assert(emit_func
[OPCODE_XPD
] == emit_XPD
);
1292 _mesa_memset(&cp
, 0, sizeof(cp
));
1296 if (p
->compiled_func
) {
1297 _mesa_free((void *)p
->compiled_func
);
1298 p
->compiled_func
= NULL
;
1301 x86_init_func(&cp
.func
);
1303 cp
.fpucntl
= RESTORE_FPU
;
1306 /* Note ctx state is not referenced in building the function, so it
1307 * depends only on the list of instructions:
1309 if (!build_vertex_program(&cp
)) {
1310 x86_release_func( &cp
.func
);
1315 p
->compiled_func
= (void (*)(struct arb_vp_machine
*))x86_get_func( &cp
.func
);
1324 _tnl_sse_codegen_vertex_program(struct tnl_compiled_program
*p
)
1326 /* Dummy version for when USE_SSE_ASM not defined */