[qfcc] Use scatter-gather for multivec expressions

This makes working with them much easier, and the type system reflects
what's in the multi-vector. Unfortunately, that does mean that large
algebras will wind up having a LOT of types, but it allows for efficient
storage of sparse multi-vectors:

    auto v = 4*(e1 + e032 + e123);

results in:

    0005 0213 1:0008<00000008>4:void     0:0000<00000000>?:invalid
              0:0044<00000044>4:void          assign (<void>), v
    0006 0213 1:000c<0000000c>4:void     0:0000<00000000>?:invalid
              0:0048<00000048>4:void          assign (<void>), {v + 4}

Where the two source vectors are:

    44:1 0 .imm float:18e [4, 0, 0, 0]
    48:1 0 .imm float:1aa [4, 0, 0, 4]

They just happen to be adjacent, but don't need to be.
This commit is contained in:
Bill Currie 2023-08-23 00:03:56 +09:00
parent dfb719c92b
commit cfcacfbf28
9 changed files with 282 additions and 157 deletions

View file

@ -38,11 +38,11 @@ typedef struct basis_blade_s {
typedef struct basis_group_s {
int count;
pr_uint_t group_mask;
pr_uivec2_t range;
basis_blade_t *blades;
int *map;
set_t *set;
struct type_s *type;
} basis_group_t;
typedef struct basis_layout_s {
@ -67,6 +67,7 @@ typedef struct algebra_s {
metric_t metric;
basis_layout_t layout;
basis_group_t *groups;
struct type_s **mvec_types;
int num_components; ///< number of componets (2^d)
int dimension; ///< number of dimensions (plus + minus + zero)
int plus; ///< number of elements squaring to +1
@ -76,13 +77,14 @@ typedef struct algebra_s {
typedef struct multivector_s {
int num_components;
int element;
int group_mask;
algebra_t *algebra;
} multivector_t;
struct expr_s;
bool is_algebra (const struct type_s *type) __attribute__((pure));
struct type_s *algebra_type (struct type_s *type, struct expr_s *params);
struct type_s *algebra_mvec_type (algebra_t *algebra, pr_uint_t group_mask);
struct symtab_s *algebra_scope (struct type_s *type, struct symtab_s *curscope);
void algebra_print_type_str (struct dstring_s *str, const struct type_s *type);
void algebra_encode_type (struct dstring_s *encoding,
@ -95,6 +97,7 @@ int metric_apply (const metric_t *metric, pr_uint_t a, pr_uint_t b) __attribute_
algebra_t *algebra_get (const struct type_s *type) __attribute__((pure));
int algebra_type_assignable (const struct type_s *dst,
const struct type_s *src) __attribute__((pure));
struct type_s *algebra_base_type (const struct type_s *type) __attribute__((pure));
struct expr_s *algebra_binary_expr (int op, struct expr_s *e1,
struct expr_s *e2);

View file

@ -281,6 +281,7 @@ typedef struct {
} ex_extend_t;
typedef struct {
struct type_s *type; ///< overall type of multivector
struct algebra_s *algebra; ///< owning algebra
int count; ///< number of component expressions
struct expr_s *components; ///< multivector components

View file

@ -164,10 +164,12 @@ basis_blade_init (basis_blade_t *blade, pr_uint_t mask)
static void
basis_group_init (basis_group_t *group, int count, basis_blade_t *blades,
algebra_t *a, int element)
algebra_t *a, int group_id)
{
pr_uint_t group_mask = 1 << group_id;
*group = (basis_group_t) {
.count = count,
.group_mask = group_mask,
.range = { ~0u, 0 },
.blades = malloc (sizeof (basis_blade_t[count])),
.set = set_new (),
@ -184,29 +186,6 @@ basis_group_init (basis_group_t *group, int count, basis_blade_t *blades,
for (int i = 0; i < count; i++) {
group->map[blades[i].mask - group->range[0]] = i;
}
if (count == 1 && blades[0].mask == 0) {
group->type = a->type;
} else {
multivector_t *mvec = malloc (sizeof (multivector_t));
*mvec = (multivector_t) {
.num_components = count,
.element = element,
.algebra = a,
};
group->type = new_type ();
*group->type = (type_t) {
.type = a->type->type,
.name = "basis group",
.alignment = 4, //FIXME
.width = count,
.meta = ty_algebra,
.t.algebra = (algebra_t *) mvec,
.freeable = true,
.allocated = true,
};
chain_type (group->type);
}
}
static void
@ -268,6 +247,12 @@ metric_apply (const metric_t *metric, pr_uint_t a, pr_uint_t b)
return count_minus (c & metric->minus);
}
static type_t **
alloc_mvec_types (int num_groups)
{
return calloc (1 << num_groups, sizeof (type_t *));
}
static void
algebra_init (algebra_t *a)
{
@ -309,6 +294,7 @@ algebra_init (algebra_t *a)
blades[13], blades[12], blades[11], blades[14],
};
a->groups = malloc (sizeof (basis_group_t[6]));
a->mvec_types = alloc_mvec_types (6);
basis_group_init (&a->groups[0], 4, pga_blades + 0, a, 0);
basis_group_init (&a->groups[1], 3, pga_blades + 4, a, 1);
basis_group_init (&a->groups[2], 1, pga_blades + 7, a, 2);
@ -325,6 +311,7 @@ algebra_init (algebra_t *a)
blades[2], blades[3], blades[1], blades[7],
};
a->groups = malloc (sizeof (basis_group_t[4]));
a->mvec_types = alloc_mvec_types (4);
basis_group_init (&a->groups[0], 1, pga_blades + 0, a, 0);
basis_group_init (&a->groups[1], 3, pga_blades + 1, a, 1);
basis_group_init (&a->groups[2], 3, pga_blades + 4, a, 2);
@ -333,6 +320,7 @@ algebra_init (algebra_t *a)
} else {
// just use the grades as the default layout
a->groups = malloc (sizeof (basis_group_t[d + 1]));
a->mvec_types = alloc_mvec_types (d + 1);
for (int i = 0; i < d + 1; i++) {
int c = counts[i];
int ind = indices[i];
@ -340,6 +328,15 @@ algebra_init (algebra_t *a)
}
basis_layout_init (&a->layout, d + 1, a->groups);
}
for (int i = 0; i < a->layout.count; i++) {
auto g = &a->layout.groups[i];
if (g->count == 1 && g->blades[0].mask == 0) {
a->mvec_types[g->group_mask] = a->type;
} else {
algebra_mvec_type (a, g->group_mask);
}
}
}
bool
@ -415,6 +412,41 @@ algebra_type (type_t *type, expr_t *params)
return find_type (t);
}
type_t *
algebra_mvec_type (algebra_t *algebra, pr_uint_t group_mask)
{
if (!group_mask) {
return 0;
}
if (!algebra->mvec_types[group_mask]) {
int count = 0;
for (int i = 0; i < algebra->layout.count; i++) {
if (group_mask & (1 << i)) {
count += algebra->layout.groups[i].count;
}
}
multivector_t *mvec = malloc (sizeof (multivector_t));
*mvec = (multivector_t) {
.num_components = count,
.group_mask = group_mask,
.algebra = algebra,
};
algebra->mvec_types[group_mask] = new_type ();
*algebra->mvec_types[group_mask] = (type_t) {
.type = algebra->type->type,
.name = "basis group",
.alignment = 4, //FIXME
.width = count,
.meta = ty_algebra,
.t.algebra = (algebra_t *) mvec,
.freeable = true,
.allocated = true,
};
chain_type (algebra->mvec_types[group_mask]);
}
return algebra->mvec_types[group_mask];
}
static int pga_swaps_2d[8] = {
[0x5] = 1, // e20
};
@ -470,17 +502,18 @@ algebra_symbol (const char *name, symtab_t *symtab)
int sign = 1 - 2 * (swaps & 1);
auto g = alg->layout.group_map[alg->layout.mask_map[blade]];
auto group = &alg->layout.groups[g[0]];
auto group_type = alg->mvec_types[group->group_mask];
ex_value_t *blade_val = 0;
if (is_float (alg->type)) {
float components[group->count] = {};
components[g[1]] = sign;
blade_val = new_type_value (group->type, (pr_type_t *)components);
blade_val = new_type_value (group_type, (pr_type_t *)components);
} else {
double components[group->count] = {};
components[g[1]] = sign;
blade_val = new_type_value (group->type, (pr_type_t *)components);
blade_val = new_type_value (group_type, (pr_type_t *)components);
}
sym = new_symbol_type (name, group->type);
sym = new_symbol_type (name, group_type);
sym->sy_type = sy_const;
sym->s.value = blade_val;
symtab_addsymbol (symtab, sym);
@ -524,8 +557,8 @@ algebra_print_type_str (dstring_t *str, const type_t *type)
} else if (type->type == ev_float || type->type == ev_double) {
auto m = type->t.multivec;
auto a = m->algebra;
dasprintf (str, " algebra(%s(%d,%d,%d):%d)", a->type->name,
a->plus, a->minus, a->zero, m->element);
dasprintf (str, " algebra(%s(%d,%d,%d):%04x)", a->type->name,
a->plus, a->minus, a->zero, m->group_mask);
} else {
internal_error (0, "invalid algebra type");
}
@ -544,8 +577,8 @@ algebra_encode_type (dstring_t *encoding, const type_t *type)
auto a = m->algebra;
dasprintf (encoding, "{∧");
encode_type (encoding, a->type);
dasprintf (encoding, "(%d,%d,%d):%d}", a->plus, a->minus, a->zero,
m->element);
dasprintf (encoding, "(%d,%d,%d):%04x}", a->plus, a->minus, a->zero,
m->group_mask);
} else {
internal_error (0, "invalid algebra type");
}
@ -609,3 +642,12 @@ algebra_type_assignable (const type_t *dst, const type_t *src)
}
return dst->t.multivec == src->t.multivec;
}
type_t *
algebra_base_type (const type_t *type)
{
if (type->type == ev_invalid) {
return type->t.algebra->type;
}
return ev_types[type->type];
}

View file

@ -201,7 +201,7 @@ get_type (expr_t *e)
case ex_extend:
return e->e.extend.type;
case ex_multivec:
return e->e.multivec.algebra->algebra_type;
return e->e.multivec.type;
case ex_count:
internal_error (e, "invalid expression");
}
@ -445,8 +445,6 @@ copy_expr (expr_t *e)
case ex_multivec:
n = new_expr ();
*n = *e;
n->e.multivec.algebra = e->e.multivec.algebra;
n->e.multivec.count = e->e.multivec.count;
n->e.multivec.components = copy_expr (e->e.multivec.components);
t = e->e.multivec.components;
e = n->e.multivec.components;

View file

@ -28,6 +28,8 @@
# include "config.h"
#endif
#include "QF/math/bitop.h"
#include "tools/qfcc/include/algebra.h"
#include "tools/qfcc/include/diagnostic.h"
#include "tools/qfcc/include/expr.h"
@ -36,6 +38,121 @@
#include "tools/qfcc/source/qc-parse.h"
static expr_t *
mvec_expr (expr_t *expr, algebra_t *algebra)
{
auto mvtype = get_type (expr);
if (expr->type == ex_multivec || is_scalar (mvtype)) {
return expr;
}
if (!is_algebra (mvtype)) {
return error (expr, "invalid operand for GA");
}
auto layout = &algebra->layout;
pr_uint_t group_mask = (1u << (layout->count + 1)) - 1;
if (mvtype->type != ev_invalid) {
group_mask = mvtype->t.multivec->group_mask;
}
if (!(group_mask & (group_mask - 1))) {
return expr;
}
auto mvec = new_expr ();
mvec->type = ex_multivec;
mvec->e.multivec = (ex_multivec_t) {
.type = algebra_mvec_type (algebra, group_mask),
.algebra = algebra,
};
expr_t **c = &mvec->e.multivec.components;
for (int i = 0; i < layout->count; i++) {
pr_uint_t mask = 1u << i;
if (mask & group_mask) {
auto comp_type = algebra_mvec_type (algebra, mask);
int comp_offset = algebra->layout.group_map[i][1];
*c = new_offset_alias_expr (comp_type, expr, comp_offset);
mvec->e.multivec.count++;
}
}
return mvec;
}
static void
mvec_scatter (expr_t **components, expr_t *mvec, algebra_t *algebra)
{
auto layout = &algebra->layout;
int group;
if (mvec->type != ex_multivec) {
auto type = get_type (mvec);
if (!is_algebra (type)) {
group = layout->group_map[layout->mask_map[0]][0];
} else {
if (type->type == ev_invalid) {
internal_error (mvec, "full algebra in mvec_scatter");
}
pr_uint_t mask = type->t.multivec->group_mask;
if (mask & (mask - 1)) {
internal_error (mvec, "bare multivector in mvec_scatter");
}
group = BITOP_LOG2 (mask);
}
components[group] = mvec;
return;
}
for (auto c = mvec->e.multivec.components; c; c = c->next) {
auto ct = get_type (c);
if (is_scalar (ct)) {
group = layout->group_map[layout->mask_map[0]][0];
components[group] = mvec;
} else if (ct->meta == ty_algebra && ct->type != ev_invalid) {
pr_uint_t mask = ct->t.multivec->group_mask;
if (mask & (mask - 1)) {
internal_error (mvec, "multivector in multivec expression");
}
group = BITOP_LOG2 (mask);
} else {
internal_error (mvec, "invalid type in multivec expression");
}
components[group] = c;
}
}
static expr_t *
mvec_gather (expr_t **components, algebra_t *algebra)
{
auto layout = &algebra->layout;
pr_uint_t group_mask = 0;
int count = 0;
expr_t *mvec = 0;
for (int i = 0; i < layout->count; i++) {
if (components[i]) {
count++;
mvec = components[i];
group_mask |= 1 << i;
}
}
if (count == 1) {
return mvec;
}
mvec = new_expr ();
mvec->type = ex_multivec;
mvec->e.multivec = (ex_multivec_t) {
.type = algebra_mvec_type (algebra, group_mask),
.algebra = algebra,
};
for (int i = layout->count; i-- > 0; ) {
if (components[i]) {
components[i]->next = mvec->e.multivec.components;
mvec->e.multivec.components = components[i];
mvec->e.multivec.count++;
}
}
return mvec;
}
static expr_t *
promote_scalar (type_t *dst_type, expr_t *scalar)
{
@ -56,29 +173,34 @@ scalar_product (expr_t *e1, expr_t *e2)
{
auto scalar = is_scalar (get_type (e1)) ? e1 : e2;
auto vector = is_scalar (get_type (e1)) ? e2 : e1;
if (vector->type == ex_multivec) {
auto comp = vector->e.multivec.components;
auto prod = scalar_product (scalar, comp);
while (comp->next) {
comp = comp->next;
auto p = scalar_product (scalar, comp);
p = fold_constants (p);
p->next = prod;
prod = p;
auto algebra = algebra_get (get_type (vector));
auto layout = &algebra->layout;
scalar = promote_scalar (algebra->type, scalar);
expr_t *components[layout->count] = {};
vector = mvec_expr (vector, algebra);
mvec_scatter (components, vector, algebra);
for (int i = 0; i < layout->count; i++) {
if (!components[i]) {
continue;
}
return prod;
} else {
auto vector_type = get_type (vector);
auto scalar_type = base_type (vector_type);
scalar = promote_scalar (scalar_type, scalar);
if (type_width (vector_type) > 4) {
auto comp_type = get_type (components[i]);
if (type_width (comp_type) == 1) {
auto prod = new_binary_expr ('*', components[i], scalar);
prod->e.expr.type = comp_type;
components[i] = fold_constants (prod);
} else if (type_width (comp_type) > 4) {
internal_error (vector, "scalar * %d-vector not implemented",
type_width (vector_type));
type_width (comp_type));
} else {
auto prod = new_binary_expr (SCALE, components[i], scalar);
prod->e.expr.type = comp_type;
components[i] = fold_constants (prod);
}
auto prod = new_binary_expr (SCALE, vector, scalar);
prod->e.expr.type = vector_type;
return fold_constants (prod);
}
return mvec_gather (components, algebra);
}
static expr_t *
@ -110,30 +232,31 @@ regressive_product (expr_t *e1, expr_t *e2)
}
static void
component_sum (int op, expr_t **components, expr_t *e,
const basis_layout_t *layout)
component_sum (int op, expr_t **c, expr_t **a, expr_t **b,
algebra_t *algebra)
{
int group;
auto t = get_type (e);
if (is_scalar (t)) {
group = layout->group_map[layout->mask_map[0]][0];
} else {
group = t->t.multivec->element;
}
if (components[group]) {
if (t != get_type (components[group])) {
internal_error (e, "tangled multivec types");
}
components[group] = new_binary_expr (op, components[group], e);
components[group]->e.expr.type = t;
} else {
if (op == '+') {
components[group] = e;
auto layout = &algebra->layout;
for (int i = 0; i < layout->count; i++) {
if (a[i] && b[i]) {
if (get_type (a[i]) != get_type (b[i])) {
internal_error (a[i], "tangled multivec types");
}
c[i] = new_binary_expr (op, a[i], b[i]);
c[i]->e.expr.type = get_type (a[i]);
c[i] = fold_constants (c[i]);
} else if (a[i]) {
c[i] = a[i];
} else if (b[i]) {
if (op == '+') {
c[i] = b[i];
} else {
c[i] = scalar_product (new_float_expr (-1), b[i]);
c[i] = fold_constants (c[i]);
}
} else {
components[group] = scalar_product (new_float_expr (-1), e);
c[i] = 0;
}
}
components[group] = fold_constants (components[group]);
}
static expr_t *
@ -141,60 +264,17 @@ multivector_sum (int op, expr_t *e1, expr_t *e2)
{
auto t1 = get_type (e1);
auto t2 = get_type (e2);
auto alg = is_algebra (t1) ? algebra_get (t1) : algebra_get (t2);
auto layout = &alg->layout;
expr_t *components[layout->count] = {};
if (e1->type == ex_multivec) {
for (auto c = e1->e.multivec.components; c; c = c->next) {
auto ct = get_type (c);
int group;
if (is_scalar (ct)) {
group = layout->group_map[layout->mask_map[0]][0];
} else {
group = ct->t.multivec->element;
}
components[group] = c;
}
} else {
int group;
if (is_scalar (t1)) {
group = layout->group_map[layout->mask_map[0]][0];
} else {
group = t1->t.multivec->element;
}
components[group] = e1;
}
if (e2->type == ex_multivec) {
for (auto c = e1->e.multivec.components; c; c = c->next) {
component_sum (op, components, c, layout);
}
} else {
component_sum (op, components, e2, layout);
}
int count = 0;
expr_t *sum = 0;
for (int i = 0; i < layout->count; i++) {
if (components[i]) {
count++;
sum = components[i];
}
}
if (count == 1) {
return sum;
}
sum = new_expr ();
sum->type = ex_multivec;
sum->e.multivec.algebra = alg;
for (int i = layout->count; i-- > 0; ) {
if (components[i]) {
components[i]->next = sum->e.multivec.components;
sum->e.multivec.components = components[i];
sum->e.multivec.count++;
}
}
return sum;
auto algebra = is_algebra (t1) ? algebra_get (t1) : algebra_get (t2);
auto layout = &algebra->layout;
expr_t *a[layout->count] = {};
expr_t *b[layout->count] = {};
expr_t *c[layout->count];
e1 = mvec_expr (e1, algebra);
e2 = mvec_expr (e2, algebra);
mvec_scatter (a, e1, algebra);
mvec_scatter (b, e2, algebra);
component_sum (op, c, a, b, algebra);
return mvec_gather (c, algebra);
}
static expr_t *
@ -302,35 +382,21 @@ algebra_assign_expr (expr_t *dst, expr_t *src)
type_t *srcType = get_type (src);
type_t *dstType = get_type (dst);
if (type_size (srcType) == type_size (dstType)) {
return new_assign_expr (dst, src);
if (src->type != ex_multivec) {
if (type_size (srcType) == type_size (dstType)) {
return new_assign_expr (dst, src);
}
}
if (dstType->meta != ty_algebra && dstType->type != ev_invalid) {
if (dstType->meta != ty_algebra && dstType != srcType) {
return 0;
}
auto layout = &dstType->t.algebra->layout;
auto algebra = algebra_get (dstType);
auto layout = &algebra->layout;
expr_t *components[layout->count] = {};
if (src->type == ex_multivec) {
for (auto c = src->e.multivec.components; c; c = c->next) {
auto ct = get_type (c);
int group;
if (is_scalar (ct)) {
group = layout->group_map[layout->mask_map[0]][0];
} else {
group = ct->t.multivec->element;
}
components[group] = c;
}
} else {
int group;
if (is_scalar (srcType)) {
group = layout->group_map[layout->mask_map[0]][0];
} else {
group = srcType->t.multivec->element;
}
components[group] = src;
}
src = mvec_expr (src, algebra);
mvec_scatter (components, src, algebra);
auto block = new_block_expr ();
int memset_base = 0;
int memset_size = 0;
@ -341,14 +407,17 @@ algebra_assign_expr (expr_t *dst, expr_t *src)
zero_components (block, dst, memset_base, memset_size);
memset_size = 0;
}
auto dst_type = layout->groups[i].type;
auto dst_type = algebra_mvec_type (algebra, 1 << i);
auto dst_alias = new_offset_alias_expr (dst_type, dst, offset);
append_expr (block, new_assign_expr (dst_alias, components[i]));
offset += type_size (dst_type);
memset_base = offset;
} else {
offset += type_size (layout->groups[i].type);
memset_size += type_size (layout->groups[i].type);
if (dstType->type == ev_invalid) {
auto dst_type = algebra_mvec_type (algebra, 1 << i);
offset += type_size (dst_type);
memset_size += type_size (dst_type);
}
}
}
if (memset_size) {

View file

@ -173,6 +173,9 @@ check_types_compatible (expr_t *dst, expr_t *src)
type_t *src_type = get_type (src);
if (dst_type == src_type) {
if (is_algebra (dst_type) || is_algebra (src_type)) {
return algebra_assign_expr (dst, src);
}
return 0;
}

View file

@ -46,6 +46,8 @@ __attribute__((const)) int algebra_type_assignable (const type_t *dst, const typ
int algebra_type_assignable (const type_t *dst, const type_t *src){return 0;}
__attribute__((const)) int is_algebra (const type_t *type);
int is_algebra (const type_t *type){return 0;}
__attribute__((const)) int algebra_base_type (const type_t *type);
int algebra_base_type (const type_t *type){return 0;}
__attribute__((const)) pr_string_t ReuseString (const char *str) {return 0;}
__attribute__((const)) codespace_t *codespace_new (void) {return 0;}

View file

@ -678,6 +678,9 @@ vector_type (const type_t *ele_type, int width)
type_t *
base_type (const type_t *vec_type)
{
if (is_algebra (vec_type)) {
return algebra_base_type (vec_type);
}
if (!is_math (vec_type)) {
return 0;
}
@ -1221,6 +1224,9 @@ is_math (const type_t *type)
if (is_vector (type) || is_quaternion (type)) {
return 1;
}
if (is_algebra (type)) {
return 1;
}
return is_scalar (type) || is_nonscalar (type);
}

View file

@ -17,7 +17,8 @@ main (void)
@algebra (PGA) {
auto p1 = 3*e1 + e2 - e3 + e0;
auto p2 = e1 + 3*e2 + e3 - e0;
pgaf1 = p1 * p2;
auto v = 4*(e1 + e032 + e123);
pgaf1 = p1 + v;// * p2;
#if 0
auto rx = e23;
auto ry = e31;