Compare commits
11 Commits
58bd336eb8
...
e9d2c0fbc7
| Author | SHA1 | Date | |
|---|---|---|---|
| e9d2c0fbc7 | |||
| a710ef0b24 | |||
| 0277931ca1 | |||
| ac7860b6bd | |||
| 29984307aa | |||
| ac96248d7e | |||
| 593eda2797 | |||
| d0ac8a9fed | |||
| 89ebbcc462 | |||
| 554a1e7342 | |||
| 6d1e308ff1 |
@@ -84,17 +84,17 @@ struct mie_attribute *mie_ctx_get_index(struct mie_ctx *ctx, size_t val)
|
|||||||
MIE_DIALECT_BEGIN(mie_builtin, struct builtin_dialect, "builtin")
|
MIE_DIALECT_BEGIN(mie_builtin, struct builtin_dialect, "builtin")
|
||||||
MIE_DIALECT_INIT(init);
|
MIE_DIALECT_INIT(init);
|
||||||
MIE_DIALECT_CLEANUP(cleanup);
|
MIE_DIALECT_CLEANUP(cleanup);
|
||||||
MIE_DIALECT_ADD_OP(mie_builtin_module);
|
MIE_DIALECT_ADD_TRAIT(mie_builtin_isolated_from_above);
|
||||||
MIE_DIALECT_ADD_TYPE(mie_builtin_string);
|
MIE_DIALECT_ADD_TRAIT(mie_builtin_symbol_table);
|
||||||
MIE_DIALECT_ADD_TYPE(mie_builtin_int);
|
MIE_DIALECT_ADD_TYPE(mie_builtin_int);
|
||||||
MIE_DIALECT_ADD_TYPE(mie_builtin_float);
|
MIE_DIALECT_ADD_TYPE(mie_builtin_float);
|
||||||
MIE_DIALECT_ADD_TYPE(mie_builtin_index);
|
MIE_DIALECT_ADD_TYPE(mie_builtin_index);
|
||||||
MIE_DIALECT_ADD_ATTRIBUTE(mie_builtin_string);
|
MIE_DIALECT_ADD_TYPE(mie_builtin_string);
|
||||||
MIE_DIALECT_ADD_ATTRIBUTE(mie_builtin_int);
|
MIE_DIALECT_ADD_ATTRIBUTE(mie_builtin_int);
|
||||||
MIE_DIALECT_ADD_ATTRIBUTE(mie_builtin_float);
|
MIE_DIALECT_ADD_ATTRIBUTE(mie_builtin_float);
|
||||||
MIE_DIALECT_ADD_ATTRIBUTE(mie_builtin_array);
|
|
||||||
MIE_DIALECT_ADD_ATTRIBUTE(mie_builtin_type);
|
MIE_DIALECT_ADD_ATTRIBUTE(mie_builtin_type);
|
||||||
MIE_DIALECT_ADD_TRAIT(mie_builtin_isolated_from_above);
|
MIE_DIALECT_ADD_ATTRIBUTE(mie_builtin_string);
|
||||||
MIE_DIALECT_ADD_TRAIT(mie_builtin_symbol_table);
|
|
||||||
MIE_DIALECT_ADD_INTERFACE(mie_builtin_symbol);
|
MIE_DIALECT_ADD_INTERFACE(mie_builtin_symbol);
|
||||||
|
MIE_DIALECT_ADD_ATTRIBUTE(mie_builtin_array);
|
||||||
|
MIE_DIALECT_ADD_OP(mie_builtin_module);
|
||||||
MIE_DIALECT_END()
|
MIE_DIALECT_END()
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#include <mie/ctx.h>
|
||||||
#include <mie/dialect/dialect.h>
|
#include <mie/dialect/dialect.h>
|
||||||
#include <mie/ir/op-definition.h>
|
#include <mie/ir/op-definition.h>
|
||||||
#include <mie/ir/op.h>
|
#include <mie/ir/op.h>
|
||||||
@@ -26,4 +27,5 @@ static enum mie_status parse(struct mie_parser *parser, struct mie_op *out)
|
|||||||
MIE_OP_DEFINITION_BEGIN(mie_builtin_module, "module")
|
MIE_OP_DEFINITION_BEGIN(mie_builtin_module, "module")
|
||||||
MIE_OP_DEFINITION_PRINT(print);
|
MIE_OP_DEFINITION_PRINT(print);
|
||||||
MIE_OP_DEFINITION_PARSE(parse);
|
MIE_OP_DEFINITION_PARSE(parse);
|
||||||
|
MIE_OP_DEFINITION_TRAIT("builtin", "isolated-from-above");
|
||||||
MIE_OP_DEFINITION_END()
|
MIE_OP_DEFINITION_END()
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ struct mie_op *mie_func_put_func(
|
|||||||
MIE_OP_DEFINITION_BEGIN(mie_func_func, "func")
|
MIE_OP_DEFINITION_BEGIN(mie_func_func, "func")
|
||||||
MIE_OP_DEFINITION_PRINT(print);
|
MIE_OP_DEFINITION_PRINT(print);
|
||||||
MIE_OP_DEFINITION_PARSE(parse);
|
MIE_OP_DEFINITION_PARSE(parse);
|
||||||
|
MIE_OP_DEFINITION_TRAIT("builtin", "isolated-from-above");
|
||||||
MIE_OP_INTERFACE_BEGIN("builtin", "symbol", struct mie_symbol)
|
MIE_OP_INTERFACE_BEGIN("builtin", "symbol", struct mie_symbol)
|
||||||
MIE_OP_INTERFACE_FUNC(sym_get_name) = NULL;
|
MIE_OP_INTERFACE_FUNC(sym_get_name) = NULL;
|
||||||
MIE_OP_INTERFACE_END()
|
MIE_OP_INTERFACE_END()
|
||||||
|
|||||||
@@ -1,9 +1,45 @@
|
|||||||
#include <mie/dialect/dialect.h>
|
#include <mie/dialect/dialect.h>
|
||||||
|
#include <mie/ir/builder.h>
|
||||||
#include <mie/ir/op-definition.h>
|
#include <mie/ir/op-definition.h>
|
||||||
|
#include <mie/ir/op.h>
|
||||||
|
#include <mie/ir/region.h>
|
||||||
#include <mie/macros.h>
|
#include <mie/macros.h>
|
||||||
|
#include <mie/print/printer.h>
|
||||||
|
|
||||||
static enum mie_status print(struct mie_printer *printer, const struct mie_op *op)
|
static enum mie_status print(struct mie_printer *printer, const struct mie_op *op)
|
||||||
{
|
{
|
||||||
|
b_stream_write_char(printer->p_stream, ' ');
|
||||||
|
mie_printer_print_op_arg(printer, &op->op_args.items[0], false);
|
||||||
|
|
||||||
|
if (MIE_VECTOR_COUNT(op->op_result) > 0) {
|
||||||
|
b_stream_write_string(printer->p_stream, " -> ", NULL);
|
||||||
|
|
||||||
|
if (MIE_VECTOR_COUNT(op->op_result) > 1) {
|
||||||
|
b_stream_write_char(printer->p_stream, '(');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < MIE_VECTOR_COUNT(op->op_result); i++) {
|
||||||
|
if (i > 0) {
|
||||||
|
b_stream_write_string(printer->p_stream, ", ", NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
mie_printer_print_type(printer, op->op_result.items[i].reg_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MIE_VECTOR_COUNT(op->op_result) > 1) {
|
||||||
|
b_stream_write_char(printer->p_stream, '(');
|
||||||
|
}
|
||||||
|
|
||||||
|
b_stream_write_char(printer->p_stream, ' ');
|
||||||
|
|
||||||
|
mie_printer_print_region(printer, &op->op_regions.items[0], 0);
|
||||||
|
|
||||||
|
if (MIE_VECTOR_COUNT(op->op_regions) > 1) {
|
||||||
|
b_stream_write_string(printer->p_stream, " else ", NULL);
|
||||||
|
mie_printer_print_region(printer, &op->op_regions.items[1], 0);
|
||||||
|
}
|
||||||
|
|
||||||
return MIE_SUCCESS;
|
return MIE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12,6 +48,35 @@ static enum mie_status parse(struct mie_parser *parser, struct mie_op *out)
|
|||||||
return MIE_SUCCESS;
|
return MIE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct mie_op *mie_scf_put_if(
|
||||||
|
struct mie_builder *builder, struct mie_register *cond,
|
||||||
|
const struct mie_type *result_type, struct mie_region **out_true,
|
||||||
|
struct mie_region **out_false, const char *name)
|
||||||
|
{
|
||||||
|
struct mie_op *op = mie_builder_put_op(builder, "scf", "if", &cond, 1);
|
||||||
|
if (!op) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result_type) {
|
||||||
|
struct mie_register *result = mie_op_add_result(op, result_type);
|
||||||
|
mie_builder_put_name(builder, &result->reg_name, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct mie_region *r_true = mie_op_add_region(op);
|
||||||
|
struct mie_region *r_false = mie_op_add_region(op);
|
||||||
|
|
||||||
|
if (out_true) {
|
||||||
|
*out_true = r_true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out_false) {
|
||||||
|
*out_false = r_false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return op;
|
||||||
|
}
|
||||||
|
|
||||||
MIE_OP_DEFINITION_BEGIN(mie_scf_if, "if")
|
MIE_OP_DEFINITION_BEGIN(mie_scf_if, "if")
|
||||||
MIE_OP_DEFINITION_PRINT(print);
|
MIE_OP_DEFINITION_PRINT(print);
|
||||||
MIE_OP_DEFINITION_PARSE(parse);
|
MIE_OP_DEFINITION_PARSE(parse);
|
||||||
|
|||||||
@@ -1,9 +1,32 @@
|
|||||||
#include <mie/dialect/dialect.h>
|
#include <mie/dialect/dialect.h>
|
||||||
|
#include <mie/ir/builder.h>
|
||||||
#include <mie/ir/op-definition.h>
|
#include <mie/ir/op-definition.h>
|
||||||
|
#include <mie/ir/op.h>
|
||||||
#include <mie/macros.h>
|
#include <mie/macros.h>
|
||||||
|
#include <mie/print/printer.h>
|
||||||
|
|
||||||
static enum mie_status print(struct mie_printer *printer, const struct mie_op *op)
|
static enum mie_status print(struct mie_printer *printer, const struct mie_op *op)
|
||||||
{
|
{
|
||||||
|
b_stream_write_char(printer->p_stream, ' ');
|
||||||
|
for (size_t i = 0; i < MIE_VECTOR_COUNT(op->op_args); i++) {
|
||||||
|
if (i > 0) {
|
||||||
|
b_stream_write_string(printer->p_stream, ", ", NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
mie_printer_print_op_arg(printer, &op->op_args.items[i], false);
|
||||||
|
}
|
||||||
|
|
||||||
|
b_stream_write_string(printer->p_stream, " : ", NULL);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < MIE_VECTOR_COUNT(op->op_args); i++) {
|
||||||
|
if (i > 0) {
|
||||||
|
b_stream_write_string(printer->p_stream, ", ", NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
mie_printer_print_type(
|
||||||
|
printer, mie_op_arg_get_type(&op->op_args.items[i]));
|
||||||
|
}
|
||||||
|
|
||||||
return MIE_SUCCESS;
|
return MIE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12,6 +35,12 @@ static enum mie_status parse(struct mie_parser *parser, struct mie_op *out)
|
|||||||
return MIE_SUCCESS;
|
return MIE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct mie_op *mie_scf_put_yield(
|
||||||
|
struct mie_builder *builder, struct mie_register *value)
|
||||||
|
{
|
||||||
|
return mie_builder_put_op(builder, "scf", "yield", &value, 1);
|
||||||
|
}
|
||||||
|
|
||||||
MIE_OP_DEFINITION_BEGIN(mie_scf_yield, "yield")
|
MIE_OP_DEFINITION_BEGIN(mie_scf_yield, "yield")
|
||||||
MIE_OP_DEFINITION_PRINT(print);
|
MIE_OP_DEFINITION_PRINT(print);
|
||||||
MIE_OP_DEFINITION_PARSE(parse);
|
MIE_OP_DEFINITION_PARSE(parse);
|
||||||
|
|||||||
@@ -4,8 +4,19 @@
|
|||||||
#include <mie/misc.h>
|
#include <mie/misc.h>
|
||||||
|
|
||||||
struct mie_ctx;
|
struct mie_ctx;
|
||||||
|
struct mie_type;
|
||||||
|
struct mie_region;
|
||||||
struct mie_dialect;
|
struct mie_dialect;
|
||||||
|
struct mie_builder;
|
||||||
|
struct mie_register;
|
||||||
|
|
||||||
MIE_API struct mie_dialect *mie_scf_dialect_create(struct mie_ctx *ctx);
|
MIE_API struct mie_dialect *mie_scf_dialect_create(struct mie_ctx *ctx);
|
||||||
|
|
||||||
|
MIE_API struct mie_op *mie_scf_put_if(
|
||||||
|
struct mie_builder *builder, struct mie_register *cond,
|
||||||
|
const struct mie_type *result_type, struct mie_region **out_true,
|
||||||
|
struct mie_region **out_false, const char *name);
|
||||||
|
MIE_API struct mie_op *mie_scf_put_yield(
|
||||||
|
struct mie_builder *builder, struct mie_register *value);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ struct mie_op {
|
|||||||
const struct mie_dialect *op_dialect;
|
const struct mie_dialect *op_dialect;
|
||||||
const struct mie_op_definition *op_info;
|
const struct mie_op_definition *op_info;
|
||||||
|
|
||||||
|
struct mie_block *op_container;
|
||||||
struct mie_file_span op_name_span;
|
struct mie_file_span op_name_span;
|
||||||
/* only valid if the F_RESOLVED flag is NOT set */
|
/* only valid if the F_RESOLVED flag is NOT set */
|
||||||
char *op_name;
|
char *op_name;
|
||||||
|
|||||||
@@ -1,6 +1,60 @@
|
|||||||
#ifndef MIE_IR_REWRITE_H_
|
#ifndef MIE_IR_REWRITE_H_
|
||||||
#define MIE_IR_REWRITE_H_
|
#define MIE_IR_REWRITE_H_
|
||||||
|
|
||||||
|
#include <mie/misc.h>
|
||||||
|
#include <mie/status.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
struct mie_op;
|
||||||
|
struct mie_ctx;
|
||||||
struct mie_rewriter;
|
struct mie_rewriter;
|
||||||
|
struct mie_register;
|
||||||
|
|
||||||
|
#define MIE_REWRITE_RESULT(result, status) \
|
||||||
|
((struct mie_rewrite_result) {.r_result = (result), .r_status = (status)})
|
||||||
|
|
||||||
|
enum mie_match_result {
|
||||||
|
MIE_NO_MATCH_FOUND = 0,
|
||||||
|
MIE_MATCH_FOUND,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mie_rewrite_result {
|
||||||
|
enum {
|
||||||
|
MIE_REWRITE_SUCCESS = 0,
|
||||||
|
MIE_REWRITE_IGNORE,
|
||||||
|
MIE_REWRITE_FAILURE,
|
||||||
|
} r_result;
|
||||||
|
enum mie_status r_status;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mie_rewrite_pattern {
|
||||||
|
struct {
|
||||||
|
const char *t_dialect_name, *t_op_name;
|
||||||
|
const struct mie_op_definition *t_op;
|
||||||
|
} p_root;
|
||||||
|
|
||||||
|
enum mie_match_result (*p_match)(const struct mie_op *);
|
||||||
|
struct mie_rewrite_result (*p_rewrite)(
|
||||||
|
struct mie_op *, struct mie_rewriter *);
|
||||||
|
};
|
||||||
|
|
||||||
|
MIE_API struct mie_rewriter *mie_rewriter_create(struct mie_ctx *ctx);
|
||||||
|
|
||||||
|
MIE_API struct mie_block *mie_rewriter_get_insertion_block(
|
||||||
|
struct mie_rewriter *rewriter);
|
||||||
|
MIE_API struct mie_op *mie_rewriter_get_insertion_point(
|
||||||
|
struct mie_rewriter *rewriter);
|
||||||
|
|
||||||
|
MIE_API struct mie_block *mie_rewriter_split_block(
|
||||||
|
struct mie_rewriter *rewriter, struct mie_block *block,
|
||||||
|
struct mie_op *before);
|
||||||
|
MIE_API struct mie_block *mie_rewriter_create_block(
|
||||||
|
struct mie_rewriter *rewriter, struct mie_block *insert_before);
|
||||||
|
|
||||||
|
MIE_API struct mie_op *mie_rewriter_put_op(
|
||||||
|
struct mie_rewriter *rewriter, const char *dialect, const char *op,
|
||||||
|
struct mie_register **args, size_t nr_args);
|
||||||
|
MIE_API enum mie_status mie_rewriter_erase_op(
|
||||||
|
struct mie_rewriter *rewriter, struct mie_op *op);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#ifndef MIE_IR_WALK_H_
|
#ifndef MIE_IR_WALK_H_
|
||||||
#define MIE_IR_WALK_H_
|
#define MIE_IR_WALK_H_
|
||||||
|
|
||||||
|
#include <blue/core/queue.h>
|
||||||
#include <mie/misc.h>
|
#include <mie/misc.h>
|
||||||
#include <mie/status.h>
|
#include <mie/status.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
@@ -26,11 +27,24 @@ enum mie_walker_flags {
|
|||||||
MIE_WALKER_F_INCLUDE_BLOCKS = 0x20u,
|
MIE_WALKER_F_INCLUDE_BLOCKS = 0x20u,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mie_walker_item {
|
enum mie_walk_item_type {
|
||||||
|
MIE_WALK_ITEM_NONE = 0,
|
||||||
|
MIE_WALK_ITEM_OP,
|
||||||
|
MIE_WALK_ITEM_BLOCK,
|
||||||
|
MIE_WALK_ITEM_REGION,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mie_walk_item {
|
||||||
|
int _f;
|
||||||
|
b_queue_entry _e;
|
||||||
|
enum mie_walk_item_type i_type;
|
||||||
|
size_t i_index;
|
||||||
size_t i_depth;
|
size_t i_depth;
|
||||||
struct mie_op *i_op;
|
union {
|
||||||
struct mie_block *i_block;
|
struct mie_op *i_op;
|
||||||
struct mie_region *i_region;
|
struct mie_block *i_block;
|
||||||
|
struct mie_region *i_region;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
MIE_API struct mie_walker *mie_walker_begin(
|
MIE_API struct mie_walker *mie_walker_begin(
|
||||||
@@ -39,6 +53,6 @@ MIE_API void mie_walker_end(struct mie_walker *walker);
|
|||||||
|
|
||||||
MIE_API enum mie_status mie_walker_step(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);
|
MIE_API struct mie_walk_item *mie_walker_get(struct mie_walker *walker);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
struct mie_op;
|
struct mie_op;
|
||||||
struct mie_ctx;
|
struct mie_ctx;
|
||||||
struct mie_pass;
|
struct mie_pass;
|
||||||
|
struct mie_dialect;
|
||||||
struct mie_op_definition;
|
struct mie_op_definition;
|
||||||
struct mie_trait_definition;
|
struct mie_trait_definition;
|
||||||
struct mie_interface_definition;
|
struct mie_interface_definition;
|
||||||
@@ -40,6 +41,7 @@ struct mie_pass_args {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct mie_pass_filter {
|
struct mie_pass_filter {
|
||||||
|
const struct mie_dialect *f_dialect;
|
||||||
const struct mie_op_definition *f_op;
|
const struct mie_op_definition *f_op;
|
||||||
const struct mie_trait_definition *f_trait;
|
const struct mie_trait_definition *f_trait;
|
||||||
const struct mie_interface_definition *f_iface;
|
const struct mie_interface_definition *f_iface;
|
||||||
|
|||||||
@@ -4,7 +4,15 @@
|
|||||||
|
|
||||||
struct mie_op *mie_block_add_op(struct mie_block *block)
|
struct mie_op *mie_block_add_op(struct mie_block *block)
|
||||||
{
|
{
|
||||||
return mie_vector_emplace_back(block->b_ops, NULL);
|
struct mie_op *op = mie_vector_emplace_back(block->b_ops, NULL);
|
||||||
|
if (!op) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mie_op_init(op);
|
||||||
|
op->op_container = block;
|
||||||
|
|
||||||
|
return op;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct mie_register *mie_block_add_param(struct mie_block *block)
|
struct mie_register *mie_block_add_param(struct mie_block *block)
|
||||||
|
|||||||
@@ -41,6 +41,23 @@ struct mie_ctx *mie_builder_get_ctx(struct mie_builder *builder)
|
|||||||
return builder->b_ctx;
|
return builder->b_ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct mie_name_map *get_current_name_map(struct mie_builder *builder)
|
||||||
|
{
|
||||||
|
b_queue_entry *cur = b_queue_last(&builder->b_scope_stack);
|
||||||
|
while (cur) {
|
||||||
|
struct builder_scope *scope
|
||||||
|
= b_unbox(struct builder_scope, cur, s_entry);
|
||||||
|
|
||||||
|
if (scope->s_region && scope->s_region->r_names) {
|
||||||
|
return scope->s_region->r_names;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur = b_queue_prev(cur);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
struct mie_block *mie_builder_get_current_block(struct mie_builder *builder)
|
struct mie_block *mie_builder_get_current_block(struct mie_builder *builder)
|
||||||
{
|
{
|
||||||
b_queue_entry *entry = b_queue_last(&builder->b_scope_stack);
|
b_queue_entry *entry = b_queue_last(&builder->b_scope_stack);
|
||||||
@@ -163,9 +180,12 @@ struct mie_op *mie_builder_put_op(
|
|||||||
enum mie_status mie_builder_put_name(
|
enum mie_status mie_builder_put_name(
|
||||||
struct mie_builder *builder, struct mie_name *name, const char *hint)
|
struct mie_builder *builder, struct mie_name *name, const char *hint)
|
||||||
{
|
{
|
||||||
struct mie_block *block = mie_builder_get_current_block(builder);
|
struct mie_name_map *map = get_current_name_map(builder);
|
||||||
struct mie_region *scope = block->b_parent;
|
if (!map) {
|
||||||
return mie_name_map_put(scope->r_names, name, hint, 0)
|
return MIE_ERR_BAD_STATE;
|
||||||
? MIE_SUCCESS
|
}
|
||||||
: MIE_ERR_NAME_EXISTS;
|
|
||||||
|
struct mie_name *result = mie_name_map_put(map, name, hint, 0);
|
||||||
|
|
||||||
|
return result ? MIE_SUCCESS : MIE_ERR_NAME_EXISTS;
|
||||||
}
|
}
|
||||||
|
|||||||
14
mie/ir/op.c
14
mie/ir/op.c
@@ -80,12 +80,24 @@ struct mie_register *mie_op_add_result(struct mie_op *op, const struct mie_type
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool is_isolated(const struct mie_op *op)
|
||||||
|
{
|
||||||
|
const struct mie_trait *isolated = mie_trait_table_get_unique(
|
||||||
|
&op->op_info->op_traits, "builtin", "isolated-from-above");
|
||||||
|
return isolated != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
struct mie_region *mie_op_add_region(struct mie_op *op)
|
struct mie_region *mie_op_add_region(struct mie_op *op)
|
||||||
{
|
{
|
||||||
|
bool isolated = is_isolated(op);
|
||||||
|
|
||||||
struct mie_region *region = mie_vector_emplace_back(op->op_regions, NULL);
|
struct mie_region *region = mie_vector_emplace_back(op->op_regions, NULL);
|
||||||
region->r_names = mie_name_map_create(NULL);
|
|
||||||
region->r_parent = op;
|
region->r_parent = op;
|
||||||
|
|
||||||
|
if (isolated) {
|
||||||
|
region->r_names = mie_name_map_create(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
return region;
|
return region;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
791
mie/ir/walk.c
791
mie/ir/walk.c
@@ -1,253 +1,595 @@
|
|||||||
|
#include <mie/dialect/dialect.h>
|
||||||
#include <mie/ir/block.h>
|
#include <mie/ir/block.h>
|
||||||
|
#include <mie/ir/op-definition.h>
|
||||||
#include <mie/ir/op.h>
|
#include <mie/ir/op.h>
|
||||||
#include <mie/ir/region.h>
|
#include <mie/ir/region.h>
|
||||||
#include <mie/ir/walk.h>
|
#include <mie/ir/walk.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#define ITEM_TYPE(f) ((f) & 0x0Fu)
|
#define WALK_ITEM_F_CHILDREN_VISITED 0x01u
|
||||||
|
#define WALK_ITEM_F_VISITED 0x02u
|
||||||
|
|
||||||
enum walk_schedule_item_flags {
|
#define ITEM_TYPE(f) ((f) & 0x0Fu)
|
||||||
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 {
|
struct mie_walker {
|
||||||
enum mie_walker_flags w_flags;
|
enum mie_walker_flags w_flags;
|
||||||
struct mie_op *w_root;
|
struct mie_op *w_root;
|
||||||
struct mie_walker_item w_cur;
|
b_queue w_stack;
|
||||||
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(
|
static bool should_ignore_item(
|
||||||
const struct mie_walker *walker, const struct walk_schedule_item *item)
|
const struct mie_walker *walker, const struct mie_walk_item *item)
|
||||||
{
|
{
|
||||||
switch (ITEM_TYPE(item->i_flags)) {
|
switch (item->i_type) {
|
||||||
case SCHED_ITEM_F_OP:
|
case MIE_WALK_ITEM_OP:
|
||||||
return (walker->w_flags & MIE_WALKER_F_INCLUDE_OPS) == 0;
|
return (walker->w_flags & MIE_WALKER_F_INCLUDE_OPS) == 0;
|
||||||
case SCHED_ITEM_F_BLOCK:
|
case MIE_WALK_ITEM_BLOCK:
|
||||||
return (walker->w_flags & MIE_WALKER_F_INCLUDE_BLOCKS) == 0;
|
return (walker->w_flags & MIE_WALKER_F_INCLUDE_BLOCKS) == 0;
|
||||||
case SCHED_ITEM_F_REGION:
|
case MIE_WALK_ITEM_REGION:
|
||||||
return (walker->w_flags & MIE_WALKER_F_INCLUDE_REGIONS) == 0;
|
return (walker->w_flags & MIE_WALKER_F_INCLUDE_REGIONS) == 0;
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void schedule_child(
|
static void push_walk_item(
|
||||||
struct mie_walker *walker, struct walk_schedule_item *parent,
|
struct mie_walker *walker, const struct mie_walk_item *item)
|
||||||
struct walk_schedule_item *child, b_queue_entry **ep)
|
|
||||||
{
|
{
|
||||||
#define REVERSE 0x04u
|
struct mie_walk_item *i = malloc(sizeof *i);
|
||||||
|
if (!i) {
|
||||||
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef REVERSE
|
memcpy(i, item, sizeof *i);
|
||||||
|
b_queue_push_back(&walker->w_stack, &i->_e);
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum mie_status schedule_children_of_region(
|
static void pop_walk_item(struct mie_walker *walker)
|
||||||
struct mie_walker *walker, struct walk_schedule_item *item)
|
|
||||||
{
|
{
|
||||||
struct mie_region *region = item->i_region;
|
b_queue_entry *entry = b_queue_pop_back(&walker->w_stack);
|
||||||
|
if (entry) {
|
||||||
b_queue_entry *tmp = &item->i_entry;
|
struct mie_walk_item *item
|
||||||
|
= b_unbox(struct mie_walk_item, entry, _e);
|
||||||
for (size_t i = 0; i < MIE_VECTOR_COUNT(region->r_blocks); i++) {
|
free(item);
|
||||||
struct mie_block *block = ®ion->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(
|
static struct mie_walk_item *current_item(struct mie_walker *walker)
|
||||||
struct mie_walker *walker, struct walk_schedule_item *item)
|
|
||||||
{
|
{
|
||||||
struct mie_block *block = item->i_block;
|
b_queue_entry *entry = b_queue_last(&walker->w_stack);
|
||||||
|
if (!entry) {
|
||||||
b_queue_entry *tmp = &item->i_entry;
|
return NULL;
|
||||||
|
|
||||||
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;
|
struct mie_walk_item *item = b_unbox(struct mie_walk_item, entry, _e);
|
||||||
|
return item->i_type != MIE_WALK_ITEM_NONE ? item : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum mie_status schedule_children_of_op(
|
static struct mie_op *get_first_child(struct mie_walker *walker, struct mie_op *op)
|
||||||
struct mie_walker *walker, struct walk_schedule_item *item)
|
|
||||||
{
|
{
|
||||||
struct mie_op *op = item->i_op;
|
bool backwards = (walker->w_flags & MIE_WALKER_F_BACKWARD) != 0;
|
||||||
|
|
||||||
b_queue_entry *tmp = &item->i_entry;
|
while (1) {
|
||||||
|
if (MIE_VECTOR_COUNT(op->op_regions) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < MIE_VECTOR_COUNT(op->op_regions); i++) {
|
size_t region_index
|
||||||
struct mie_region *region = &op->op_regions.items[i];
|
= backwards ? MIE_VECTOR_COUNT(op->op_regions) - 1 : 0;
|
||||||
struct walk_schedule_item *child
|
struct mie_region *region = &op->op_regions.items[region_index];
|
||||||
= region_schedule_item_create(region);
|
if (MIE_VECTOR_COUNT(region->r_blocks) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
schedule_child(walker, item, child, &tmp);
|
size_t block_index
|
||||||
|
= backwards ? MIE_VECTOR_COUNT(region->r_blocks) - 1 : 0;
|
||||||
|
struct mie_block *block = ®ion->r_blocks.items[block_index];
|
||||||
|
if (MIE_VECTOR_COUNT(block->b_ops) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t op_index
|
||||||
|
= backwards ? MIE_VECTOR_COUNT(block->b_ops) - 1 : 0;
|
||||||
|
op = &block->b_ops.items[op_index];
|
||||||
}
|
}
|
||||||
|
|
||||||
return MIE_SUCCESS;
|
return op;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum mie_status schedule_children(
|
static size_t walk_item_get_nr_children(const struct mie_walk_item *item)
|
||||||
struct mie_walker *walker, struct walk_schedule_item *item)
|
|
||||||
{
|
{
|
||||||
if (item->i_flags & SCHED_ITEM_F_CHILDREN_SCHEDULED) {
|
switch (item->i_type) {
|
||||||
return MIE_SUCCESS;
|
case MIE_WALK_ITEM_OP:
|
||||||
|
return MIE_VECTOR_COUNT(item->i_op->op_regions);
|
||||||
|
case MIE_WALK_ITEM_BLOCK:
|
||||||
|
return MIE_VECTOR_COUNT(item->i_block->b_ops);
|
||||||
|
case MIE_WALK_ITEM_REGION:
|
||||||
|
return MIE_VECTOR_COUNT(item->i_region->r_blocks);
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool should_correct_depth(
|
||||||
|
struct mie_walker *walker, const struct mie_walk_item *item)
|
||||||
|
{
|
||||||
|
if (should_ignore_item(walker, item)) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum mie_status status;
|
size_t temp = 0;
|
||||||
|
switch (item->i_type) {
|
||||||
switch (ITEM_TYPE(item->i_flags)) {
|
case MIE_WALK_ITEM_BLOCK:
|
||||||
case SCHED_ITEM_F_BLOCK:
|
return false; //(walker->w_flags & MIE_WALKER_F_INCLUDE_OPS) == 0;
|
||||||
status = schedule_children_of_block(walker, item);
|
default:
|
||||||
break;
|
break;
|
||||||
case SCHED_ITEM_F_REGION:
|
}
|
||||||
status = schedule_children_of_region(walker, item);
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool walk_item_get_child(
|
||||||
|
struct mie_walker *walker, const struct mie_walk_item *item,
|
||||||
|
size_t index, struct mie_walk_item *child)
|
||||||
|
{
|
||||||
|
bool ok = false;
|
||||||
|
|
||||||
|
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;
|
||||||
break;
|
break;
|
||||||
case SCHED_ITEM_F_OP:
|
case MIE_WALK_ITEM_BLOCK:
|
||||||
status = schedule_children_of_op(walker, item);
|
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;
|
||||||
|
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;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
status = MIE_ERR_BAD_STATE;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
item->i_flags |= SCHED_ITEM_F_CHILDREN_SCHEDULED;
|
if (!ok) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
child->i_depth = item->i_depth + 1;
|
||||||
|
child->i_index = index;
|
||||||
|
|
||||||
|
if (should_correct_depth(walker, item)) {
|
||||||
|
child->i_depth--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool walk_item_get_next_sibling(
|
||||||
|
const struct mie_walk_item *item, struct mie_walk_item *sibling)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
struct mie_region *parent_r = NULL;
|
||||||
|
struct mie_block *parent_b = NULL;
|
||||||
|
struct mie_op *parent_o = NULL;
|
||||||
|
|
||||||
|
switch (item->i_type) {
|
||||||
|
case MIE_WALK_ITEM_OP:
|
||||||
|
parent_b = item->i_op->op_container;
|
||||||
|
if (!parent_b) {
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sibling->i_depth = item->i_depth;
|
||||||
|
sibling->i_type = item->i_type;
|
||||||
|
sibling->i_index = item->i_index - 1;
|
||||||
|
|
||||||
|
struct mie_region *parent_r = NULL;
|
||||||
|
struct mie_block *parent_b = NULL;
|
||||||
|
struct mie_op *parent_o = NULL;
|
||||||
|
|
||||||
|
switch (item->i_type) {
|
||||||
|
case MIE_WALK_ITEM_OP:
|
||||||
|
parent_b = item->i_op->op_container;
|
||||||
|
if (!parent_b) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sibling->i_op = &parent_b->b_ops.items[item->i_index - 1];
|
||||||
|
return true;
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_stack(struct mie_walker *walker)
|
||||||
|
{
|
||||||
|
b_queue_entry *entry = b_queue_last(&walker->w_stack);
|
||||||
|
|
||||||
|
while (entry) {
|
||||||
|
struct mie_walk_item *item
|
||||||
|
= b_unbox(struct mie_walk_item, entry, _e);
|
||||||
|
|
||||||
|
switch (item->i_type) {
|
||||||
|
case MIE_WALK_ITEM_OP:
|
||||||
|
printf("* %zu: op %p", item->i_depth, item->i_op);
|
||||||
|
if (item->i_op->op_flags & MIE_OP_F_OP_RESOLVED) {
|
||||||
|
printf(" %s.%s", item->i_op->op_dialect->d_name,
|
||||||
|
item->i_op->op_info->op_name);
|
||||||
|
} else {
|
||||||
|
printf(" %s", item->i_op->op_name);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MIE_WALK_ITEM_BLOCK:
|
||||||
|
printf("* %zu: block %p %s", item->i_depth,
|
||||||
|
item->i_block, item->i_block->b_name.n_str);
|
||||||
|
break;
|
||||||
|
case MIE_WALK_ITEM_REGION:
|
||||||
|
printf("* %zu: region %p", item->i_depth, item->i_region);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf(" unknown");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
entry = b_queue_prev(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool step_pre_f(struct mie_walker *walker)
|
||||||
|
{
|
||||||
|
/* forward, pre-order traversal:
|
||||||
|
* if this item has children, we need to visit the first one.
|
||||||
|
* if this item has no children, we need to visit the next sibling.
|
||||||
|
* if this is the last sibling, step up until we find an item that
|
||||||
|
* has a next sibling.
|
||||||
|
* if no item is found, we are done. */
|
||||||
|
|
||||||
|
struct mie_walk_item *item = current_item(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);
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
|
push_walk_item(walker, &next);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (walk_item_get_next_sibling(item, &next)) {
|
||||||
|
pop_walk_item(walker);
|
||||||
|
push_walk_item(walker, &next);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i = 0;
|
||||||
|
while (1) {
|
||||||
|
pop_walk_item(walker);
|
||||||
|
struct mie_walk_item *parent = current_item(walker);
|
||||||
|
if (!parent) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = walk_item_get_next_sibling(parent, &next);
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
|
pop_walk_item(walker);
|
||||||
|
push_walk_item(walker, &next);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool step_pre_b(struct mie_walker *walker)
|
||||||
|
{
|
||||||
|
/* backward, pre-order traversal:
|
||||||
|
* if this item has children, we need to visit the last one.
|
||||||
|
* if this item has no children, we need to visit the previous sibling.
|
||||||
|
* if this is the first sibling, step up until we find an item that
|
||||||
|
* has a previous sibling.
|
||||||
|
* if no item is found, we are done. */
|
||||||
|
|
||||||
|
struct mie_walk_item *item = current_item(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);
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
|
push_walk_item(walker, &next);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (walk_item_get_prev_sibling(item, &next)) {
|
||||||
|
pop_walk_item(walker);
|
||||||
|
push_walk_item(walker, &next);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i = 0;
|
||||||
|
while (1) {
|
||||||
|
pop_walk_item(walker);
|
||||||
|
struct mie_walk_item *parent = current_item(walker);
|
||||||
|
if (!parent) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = walk_item_get_prev_sibling(parent, &next);
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
|
pop_walk_item(walker);
|
||||||
|
push_walk_item(walker, &next);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool step_post_f(struct mie_walker *walker)
|
||||||
|
{
|
||||||
|
/* forward, post-order traversal:
|
||||||
|
* if this item has children:
|
||||||
|
* if they have already been visited, visit the item itself
|
||||||
|
* if they haven't been visited yet, visit the first one.
|
||||||
|
* if this item has a next sibling, move to the sibling and goto step 1.
|
||||||
|
* if this item is the last child, we need to visit the parent next. */
|
||||||
|
struct mie_walk_item *item = current_item(walker);
|
||||||
|
item->_f |= WALK_ITEM_F_VISITED;
|
||||||
|
|
||||||
|
struct mie_walk_item next;
|
||||||
|
bool ok = false;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
item = current_item(walker);
|
||||||
|
size_t nr_children = walk_item_get_nr_children(item);
|
||||||
|
|
||||||
|
if (nr_children > 0 && !(item->_f & WALK_ITEM_F_CHILDREN_VISITED)) {
|
||||||
|
ok = walk_item_get_child(walker, item, 0, &next);
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
|
push_walk_item(walker, &next);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nr_children == 0 && !(item->_f & WALK_ITEM_F_VISITED)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (walk_item_get_next_sibling(item, &next)) {
|
||||||
|
pop_walk_item(walker);
|
||||||
|
push_walk_item(walker, &next);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pop_walk_item(walker);
|
||||||
|
struct mie_walk_item *parent = current_item(walker);
|
||||||
|
if (parent) {
|
||||||
|
parent->_f |= WALK_ITEM_F_CHILDREN_VISITED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent != NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool step_post_b(struct mie_walker *walker)
|
||||||
|
{
|
||||||
|
/* backward, post-order traversal:
|
||||||
|
* if this item has children:
|
||||||
|
* if they have already been visited, visit the item itself
|
||||||
|
* if they haven't been visited yet, visit the last one.
|
||||||
|
* if this item has a previous sibling, move to the sibling and goto step 1.
|
||||||
|
* if this item is the first child, we need to visit the parent next. */
|
||||||
|
struct mie_walk_item *item = current_item(walker);
|
||||||
|
item->_f |= WALK_ITEM_F_VISITED;
|
||||||
|
|
||||||
|
struct mie_walk_item next;
|
||||||
|
bool ok = false;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
item = current_item(walker);
|
||||||
|
size_t nr_children = walk_item_get_nr_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 (ok) {
|
||||||
|
push_walk_item(walker, &next);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nr_children == 0 && !(item->_f & WALK_ITEM_F_VISITED)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (walk_item_get_prev_sibling(item, &next)) {
|
||||||
|
pop_walk_item(walker);
|
||||||
|
push_walk_item(walker, &next);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pop_walk_item(walker);
|
||||||
|
struct mie_walk_item *parent = current_item(walker);
|
||||||
|
if (parent) {
|
||||||
|
parent->_f |= WALK_ITEM_F_CHILDREN_VISITED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent != NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool step(struct mie_walker *walker)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
struct mie_walk_item *item = malloc(sizeof *item);
|
||||||
|
if (!item) {
|
||||||
|
return MIE_ERR_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(item, 0x0, sizeof *item);
|
||||||
|
item->i_op = walker->w_root;
|
||||||
|
item->i_type = MIE_WALK_ITEM_OP;
|
||||||
|
|
||||||
|
push_walk_item(walker, item);
|
||||||
|
|
||||||
return MIE_SUCCESS;
|
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)
|
||||||
{
|
{
|
||||||
b_queue_entry *cur = b_queue_last(&walker->w_sched);
|
struct mie_walk_item item = {};
|
||||||
while (cur) {
|
item.i_op = walker->w_root;
|
||||||
struct walk_schedule_item *item
|
item.i_type = MIE_WALK_ITEM_OP;
|
||||||
= b_unbox(struct walk_schedule_item, cur, i_entry);
|
|
||||||
schedule_children(walker, item);
|
|
||||||
|
|
||||||
cur = b_queue_prev(cur);
|
push_walk_item(walker, &item);
|
||||||
|
|
||||||
|
bool backward = walker->w_flags & MIE_WALKER_F_BACKWARD;
|
||||||
|
bool ok = false;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ok = false;
|
||||||
|
|
||||||
|
if (backward) {
|
||||||
|
ok = walk_item_get_child(
|
||||||
|
walker, current, nr_children - 1, &child);
|
||||||
|
} else {
|
||||||
|
ok = walk_item_get_child(walker, current, 0, &child);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
push_walk_item(walker, &child);
|
||||||
}
|
}
|
||||||
|
|
||||||
return MIE_SUCCESS;
|
return ok ? MIE_SUCCESS : MIE_ERR_NO_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct mie_walker *mie_walker_begin(struct mie_op *op, enum mie_walker_flags flags)
|
struct mie_walker *mie_walker_begin(struct mie_op *op, enum mie_walker_flags flags)
|
||||||
@@ -262,11 +604,15 @@ struct mie_walker *mie_walker_begin(struct mie_op *op, enum mie_walker_flags fla
|
|||||||
out->w_flags = flags;
|
out->w_flags = flags;
|
||||||
out->w_root = op;
|
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) {
|
if (flags & MIE_WALKER_F_POSTORDER) {
|
||||||
prepare_postorder_traversal(out);
|
prepare_postorder_traversal(out);
|
||||||
|
} else {
|
||||||
|
prepare_preorder_traversal(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct mie_walk_item *cur = current_item(out);
|
||||||
|
if (should_ignore_item(out, cur)) {
|
||||||
|
mie_walker_step(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
@@ -274,74 +620,23 @@ struct mie_walker *mie_walker_begin(struct mie_op *op, enum mie_walker_flags fla
|
|||||||
|
|
||||||
void mie_walker_end(struct mie_walker *walker)
|
void mie_walker_end(struct mie_walker *walker)
|
||||||
{
|
{
|
||||||
}
|
/* TODO */
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (walker->w_flags & MIE_WALKER_F_RECURSIVE || cur->i_depth == 0) {
|
|
||||||
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)
|
enum mie_status mie_walker_step(struct mie_walker *walker)
|
||||||
{
|
{
|
||||||
struct walk_schedule_item *cur = current_item(walker);
|
struct mie_walk_item *cur = current_item(walker);
|
||||||
|
bool ok = false;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
step(walker);
|
ok = step(walker);
|
||||||
cur = current_item(walker);
|
cur = current_item(walker);
|
||||||
} while (cur && should_ignore_item(walker, cur));
|
} while (cur && should_ignore_item(walker, cur));
|
||||||
|
|
||||||
return cur ? MIE_SUCCESS : MIE_ERR_NO_DATA;
|
return cur ? MIE_SUCCESS : MIE_ERR_NO_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct mie_walker_item *mie_walker_get(struct mie_walker *walker)
|
struct mie_walk_item *mie_walker_get(struct mie_walker *walker)
|
||||||
{
|
{
|
||||||
struct walk_schedule_item *cur = current_item(walker);
|
return 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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -837,8 +837,7 @@ bool mie_parser_parse_anonymous_block(
|
|||||||
{
|
{
|
||||||
mie_parser_parse_linefeed(ctx);
|
mie_parser_parse_linefeed(ctx);
|
||||||
|
|
||||||
struct mie_op *op = mie_vector_emplace_back(block->b_ops, NULL);
|
struct mie_op *op = mie_block_add_op(block);
|
||||||
mie_op_init(op);
|
|
||||||
|
|
||||||
if (!mie_parser_parse_op(ctx, names, op)) {
|
if (!mie_parser_parse_op(ctx, names, op)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -855,8 +854,7 @@ bool mie_parser_parse_anonymous_block(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct mie_op *op = mie_vector_emplace_back(block->b_ops, NULL);
|
struct mie_op *op = mie_block_add_op(block);
|
||||||
mie_op_init(op);
|
|
||||||
|
|
||||||
if (!mie_parser_parse_op(ctx, names, op)) {
|
if (!mie_parser_parse_op(ctx, names, op)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -950,8 +948,7 @@ bool mie_parser_parse_block(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct mie_op *op = mie_vector_emplace_back(block->b_ops, NULL);
|
struct mie_op *op = mie_block_add_op(block);
|
||||||
mie_op_init(op);
|
|
||||||
if (!mie_parser_parse_op(ctx, names, op)) {
|
if (!mie_parser_parse_op(ctx, names, op)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1150,9 +1147,6 @@ static bool parse_graph_op(
|
|||||||
bool mie_parser_parse_op(
|
bool mie_parser_parse_op(
|
||||||
struct mie_parser *ctx, struct mie_name_map *names, struct mie_op *dest)
|
struct mie_parser *ctx, struct mie_name_map *names, struct mie_op *dest)
|
||||||
{
|
{
|
||||||
memset(dest, 0x0, sizeof *dest);
|
|
||||||
mie_attribute_map_init(&dest->op_attrib);
|
|
||||||
|
|
||||||
if (mie_parser_check_eof(ctx)) {
|
if (mie_parser_check_eof(ctx)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,18 +87,19 @@ enum mie_status mie_pass_manager_filter_op(
|
|||||||
return MIE_SUCCESS;
|
return MIE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dialect_name || !op_name) {
|
if (!dialect_name) {
|
||||||
return MIE_ERR_INVALID_ARGUMENT;
|
return MIE_ERR_INVALID_ARGUMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct mie_op_definition *op
|
if (!op_name) {
|
||||||
= mie_ctx_get_op_definition(pm->pm_ctx, dialect_name, op_name);
|
pm->pm_filter.f_dialect
|
||||||
if (!op) {
|
= mie_ctx_get_dialect(pm->pm_ctx, dialect_name);
|
||||||
return MIE_ERR_NO_ENTRY;
|
return pm->pm_filter.f_dialect ? MIE_SUCCESS : MIE_ERR_NO_ENTRY;
|
||||||
}
|
}
|
||||||
|
|
||||||
pm->pm_filter.f_op = op;
|
pm->pm_filter.f_op
|
||||||
return MIE_SUCCESS;
|
= mie_ctx_get_op_definition(pm->pm_ctx, dialect_name, op_name);
|
||||||
|
return pm->pm_filter.f_op ? MIE_SUCCESS : MIE_ERR_NO_ENTRY;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum mie_status mie_pass_manager_filter_trait(
|
enum mie_status mie_pass_manager_filter_trait(
|
||||||
@@ -152,6 +153,10 @@ static bool filter_check_op(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (filter->f_dialect && op->op_dialect != filter->f_dialect) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (filter->f_trait
|
if (filter->f_trait
|
||||||
&& !mie_op_has_trait(
|
&& !mie_op_has_trait(
|
||||||
op, filter->f_trait->tr_parent->d_name,
|
op, filter->f_trait->tr_parent->d_name,
|
||||||
@@ -203,7 +208,7 @@ static void schedule_passes(
|
|||||||
while (mie_walker_step(walker) == MIE_SUCCESS) {
|
while (mie_walker_step(walker) == MIE_SUCCESS) {
|
||||||
for (size_t i = 0; i < MIE_VECTOR_COUNT(pm->pm_nested); i++) {
|
for (size_t i = 0; i < MIE_VECTOR_COUNT(pm->pm_nested); i++) {
|
||||||
struct mie_pass_manager *nested = pm->pm_nested.items[i];
|
struct mie_pass_manager *nested = pm->pm_nested.items[i];
|
||||||
struct mie_walker_item *item = mie_walker_get(walker);
|
struct mie_walk_item *item = mie_walker_get(walker);
|
||||||
schedule_passes(nested, schedule, item->i_op, depth + 1);
|
schedule_passes(nested, schedule, item->i_op, depth + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ const struct mie_trait *mie_trait_table_get_unique(
|
|||||||
|
|
||||||
const mie_id *result = mie_id_map_get(&table->tr_unique, &id);
|
const mie_id *result = mie_id_map_get(&table->tr_unique, &id);
|
||||||
const struct unique_trait *entry
|
const struct unique_trait *entry
|
||||||
= b_unbox(struct unique_trait, entry, u_id);
|
= b_unbox(struct unique_trait, result, u_id);
|
||||||
return entry ? entry->u_trait : NULL;
|
return entry ? entry->u_trait : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user