[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:
Bill Currie 2022-05-01 10:02:26 +09:00
parent 3c20dd515e
commit ef9960c6f9
11 changed files with 347 additions and 6 deletions

View file

@ -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).

View file

@ -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)
///@}

View file

@ -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);

View file

@ -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;

View file

@ -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) {

View file

@ -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);

View file

@ -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");

View file

@ -654,7 +654,12 @@ 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
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;
@ -663,6 +668,16 @@ convert_scalar (expr_t *scalar, int op, expr_t *vec)
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 *
pointer_arithmetic (int op, expr_t *e1, expr_t *e2)
{

View file

@ -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)

View file

@ -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,

View file

@ -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)
{