mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-22 12:31:10 +00:00
[qfcc] Implement support for the swizzle operator
The destination operand must be a full four component vector, but the source can be smaller and small sources do not need to be aligned: the offset of the source operand and the swizzle indices are adjusted. The adjustments are done during final statement emission in order to avoid confusing the data flow analyser (and that's when def offsets are known).
This commit is contained in:
parent
3c20dd515e
commit
ef9960c6f9
11 changed files with 347 additions and 6 deletions
|
@ -258,6 +258,17 @@ typedef struct {
|
|||
struct type_s *type; ///< result type
|
||||
} ex_horizontal_t;
|
||||
|
||||
//NOTE always operates on vec4 or dvec4, so needs a suitable destination and
|
||||
//care must be taken when working with smaller source operands (check aligmnet
|
||||
//and adjust swizzle operation as needed)
|
||||
typedef struct {
|
||||
struct expr_s *src; ///< source expression
|
||||
unsigned source[4]; ///< src component indices
|
||||
unsigned neg; ///< bitmask of dst components to negate
|
||||
unsigned zero; ///< bitmask of dst components to 0
|
||||
struct type_s *type; ///< result type
|
||||
} ex_swizzle_t;
|
||||
|
||||
#define POINTER_VAL(p) (((p).def ? (p).def->offset : 0) + (p).val)
|
||||
|
||||
typedef struct expr_s {
|
||||
|
@ -293,6 +304,7 @@ typedef struct expr_s {
|
|||
ex_with_t with; ///< with expr param
|
||||
struct type_s *nil; ///< type for nil if known
|
||||
ex_horizontal_t hop; ///< horizontal vector operation
|
||||
ex_swizzle_t swizzle; ///< vector swizzle operation
|
||||
} e;
|
||||
} expr_t;
|
||||
|
||||
|
@ -480,6 +492,8 @@ expr_t *new_unary_expr (int op, expr_t *e1);
|
|||
*/
|
||||
expr_t *new_horizontal_expr (int op, expr_t *vec, struct type_s *type);
|
||||
|
||||
expr_t *new_swizzle_expr (expr_t *src, const char *swizzle);
|
||||
|
||||
/** Create a new def reference (non-temporary variable) expression node.
|
||||
|
||||
\return The new def reference expression node (::def_t).
|
||||
|
|
|
@ -63,5 +63,6 @@ EX_EXPR(adjstk) ///< stack adjust expression (::ex_adjstk_t)
|
|||
EX_EXPR(with) ///< with expression (::ex_with_t)
|
||||
EX_EXPR(args) ///< @args marker in parameter list. no data
|
||||
EX_EXPR(horizontal) ///< horizontal vector operation (::ex_horzontal_t)
|
||||
EX_EXPR(swizzle) ///< vector swizzle operation (::ex_swizzle_t)
|
||||
|
||||
///@}
|
||||
|
|
|
@ -162,6 +162,36 @@ type_t *field_type (type_t *aux);
|
|||
type_t *pointer_type (type_t *aux);
|
||||
type_t *vector_type (const type_t *ele_type, int width) __attribute__((pure));
|
||||
type_t *base_type (const type_t *vec_type) __attribute__((pure));
|
||||
|
||||
/** Return an integral type of same size as the provided type.
|
||||
|
||||
Any 32-bit type will produce type_int (or one of ivec2, ivec3 or ivec4).
|
||||
Any 64-bit type will produce type_long (lor one of lvec2, lvec3, or lvec4).
|
||||
|
||||
Both type_width() and type_size() of the returned type will match the
|
||||
provided type.
|
||||
|
||||
\param base Type on which the return type will be based.
|
||||
\return Matching integral type (int, long, or a vector form), or
|
||||
null if no such match can be made.
|
||||
*/
|
||||
type_t *int_type (const type_t *base) __attribute__((pure));
|
||||
|
||||
/** Return a floating point type of same size as the provided type.
|
||||
|
||||
Any 32-bit type will produce type_float (or one of vec2, vec3 or vec4).
|
||||
Any 64-bit type will produce type_double (lor one of dvec2, dvec3, or
|
||||
dvec4).
|
||||
|
||||
Both type_width() and type_size() of the returned type will match the
|
||||
provided type.
|
||||
|
||||
\param base Type on which the return type will be based.
|
||||
\return Matching floating point type (float, double, or a vector
|
||||
form), or null if no such match can be made.
|
||||
*/
|
||||
type_t *float_type (const type_t *base) __attribute__((pure));
|
||||
|
||||
type_t *array_type (type_t *aux, int size);
|
||||
type_t *based_array_type (type_t *aux, int base, int top);
|
||||
type_t *alias_type (type_t *type, type_t *alias_chain, const char *name);
|
||||
|
|
|
@ -555,6 +555,41 @@ print_args (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next)
|
|||
e->line);
|
||||
}
|
||||
|
||||
static void
|
||||
print_horizontal (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next)
|
||||
{
|
||||
int indent = level * 2 + 2;
|
||||
|
||||
_print_expr (dstr, e->e.hop.vec, level, id, next);
|
||||
dasprintf (dstr, "%*se_%p -> \"e_%p\";\n", indent, "", e, e->e.hop.vec);
|
||||
dasprintf (dstr, "%*se_%p [label=\"hop %s\\n%d\"];\n", indent, "", e,
|
||||
get_op_string (e->e.hop.op), e->line);
|
||||
}
|
||||
|
||||
static void
|
||||
print_swizzle (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next)
|
||||
{
|
||||
static char swizzle_components[] = "xyzw";
|
||||
int indent = level * 2 + 2;
|
||||
ex_swizzle_t swiz = e->e.swizzle;
|
||||
const char *swizzle = "";
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (swiz.zero & (1 << i)) {
|
||||
swizzle = va (0, "%s0", swizzle);
|
||||
} else {
|
||||
swizzle = va (0, "%s%s%c", swizzle,
|
||||
swiz.neg & (1 << i) ? "-" : "",
|
||||
swizzle_components[swiz.source[i]]);
|
||||
}
|
||||
}
|
||||
|
||||
_print_expr (dstr, swiz.src, level, id, next);
|
||||
dasprintf (dstr, "%*se_%p -> \"e_%p\";\n", indent, "", e, swiz.src);
|
||||
dasprintf (dstr, "%*se_%p [label=\"swizzle %s\\n%d\"];\n", indent, "", e,
|
||||
swizzle, e->line);
|
||||
}
|
||||
|
||||
static void
|
||||
_print_expr (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next)
|
||||
{
|
||||
|
@ -584,6 +619,8 @@ _print_expr (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next)
|
|||
[ex_adjstk] = print_adjstk,
|
||||
[ex_with] = print_with,
|
||||
[ex_args] = print_args,
|
||||
[ex_horizontal] = print_horizontal,
|
||||
[ex_swizzle] = print_swizzle,
|
||||
};
|
||||
int indent = level * 2 + 2;
|
||||
|
||||
|
|
|
@ -179,6 +179,74 @@ use_tempop (operand_t *op, expr_t *expr)
|
|||
bug (expr, "temp users went negative: %s", operand_string (op));
|
||||
}
|
||||
|
||||
static def_t *
|
||||
cover_def_32 (def_t *def, int *adj)
|
||||
{
|
||||
int offset = def->offset;
|
||||
def_t *cover = def;
|
||||
|
||||
if (def->alias) {
|
||||
offset += def->alias->offset;
|
||||
}
|
||||
*adj = offset & 3;
|
||||
if (offset & 3) {
|
||||
if (def->alias) {
|
||||
cover = alias_def (def->alias, def->type, def->offset);
|
||||
} else {
|
||||
cover = alias_def (def, def->type, 0);
|
||||
}
|
||||
cover->offset -= offset & 3;
|
||||
}
|
||||
return cover;
|
||||
}
|
||||
|
||||
static def_t *
|
||||
cover_def_64 (def_t *def, int *adj)
|
||||
{
|
||||
int offset = def->offset;
|
||||
def_t *cover = def;
|
||||
|
||||
if (def->alias) {
|
||||
offset += def->alias->offset;
|
||||
}
|
||||
if (offset & 1) {
|
||||
internal_error (0, "misaligned 64-bit swizzle source");
|
||||
}
|
||||
*adj = (offset & 6) >> 1;
|
||||
if (offset & 6) {
|
||||
if (def->alias) {
|
||||
cover = alias_def (def->alias, def->type, def->offset);
|
||||
} else {
|
||||
cover = alias_def (def, def->type, 0);
|
||||
}
|
||||
cover->offset -= offset & 6;
|
||||
}
|
||||
return cover;
|
||||
}
|
||||
|
||||
static def_t *
|
||||
cover_def (def_t *def, int *adj)
|
||||
{
|
||||
if (type_size (base_type (def->type)) == 1) {
|
||||
return cover_def_32 (def, adj);
|
||||
} else {
|
||||
return cover_def_64 (def, adj);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
adjust_swizzle (def_t *def, int adj)
|
||||
{
|
||||
pr_ushort_t swiz = def->offset;
|
||||
for (int i = 0; i < 8; i += 2) {
|
||||
pr_ushort_t mask = 3 << i;
|
||||
pr_ushort_t ind = swiz & mask;
|
||||
swiz &= ~mask;
|
||||
swiz |= (ind + (adj << i)) & mask;
|
||||
}
|
||||
def->offset = swiz;
|
||||
}
|
||||
|
||||
static void
|
||||
emit_statement (statement_t *statement)
|
||||
{
|
||||
|
@ -202,12 +270,28 @@ emit_statement (statement_t *statement)
|
|||
op_b = statement->opb;
|
||||
op_c = statement->opc;
|
||||
}
|
||||
|
||||
def_a = get_operand_def (statement->expr, op_a);
|
||||
use_tempop (op_a, statement->expr);
|
||||
def_b = get_operand_def (statement->expr, op_b);
|
||||
use_tempop (op_b, statement->expr);
|
||||
def_c = get_operand_def (statement->expr, op_c);
|
||||
use_tempop (op_c, statement->expr);
|
||||
|
||||
if (strcmp (opcode, "swizzle") == 0) {
|
||||
op_c->type = float_type (op_c->type);
|
||||
op_a->type = float_type (op_a->type);
|
||||
if (!op_c->type || !op_a->type) {
|
||||
internal_error (statement->expr, "invalid types in swizzle");
|
||||
}
|
||||
if (op_a->width < 4) {
|
||||
int adj;
|
||||
def_a = cover_def (def_a, &adj);
|
||||
adjust_swizzle (def_b, adj);
|
||||
op_a->width = 4;
|
||||
}
|
||||
}
|
||||
|
||||
inst = opcode_find (opcode, op_a, op_b, op_c);
|
||||
|
||||
if (!inst) {
|
||||
|
|
|
@ -195,6 +195,8 @@ get_type (expr_t *e)
|
|||
return &type_va_list;
|
||||
case ex_horizontal:
|
||||
return e->e.hop.type;
|
||||
case ex_swizzle:
|
||||
return e->e.swizzle.type;
|
||||
case ex_count:
|
||||
internal_error (e, "invalid expression");
|
||||
}
|
||||
|
@ -425,6 +427,11 @@ copy_expr (expr_t *e)
|
|||
*n = *e;
|
||||
e->e.hop.vec = copy_expr (e->e.hop.vec);
|
||||
return n;
|
||||
case ex_swizzle:
|
||||
n = new_expr ();
|
||||
*n = *e;
|
||||
e->e.swizzle.src = copy_expr (e->e.swizzle.src);
|
||||
return n;
|
||||
case ex_count:
|
||||
break;
|
||||
}
|
||||
|
@ -611,6 +618,74 @@ new_horizontal_expr (int op, expr_t *vec, type_t *type)
|
|||
return e;
|
||||
}
|
||||
|
||||
expr_t *
|
||||
new_swizzle_expr (expr_t *src, const char *swizzle)
|
||||
{
|
||||
type_t *src_type = get_type (src);
|
||||
if (!src_type) {
|
||||
return src;
|
||||
}
|
||||
int src_width = type_width (src_type);
|
||||
// swizzle always generates a *vec4
|
||||
ex_swizzle_t swiz = {};
|
||||
|
||||
#define m(x) (1 << ((x) - 'a'))
|
||||
#define v(x, mask) (((x) & 0x60) == 0x60 && (m(x) & (mask)))
|
||||
#define vind(x) ((x) & 3)
|
||||
#define cind(x) (-(((x) >> 3) ^ (x)) & 3)
|
||||
#define tind(x) ((((~(x+1)>>2)&1) + x + 1) & 3)
|
||||
const int color = m('r') | m('g') | m('b') | m('a');
|
||||
const int vector = m('x') | m('y') | m('z') | m('w');
|
||||
const int texture = m('s') | m('t') | m('p') | m('q');
|
||||
|
||||
int type_mask = 0;
|
||||
int comp_count = 0;
|
||||
|
||||
for (const char *s = swizzle; *s; s++) {
|
||||
if (comp_count >= 4) {
|
||||
return error (src, "too many components in swizzle");
|
||||
}
|
||||
if (*s == '0') {
|
||||
swiz.zero |= 1 << comp_count;
|
||||
comp_count++;
|
||||
} else if (*s == '-') {
|
||||
swiz.neg |= 1 << comp_count;
|
||||
} else {
|
||||
int ind = 0;
|
||||
int mask = 0;
|
||||
if (v (*s, vector)) {
|
||||
ind = vind (*s);
|
||||
mask = 1;
|
||||
} else if (v (*s, color)) {
|
||||
ind = cind (*s);
|
||||
mask = 2;
|
||||
} else if (v (*s, texture)) {
|
||||
ind = tind (*s);
|
||||
mask = 4;
|
||||
}
|
||||
if (!mask) {
|
||||
return error (src, "invalid component in swizzle");
|
||||
}
|
||||
if (type_mask & ~mask) {
|
||||
return error (src, "mixed components in swizzle");
|
||||
}
|
||||
if (ind >= src_width) {
|
||||
return error (src, "swizzle component out of bounds");
|
||||
}
|
||||
type_mask |= mask;
|
||||
swiz.source[comp_count++] = ind;
|
||||
}
|
||||
}
|
||||
swiz.zero |= (0xf << comp_count) & 0xf;
|
||||
swiz.src = new_alias_expr (vector_type (&type_float, src_width), src);
|
||||
swiz.type = vector_type (base_type (src_type), 4);
|
||||
|
||||
expr_t *expr = new_expr ();
|
||||
expr->type = ex_swizzle;
|
||||
expr->e.swizzle = swiz;
|
||||
return expr;
|
||||
}
|
||||
|
||||
expr_t *
|
||||
new_def_expr (def_t *def)
|
||||
{
|
||||
|
@ -1547,6 +1622,8 @@ has_function_call (expr_t *e)
|
|||
return has_function_call (e->e.retrn.ret_val);
|
||||
case ex_horizontal:
|
||||
return has_function_call (e->e.hop.vec);
|
||||
case ex_swizzle:
|
||||
return has_function_call (e->e.swizzle.src);
|
||||
case ex_error:
|
||||
case ex_state:
|
||||
case ex_label:
|
||||
|
@ -1692,6 +1769,7 @@ unary_expr (int op, expr_t *e)
|
|||
case ex_alias:
|
||||
case ex_assign:
|
||||
case ex_horizontal:
|
||||
case ex_swizzle:
|
||||
{
|
||||
expr_t *n = new_unary_expr (op, e);
|
||||
|
||||
|
@ -1785,6 +1863,7 @@ unary_expr (int op, expr_t *e)
|
|||
case ex_address:
|
||||
case ex_assign:
|
||||
case ex_horizontal:
|
||||
case ex_swizzle:
|
||||
if (options.code.progsversion == PROG_VERSION) {
|
||||
return binary_expr (EQ, e, new_nil_expr ());
|
||||
} else {
|
||||
|
@ -1874,6 +1953,7 @@ unary_expr (int op, expr_t *e)
|
|||
case ex_alias:
|
||||
case ex_assign:
|
||||
case ex_horizontal:
|
||||
case ex_swizzle:
|
||||
bitnot_expr:
|
||||
if (options.code.progsversion == PROG_ID_VERSION) {
|
||||
expr_t *n1 = new_int_expr (-1);
|
||||
|
|
|
@ -142,6 +142,7 @@ is_lvalue (const expr_t *expr)
|
|||
case ex_with:
|
||||
case ex_args:
|
||||
case ex_horizontal:
|
||||
case ex_swizzle:
|
||||
break;
|
||||
case ex_count:
|
||||
internal_error (expr, "invalid expression");
|
||||
|
|
|
@ -654,13 +654,28 @@ convert_scalar (expr_t *scalar, int op, expr_t *vec)
|
|||
if (!*s_op) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// expand the scalar to a vector of the same width as vec
|
||||
for (int i = 1; i < type_width (get_type (vec)); i++) {
|
||||
expr_t *s = copy_expr (scalar);
|
||||
s->next = scalar;
|
||||
scalar = s;
|
||||
type_t *vec_type = get_type (vec);
|
||||
|
||||
if (scalar->type == ex_symbol || scalar->type == ex_def
|
||||
|| is_constant (scalar)) {
|
||||
for (int i = 1; i < type_width (get_type (vec)); i++) {
|
||||
expr_t *s = copy_expr (scalar);
|
||||
s->next = scalar;
|
||||
scalar = s;
|
||||
}
|
||||
return new_vector_list (scalar);
|
||||
}
|
||||
return new_vector_list (scalar);
|
||||
|
||||
char swizzle[] = "xxxx";
|
||||
type_t *vec_base = base_type (vec_type);
|
||||
expr_t *tmp = new_temp_def_expr (vector_type (vec_base, 4));
|
||||
expr_t *block = new_block_expr ();
|
||||
swizzle[type_width (vec_type)] = 0;
|
||||
append_expr (block, assign_expr (tmp, new_swizzle_expr (scalar, swizzle)));
|
||||
block->e.block.result = new_alias_expr (vec_type, tmp);
|
||||
return block;
|
||||
}
|
||||
|
||||
static expr_t *
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
|
||||
#include <QF/hash.h>
|
||||
|
||||
#include "tools/qfcc/include/diagnostic.h"
|
||||
#include "tools/qfcc/include/opcodes.h"
|
||||
#include "tools/qfcc/include/options.h"
|
||||
#include "tools/qfcc/include/qfcc.h"
|
||||
|
@ -302,7 +303,26 @@ operand_width (const char *opname, operand_t *op)
|
|||
}
|
||||
return op->width;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (!strcmp (name, "swizzle")) {
|
||||
adjust_swizzle_op (&search_op, 0);
|
||||
adjust_swizzle_op (&search_op, 2);
|
||||
}
|
||||
static void
|
||||
adjust_swizzle_op (opcode_t *op, int opind)
|
||||
{
|
||||
// swizzle instructions require both operands to be 4 components (4 or 8
|
||||
// words) in size with the same alignment.
|
||||
op->widths[opind] = 4;
|
||||
if (pr_type_size[op->types[opind]] == 1) {
|
||||
op->types[opind] = ev_float;
|
||||
} else if (pr_type_size[op->types[opind]] == 2) {
|
||||
op->types[opind] = ev_double;
|
||||
} else {
|
||||
internal_error (0, "unexpected swizzle op size");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
static opcode_t *
|
||||
rua_opcode_find (const char *name, operand_t *op_a, operand_t *op_b,
|
||||
operand_t *op_c)
|
||||
|
|
|
@ -1783,6 +1783,32 @@ expr_horizontal (sblock_t *sblock, expr_t *e, operand_t **op)
|
|||
return sblock;
|
||||
}
|
||||
|
||||
static sblock_t *
|
||||
expr_swizzle (sblock_t *sblock, expr_t *e, operand_t **op)
|
||||
{
|
||||
const char *opcode = "swizzle";
|
||||
statement_t *s;
|
||||
int swiz = 0;
|
||||
type_t *res_type = e->e.swizzle.type;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
swiz |= e->e.swizzle.source[i] & 3;
|
||||
}
|
||||
swiz |= (e->e.swizzle.neg & 0xf) << 8;
|
||||
swiz |= (e->e.swizzle.zero & 0xf) << 12;
|
||||
|
||||
s = new_statement (st_expr, opcode, e);
|
||||
sblock = statement_subexpr (sblock, e->e.swizzle.src, &s->opa);
|
||||
s->opb = short_operand (swiz, e);
|
||||
if (!*op) {
|
||||
*op = temp_operand (res_type, e);
|
||||
}
|
||||
s->opc = *op;
|
||||
sblock_add_statement (sblock, s);
|
||||
|
||||
return sblock;
|
||||
}
|
||||
|
||||
static sblock_t *
|
||||
expr_def (sblock_t *sblock, expr_t *e, operand_t **op)
|
||||
{
|
||||
|
@ -1915,6 +1941,7 @@ statement_subexpr (sblock_t *sblock, expr_t *e, operand_t **op)
|
|||
[ex_expr] = expr_expr,
|
||||
[ex_uexpr] = expr_uexpr,
|
||||
[ex_horizontal] = expr_horizontal,
|
||||
[ex_swizzle] = expr_swizzle,
|
||||
[ex_def] = expr_def,
|
||||
[ex_symbol] = expr_symbol,
|
||||
[ex_temp] = expr_temp,
|
||||
|
|
|
@ -586,6 +586,38 @@ base_type (const type_t *vec_type)
|
|||
return ev_types[vec_type->type];
|
||||
}
|
||||
|
||||
type_t *
|
||||
int_type (const type_t *base)
|
||||
{
|
||||
int width = type_width (base);
|
||||
base = base_type (base);
|
||||
if (!base) {
|
||||
return 0;
|
||||
}
|
||||
if (type_size (base) == 1) {
|
||||
base = &type_int;
|
||||
} else if (type_size (base) == 2) {
|
||||
base = &type_long;
|
||||
}
|
||||
return vector_type (base, width);
|
||||
}
|
||||
|
||||
type_t *
|
||||
float_type (const type_t *base)
|
||||
{
|
||||
int width = type_width (base);
|
||||
base = base_type (base);
|
||||
if (!base) {
|
||||
return 0;
|
||||
}
|
||||
if (type_size (base) == 1) {
|
||||
base = &type_float;
|
||||
} else if (type_size (base) == 2) {
|
||||
base = &type_double;
|
||||
}
|
||||
return vector_type (base, width);
|
||||
}
|
||||
|
||||
type_t *
|
||||
array_type (type_t *aux, int size)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue