diff --git a/mie/include/mie/select/builder.h b/mie/include/mie/select/builder.h index e14317b..91e0b62 100644 --- a/mie/include/mie/select/builder.h +++ b/mie/include/mie/select/builder.h @@ -49,4 +49,7 @@ MIE_API enum mie_status mie_select_builder_set_mem_access( struct mie_select_builder *builder, struct mie_value *ir_val, struct mie_select_value *graph_val); +MIE_API enum mie_status mie_select_builder_collapse_chain_ends( + struct mie_select_builder *builder, struct mie_select_value *out); + #endif diff --git a/mie/include/mie/select/graph.h b/mie/include/mie/select/graph.h index b514b61..af082d6 100644 --- a/mie/include/mie/select/graph.h +++ b/mie/include/mie/select/graph.h @@ -21,11 +21,16 @@ struct mie_select_use { b_queue_entry u_entry; }; +struct mie_select_chain_end { + struct mie_select_value c_value; + b_queue_entry c_entry; +}; + struct mie_select_graph { b_queue g_nodes; struct mie_select_node *g_root; struct mie_select_value g_entry; - struct mie_select_value g_last_chain; + b_queue g_chain_ends; size_t g_frame_index; size_t g_node_id; }; diff --git a/mie/select/builder.c b/mie/select/builder.c index 99dc331..4dbdaae 100644 --- a/mie/select/builder.c +++ b/mie/select/builder.c @@ -94,18 +94,6 @@ const struct mie_target *mie_select_builder_get_target( return builder->b_target; } -static void clear_node_map(struct mie_select_builder *builder) -{ - b_iterator *it = b_iterator_begin(builder->b_nodes); - while (b_iterator_is_valid(it)) { - b_hashmap_item *item = b_iterator_get_value(it).v_ptr; - free(item->value.value_data); - b_iterator_erase(it); - } - - b_iterator_unref(it); -} - struct mie_select_graph *mie_select_builder_finish(struct mie_select_builder *builder) { enum mie_status status = MIE_SUCCESS; @@ -116,8 +104,14 @@ struct mie_select_graph *mie_select_builder_finish(struct mie_select_builder *bu }; size_t nr_root_operands = 0; - if (graph->g_last_chain.v_node) { - root_operands[0] = &graph->g_last_chain; + struct mie_select_value incoming_chain = {}; + status = mie_select_builder_collapse_chain_ends(builder, &incoming_chain); + if (status != MIE_SUCCESS) { + return NULL; + } + + if (incoming_chain.v_node) { + root_operands[0] = &incoming_chain; nr_root_operands++; } @@ -129,9 +123,13 @@ struct mie_select_graph *mie_select_builder_finish(struct mie_select_builder *bu return NULL; } - clear_node_map(builder); + b_hashmap_unref(builder->b_nodes); + builder->b_nodes = b_hashmap_create(NULL, NULL); - builder->b_graph = NULL; + b_hashmap_unref(builder->b_mem_access); + builder->b_mem_access = b_hashmap_create(NULL, NULL); + + builder->b_graph = mie_select_graph_create(builder->b_ctx); return graph; } @@ -251,6 +249,32 @@ static enum mie_status get_data_node( return MIE_SUCCESS; } +static enum mie_status get_external_value_node( + struct mie_select_builder *builder, struct mie_value *ir_val, + struct mie_select_value *out) +{ + struct mie_type *type = mie_value_get_type(ir_val, builder->b_ctx); + + if (!type) { + return MIE_ERR_INVALID_VALUE; + } + + struct mie_select_node *node; + enum mie_status status = mie_select_graph_get_node( + builder->b_graph, mie_target_builtin(), MIE_SELECT_OP_REGISTER, + NULL, 0, &type, 1, &node); + if (status != MIE_SUCCESS) { + return status; + } + + node->n_flags = MIE_SELECT_NODE_F_PVALUE; + node->n_value.v = ir_val; + + mie_select_node_get_value(node, type, 0, out); + + return MIE_SUCCESS; +} + struct mie_select_value *mie_select_builder_get_value( struct mie_select_builder *builder, struct mie_value *ir_val) { @@ -280,6 +304,7 @@ struct mie_select_value *mie_select_builder_get_value( status = get_data_node(builder, ir_val, select_val); break; default: + status = get_external_value_node(builder, ir_val, select_val); break; } @@ -368,6 +393,77 @@ enum mie_status mie_select_builder_set_mem_access( return MIE_SUCCESS; } +enum mie_status mie_select_builder_collapse_chain_ends( + struct mie_select_builder *builder, struct mie_select_value *out) +{ + size_t nr_chains = b_queue_length(&builder->b_graph->g_chain_ends); + b_queue_entry *entry = NULL; + struct mie_select_chain_end *end = NULL; + + switch (nr_chains) { + case 0: + memset(out, 0x0, sizeof *out); + return MIE_SUCCESS; + case 1: + entry = b_queue_first(&builder->b_graph->g_chain_ends); + end = b_unbox(struct mie_select_chain_end, entry, c_entry); + memcpy(out, end, sizeof *out); + return MIE_SUCCESS; + default: + break; + } + + struct mie_type *chain_type + = mie_ctx_get_type(builder->b_ctx, MIE_TYPE_OTHER); + + struct mie_select_value **operands = calloc(nr_chains, sizeof *operands); + if (!operands) { + return MIE_ERR_NO_MEMORY; + } + + entry = b_queue_first(&builder->b_graph->g_chain_ends); + size_t i = 0; + while (entry) { + end = b_unbox(struct mie_select_chain_end, entry, c_entry); + operands[i++] = &end->c_value; + entry = b_queue_next(entry); + } + + struct mie_select_node *group = NULL; + enum mie_status status = mie_select_graph_get_node( + builder->b_graph, mie_target_builtin(), MIE_SELECT_OP_CHAIN_GROUP, + operands, nr_chains, &chain_type, 1, &group); + free(operands); + + if (status != MIE_SUCCESS) { + return status; + } + + entry = b_queue_first(&builder->b_graph->g_chain_ends); + while (entry) { + end = b_unbox(struct mie_select_chain_end, entry, c_entry); + b_queue_entry *next = b_queue_next(entry); + + b_queue_delete(&builder->b_graph->g_chain_ends, entry); + free(end); + + entry = next; + } + + struct mie_select_chain_end *group_end = malloc(sizeof *group_end); + if (!group_end) { + return MIE_ERR_NO_MEMORY; + } + + memset(group_end, 0x0, sizeof *group_end); + + mie_select_node_get_value(group, chain_type, 0, &group_end->c_value); + b_queue_push_back(&builder->b_graph->g_chain_ends, &group_end->c_entry); + *out = group_end->c_value; + + return MIE_SUCCESS; +} + struct mie_select_node *mie_select_builder_find_node_with_ivalue( struct mie_select_builder *builder, const struct mie_target *target, unsigned int opcode, long long val) diff --git a/mie/select/graph.c b/mie/select/graph.c index aa16a07..4f5c04d 100644 --- a/mie/select/graph.c +++ b/mie/select/graph.c @@ -62,6 +62,43 @@ void mie_select_graph_destroy(struct mie_select_graph *graph) free(graph); } +static enum mie_status update_chain_ends( + struct mie_select_graph *graph, struct mie_select_value *new_chain) +{ + struct mie_select_node *new_node = new_chain->v_node; + + b_queue_entry *entry = b_queue_first(&graph->g_chain_ends); + while (entry) { + struct mie_select_chain_end *end + = b_unbox(struct mie_select_chain_end, entry, c_entry); + struct mie_select_node *value_node = end->c_value.v_node; + + for (size_t i = 0; i < new_node->n_nr_operands; i++) { + struct mie_select_node *operand_node + = new_node->n_operands[i].u_value.v_node; + + if (value_node == operand_node) { + memcpy(&end->c_value, new_chain, sizeof *new_chain); + return MIE_SUCCESS; + } + } + + entry = b_queue_next(entry); + } + + struct mie_select_chain_end *end = malloc(sizeof *end); + if (!end) { + return MIE_ERR_NO_MEMORY; + } + + memset(end, 0x0, sizeof *end); + memcpy(&end->c_value, new_chain, sizeof *new_chain); + + b_queue_push_back(&graph->g_chain_ends, &end->c_entry); + + return MIE_SUCCESS; +} + enum mie_status mie_select_graph_get_node( struct mie_select_graph *graph, const struct mie_target *target, unsigned int op, struct mie_select_value **operands, size_t nr_operands, @@ -87,9 +124,18 @@ enum mie_status mie_select_graph_get_node( node->n_target = target; for (size_t i = 0; i < nr_values; i++) { - if (values[i]->t_id == MIE_TYPE_OTHER) { - graph->g_last_chain.v_node = node; - graph->g_last_chain.v_index = i; + if (values[i]->t_id != MIE_TYPE_OTHER) { + continue; + } + + struct mie_select_value chain = { + .v_node = node, + .v_index = i, + }; + + enum mie_status status = update_chain_ends(graph, &chain); + if (status != MIE_SUCCESS) { + return status; } }