mie: ir: implement a walker interface for traversing ir structures
This commit is contained in:
42
mie/include/mie/ir/walk.h
Normal file
42
mie/include/mie/ir/walk.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#ifndef MIE_IR_WALK_H_
|
||||
#define MIE_IR_WALK_H_
|
||||
|
||||
#include <mie/misc.h>
|
||||
#include <mie/status.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct mie_op;
|
||||
struct mie_walker;
|
||||
|
||||
enum mie_walker_flags {
|
||||
MIE_WALKER_F_NONE = 0x00u,
|
||||
|
||||
/* 0: default behaviour */
|
||||
MIE_WALKER_F_PREORDER = 0x00u,
|
||||
MIE_WALKER_F_POSTORDER = 0x01u,
|
||||
|
||||
/* 0: default behaviour */
|
||||
MIE_WALKER_F_FORWARD = 0x00u,
|
||||
MIE_WALKER_F_BACKWARD = 0x02u,
|
||||
|
||||
MIE_WALKER_F_INCLUDE_OPS = 0x04u,
|
||||
MIE_WALKER_F_INCLUDE_REGIONS = 0x08u,
|
||||
MIE_WALKER_F_INCLUDE_BLOCKS = 0x10u,
|
||||
};
|
||||
|
||||
struct mie_walker_item {
|
||||
size_t i_depth;
|
||||
struct mie_op *i_op;
|
||||
struct mie_block *i_block;
|
||||
struct mie_region *i_region;
|
||||
};
|
||||
|
||||
MIE_API struct mie_walker *mie_walker_begin(
|
||||
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);
|
||||
|
||||
MIE_API struct mie_walker_item *mie_walker_get(struct mie_walker *walker);
|
||||
|
||||
#endif
|
||||
345
mie/ir/walk.c
Normal file
345
mie/ir/walk.c
Normal file
@@ -0,0 +1,345 @@
|
||||
#include <mie/ir/block.h>
|
||||
#include <mie/ir/op.h>
|
||||
#include <mie/ir/region.h>
|
||||
#include <mie/ir/walk.h>
|
||||
|
||||
#define ITEM_TYPE(f) ((f) & 0x0Fu)
|
||||
|
||||
enum walk_schedule_item_flags {
|
||||
SCHED_ITEM_F_NONE = 0,
|
||||
SCHED_ITEM_F_OP = 0x01u,
|
||||
SCHED_ITEM_F_BLOCK = 0x02u,
|
||||
SCHED_ITEM_F_REGION = 0x04u,
|
||||
|
||||
SCHED_ITEM_F_VISITED = 0x10u,
|
||||
SCHED_ITEM_F_CHILDREN_SCHEDULED = 0x20u,
|
||||
};
|
||||
|
||||
struct walk_schedule_item {
|
||||
enum walk_schedule_item_flags i_flags;
|
||||
b_queue_entry i_entry;
|
||||
size_t i_depth;
|
||||
|
||||
union {
|
||||
struct mie_op *i_op;
|
||||
struct mie_block *i_block;
|
||||
struct mie_region *i_region;
|
||||
};
|
||||
};
|
||||
|
||||
struct mie_walker {
|
||||
enum mie_walker_flags w_flags;
|
||||
struct mie_op *w_root;
|
||||
struct mie_walker_item w_cur;
|
||||
b_queue w_sched;
|
||||
};
|
||||
|
||||
static struct walk_schedule_item *op_schedule_item_create(struct mie_op *op)
|
||||
{
|
||||
struct walk_schedule_item *out = malloc(sizeof *out);
|
||||
if (!out) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(out, 0x0, sizeof *out);
|
||||
|
||||
out->i_flags = SCHED_ITEM_F_OP;
|
||||
out->i_op = op;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static struct walk_schedule_item *block_schedule_item_create(struct mie_block *block)
|
||||
{
|
||||
struct walk_schedule_item *out = malloc(sizeof *out);
|
||||
if (!out) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(out, 0x0, sizeof *out);
|
||||
|
||||
out->i_flags = SCHED_ITEM_F_BLOCK;
|
||||
out->i_block = block;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static struct walk_schedule_item *region_schedule_item_create(
|
||||
struct mie_region *region)
|
||||
{
|
||||
struct walk_schedule_item *out = malloc(sizeof *out);
|
||||
if (!out) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(out, 0x0, sizeof *out);
|
||||
|
||||
out->i_flags = SCHED_ITEM_F_REGION;
|
||||
out->i_region = region;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool should_ignore_item(
|
||||
const struct mie_walker *walker, const struct walk_schedule_item *item)
|
||||
{
|
||||
switch (ITEM_TYPE(item->i_flags)) {
|
||||
case SCHED_ITEM_F_OP:
|
||||
return (walker->w_flags & MIE_WALKER_F_INCLUDE_OPS) == 0;
|
||||
case SCHED_ITEM_F_BLOCK:
|
||||
return (walker->w_flags & MIE_WALKER_F_INCLUDE_BLOCKS) == 0;
|
||||
case SCHED_ITEM_F_REGION:
|
||||
return (walker->w_flags & MIE_WALKER_F_INCLUDE_REGIONS) == 0;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static void schedule_child(
|
||||
struct mie_walker *walker, struct walk_schedule_item *parent,
|
||||
struct walk_schedule_item *child, b_queue_entry **ep)
|
||||
{
|
||||
#define REVERSE 0x04u
|
||||
|
||||
enum {
|
||||
PREORDER = 0x01u,
|
||||
PREORDER_REVERSE = PREORDER | REVERSE,
|
||||
POSTORDER = 0x02u,
|
||||
POSTORDER_REVERSE = POSTORDER | REVERSE,
|
||||
} mode;
|
||||
|
||||
if (walker->w_flags & MIE_WALKER_F_POSTORDER) {
|
||||
mode = POSTORDER;
|
||||
} else {
|
||||
mode = PREORDER;
|
||||
}
|
||||
|
||||
if (walker->w_flags & MIE_WALKER_F_BACKWARD) {
|
||||
mode |= REVERSE;
|
||||
}
|
||||
|
||||
child->i_depth = parent->i_depth;
|
||||
if (!should_ignore_item(walker, child)) {
|
||||
child->i_depth++;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case PREORDER:
|
||||
b_queue_insert_after(&walker->w_sched, &child->i_entry, *ep);
|
||||
*ep = &child->i_entry;
|
||||
break;
|
||||
case PREORDER_REVERSE:
|
||||
b_queue_insert_after(
|
||||
&walker->w_sched, &child->i_entry, &parent->i_entry);
|
||||
*ep = &parent->i_entry;
|
||||
break;
|
||||
case POSTORDER:
|
||||
if (*ep == &parent->i_entry) {
|
||||
b_queue_insert_before(
|
||||
&walker->w_sched, &child->i_entry, *ep);
|
||||
} else {
|
||||
b_queue_insert_after(
|
||||
&walker->w_sched, &child->i_entry, *ep);
|
||||
}
|
||||
|
||||
*ep = &child->i_entry;
|
||||
break;
|
||||
case POSTORDER_REVERSE:
|
||||
b_queue_insert_before(&walker->w_sched, &child->i_entry, *ep);
|
||||
*ep = &child->i_entry;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
#undef REVERSE
|
||||
}
|
||||
|
||||
static enum mie_status schedule_children_of_region(
|
||||
struct mie_walker *walker, struct walk_schedule_item *item)
|
||||
{
|
||||
struct mie_region *region = item->i_region;
|
||||
|
||||
b_queue_entry *tmp = &item->i_entry;
|
||||
|
||||
for (size_t i = 0; i < MIE_VECTOR_COUNT(region->r_blocks); i++) {
|
||||
struct mie_block *block = region->r_blocks.items[i];
|
||||
struct walk_schedule_item *child
|
||||
= block_schedule_item_create(block);
|
||||
|
||||
schedule_child(walker, item, child, &tmp);
|
||||
}
|
||||
|
||||
return MIE_SUCCESS;
|
||||
}
|
||||
|
||||
static enum mie_status schedule_children_of_block(
|
||||
struct mie_walker *walker, struct walk_schedule_item *item)
|
||||
{
|
||||
struct mie_block *block = item->i_block;
|
||||
|
||||
b_queue_entry *tmp = &item->i_entry;
|
||||
|
||||
for (size_t i = 0; i < MIE_VECTOR_COUNT(block->b_ops); i++) {
|
||||
struct mie_op *op = &block->b_ops.items[i];
|
||||
struct walk_schedule_item *child = op_schedule_item_create(op);
|
||||
|
||||
schedule_child(walker, item, child, &tmp);
|
||||
}
|
||||
|
||||
return MIE_SUCCESS;
|
||||
}
|
||||
|
||||
static enum mie_status schedule_children_of_op(
|
||||
struct mie_walker *walker, struct walk_schedule_item *item)
|
||||
{
|
||||
struct mie_op *op = item->i_op;
|
||||
|
||||
b_queue_entry *tmp = &item->i_entry;
|
||||
|
||||
for (size_t i = 0; i < MIE_VECTOR_COUNT(op->op_regions); i++) {
|
||||
struct mie_region *region = &op->op_regions.items[i];
|
||||
struct walk_schedule_item *child
|
||||
= region_schedule_item_create(region);
|
||||
|
||||
schedule_child(walker, item, child, &tmp);
|
||||
}
|
||||
|
||||
return MIE_SUCCESS;
|
||||
}
|
||||
|
||||
static enum mie_status schedule_children(
|
||||
struct mie_walker *walker, struct walk_schedule_item *item)
|
||||
{
|
||||
if (item->i_flags & SCHED_ITEM_F_CHILDREN_SCHEDULED) {
|
||||
return MIE_SUCCESS;
|
||||
}
|
||||
|
||||
enum mie_status status;
|
||||
|
||||
switch (ITEM_TYPE(item->i_flags)) {
|
||||
case SCHED_ITEM_F_BLOCK:
|
||||
status = schedule_children_of_block(walker, item);
|
||||
break;
|
||||
case SCHED_ITEM_F_REGION:
|
||||
status = schedule_children_of_region(walker, item);
|
||||
break;
|
||||
case SCHED_ITEM_F_OP:
|
||||
status = schedule_children_of_op(walker, item);
|
||||
break;
|
||||
default:
|
||||
status = MIE_ERR_BAD_STATE;
|
||||
break;
|
||||
}
|
||||
|
||||
item->i_flags |= SCHED_ITEM_F_CHILDREN_SCHEDULED;
|
||||
return MIE_SUCCESS;
|
||||
}
|
||||
|
||||
static enum mie_status prepare_postorder_traversal(struct mie_walker *walker)
|
||||
{
|
||||
b_queue_entry *cur = b_queue_last(&walker->w_sched);
|
||||
while (cur) {
|
||||
struct walk_schedule_item *item
|
||||
= b_unbox(struct walk_schedule_item, cur, i_entry);
|
||||
schedule_children(walker, item);
|
||||
|
||||
cur = b_queue_prev(cur);
|
||||
}
|
||||
|
||||
return MIE_SUCCESS;
|
||||
}
|
||||
|
||||
struct mie_walker *mie_walker_begin(struct mie_op *op, enum mie_walker_flags flags)
|
||||
{
|
||||
struct mie_walker *out = malloc(sizeof *out);
|
||||
if (!out) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(out, 0x0, sizeof *out);
|
||||
|
||||
out->w_flags = flags;
|
||||
out->w_root = op;
|
||||
|
||||
struct walk_schedule_item *item = op_schedule_item_create(op);
|
||||
b_queue_push_back(&out->w_sched, &item->i_entry);
|
||||
|
||||
if (flags & MIE_WALKER_F_POSTORDER) {
|
||||
prepare_postorder_traversal(out);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void mie_walker_end(struct mie_walker *walker)
|
||||
{
|
||||
}
|
||||
|
||||
static struct walk_schedule_item *current_item(struct mie_walker *walker)
|
||||
{
|
||||
b_queue_entry *top = b_queue_first(&walker->w_sched);
|
||||
if (!top) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return b_unbox(struct walk_schedule_item, top, i_entry);
|
||||
}
|
||||
|
||||
static enum mie_status step(struct mie_walker *walker)
|
||||
{
|
||||
struct walk_schedule_item *cur = current_item(walker);
|
||||
if (!cur) {
|
||||
return MIE_ERR_NO_DATA;
|
||||
}
|
||||
|
||||
schedule_children(walker, cur);
|
||||
|
||||
b_queue_delete(&walker->w_sched, &cur->i_entry);
|
||||
free(cur);
|
||||
|
||||
if (!current_item(walker)) {
|
||||
return MIE_ERR_NO_DATA;
|
||||
}
|
||||
|
||||
return MIE_SUCCESS;
|
||||
}
|
||||
|
||||
enum mie_status mie_walker_step(struct mie_walker *walker)
|
||||
{
|
||||
struct walk_schedule_item *cur = current_item(walker);
|
||||
do {
|
||||
step(walker);
|
||||
cur = current_item(walker);
|
||||
} while (cur && should_ignore_item(walker, cur));
|
||||
|
||||
return cur ? MIE_SUCCESS : MIE_ERR_NO_DATA;
|
||||
}
|
||||
|
||||
struct mie_walker_item *mie_walker_get(struct mie_walker *walker)
|
||||
{
|
||||
struct walk_schedule_item *cur = current_item(walker);
|
||||
if (!cur) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(&walker->w_cur, 0x0, sizeof walker->w_cur);
|
||||
|
||||
walker->w_cur.i_depth = cur->i_depth;
|
||||
|
||||
switch (ITEM_TYPE(cur->i_flags)) {
|
||||
case SCHED_ITEM_F_OP:
|
||||
walker->w_cur.i_op = cur->i_op;
|
||||
break;
|
||||
case SCHED_ITEM_F_BLOCK:
|
||||
walker->w_cur.i_block = cur->i_block;
|
||||
break;
|
||||
case SCHED_ITEM_F_REGION:
|
||||
walker->w_cur.i_region = cur->i_region;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &walker->w_cur;
|
||||
}
|
||||
Reference in New Issue
Block a user