| /* Standard libraries */ |
| #include <stdlib.h> |
| #include <string.h> |
| |
| /* Odin_II libraries */ |
| #include "odin_globals.h" |
| #include "odin_types.h" |
| #include "ast_util.h" |
| #include "ast_elaborate.h" |
| #include "parse_making_ast.h" |
| #include "netlist_create_from_ast.h" |
| #include "odin_util.h" |
| #include "vtr_memory.h" |
| #include "vtr_util.h" |
| |
| /* This files header */ |
| #include "ast_loop_unroll.h" |
| |
| ast_node_t *unroll_for_loop(ast_node_t* node, ast_node_t *parent, int *num_unrolled, sc_hierarchy *local_ref, bool is_generate) |
| { |
| oassert(node && node->type == FOR); |
| |
| ast_node_t* unrolled_for = resolve_for(node); |
| oassert(unrolled_for != nullptr); |
| |
| *num_unrolled = unrolled_for->num_children; |
| |
| /* update parent */ |
| int i; |
| int this_genblk = 0; |
| for (i = 0; i < parent->num_children; i++) |
| { |
| if (node == parent->children[i]) |
| { |
| int j; |
| for (j = i; j < (unrolled_for->num_children + i); j++) |
| { |
| ast_node_t *child = unrolled_for->children[j-i]; |
| add_child_to_node_at_index(parent, child, j); |
| unrolled_for->children[j-i] = NULL; |
| |
| /* create scopes as necessary */ |
| if (is_generate) |
| { |
| oassert(child->type == BLOCK); |
| |
| /* generate blocks always have scopes; parent has access to named block |
| but not unnamed, and child always has access to parent */ |
| sc_hierarchy *child_hierarchy = init_sc_hierarchy(); |
| child->types.hierarchy = child_hierarchy; |
| |
| child_hierarchy->top_node = child; |
| child_hierarchy->parent = local_ref; |
| |
| if (child->types.identifier != NULL) |
| { |
| local_ref->block_children = (sc_hierarchy **)vtr::realloc(local_ref->block_children, sizeof(sc_hierarchy *)*(local_ref->num_block_children + 1)); |
| local_ref->block_children[local_ref->num_block_children] = child_hierarchy; |
| local_ref->num_block_children++; |
| |
| /* add an array reference to this label */ |
| std::string new_id(child->types.identifier); |
| new_id = new_id + "[" + std::to_string(j-i) + "]"; |
| vtr::free(child->types.identifier); |
| child->types.identifier = vtr::strdup(new_id.c_str()); |
| |
| child_hierarchy->scope_id = node->types.identifier; |
| child_hierarchy->instance_name_prefix = make_full_ref_name(local_ref->instance_name_prefix, NULL, child->types.identifier, NULL, -1); |
| } |
| else |
| { |
| /* create a unique scope id/instance name prefix for internal use */ |
| this_genblk = local_ref->num_unnamed_genblks + 1; |
| std::string new_scope_id("genblk"); |
| new_scope_id = new_scope_id + std::to_string(this_genblk) + "[" + std::to_string(j-i) + "]"; |
| child_hierarchy->scope_id = vtr::strdup(new_scope_id.c_str()); |
| child_hierarchy->instance_name_prefix = make_full_ref_name(local_ref->instance_name_prefix, NULL, child_hierarchy->scope_id, NULL, -1); |
| } |
| |
| /* string caches */ |
| create_param_table_for_scope(child, child_hierarchy); |
| create_symbol_table_for_scope(child, child_hierarchy); |
| } |
| else if (child->type == BLOCK && child->types.identifier != NULL) |
| { |
| /* only create scope if child is named block */ |
| sc_hierarchy *child_hierarchy = init_sc_hierarchy(); |
| child->types.hierarchy = child_hierarchy; |
| |
| child_hierarchy->top_node = child; |
| child_hierarchy->parent = local_ref; |
| |
| local_ref->block_children = (sc_hierarchy **)vtr::realloc(local_ref->block_children, sizeof(sc_hierarchy *)*(local_ref->num_block_children + 1)); |
| local_ref->block_children[local_ref->num_block_children] = child_hierarchy; |
| local_ref->num_block_children++; |
| |
| /* add an array reference to this label */ |
| std::string new_id(child->types.identifier); |
| new_id = new_id + "[" + std::to_string(j-i) + "]"; |
| vtr::free(child->types.identifier); |
| child->types.identifier = vtr::strdup(new_id.c_str()); |
| |
| child_hierarchy->scope_id = node->types.identifier; |
| child_hierarchy->instance_name_prefix = make_full_ref_name(local_ref->instance_name_prefix, NULL, child->types.identifier, NULL, -1); |
| |
| /* string caches */ |
| create_param_table_for_scope(child, child_hierarchy); |
| create_symbol_table_for_scope(child, child_hierarchy); |
| } |
| } |
| |
| oassert(j == (unrolled_for->num_children + i) && parent->children[j] == node); |
| remove_child_from_node_at_index(parent, j); |
| |
| break; |
| } |
| } |
| |
| if (this_genblk > 0) |
| { |
| local_ref->num_unnamed_genblks++; |
| } |
| |
| free_whole_tree(unrolled_for); |
| return parent->children[i]; |
| } |
| |
| /* |
| * (function: resolve_for) |
| */ |
| ast_node_t* resolve_for(ast_node_t* node) |
| { |
| oassert(is_for_node(node)); |
| oassert(node != nullptr); |
| ast_node_t* body_parent = nullptr; |
| |
| ast_node_t* pre = node->children[0]; |
| ast_node_t* cond = node->children[1]; |
| ast_node_t* post = node->children[2]; |
| ast_node_t* body = node->children[3]; |
| |
| ast_node_t* value = 0; |
| if(resolve_pre_condition(pre, &value)) |
| { |
| error_message(PARSE_ERROR, pre->line_number, pre->file_number, "%s", "Unsupported pre-condition node in for loop"); |
| } |
| |
| int error_code = 0; |
| condition_function cond_func = resolve_condition(cond, pre->children[0], &error_code); |
| if(error_code) |
| { |
| error_message(PARSE_ERROR, cond->line_number, cond->file_number, "%s", "Unsupported condition node in for loop"); |
| } |
| |
| post_condition_function post_func = resolve_post_condition(post, pre->children[0], &error_code); |
| if(error_code) |
| { |
| error_message(PARSE_ERROR, post->line_number, post->file_number, "%s", "Unsupported post-condition node in for loop"); |
| } |
| |
| bool dup_body = cond_func(value->types.vnumber->get_value()); |
| while(dup_body) |
| { |
| ast_node_t* new_body = dup_and_fill_body(body, pre, &value, &error_code); |
| if(error_code) |
| { |
| error_message(PARSE_ERROR, pre->line_number, pre->file_number, "%s", "Unsupported pre-condition node in for loop"); |
| } |
| |
| VNumber *temp_vnum = value->types.vnumber; |
| value->types.vnumber = new VNumber(post_func(temp_vnum->get_value())); |
| delete temp_vnum; |
| |
| body_parent = body_parent ? newList_entry(body_parent, new_body) : newList(BLOCK, new_body, new_body->line_number); |
| |
| dup_body = cond_func(value->types.vnumber->get_value()); |
| } |
| |
| free_whole_tree(value); |
| return body_parent; |
| } |
| |
| /* |
| * (function: resolve_pre_condition) |
| * return 0 if the first value of the variable set |
| * in the pre condition of a `for` node has been put in location |
| * pointed to by the number pointer. |
| * |
| * return a non-zero number on failure. |
| * define failure constants in header. |
| */ |
| int resolve_pre_condition(ast_node_t* node, ast_node_t** number_node) |
| { |
| /* Add new for loop support here. Keep current work in the TODO |
| * Currently supporting: |
| * for(VAR = NUM; ...; ...) ... |
| * TODO: |
| * for(VAR = function(PARAMS...); ...; ...) ... |
| */ |
| /* IMPORTANT: if support for more complex continue conditions is added, update this inline function. */ |
| if(is_unsupported_pre(node)) |
| { |
| return UNSUPPORTED_PRE_CONDITION_NODE; |
| } |
| if (*number_node) free_whole_tree(*number_node); |
| *number_node = ast_node_deep_copy(node->children[1]); |
| return 0; |
| } |
| |
| /** IMPORTANT: as support for more complex continue conditions is added, update this function. |
| * (function: is_unsupported_condition) |
| * returns true if, given the supplied symbol, the node can be simplifed |
| * to true or false if the symbol is replaced with some value. |
| */ |
| bool is_unsupported_condition(ast_node_t* node, ast_node_t* symbol){ |
| bool invalid_inequality = ( node->type != BINARY_OPERATION || |
| !( node->types.operation.op == LT || |
| node->types.operation.op == GT || |
| node->types.operation.op == LOGICAL_EQUAL || |
| node->types.operation.op == NOT_EQUAL || |
| node->types.operation.op == LTE || |
| node->types.operation.op == GTE) || |
| node->num_children != 2 || |
| node->children[1] == nullptr || |
| !( node->children[1]->type == NUMBERS || |
| node->children[1]->type == IDENTIFIERS ) || |
| node->children[0] == nullptr || |
| !( node->children[0]->type == NUMBERS || |
| node->children[0]->type == IDENTIFIERS )); |
| |
| bool invalid_logical_concatenation = ( node->type != BINARY_OPERATION || |
| !( node->types.operation.op == LOGICAL_OR || |
| node->types.operation.op == LOGICAL_AND) || |
| node->num_children != 2 || |
| node->children[1] == nullptr || |
| is_unsupported_condition(node->children[1], symbol) || |
| node->children[0] == nullptr || |
| is_unsupported_condition(node->children[0], symbol)); |
| |
| bool invalid_negation = ( node->type != UNARY_OPERATION || |
| node->types.operation.op != LOGICAL_NOT || |
| node->num_children != 1 || |
| node->children[0] == nullptr || |
| is_unsupported_condition(node->children[0], symbol)); |
| |
| bool contains_unknown_symbols = !(invalid_inequality) && ( |
| ( node->children[0]->type == IDENTIFIERS && |
| strcmp(node->children[0]->types.identifier, symbol->types.identifier)) || |
| ( node->children[1]->type == IDENTIFIERS && |
| strcmp(node->children[1]->types.identifier, symbol->types.identifier))); |
| |
| return ((invalid_inequality || contains_unknown_symbols) && invalid_logical_concatenation && invalid_negation); |
| } |
| |
| /* |
| * (function: resolve_condition) |
| * return a lambda which tests the loop condition for a given value |
| */ |
| condition_function resolve_condition(ast_node_t* node, ast_node_t* symbol, int* error_code) |
| { |
| /* Add new for loop support here. Keep current work in the TODO |
| * Currently supporting: |
| * for(...; VAR {<, >, ==, !=, <=, >=} NUM; ...) ... |
| * for(...; CONDITION_A {&&, ||} CONDITION_B;...) ... |
| * for(...; !(CONDITION);...) ... |
| * TODO: |
| * for(...; {EXPRESSION_OF_VAR, NUM} {<, >, ==, !=, <=, >=} {EXPRESSION_OF_VAR, NUM}; ...) ... |
| */ |
| /* IMPORTANT: if support for more complex continue conditions is added, update this inline function. */ |
| if(is_unsupported_condition(node, symbol)) |
| { |
| *error_code = UNSUPPORTED_CONDITION_NODE; |
| return nullptr; |
| } |
| *error_code = 0; |
| /* Resursive calls need to report errors before returning a lambda */ |
| condition_function left = nullptr; |
| condition_function right = nullptr; |
| condition_function inner = nullptr; |
| switch(node->types.operation.op){ |
| case LOGICAL_OR: |
| left = resolve_condition(node->children[0], symbol, error_code); |
| if(*error_code) |
| return nullptr; |
| right = resolve_condition(node->children[1], symbol, error_code); |
| if(*error_code) |
| return nullptr; |
| return [=](long value) { |
| return (left(value) || right(value)); |
| }; |
| case LOGICAL_AND: |
| left = resolve_condition(node->children[0], symbol, error_code); |
| if(*error_code) |
| return nullptr; |
| right = resolve_condition(node->children[1], symbol, error_code); |
| if(*error_code) |
| return nullptr; |
| return [=](long value) { |
| return (left(value) && right(value)); |
| }; |
| case LOGICAL_NOT: |
| inner = resolve_condition(node->children[0], symbol, error_code); |
| if(*error_code) |
| return nullptr; |
| return [=](long value) { |
| bool inner_true = inner(value); |
| return !inner_true; |
| }; |
| default: |
| break; |
| } |
| /* Non-recursive calls can type check in the lambda to save copy/paste */ |
| return [=](long value) { |
| switch(node->types.operation.op){ |
| case LT: |
| return value < node->children[1]->types.vnumber->get_value(); |
| case GT: |
| return value > node->children[1]->types.vnumber->get_value(); |
| case NOT_EQUAL: |
| return value != node->children[1]->types.vnumber->get_value(); |
| case LOGICAL_EQUAL: |
| return value == node->children[1]->types.vnumber->get_value(); |
| case LTE: |
| return value <= node->children[1]->types.vnumber->get_value(); |
| case GTE: |
| return value >= node->children[1]->types.vnumber->get_value(); |
| default: |
| return false; |
| } |
| }; |
| } |
| |
| |
| /* IMPORTANT: as support for more complex post conditions is added, update this function. |
| * (function: is_unsupported_post) |
| * returns true if the post condition blocking assignment is more complex than |
| * can currently be unrolled statically |
| */ |
| bool is_unsupported_post(ast_node_t* node, ast_node_t* symbol){ |
| return (node == nullptr || |
| node->type != BINARY_OPERATION || |
| !( node->types.operation.op == ADD || |
| node->types.operation.op == MINUS || |
| node->types.operation.op == MULTIPLY || |
| node->types.operation.op == DIVIDE) || |
| node->num_children != 2 || |
| node->children[1] == nullptr || |
| !( ( node->children[1]->type == IDENTIFIERS && |
| !strcmp(node->children[1]->types.identifier, symbol->types.identifier))|| |
| node->children[1]->type == NUMBERS || |
| !is_unsupported_post(node->children[0], symbol)) || |
| node->children[0] == nullptr || |
| !( ( node->children[0]->type == IDENTIFIERS && |
| !strcmp(node->children[0]->types.identifier, symbol->types.identifier))|| |
| node->children[0]->type == NUMBERS || |
| !is_unsupported_post(node->children[0], symbol))); |
| } |
| |
| post_condition_function resolve_binary_operation(ast_node_t* node) |
| { |
| if(node->type == NUMBERS){ |
| return [=](long value){ |
| /* |
| * this lambda triggers a warning for unused variable unless |
| * we use value to generate a 0 |
| */ |
| return node->types.vnumber->get_value() + (value - value); |
| }; |
| } else if (node->type == IDENTIFIERS) { |
| return [=](long value){ |
| return value; |
| }; |
| } else { |
| return [=](long value) { |
| post_condition_function left_func = resolve_binary_operation(node->children[0]); |
| post_condition_function right_func = resolve_binary_operation(node->children[1]); |
| switch(node->types.operation.op){ |
| case ADD: |
| return left_func(value) + right_func(value); |
| case MINUS: |
| return left_func(value) - right_func(value); |
| case MULTIPLY: |
| return left_func(value) * right_func(value); |
| case DIVIDE: |
| return left_func(value) / right_func(value); |
| default: |
| return 0x0L; |
| } |
| }; |
| } |
| } |
| |
| /* |
| * (function: resolve_post_condition) |
| * return a lambda which gives the next value |
| * of the loop variable given the current value |
| * of the loop variable |
| */ |
| post_condition_function resolve_post_condition(ast_node_t* assignment, ast_node_t* symbol, int* error_code) |
| { |
| /* Add new for loop support here. Keep current work in the TODO |
| * Given iteration t, and VAR[t] is the value of VAR at iteration t, |
| * VAR[0] is init, EXPRESSION_OF_VAR[t] is the value of the post |
| * expression evaluated at iteration t, and VAR[t+1] is the |
| * value of VAR after the current iteration: |
| * Currently supporting: |
| * for(...; ...; VAR = VAR {+, -, *, /} NUM) ... |
| * for(...; ...; VAR[t+1] = EXPRESSION_OF_VAR[t]) |
| * TODO: |
| * for(...; ...; VAR[t+1] = function_call(VAR[t])) |
| */ |
| ast_node_t* node = nullptr; |
| /* Check that the post condition assignment node is a valid assignment */ |
| if( assignment != nullptr && |
| assignment->type == BLOCKING_STATEMENT && |
| assignment->num_children == 2 && |
| assignment->children[0] != nullptr && |
| assignment->children[1] != nullptr) |
| { |
| node = assignment->children[1]; |
| } |
| /* IMPORTANT: If support for more complex post conditions is added, update this inline function */ |
| if(is_unsupported_post(node, symbol)) |
| { |
| *error_code = UNSUPPORTED_POST_CONDITION_NODE; |
| return nullptr; |
| } |
| *error_code = 0; |
| return resolve_binary_operation(node); |
| } |
| |
| ast_node_t* dup_and_fill_body(ast_node_t* body, ast_node_t* pre, ast_node_t** value, int* error_code) |
| { |
| ast_node_t* copy = ast_node_deep_copy(body); |
| for(long i = 0; i<copy->num_children; i++) |
| { |
| ast_node_t* child = copy->children[i]; |
| if (child) |
| { |
| bool is_unrolled = false; |
| |
| if(child->type == IDENTIFIERS) |
| { |
| if(!strcmp(child->types.identifier, pre->children[0]->types.identifier)) |
| { |
| ast_node_t* new_num = ast_node_copy(*value); |
| child = free_whole_tree(child); |
| copy->children[i] = new_num; |
| } |
| } |
| else if(child->type == MODULE_INSTANCE && child->children[0]->type != MODULE_INSTANCE) |
| { |
| /* find and replace iteration symbol for port connections and parameters */ |
| ast_node_t *named_instance = child->children[1]; |
| copy->children[i]->children[1] = dup_and_fill_body(named_instance, pre, value, error_code); |
| free_whole_tree(named_instance); |
| |
| is_unrolled = true; |
| } |
| |
| if(child && child->num_children > 0) |
| { |
| if (!is_unrolled) |
| { |
| for (int j = 0; j < copy->children[i]->num_children; j++) |
| { |
| if (copy->children[i]->children[j] != child->children[j]) free_whole_tree(copy->children[i]->children[j]); |
| } |
| |
| copy->children[i] = dup_and_fill_body(child, pre, value, error_code); |
| free_whole_tree(child); |
| } |
| } |
| } |
| } |
| return copy; |
| } |