#include "vec/vec.h"
#include "idiom.h"
#include "opt.h"
#include "mem/heap.h"
#include <string.h>
static inline ray_op_t* op_node(ray_graph_t* g, uint32_t id) {
return id == RAY_OP_NONE ? NULL : &g->nodes[id];
}
static inline ray_op_t* op_child(ray_graph_t* g, const ray_op_t* op, int i) {
return op_node(g, op->in_id[i]);
}
#define RAY_IDIOM_OPCODE_CAP 128
#define RAY_IDIOM_MAX_ROWS 64
static ray_op_t* rw_count_distinct(ray_graph_t* g, ray_op_t* node) {
ray_op_t* distinct = op_child(g, node, 0);
if (!distinct || !op_child(g, distinct, 0)) return NULL;
ray_op_t* src = op_child(g, distinct, 0);
ray_op_t* repl = graph_alloc_node_opt(g);
if (!repl) return NULL;
repl->opcode = OP_COUNT_DISTINCT;
repl->arity = 1;
repl->in_id[0] = src ? src->id : RAY_OP_NONE;
repl->out_type = RAY_I64;
repl->est_rows = 1;
return repl;
}
static ray_op_t* rw_count_passthrough(ray_graph_t* g, ray_op_t* node) {
ray_op_t* inner = op_child(g, node, 0);
if (!inner || !op_child(g, inner, 0)) return NULL;
ray_op_t* src = op_child(g, inner, 0);
ray_op_t* repl = graph_alloc_node_opt(g);
if (!repl) return NULL;
repl->opcode = OP_COUNT;
repl->arity = 1;
repl->in_id[0] = src ? src->id : RAY_OP_NONE;
repl->out_type = RAY_I64;
repl->est_rows = 1;
return repl;
}
static ray_t* scan_source_col(ray_graph_t* g, ray_op_t* src) {
if (!g || !src || src->opcode != OP_SCAN) return NULL;
ray_op_ext_t* ext = find_ext(g, src->id);
if (!ext) return NULL;
uint16_t stored_table_id = 0;
memcpy(&stored_table_id, src->pad, sizeof(uint16_t));
ray_t* tbl = NULL;
if (stored_table_id > 0) {
uint16_t table_id = (uint16_t)(stored_table_id - 1);
if (g->tables && table_id < g->n_tables)
tbl = g->tables[table_id];
} else {
tbl = g->table;
}
if (!tbl) return NULL;
return ray_table_get_col(tbl, ext->sym);
}
static bool pre_no_nulls_on_asc_input(ray_graph_t* g, ray_op_t* node) {
ray_op_t* asc = op_child(g, node, 0);
if (!asc || !op_child(g, asc, 0)) return false;
ray_op_t* src = op_child(g, asc, 0);
if (src->opcode == OP_CONST) {
ray_op_ext_t* ext = find_ext(g, src->id);
if (!ext || !ext->literal) return false;
ray_t* lit = ext->literal;
if (!ray_is_vec(lit)) return false;
return !ray_vec_has_nulls(lit);
}
if (src->opcode == OP_SCAN) {
ray_t* col = scan_source_col(g, src);
if (!col) return false;
if (!ray_is_vec(col) && !RAY_IS_PARTED(col->type)) return false;
return !ray_vec_has_nulls(col);
}
return false;
}
static ray_op_t* rw_first_asc_to_min(ray_graph_t* g, ray_op_t* node) {
ray_op_t* asc = op_child(g, node, 0);
if (!asc || !op_child(g, asc, 0)) return NULL;
ray_op_t* src = op_child(g, asc, 0);
ray_op_t* repl = graph_alloc_node_opt(g);
if (!repl) return NULL;
repl->opcode = OP_MIN;
repl->arity = 1;
repl->in_id[0] = src ? src->id : RAY_OP_NONE;
repl->out_type = src->out_type;
repl->est_rows = 1;
return repl;
}
static ray_op_t* rw_last_asc_to_max(ray_graph_t* g, ray_op_t* node) {
ray_op_t* asc = op_child(g, node, 0);
if (!asc || !op_child(g, asc, 0)) return NULL;
ray_op_t* src = op_child(g, asc, 0);
ray_op_t* repl = graph_alloc_node_opt(g);
if (!repl) return NULL;
repl->opcode = OP_MAX;
repl->arity = 1;
repl->in_id[0] = src ? src->id : RAY_OP_NONE;
repl->out_type = src->out_type;
repl->est_rows = 1;
return repl;
}
const ray_idiom_t ray_idioms[] = {
{ OP_COUNT, OP_DISTINCT, NULL, rw_count_distinct, "count(distinct) -> count_distinct" },
{ OP_COUNT, OP_ASC, NULL, rw_count_passthrough, "count(asc) -> count" },
{ OP_COUNT, OP_DESC, NULL, rw_count_passthrough, "count(desc) -> count" },
{ OP_COUNT, OP_REVERSE, NULL, rw_count_passthrough, "count(reverse) -> count" },
{ OP_FIRST, OP_ASC, pre_no_nulls_on_asc_input, rw_first_asc_to_min, "first(asc) -> min [no-nulls]" },
{ OP_LAST, OP_ASC, pre_no_nulls_on_asc_input, rw_last_asc_to_max, "last(asc) -> max [no-nulls]" },
};
const int ray_idioms_count = (int)(sizeof(ray_idioms) / sizeof(ray_idioms[0]));
_Static_assert(sizeof(ray_idioms) / sizeof(ray_idioms[0]) <= RAY_IDIOM_MAX_ROWS,
"idiom row count exceeds dispatch index capacity");
static int8_t first_idiom[RAY_IDIOM_OPCODE_CAP];
static int8_t next_idiom [RAY_IDIOM_MAX_ROWS];
static bool index_built;
static void build_index(void) {
if (index_built) return;
memset(first_idiom, -1, sizeof(first_idiom));
memset(next_idiom, -1, sizeof(next_idiom));
for (int i = 0; i < ray_idioms_count; i++) {
uint16_t op = ray_idioms[i].root_op;
if (op >= RAY_IDIOM_OPCODE_CAP) continue;
next_idiom[i] = first_idiom[op];
first_idiom[op] = (int8_t)i;
}
index_built = true;
}
static bool is_ext_root(uint16_t opcode) {
return opcode == OP_GROUP || opcode == OP_SORT || opcode == OP_JOIN ||
opcode == OP_WINDOW || opcode == OP_WINDOW_JOIN || opcode == OP_SELECT;
}
static ray_op_t* try_rewrite(ray_graph_t* g, ray_op_t* node) {
if (!node || (node->flags & OP_FLAG_DEAD)) return NULL;
if (is_ext_root(node->opcode)) return NULL;
if (node->opcode >= RAY_IDIOM_OPCODE_CAP) return NULL;
int idx = first_idiom[node->opcode];
while (idx >= 0) {
const ray_idiom_t* row = &ray_idioms[idx];
ray_op_t* c0 = op_child(g, node, 0);
if (c0 && c0->opcode == row->child0_op) {
if (!row->pre || row->pre(g, node)) {
ray_op_t* repl = row->rewrite(g, node);
if (repl) {
redirect_consumers(g, node->id, repl, UINT32_MAX, UINT32_MAX);
node->flags |= OP_FLAG_DEAD;
return repl;
}
}
}
idx = next_idiom[idx];
}
return NULL;
}
ray_op_t* ray_idiom_pass(ray_graph_t* g, ray_op_t* root) {
if (!g || !root || g->node_count == 0) return root;
build_index();
uint32_t nc = g->node_count;
if (nc > UINT32_MAX / 4) return root;
uint32_t cap = nc * 2;
uint32_t stk1_local[256], stk2_local[256];
uint32_t* stk1 = cap <= 256 ? stk1_local : (uint32_t*)ray_alloc_raw(cap * sizeof(uint32_t));
uint32_t* stk2 = cap <= 256 ? stk2_local : (uint32_t*)ray_alloc_raw(cap * sizeof(uint32_t));
if (!stk1 || !stk2) {
if (stk1 && stk1 != stk1_local) ray_free_raw(stk1);
if (stk2 && stk2 != stk2_local) ray_free_raw(stk2);
return root;
}
uint8_t visited_local[256];
uint8_t* visited = nc <= 256 ? visited_local : (uint8_t*)ray_alloc_raw(nc);
if (!visited) {
if (stk1 != stk1_local) ray_free_raw(stk1);
if (stk2 != stk2_local) ray_free_raw(stk2);
return root;
}
memset(visited, 0, nc);
int sp1 = 0, sp2 = 0;
stk1[sp1++] = root->id;
while (sp1 > 0) {
uint32_t nid = stk1[--sp1];
if (nid >= nc || visited[nid]) continue;
visited[nid] = 1;
stk2[sp2++] = nid;
ray_op_t* n = &g->nodes[nid];
if (n->flags & OP_FLAG_DEAD) continue;
for (int i = 0; i < n->arity && i < 2; i++) {
ray_op_t* ci = op_child(g, n, i);
if (ci && sp1 < (int)cap)
stk1[sp1++] = ci->id;
}
}
uint32_t root_id = root->id;
while (sp2 > 0) {
uint32_t nid = stk2[--sp2];
ray_op_t* repl = try_rewrite(g, &g->nodes[nid]);
if (repl && nid == root_id) {
root = repl;
root_id = repl->id;
}
}
if (visited != visited_local) ray_free_raw(visited);
if (stk1 != stk1_local) ray_free_raw(stk1);
if (stk2 != stk2_local) ray_free_raw(stk2);
return root;
}