mie: ir: walk: implement non-recursive traversal

struct mie_walker is now a public struct that can (and should) be stack
allocated. this means that non-recursive traversal of an Op's children
uses no dynamically allocated memory.
This commit is contained in:
2026-01-25 14:58:51 +00:00
parent e8534f8d70
commit fe511011ec
2 changed files with 287 additions and 121 deletions

View File

@@ -7,7 +7,6 @@
#include <stddef.h>
struct mie_op;
struct mie_walker;
enum mie_walker_flags {
MIE_WALKER_F_NONE = 0x00u,
@@ -38,7 +37,7 @@ struct mie_walk_item {
int _f;
b_queue_entry _e;
enum mie_walk_item_type i_type;
size_t i_index;
// size_t i_index;
size_t i_depth;
union {
struct mie_op *i_op;
@@ -47,8 +46,21 @@ struct mie_walk_item {
};
};
MIE_API struct mie_walker *mie_walker_begin(
struct mie_op *op, enum mie_walker_flags flags);
struct mie_walker {
enum mie_walker_flags w_flags;
union {
/* used for recursive walks */
b_queue w_stack;
/* used for non-recursive walks */
struct {
struct mie_walk_item w_region, w_block, w_op;
};
};
};
MIE_API void mie_walker_begin(
struct mie_walker *walker, const struct mie_op *op,
enum mie_walker_flags flags);
MIE_API void mie_walker_end(struct mie_walker *walker);
MIE_API enum mie_status mie_walker_step(struct mie_walker *walker);

View File

@@ -11,12 +11,6 @@
#define ITEM_TYPE(f) ((f) & 0x0Fu)
struct mie_walker {
enum mie_walker_flags w_flags;
struct mie_op *w_root;
b_queue w_stack;
};
static bool should_ignore_item(
const struct mie_walker *walker, const struct mie_walk_item *item)
{
@@ -56,15 +50,31 @@ static void pop_walk_item(struct mie_walker *walker)
static struct mie_walk_item *current_item(struct mie_walker *walker)
{
struct mie_walk_item *item = NULL;
bool recursive = (walker->w_flags & MIE_WALKER_F_RECURSIVE) != 0;
if (!recursive) {
if (walker->w_flags & MIE_WALKER_F_INCLUDE_REGIONS) {
item = &walker->w_region;
} else if (walker->w_flags & MIE_WALKER_F_INCLUDE_BLOCKS) {
item = &walker->w_block;
} else {
item = &walker->w_op;
}
bool eof = !item || item->i_type != MIE_WALK_ITEM_NONE;
return eof ? item : NULL;
}
b_queue_entry *entry = b_queue_last(&walker->w_stack);
if (!entry) {
return NULL;
}
struct mie_walk_item *item = b_unbox(struct mie_walk_item, entry, _e);
item = b_unbox(struct mie_walk_item, entry, _e);
return item->i_type != MIE_WALK_ITEM_NONE ? item : NULL;
}
#if 0
static struct mie_op *get_first_child(struct mie_walker *walker, struct mie_op *op)
{
bool backwards = (walker->w_flags & MIE_WALKER_F_BACKWARD) != 0;
@@ -95,16 +105,17 @@ static struct mie_op *get_first_child(struct mie_walker *walker, struct mie_op *
return op;
}
#endif
static size_t walk_item_get_nr_children(const struct mie_walk_item *item)
static bool walk_item_has_children(const struct mie_walk_item *item)
{
switch (item->i_type) {
case MIE_WALK_ITEM_OP:
return MIE_VECTOR_COUNT(item->i_op->op_regions);
return mie_op_get_first_region(item->i_op) != NULL;
case MIE_WALK_ITEM_BLOCK:
return MIE_VECTOR_COUNT(item->i_block->b_ops);
return mie_block_get_first_op(item->i_block) != NULL;
case MIE_WALK_ITEM_REGION:
return MIE_VECTOR_COUNT(item->i_region->r_blocks);
return mie_region_get_first_block(item->i_region) != NULL;
default:
return 0;
}
@@ -128,42 +139,68 @@ static bool should_correct_depth(
return false;
}
static bool walk_item_get_child(
static bool walk_item_get_first_child(
struct mie_walker *walker, const struct mie_walk_item *item,
size_t index, struct mie_walk_item *child)
struct mie_walk_item *child)
{
bool ok = false;
b_queue_entry *entry = NULL;
switch (item->i_type) {
case MIE_WALK_ITEM_OP:
if (MIE_VECTOR_COUNT(item->i_op->op_regions) <= index) {
ok = false;
break;
}
child->i_type = MIE_WALK_ITEM_REGION;
child->i_region = &item->i_op->op_regions.items[index];
ok = true;
child->i_region = mie_op_get_first_region(item->i_op);
ok = child->i_region != NULL;
break;
case MIE_WALK_ITEM_BLOCK:
if (MIE_VECTOR_COUNT(item->i_block->b_ops) <= index) {
ok = false;
break;
}
child->i_type = MIE_WALK_ITEM_OP;
child->i_op = &item->i_block->b_ops.items[index];
ok = true;
child->i_op = mie_block_get_first_op(item->i_block);
ok = child->i_op != NULL;
break;
case MIE_WALK_ITEM_REGION:
if (MIE_VECTOR_COUNT(item->i_region->r_blocks) <= index) {
ok = false;
break;
}
child->i_type = MIE_WALK_ITEM_BLOCK;
child->i_block = &item->i_region->r_blocks.items[index];
ok = true;
child->i_block = mie_region_get_first_block(item->i_region);
ok = child->i_block != NULL;
break;
default:
break;
}
if (!ok) {
return false;
}
child->i_depth = item->i_depth + 1;
if (should_correct_depth(walker, item)) {
child->i_depth--;
}
return true;
}
static bool walk_item_get_last_child(
struct mie_walker *walker, const struct mie_walk_item *item,
struct mie_walk_item *child)
{
bool ok = false;
b_queue_entry *entry = NULL;
switch (item->i_type) {
case MIE_WALK_ITEM_OP:
child->i_type = MIE_WALK_ITEM_REGION;
child->i_region = mie_op_get_last_region(item->i_op);
ok = child->i_region != NULL;
break;
case MIE_WALK_ITEM_BLOCK:
child->i_type = MIE_WALK_ITEM_OP;
child->i_op = mie_block_get_last_op(item->i_block);
ok = child->i_op != NULL;
break;
case MIE_WALK_ITEM_REGION:
child->i_type = MIE_WALK_ITEM_BLOCK;
child->i_block = mie_region_get_last_block(item->i_region);
ok = child->i_block != NULL;
break;
default:
break;
@@ -174,7 +211,6 @@ static bool walk_item_get_child(
}
child->i_depth = item->i_depth + 1;
child->i_index = index;
if (should_correct_depth(walker, item)) {
child->i_depth--;
@@ -192,8 +228,9 @@ static bool walk_item_get_next_sibling(
sibling->i_depth = item->i_depth;
sibling->i_type = item->i_type;
sibling->i_index = item->i_index + 1;
size_t index = 0;
b_queue_entry *entry = NULL;
struct mie_region *parent_r = NULL;
struct mie_block *parent_b = NULL;
struct mie_op *parent_o = NULL;
@@ -205,36 +242,26 @@ static bool walk_item_get_next_sibling(
return false;
}
if (item->i_index + 1 >= MIE_VECTOR_COUNT(parent_b->b_ops)) {
return false;
}
sibling->i_op = &parent_b->b_ops.items[item->i_index + 1];
return true;
sibling->i_op = mie_block_get_next_op(parent_b, item->i_op);
return sibling->i_op != NULL;
case MIE_WALK_ITEM_BLOCK:
parent_r = item->i_block->b_parent;
if (!parent_r) {
return false;
}
if (item->i_index + 1 >= MIE_VECTOR_COUNT(parent_r->r_blocks)) {
return false;
}
sibling->i_block = &parent_r->r_blocks.items[item->i_index + 1];
return true;
sibling->i_block
= mie_region_get_next_block(parent_r, item->i_block);
return sibling->i_block != NULL;
case MIE_WALK_ITEM_REGION:
parent_o = item->i_region->r_parent;
if (!parent_o) {
return false;
}
if (item->i_index + 1 >= MIE_VECTOR_COUNT(parent_o->op_regions)) {
return false;
}
sibling->i_region = &parent_o->op_regions.items[item->i_index + 1];
return true;
sibling->i_region
= mie_op_get_next_region(parent_o, item->i_region);
return sibling->i_region != NULL;
default:
return false;
}
@@ -243,14 +270,14 @@ static bool walk_item_get_next_sibling(
static bool walk_item_get_prev_sibling(
const struct mie_walk_item *item, struct mie_walk_item *sibling)
{
if (item->i_depth == 0 || item->i_index == 0) {
if (item->i_depth == 0) {
return false;
}
sibling->i_depth = item->i_depth;
sibling->i_type = item->i_type;
sibling->i_index = item->i_index - 1;
size_t index = 0;
struct mie_region *parent_r = NULL;
struct mie_block *parent_b = NULL;
struct mie_op *parent_o = NULL;
@@ -262,24 +289,26 @@ static bool walk_item_get_prev_sibling(
return false;
}
sibling->i_op = &parent_b->b_ops.items[item->i_index - 1];
return true;
sibling->i_op = mie_block_get_prev_op(parent_b, item->i_op);
return sibling->i_op != NULL;
case MIE_WALK_ITEM_BLOCK:
parent_r = item->i_block->b_parent;
if (!parent_r) {
return false;
}
sibling->i_block = &parent_r->r_blocks.items[item->i_index - 1];
return true;
sibling->i_block
= mie_region_get_prev_block(parent_r, item->i_block);
return sibling->i_block != NULL;
case MIE_WALK_ITEM_REGION:
parent_o = item->i_region->r_parent;
if (!parent_o) {
return false;
}
sibling->i_region = &parent_o->op_regions.items[item->i_index - 1];
return true;
sibling->i_region
= mie_op_get_prev_region(parent_o, item->i_region);
return sibling->i_region != NULL;
default:
return false;
}
@@ -334,9 +363,9 @@ static bool step_pre_f(struct mie_walker *walker)
struct mie_walk_item next;
bool ok = false;
size_t nr_children = walk_item_get_nr_children(item);
if (nr_children > 0) {
ok = walk_item_get_child(walker, item, 0, &next);
bool has_children = walk_item_has_children(item);
if (has_children) {
ok = walk_item_get_first_child(walker, item, &next);
if (ok) {
push_walk_item(walker, &next);
@@ -384,9 +413,9 @@ static bool step_pre_b(struct mie_walker *walker)
struct mie_walk_item next;
bool ok = false;
size_t nr_children = walk_item_get_nr_children(item);
if (nr_children > 0) {
ok = walk_item_get_child(walker, item, nr_children - 1, &next);
bool has_children = walk_item_has_children(item);
if (has_children) {
ok = walk_item_get_last_child(walker, item, &next);
if (ok) {
push_walk_item(walker, &next);
@@ -437,10 +466,10 @@ static bool step_post_f(struct mie_walker *walker)
while (1) {
item = current_item(walker);
size_t nr_children = walk_item_get_nr_children(item);
bool has_children = walk_item_has_children(item);
if (nr_children > 0 && !(item->_f & WALK_ITEM_F_CHILDREN_VISITED)) {
ok = walk_item_get_child(walker, item, 0, &next);
if (has_children && !(item->_f & WALK_ITEM_F_CHILDREN_VISITED)) {
ok = walk_item_get_first_child(walker, item, &next);
if (ok) {
push_walk_item(walker, &next);
@@ -449,7 +478,7 @@ static bool step_post_f(struct mie_walker *walker)
return ok;
}
if (nr_children == 0 && !(item->_f & WALK_ITEM_F_VISITED)) {
if (!has_children && !(item->_f & WALK_ITEM_F_VISITED)) {
return true;
}
@@ -469,6 +498,55 @@ static bool step_post_f(struct mie_walker *walker)
}
}
static bool step_flat_f(struct mie_walker *walker)
{
struct mie_walk_item *stack[] = {
&walker->w_region,
&walker->w_block,
&walker->w_op,
};
const int stack_size = sizeof stack / sizeof stack[0];
int start;
if (walker->w_flags & MIE_WALKER_F_INCLUDE_REGIONS) {
start = 0;
} else if (walker->w_flags & MIE_WALKER_F_INCLUDE_BLOCKS) {
start = 1;
} else {
start = 2;
}
int i;
for (i = start; i >= 0; i--) {
struct mie_walk_item *item = stack[i];
struct mie_walk_item next;
if (walk_item_get_next_sibling(item, &next)) {
*item = next;
break;
}
}
if (i < 0) {
return false;
}
for (int k = i; k < stack_size - 1; k++) {
struct mie_walk_item *parent = stack[k];
struct mie_walk_item *child = stack[k + 1];
if (!walk_item_get_first_child(walker, parent, child)) {
return false;
}
}
return true;
}
static bool step_flat_b(struct mie_walker *walker)
{
return false;
}
static bool step_post_b(struct mie_walker *walker)
{
/* backward, post-order traversal:
@@ -485,11 +563,10 @@ static bool step_post_b(struct mie_walker *walker)
while (1) {
item = current_item(walker);
size_t nr_children = walk_item_get_nr_children(item);
bool has_children = walk_item_has_children(item);
if (nr_children > 0 && !(item->_f & WALK_ITEM_F_CHILDREN_VISITED)) {
ok = walk_item_get_child(
walker, item, nr_children - 1, &next);
if (has_children && !(item->_f & WALK_ITEM_F_CHILDREN_VISITED)) {
ok = walk_item_get_last_child(walker, item, &next);
if (ok) {
push_walk_item(walker, &next);
@@ -498,7 +575,7 @@ static bool step_post_b(struct mie_walker *walker)
return ok;
}
if (nr_children == 0 && !(item->_f & WALK_ITEM_F_VISITED)) {
if (!has_children && !(item->_f & WALK_ITEM_F_VISITED)) {
return true;
}
@@ -520,25 +597,106 @@ static bool step_post_b(struct mie_walker *walker)
static bool step(struct mie_walker *walker)
{
bool recursive = (walker->w_flags & MIE_WALKER_F_RECURSIVE) != 0;
bool backward = (walker->w_flags & MIE_WALKER_F_BACKWARD) != 0;
bool postorder = (walker->w_flags & MIE_WALKER_F_POSTORDER) != 0;
if (backward) {
if (postorder) {
return step_post_b(walker);
if (!recursive) {
return backward ? step_flat_b(walker) : step_flat_f(walker);
} else if (postorder) {
return backward ? step_post_b(walker) : step_post_f(walker);
} else {
return backward ? step_pre_b(walker) : step_pre_f(walker);
}
return step_pre_b(walker);
}
if (postorder) {
return step_post_f(walker);
}
return step_pre_f(walker);
}
static enum mie_status prepare_preorder_traversal(struct mie_walker *walker)
static enum mie_status prepare_flat_traversal(
struct mie_walker *walker, const struct mie_op *op)
{
bool backward = (walker->w_flags & MIE_WALKER_F_BACKWARD) != 0;
bool ok = false;
enum mie_walk_item_type target_type = MIE_WALK_ITEM_NONE;
enum mie_status status = MIE_SUCCESS;
struct mie_walk_item root = {};
root.i_op = (struct mie_op *)op;
root.i_type = MIE_WALK_ITEM_OP;
if (walker->w_flags & MIE_WALKER_F_INCLUDE_REGIONS) {
target_type = MIE_WALK_ITEM_REGION;
} else if (walker->w_flags & MIE_WALKER_F_INCLUDE_BLOCKS) {
target_type = MIE_WALK_ITEM_BLOCK;
} else {
target_type = MIE_WALK_ITEM_OP;
}
bool has_children = walk_item_has_children(&root);
if (!has_children) {
return MIE_ERR_NO_DATA;
}
if (backward) {
ok = walk_item_get_last_child(walker, &root, &walker->w_region);
status = ok ? MIE_SUCCESS : MIE_ERR_NO_DATA;
} else {
ok = walk_item_get_first_child(walker, &root, &walker->w_region);
status = ok ? MIE_SUCCESS : MIE_ERR_NO_DATA;
}
if (status != MIE_SUCCESS) {
return status;
}
if (target_type == MIE_WALK_ITEM_REGION) {
walker->w_region.i_depth = 1;
return MIE_SUCCESS;
}
has_children = walk_item_has_children(&walker->w_region);
if (!has_children) {
return MIE_ERR_NO_DATA;
}
if (backward) {
ok = walk_item_get_last_child(
walker, &walker->w_region, &walker->w_block);
status = ok ? MIE_SUCCESS : MIE_ERR_NO_DATA;
} else {
ok = walk_item_get_first_child(
walker, &walker->w_region, &walker->w_block);
status = ok ? MIE_SUCCESS : MIE_ERR_NO_DATA;
}
if (status != MIE_SUCCESS) {
return status;
}
if (target_type == MIE_WALK_ITEM_BLOCK) {
walker->w_block.i_depth = 1;
return MIE_SUCCESS;
}
has_children = walk_item_has_children(&walker->w_block);
if (!has_children) {
return MIE_ERR_NO_DATA;
}
if (backward) {
ok = walk_item_get_last_child(
walker, &walker->w_block, &walker->w_op);
status = ok ? MIE_SUCCESS : MIE_ERR_NO_DATA;
} else {
ok = walk_item_get_first_child(
walker, &walker->w_block, &walker->w_op);
status = ok ? MIE_SUCCESS : MIE_ERR_NO_DATA;
}
walker->w_op.i_depth = 1;
return status;
}
static enum mie_status prepare_preorder_traversal(
struct mie_walker *walker, const struct mie_op *root)
{
struct mie_walk_item *item = malloc(sizeof *item);
if (!item) {
@@ -546,7 +704,7 @@ static enum mie_status prepare_preorder_traversal(struct mie_walker *walker)
}
memset(item, 0x0, sizeof *item);
item->i_op = walker->w_root;
item->i_op = (struct mie_op *)root;
item->i_type = MIE_WALK_ITEM_OP;
push_walk_item(walker, item);
@@ -554,10 +712,11 @@ static enum mie_status prepare_preorder_traversal(struct mie_walker *walker)
return MIE_SUCCESS;
}
static enum mie_status prepare_postorder_traversal(struct mie_walker *walker)
static enum mie_status prepare_postorder_traversal(
struct mie_walker *walker, const struct mie_op *root)
{
struct mie_walk_item item = {};
item.i_op = walker->w_root;
item.i_op = (struct mie_op *)root;
item.i_type = MIE_WALK_ITEM_OP;
push_walk_item(walker, &item);
@@ -568,18 +727,17 @@ static enum mie_status prepare_postorder_traversal(struct mie_walker *walker)
while (1) {
struct mie_walk_item *current = current_item(walker);
struct mie_walk_item child;
size_t nr_children = walk_item_get_nr_children(current);
if (!nr_children) {
bool has_children = walk_item_has_children(current);
if (!has_children) {
break;
}
bool ok = false;
if (backward) {
ok = walk_item_get_child(
walker, current, nr_children - 1, &child);
ok = walk_item_get_last_child(walker, current, &child);
} else {
ok = walk_item_get_child(walker, current, 0, &child);
ok = walk_item_get_first_child(walker, current, &child);
}
if (!ok) {
@@ -592,30 +750,26 @@ static enum mie_status prepare_postorder_traversal(struct mie_walker *walker)
return ok ? MIE_SUCCESS : MIE_ERR_NO_DATA;
}
struct mie_walker *mie_walker_begin(struct mie_op *op, enum mie_walker_flags flags)
void mie_walker_begin(
struct mie_walker *walker, const struct mie_op *op,
enum mie_walker_flags flags)
{
struct mie_walker *out = malloc(sizeof *out);
if (!out) {
return NULL;
}
memset(walker, 0x0, sizeof *walker);
memset(out, 0x0, sizeof *out);
walker->w_flags = flags;
out->w_flags = flags;
out->w_root = op;
if (flags & MIE_WALKER_F_POSTORDER) {
prepare_postorder_traversal(out);
if (!(flags & MIE_WALKER_F_RECURSIVE)) {
prepare_flat_traversal(walker, op);
} else if (flags & MIE_WALKER_F_POSTORDER) {
prepare_postorder_traversal(walker, op);
} else {
prepare_preorder_traversal(out);
prepare_preorder_traversal(walker, op);
}
struct mie_walk_item *cur = current_item(out);
if (should_ignore_item(out, cur)) {
mie_walker_step(out);
struct mie_walk_item *cur = current_item(walker);
if (cur && should_ignore_item(walker, cur)) {
mie_walker_step(walker);
}
return out;
}
void mie_walker_end(struct mie_walker *walker)
@@ -631,9 +785,9 @@ enum mie_status mie_walker_step(struct mie_walker *walker)
do {
ok = step(walker);
cur = current_item(walker);
} while (cur && should_ignore_item(walker, cur));
} while (ok && cur && should_ignore_item(walker, cur));
return cur ? MIE_SUCCESS : MIE_ERR_NO_DATA;
return (ok && cur) ? MIE_SUCCESS : MIE_ERR_NO_DATA;
}
struct mie_walk_item *mie_walker_get(struct mie_walker *walker)