void clay_relation_normalize_alpha(osl_relation_p relation) { int row, col; osl_int_t gcd; osl_int_init(relation->precision, &gcd); // Normalize equalities. clay_relation_output_form(relation); // Normalize inequalities. for (row = 0; row < relation->nb_rows; row++) { if (osl_int_zero(relation->precision, relation->m[row][0])) { continue; // ignore equalities } gcd = clay_relation_line_gcd(relation, row, 1, relation->nb_columns); for (col = 1; col < relation->nb_columns; col++) { if (col >= 1 && col < relation->nb_output_dims + 1 && (col % 2) == 1) { continue; // ignore beta dimensions } osl_int_div_exact(relation->precision, &relation->m[row][col], relation->m[row][col], gcd); } } clay_relation_sort_rows(relation); osl_int_clear(relation->precision, &gcd); }
void clay_relation_substitute(osl_relation_p relation, int original_col, int target_col, int factor) { int row; osl_int_t tmp; // Do not allow substituting e/i flag or the constant and // do not allow substituting by e/i flag (but allow by constant). if (original_col <= 0 || original_col > relation->nb_columns - 1 || target_col <= 0 || target_col > relation->nb_columns) return; osl_int_init(relation->precision, &tmp); for (row = 0; row < relation->nb_rows; row++) { if (osl_int_zero(relation->precision, relation->m[row][original_col])) { continue; } osl_int_mul_si(relation->precision, &tmp, relation->m[row][original_col], factor); osl_int_add(relation->precision, &relation->m[row][target_col], relation->m[row][target_col], tmp); } osl_int_clear(relation->precision, &tmp); }
/** * osl_scop_check_compatible_scoplib function: * This function checks that a scop is "well formed". It returns 0 if the * check failed or 1 if no problem has been detected. * \param scop The scop we want to check. * \return 0 if the integrity check fails, 1 otherwise. */ int osl_scop_check_compatible_scoplib(osl_scop_p scop) { if (!osl_scop_integrity_check(scop)) return 0; if (scop->next != NULL) return 0; if (scop == NULL || scop->statement == NULL) return 1; osl_relation_p domain; osl_statement_p statement; osl_relation_p scattering; int precision = scop->statement->scattering->precision; int i, j; statement = scop->statement; while (statement != NULL) { scattering = statement->scattering; if (scattering->nb_local_dims != 0) { OSL_error("Local dims in scattering matrix"); return 0; } domain = statement->domain; while (domain != NULL) { if (domain->nb_local_dims != 0) { OSL_error("Local dims in domain matrix"); return 0; } domain = domain->next; } // Check if there is only the -Identity in the output_dims // and the lines MUST be in the right order for (i = 0 ; i < scattering->nb_rows ; i++) { for (j = 0 ; j < scattering->nb_output_dims ; j++) { if (i == j) { // -1 if (!osl_int_mone(precision, scattering->m[i][j+1])) { OSL_error("Wrong -Identity"); return 0; } } else { // 0 if (!osl_int_zero(precision, scattering->m[i][j+1])) { OSL_error("Wrong -Identity"); return 0; } } } } statement = statement->next; } return 1; }
static bool isParametricBoundary(int row, osl_relation_p scattering) { int firstParameterDim = scattering->nb_input_dims + scattering->nb_output_dims + scattering->nb_local_dims + 1; for (int i = 0; i < scattering->nb_parameters; i++) { if (!osl_int_zero(scattering->precision, scattering->m[row][firstParameterDim + i])) return true; } return false; }
static bool isVariableBoundary(int row, osl_relation_p scattering, int dimIdx) { bool variable = false; for (int i = 0; i < scattering->nb_input_dims + scattering->nb_output_dims + scattering->nb_local_dims; i++) { if (i + 1 == dimIdx) continue; if (!osl_int_zero(scattering->precision, scattering->m[row][1 + i])) variable = true; } return variable; }
/** * clay_util_relation_get_line function: * Because the lines in the scattering matrix may have not ordered, we have to * search the corresponding line. It returns the first line where the value is * different from zero in the `column'. `column' is between 0 and * nb_output_dims-1 * \param[in] relation * \param[in] column Line to search * \return Return the real line */ int clay_util_relation_get_line(osl_relation_p relation, int column) { if (column < 0 || column > relation->nb_output_dims) return -1; int i; int precision = relation->precision; for (i = 0 ; i < relation->nb_rows ; i++) { if (!osl_int_zero(precision, relation->m[i][column+1])) { break; } } return (i == relation->nb_rows ? -1 : i ); }
void clay_relation_alpha_equation_rows(clay_array_p equation_rows, osl_relation_p relation) { int i; // Take only alpha equations. for (i = 0; i < relation->nb_rows; i++) { if (osl_int_zero(relation->precision, relation->m[i][0])) { if (!clay_util_is_row_beta_definition(relation, i)) { clay_array_add(equation_rows, i); } } } }
// assumes beta-structure; depth 1-based // TODO: do not return, possible leak osl_int_t clay_relation_gcd(osl_relation_p relation, int depth) { osl_int_t gcd; int row, col; int gcd_assigned = 0; int column = 2*depth; osl_int_init(relation->precision, &gcd); if (depth < -1 || depth == 0 || depth > relation->nb_output_dims / 2) { CLAY_debug("Called clay_relation_gcd with column outside bounds"); osl_int_set_si(relation->precision, &gcd, 0); return gcd; } for (row = 0; row < relation->nb_rows; row++) { if (depth != -1 && osl_int_zero(relation->precision, relation->m[row][column])) { continue; } for (col = 2; col < relation->nb_columns; col++) { // if beta, ignore if (col >= 1 && col < relation->nb_output_dims + 1 && (col % 2) == 1) { continue; } if (col == column) { continue; } if (gcd_assigned) { osl_int_gcd(relation->precision, &gcd, gcd, relation->m[row][col]); } else { if (!osl_int_zero(relation->precision, relation->m[row][col])) { osl_int_assign(relation->precision, &gcd, relation->m[row][col]); gcd_assigned = 1; } } } } return gcd; }
// aware of beta structure static int clay_relation_line_is_zero(osl_relation_p relation, int row, int begin, int end) { int i; int all_zero = 1; for (i = begin; i < end; i++) { if (i >= 1 && i < 1 + relation->nb_output_dims && (i % 2) == 1) { continue; // ignore betas } all_zero = all_zero && osl_int_zero(relation->precision, relation->m[row][i]); if (!all_zero) { break; } } return all_zero; }
// aware of beta structure osl_int_t clay_relation_line_gcd(osl_relation_p relation, int line, int column_start, int column_end) { osl_int_t gcd, tmp; int i; int gcd_assigned = 0; osl_int_init(relation->precision, &gcd); osl_int_init(relation->precision, &tmp); if (column_end - column_start < 1 || column_start >= relation->nb_columns || column_end > relation->nb_columns) { osl_int_set_si(relation->precision, &gcd, 0); return gcd; } else if (column_end - column_start == 1) { osl_int_assign(relation->precision, &gcd, relation->m[line][column_start]); return gcd; } for (i = column_start; i < column_end; i++) { if (i >= 1 && i < relation->nb_output_dims + 1 && (i % 2) == 1) { continue; // ignore betas } if (osl_int_zero(relation->precision, relation->m[line][i])) { continue; // ignore zeros } osl_int_abs(relation->precision, &tmp, relation->m[line][i]); if (!gcd_assigned) { osl_int_assign(relation->precision, &gcd, tmp); gcd_assigned = 1; } else { osl_int_gcd(relation->precision, &gcd, gcd, tmp); } } if (!gcd_assigned) { // if gcd zero or not found, default to 1. osl_int_set_si(relation->precision, &gcd, 1); } osl_int_clear(relation->precision, &tmp); return gcd; }
/** * clay_util_body_regenerate_access function: * Read the access array and re-generate the code in the body * \param[in] ebody An extbody structure * \param[in] access The relation to regenerate the code * \param[in] index nth access (needed to access to the array start and * length of the extbody structure) */ void clay_util_body_regenerate_access(osl_extbody_p ebody, osl_relation_p access, int index, osl_arrays_p arrays, osl_scatnames_p scatnames, osl_strings_p params) { if (!arrays || !scatnames || !params || access->nb_output_dims == 0 || index >= ebody->nb_access) return; const int precision = access->precision; int i, j, k, row, val, print_plus; // check if there are no inequ for (i = 0 ; i < access->nb_rows ; i++) { if (!osl_int_zero(precision, access->m[i][0])) CLAY_error("I don't know how to regenerate access with inequalities"); } // check identity matrix in output dims int n; for (j = 0 ; j < access->nb_output_dims ; j++) { n = 0; for (i = 0 ; i < access->nb_rows ; i++) if (!osl_int_zero(precision, access->m[i][j+1])) { if (n >= 1) CLAY_error("I don't know how to regenerate access with " "dependences in output dims"); n++; } } char *body = ebody->body->expression->string[0]; int body_len = strlen(body); int start = ebody->start[index]; int len = ebody->length[index]; int is_zero; // if the line contains only zeros if (start >= body_len || start + len >= body_len || (start == -1 && len == -1)) return; char *new_body; char end_body[OSL_MAX_STRING]; int hwm = OSL_MAX_STRING; CLAY_malloc(new_body, char *, OSL_MAX_STRING * sizeof(char)); // copy the beginning of the body if (start+1 >= OSL_MAX_STRING) CLAY_error("memcpy: please recompile osl with a higher OSL_MAX_STRING"); memcpy(new_body, body, start); new_body[start] = '\0'; // save the end in a buffer int sz = body_len - start - len; if (sz + 1 >= OSL_MAX_STRING) CLAY_error("memcpy: please recompile osl with a higher OSL_MAX_STRING"); memcpy(end_body, body + start + len, sz); end_body[sz] = '\0'; // copy access name string val = osl_relation_get_array_id(access); val = clay_util_arrays_search(arrays, val); // get the index in th array osl_util_safe_strcat(&new_body, arrays->names[val], &hwm); // generate each dims for (i = 1 ; i < access->nb_output_dims ; i++) { row = clay_util_relation_get_line(access, i); if (row == -1) continue; osl_util_safe_strcat(&new_body, "[", &hwm); is_zero = 1; print_plus = 0; k = 1 + access->nb_output_dims; // iterators for (j = 0 ; j < access->nb_input_dims ; j++, k++) { val = osl_int_get_si(precision, access->m[row][k]); if (val != 0) { clay_util_name_sprint(&new_body, &hwm, &print_plus, val, scatnames->names->string[j*2+1]); is_zero = 0; } } // params for (j = 0 ; j < access->nb_parameters ; j++, k++) { val = osl_int_get_si(precision, access->m[row][k]); if (val != 0) { clay_util_name_sprint(&new_body, &hwm, &print_plus, val, params->string[j]); is_zero = 0; } } // const val = osl_int_get_si(precision, access->m[row][k]); if (val != 0 || is_zero) clay_util_name_sprint(&new_body, &hwm, &print_plus, val, NULL); osl_util_safe_strcat(&new_body, "]", &hwm); } // length of the generated access ebody->length[index] = strlen(new_body) - start; // concat the end osl_util_safe_strcat(&new_body, end_body, &hwm); // update ebody free(ebody->body->expression->string[0]); ebody->body->expression->string[0] = new_body; // shift the start int diff = ebody->length[index] - len; for (i = index+1 ; i < ebody->nb_access ; i++) if (ebody->start[i] != -1) ebody->start[i] += diff; }
/** * clay_util_statement_set_vector function: * Set the equation on each line where the column of the output dim is * different of zero * \param[in,out] statement * \param[in] vector {(([output, ...],) [param, ..],) [const]} * \param[in] column column on the output dim */ void clay_util_statement_set_vector( osl_statement_p statement, clay_list_p vector, int column) { osl_relation_p scattering = statement->scattering; clay_array_p arr_dims = NULL, arr_params = NULL, arr_const = NULL; int i, j, k; int precision = scattering->precision; osl_int_p tmp; tmp = osl_int_malloc(precision); if (vector->size > 3) { CLAY_error("list with more than 3 arrays not supported"); } else if (vector->size == 3) { arr_dims = vector->data[0]; arr_params = vector->data[1]; arr_const = vector->data[2]; } else if (vector->size == 2) { arr_params = vector->data[0]; arr_const = vector->data[1]; } else { arr_const = vector->data[0]; } // for each line where there is a number different from zero on the // column for (k = 0 ; k < scattering->nb_rows ; k++) { if (!osl_int_zero(precision, scattering->m[k][1+column])) { // scattering = coeff_outputdim * shifting // affect output dims if (vector->size >= 3) { i = 1; for (j = 0 ; j < arr_dims->size ; j++) { osl_int_mul_si(precision, &scattering->m[k][i], scattering->m[k][1+column], arr_dims->data[j]); i++; } } // here we add we the last value // scattering += coeff_outputdim * shifting // affects parameters if (vector->size >= 2) { i = 1 + scattering->nb_output_dims + scattering->nb_input_dims + scattering->nb_local_dims; for (j = 0 ; j < arr_params->size ; j++) { osl_int_mul_si(precision, tmp, scattering->m[k][1+column], arr_params->data[j]); osl_int_add(precision, &scattering->m[k][i], scattering->m[k][i], *tmp); i++; } } // set the constant if (vector->size >= 1 && arr_const->size == 1) { osl_int_mul_si(precision, tmp, scattering->m[k][1+column], arr_const->data[0]); osl_int_add(precision, &scattering->m[k][scattering->nb_columns-1], scattering->m[k][scattering->nb_columns-1], *tmp); } } } osl_int_free(precision, tmp); }
// make i-th coefficient in the row_i zero by subtracting row_j multiplied by // a constant factor from row_i. void clay_relation_zero_coefficient(osl_relation_p relation, int row_i, int row_j, int col) { int k; osl_int_t lcm, multiplier_i, multiplier_j; osl_int_init(relation->precision, &lcm); osl_int_init(relation->precision, &multiplier_i); osl_int_init(relation->precision, &multiplier_j); // If the target coefficient is already zero, if (osl_int_zero(relation->precision, relation->m[row_j][col])) { // do nothing. } // If the source coefficient is zero, but the target one is not, else if (osl_int_zero(relation->precision, relation->m[row_i][col])) { // swap lines. for (k = 1; k < relation->nb_columns; k++) { osl_int_swap(relation->precision, &relation->m[row_i][k], &relation->m[row_j][k]); } } else { osl_int_lcm(relation->precision, &lcm, relation->m[row_i][col], relation->m[row_j][col]); osl_int_div_exact(relation->precision, &multiplier_i, lcm, relation->m[row_i][col]); osl_int_div_exact(relation->precision, &multiplier_j, lcm, relation->m[row_j][col]); for (k = 1; k < relation->nb_columns; k++) { if (k < relation->nb_output_dims + 1 && (k % 2) == 1) { // ignore beta dimensions continue; } osl_int_mul(relation->precision, &relation->m[row_i][k], relation->m[row_i][k], multiplier_i); osl_int_mul(relation->precision, &relation->m[row_j][k], relation->m[row_j][k], multiplier_j); osl_int_sub(relation->precision, &relation->m[row_j][k], relation->m[row_j][k], relation->m[row_i][k]); } } // Divide by constant factor if possible (similar to densify transformation). multiplier_i = clay_relation_line_gcd(relation, row_i, 1, relation->nb_columns); multiplier_j = clay_relation_line_gcd(relation, row_j, 1, relation->nb_columns); for (k = 1; k < relation->nb_columns; k++) { if (k < relation->nb_output_dims + 1 && (k % 2) == 1) { // ignore beta dimensions continue; } if (!osl_int_zero(relation->precision, multiplier_i)) { osl_int_div_exact(relation->precision, &relation->m[row_i][k], relation->m[row_i][k], multiplier_i); } if (!osl_int_zero(relation->precision, multiplier_j)) { osl_int_div_exact(relation->precision, &relation->m[row_j][k], relation->m[row_j][k], multiplier_j); } } osl_int_clear(relation->precision, &lcm); osl_int_clear(relation->precision, &multiplier_i); osl_int_clear(relation->precision, &multiplier_j); }
/** * @brief Allocate and construct the vectorization profile by analyzing the * osl_statement. * * @param[in] scop The scop of the analyzed osl_statement. * @param[in] statement The analyzed osl_statement. * * @return A constructed vectorization profile. */ struct substrate_vectorization_profile substrate_vectorization_profile_constructor( struct osl_scop * scop, struct osl_statement * statement) { struct substrate_vectorization_profile res = {NULL,0}; unsigned int i = 0; struct osl_relation_list *relation_list_cursor = NULL; struct osl_relation *relation_cursor = NULL; unsigned col = 0; unsigned line = 0; osl_int_t val; unsigned common_iterators_vectorized_dim[statement->domain->nb_output_dims]; int precision = statement->domain->precision; struct candl_options *candl_options = NULL; struct osl_dependence *candl_dep = NULL, *candl_dep_cursor = NULL; struct osl_scop *candl_scop = NULL; //Preparing and allocating the vectorization structure. res.size = statement->domain->nb_output_dims; res.vectorizable_loops = (bool*) malloc(res.size * sizeof(bool)); memset(res.vectorizable_loops,false,res.size); //For every possible iterator, we select only those that are used in the //vectorized output dimension (the first if column major, last if row major). // //If an access relation of the statement doesn't used the i^th iterator, //then this iterator can be used to vectorize the statement. for(i=0 ; i<res.size ; i++) { common_iterators_vectorized_dim[i] = true; relation_list_cursor = statement->access; while((relation_list_cursor != NULL) && (common_iterators_vectorized_dim[i])) { relation_cursor = relation_list_cursor->elt; while((relation_cursor != NULL) && (common_iterators_vectorized_dim[i])) { col = g_substrate_options.row_major ? relation_cursor->nb_output_dims-1 : 1; line = candl_util_relation_get_line(relation_cursor, col); val = relation_cursor->m[line][1 + relation_cursor->nb_output_dims + i]; common_iterators_vectorized_dim[i] = !osl_int_zero(precision, val); relation_cursor = relation_cursor->next; } relation_list_cursor = relation_list_cursor->next; } } //Creating a dummy scop with only the current analyzed statement for candl. //Also reset the beta depth of the statement, because it's supposed to be //alone now. candl_scop = substrate_osl_scop_nclone_except_statement(scop, 1); candl_scop->statement = osl_statement_nclone(statement,1); substrate_reset_beta_depth(candl_scop->statement); //Initiating candl, then using it to generate the dependences, //and finally extracting them to use later. candl_options = candl_options_malloc(); candl_scop_usr_init(candl_scop); candl_dependence_add_extension(candl_scop, candl_options); candl_dep = osl_generic_lookup(candl_scop->extension, OSL_URI_DEPENDENCE); for(i=0 ; i<res.size; i++) { if( common_iterators_vectorized_dim[i] == false ) continue; res.vectorizable_loops[i] = true; candl_dep_cursor = candl_dep; while( (candl_dep_cursor != NULL) && (res.vectorizable_loops[i]) ) { if(candl_dependence_is_loop_carried(candl_dep_cursor, i)) { switch(candl_dep_cursor->type) { //If the dependence is RaR (which should not be encountered because //we only analyze the self-dependence of the statement) or WaR, //then we don't care : these types doesn't prevent vectorization. case OSL_DEPENDENCE_RAR : case OSL_DEPENDENCE_WAR : break; //If a dependence is RaW or WaW, the i^th cannot be vectorized //because these type of dependence prevent it. case OSL_DEPENDENCE_RAW : case OSL_DEPENDENCE_WAW : res.vectorizable_loops[i] = false; break; default : OSL_error("In function substrate_vectorization_profile_constructor :" " an osl_dependence without a type has been encountered"); break; } } candl_dep_cursor = candl_dep_cursor->next; } } candl_options_free(candl_options); candl_scop_usr_cleanup(candl_scop); osl_scop_free(candl_scop); return res; }