2 /*--------------------------------------------------------------------+
4 |--------------------------------------------------------------------|
6 |--------------------------------------------------------------------|
7 | First version: 03/04/2012 |
8 +--------------------------------------------------------------------+
10 +--------------------------------------------------------------------------+
11 | / __)( ) /__\ ( \/ ) |
12 | ( (__ )(__ /(__)\ \ / Chunky Loop Alteration wizardrY |
13 | \___)(____)(__)(__)(__) |
14 +--------------------------------------------------------------------------+
15 | Copyright (C) 2012 University of Paris-Sud |
17 | This library is free software; you can redistribute it and/or modify it |
18 | under the terms of the GNU Lesser General Public License as published by |
19 | the Free Software Foundation; either version 2.1 of the License, or |
20 | (at your option) any later version. |
22 | This library is distributed in the hope that it will be useful but |
23 | WITHOUT ANY WARRANTY; without even the implied warranty of |
24 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser |
25 | General Public License for more details. |
27 | You should have received a copy of the GNU Lesser General Public License |
28 | along with this software; if not, write to the Free Software Foundation, |
29 | Inc., 51 Franklin Street, Fifth Floor, |
30 | Boston, MA 02110-1301 USA |
32 | Clay, the Chunky Loop Alteration wizardrY |
33 | Written by Joel Poudroux, joel.poudroux@u-psud.fr |
34 +--------------------------------------------------------------------------*/
38 #include <clay/array.h>
39 #include <clay/beta.h>
40 #include <clay/macros.h>
41 #include <clay/util.h>
42 #include <clay/errors.h>
44 #include <osl/statement.h>
46 #include <osl/extensions/extbody.h>
47 #include <osl/extensions/scatnames.h>
49 #include <osl/generic.h>
51 #include <osl/relation.h>
52 #include <osl/relation_list.h>
53 #include <osl/macros.h>
56 * clay_util_statement_insert_inequation function:
57 * Insert a new inequation at the end of the scattering
58 * \param[in,out] statement
59 * \param[in] inequ [iter1, iter2, ..., param1, param2, ..., const]
60 * \param[in] nb_input_dims Nb input dims in the array
61 * \param[in] nb_params Nb params in the array
63 /*void clay_util_statement_insert_inequation(osl_statement_p statement,
64 clay_array_p inequ, int nb_input_dims, int nb_params) {
66 osl_relation_p scattering = statement->scattering;
67 int row = scattering->nb_rows;
69 int precision = scattering->precision;
71 // insert the inequation spliting (local dims are not in the inequation)
73 osl_relation_insert_blank_row(scattering, row);
74 osl_int_set_si(precision, scattering->m[row], 0, 1); // type inequation
77 i = scattering->nb_output_dims+1;
78 for (j = 0 ; j < nb_input_dims ; j++) {
79 osl_int_set_si(precision,
80 scattering->m[row], i,
85 i = 1 + scattering->nb_output_dims + scattering->nb_input_dims +
86 scattering->nb_local_dims;
87 for (; j < nb_params + nb_input_dims ; j++) {
88 osl_int_set_si(precision,
89 scattering->m[row], i,
94 osl_int_set_si(precision,
95 scattering->m[row], scattering->nb_columns-1,
96 inequ->data[inequ->size-1]);
103 * clay_util_array_output_dims_pad_zero function:
104 * Pad zeros for alpha columns
105 * For example if we have [i, j], the result will be [0, i, 0, j, 0]
106 * \param[in,out] array
108 void clay_util_array_output_dims_pad_zero(clay_array_p a
) {
110 int end
= a
->size
*2+1;
111 int old_size
= a
->size
;
113 for (i
= old_size
; i
< end
; i
++)
114 clay_array_add(a
, 0);
116 for (i
= old_size
- 1 ; i
>= 0 ; i
--) {
117 a
->data
[i
*2+1] = a
->data
[i
];
122 // update the provided part of the beta and leave everything else untouched
123 void clay_util_scattering_update_beta(osl_relation_p scattering
,
126 if (!scattering
|| !beta
|| beta
->size
== 0) {
130 for (i
= 0; i
< OSL_min(beta
->size
, scattering
->nb_output_dims
/ 2 + 1); i
++) {
131 row
= clay_util_relation_get_line(scattering
, i
* 2);
132 osl_int_set_si(scattering
->precision
,
133 &scattering
->m
[row
][scattering
->nb_columns
- 1],
138 // update the provided part of the beta for every part of the scattering relation union
139 // that matches the given old beta
140 void clay_util_statement_replace_beta(osl_statement_p statement
,
141 clay_array_p old_beta
,
143 osl_relation_p scattering
= statement
->scattering
;
144 while (scattering
!= NULL
) {
145 if (clay_beta_check_relation(scattering
, old_beta
)) {
146 clay_util_scattering_update_beta(scattering
, beta
);
148 scattering
= scattering
->next
;
152 void clay_util_relation_insert_inequation(osl_relation_p relation
,
154 clay_array_p dims
= NULL
,
157 int row
= relation
->nb_rows
;
158 int precision
= relation
->precision
;
161 // Insert a blank row at the end and fill it in.
162 osl_relation_insert_blank_row(relation
, row
);
163 osl_int_set_si(precision
, &relation
->m
[row
][0], 1); // inequality
165 if (inequ
->size
> 3) {
166 CLAY_error("list with more than 3 arrays not supported");
167 } else if (inequ
->size
== 3) {
168 dims
= inequ
->data
[0];
169 params
= inequ
->data
[1];
170 consts
= inequ
->data
[2];
171 } else if (inequ
->size
== 2) {
172 params
= inequ
->data
[0];
173 consts
= inequ
->data
[1];
175 consts
= inequ
->data
[0];
178 if (consts
->size
!= 1) {
179 CLAY_error("constant must be a single value");
182 // Decomposition switch, fallthoroughs are _intentional_.
183 switch (inequ
->size
) {
185 for (j
= 0, i
= 1; j
< dims
->size
; j
++, i
++) {
186 osl_int_set_si(precision
,
187 &relation
->m
[row
][i
],
190 // intentional fall through
192 for (j
= 0, i
= 1 + relation
->nb_output_dims
+
193 relation
->nb_input_dims
+ relation
->nb_local_dims
;
196 osl_int_set_si(precision
,
197 &relation
->m
[row
][i
],
200 // intentional fall through
202 osl_int_set_si(precision
,
203 &relation
->m
[row
][relation
->nb_columns
-1],
207 CLAY_error("list with more than 3 arrays not supported");
213 * clay_util_statement_set_inequation function:
214 * Insert a new inequation at the end of the scattering
215 * The list must have less or equal than 3 arrays
216 * Warning: here the output dims are complete
217 * example: if the output dims are : 0 i 0 j 0, you have
218 * to give all these numbers
219 * \param[in,out] statement
220 * \param[in] inequ {(([output, ...],) [param, ..],) [const]}
222 void clay_util_statement_set_inequation(
223 osl_statement_p statement
,
226 osl_relation_p scattering
= statement
->scattering
;
227 clay_array_p arr_dims
= NULL
, arr_params
= NULL
, arr_const
= NULL
;
228 int row
= scattering
->nb_rows
;
230 int precision
= scattering
->precision
;
232 // insert the inequation spliting (local dims are not in the inequation)
234 osl_relation_insert_blank_row(scattering
, row
);
235 osl_int_set_si(precision
, &scattering
->m
[row
][0], 1); // type inequation
237 if (inequ
->size
> 3) {
238 CLAY_error("list with more than 3 arrays not supported");
239 } else if (inequ
->size
== 3) {
240 arr_dims
= inequ
->data
[0];
241 arr_params
= inequ
->data
[1];
242 arr_const
= inequ
->data
[2];
243 } else if (inequ
->size
== 2) {
244 arr_params
= inequ
->data
[0];
245 arr_const
= inequ
->data
[1];
247 arr_const
= inequ
->data
[0];
250 // affects output dims
251 if (inequ
->size
== 3) {
253 for (j
= 0 ; j
< arr_dims
->size
; j
++) {
254 osl_int_set_si(precision
,
255 &scattering
->m
[row
][i
],
261 // affects parameters
262 if (inequ
->size
>= 2) {
263 i
= 1 + scattering
->nb_output_dims
+ scattering
->nb_input_dims
+
264 scattering
->nb_local_dims
;
265 for (j
= 0; j
< arr_params
->size
; j
++) {
266 osl_int_set_si(precision
,
267 &scattering
->m
[row
][i
],
268 arr_params
->data
[j
]);
274 if (inequ
->size
>= 1 && arr_const
->size
== 1) {
275 osl_int_set_si(precision
,
276 &scattering
->m
[row
][scattering
->nb_columns
-1],
283 * clay_util_statement_set_vector function:
284 * Set the equation on each line where the column of the output dim is
286 * \param[in,out] statement
287 * \param[in] vector {(([output, ...],) [param, ..],) [const]}
288 * \param[in] column column on the output dim
290 void clay_util_relation_set_vector(
291 osl_relation_p scattering
,
292 clay_list_p vector
, int column
) {
294 clay_array_p arr_dims
= NULL
, arr_params
= NULL
, arr_const
= NULL
;
296 int precision
= scattering
->precision
;
299 tmp
= osl_int_malloc(precision
);
301 if (vector
->size
> 3) {
302 CLAY_error("list with more than 3 arrays not supported");
303 } else if (vector
->size
== 3) {
304 arr_dims
= vector
->data
[0];
305 arr_params
= vector
->data
[1];
306 arr_const
= vector
->data
[2];
307 } else if (vector
->size
== 2) {
308 arr_params
= vector
->data
[0];
309 arr_const
= vector
->data
[1];
311 arr_const
= vector
->data
[0];
314 // for each line where there is a number different from zero on the
316 for (k
= 0 ; k
< scattering
->nb_rows
; k
++) {
317 if (!osl_int_zero(precision
, scattering
->m
[k
][1+column
])) {
319 // scattering = coeff_outputdim * shifting
321 // affect output dims
322 if (vector
->size
>= 3) {
324 for (j
= 0 ; j
< arr_dims
->size
; j
++) {
325 osl_int_mul_si(precision
,
326 &scattering
->m
[k
][i
],
327 scattering
->m
[k
][1+column
],
333 // here we add we the last value
334 // scattering += coeff_outputdim * shifting
336 // affects parameters
337 if (vector
->size
>= 2) {
338 i
= 1 + scattering
->nb_output_dims
+ scattering
->nb_input_dims
+
339 scattering
->nb_local_dims
;
340 for (j
= 0 ; j
< arr_params
->size
; j
++) {
341 osl_int_mul_si(precision
,
343 scattering
->m
[k
][1+column
],
344 arr_params
->data
[j
]);
345 osl_int_add(precision
,
346 &scattering
->m
[k
][i
],
354 if (vector
->size
>= 1 && arr_const
->size
== 1) {
355 osl_int_mul_si(precision
,
357 scattering
->m
[k
][1+column
],
359 osl_int_add(precision
,
360 &scattering
->m
[k
][scattering
->nb_columns
-1],
361 scattering
->m
[k
][scattering
->nb_columns
-1],
367 osl_int_free(precision
, tmp
);
372 * clay_util_relation_negate_row function:
373 * Negate the line at `row' (doesn't affect the e/i column)
374 * \param[in,out] statement
375 * \param[in] row row to negate
377 void clay_util_relation_negate_row(osl_relation_p r
, int row
) {
379 int precision
= r
->precision
;
380 for (i
= 1 ; i
< r
->nb_columns
; i
++) {
381 osl_int_oppose(precision
,
385 osl_int_decrement(precision
,
386 &r
->m
[row
][r
->nb_columns
-1],
387 r
->m
[row
][r
->nb_columns
-1]);
392 * clay_util_statement_insert function:
393 * Insert `newstatement' before `statement', and set his beta value
394 * \param[in,out] statement
395 * \param[in,out] newstatement
396 * \param[in] column column on the output dim (where we want to split)
397 * this is a `alpha column' of the 2*d+1
398 * \param[in] order new beta value
399 * \return return statement->next (so newtstatement)
401 osl_statement_p
clay_util_statement_insert(osl_statement_p statement
,
402 osl_statement_p newstatement
,
405 osl_relation_p scattering
= statement
->scattering
;
407 // the current statement is after the new statement
408 int row
= clay_util_relation_get_line(scattering
, column
);
409 osl_int_set_si(scattering
->precision
,
410 &scattering
->m
[row
][scattering
->nb_columns
-1],
413 // the order is not important in the statements list
414 newstatement
->next
= statement
->next
;
415 statement
->next
= newstatement
;
416 statement
= statement
->next
;
423 * clay_util_string_replace function:
424 * Search and replace a string with another string , in a string
425 * Minor modifications from :
426 * http://www.binarytides.com/blog/str_replace-for-c/
431 char* clay_util_string_replace(char *search
, char *replace
, char *string
) {
432 char *ptr
= NULL
, *old
= NULL
, *new_string
= NULL
;
433 int count
= 0 , search_size
;
435 search_size
= strlen(search
);
437 // Count how many occurences
438 for(ptr
= strstr(string
, search
) ; ptr
!= NULL
;
439 ptr
= strstr(ptr
+ search_size
, search
)) {
444 count
= (strlen(replace
) - search_size
)*count
+ strlen(string
) + 1;
445 new_string
= calloc(count
, 1);
447 // The start position
450 for(ptr
= strstr(string
, search
) ; ptr
!= NULL
;
451 ptr
= strstr(ptr
+ search_size
, search
)) {
452 // move ahead and copy some text from original subject , from a
454 strncpy(new_string
+ strlen(new_string
), old
, ptr
- old
);
456 // move ahead and copy the replacement text
457 strcpy(new_string
+ strlen(new_string
) , replace
);
459 // The new start position after this search match
460 old
= ptr
+ search_size
;
463 // Copy the part after the last search match
464 strcpy(new_string
+ strlen(new_string
) , old
);
471 * clay_util_scatnames_exists_iterator_iterator function:
472 * Return true if the iterator name is already in the scattering.
473 * \param[in] scattering
476 bool clay_util_scatnames_exists(osl_scatnames_p scatnames
, char *iter
) {
477 osl_strings_p names
= scatnames
->names
;
478 if (names
== NULL
|| names
->string
[0] == NULL
)
481 char **ptr
= names
->string
;
483 while (*ptr
!= NULL
) {
484 if (strcmp(*ptr
, iter
) == 0)
494 * clay_util_statement_find_iterator function:
495 * Return the index if iter is found in the original iterators list.
497 * \param[in] iter name of the original iterator we want to search
500 int clay_util_statement_find_iterator(osl_statement_p statement
, char *iter
) {
502 osl_extbody_p extbody
= NULL
;
504 extbody
= osl_generic_lookup(statement
->extension
, OSL_URI_EXTBODY
);
506 body
= extbody
->body
;
508 body
= osl_generic_lookup(statement
->extension
, OSL_URI_BODY
);
510 char **ptr
= body
->iterators
->string
;
513 while (*ptr
!= NULL
) {
514 if (strcmp(*ptr
, iter
) == 0)
525 * clay_util_scop_export_body function:
526 * Convert each extbody to a body structure
529 void clay_util_scop_export_body(osl_scop_p scop
) {
533 osl_statement_p stmt
= scop
->statement
;
534 osl_extbody_p ebody
= NULL
;
535 osl_body_p body
= NULL
;
536 osl_generic_p gen
= NULL
;
539 ebody
= osl_generic_lookup(stmt
->extension
, OSL_URI_EXTBODY
);
542 body
= osl_generic_lookup(stmt
->extension
, OSL_URI_BODY
);
544 osl_generic_remove(&stmt
->extension
, OSL_URI_BODY
);
546 body
= osl_body_clone(ebody
->body
);
547 gen
= osl_generic_shell(body
, osl_body_interface());
548 osl_generic_add(&stmt
->extension
, gen
);
549 osl_generic_remove(&stmt
->extension
, OSL_URI_EXTBODY
);
558 void static clay_util_name_sprint(char **dst
, int *hwm
,
559 int *print_plus
, int val
, char *name
) {
561 osl_util_safe_strcat(dst
, " + ", hwm
);
568 snprintf(buffer
, 32, "%d", val
);
569 osl_util_safe_strcat(dst
, buffer
, hwm
);
572 osl_util_safe_strcat(dst
, name
, hwm
);
573 } else if (val
== -1) {
574 osl_util_safe_strcat(dst
, "-", hwm
);
575 osl_util_safe_strcat(dst
, name
, hwm
);
577 snprintf(buffer
, 32, "%d*", val
);
578 osl_util_safe_strcat(dst
, buffer
, hwm
);
579 osl_util_safe_strcat(dst
, name
, hwm
);
586 * clay_util_body_regenerate_access function:
587 * Read the access array and re-generate the code in the body
588 * \param[in] ebody An extbody structure
589 * \param[in] access The relation to regenerate the code
590 * \param[in] index nth access (needed to access to the array start and
591 * length of the extbody structure)
593 void clay_util_body_regenerate_access(osl_extbody_p ebody
,
594 osl_relation_p access
,
597 osl_scatnames_p scatnames
,
598 osl_strings_p params
) {
600 if (!arrays
|| !scatnames
|| !params
|| access
->nb_output_dims
== 0 ||
601 index
>= ebody
->nb_access
)
604 const int precision
= access
->precision
;
605 int i
, j
, k
, row
, val
, print_plus
;
607 // check if there are no inequ
608 for (i
= 0 ; i
< access
->nb_rows
; i
++) {
609 if (!osl_int_zero(precision
, access
->m
[i
][0]))
610 CLAY_error("I don't know how to regenerate access with inequalities");
613 // check identity matrix in output dims
615 for (j
= 0 ; j
< access
->nb_output_dims
; j
++) {
617 for (i
= 0 ; i
< access
->nb_rows
; i
++)
618 if (!osl_int_zero(precision
, access
->m
[i
][j
+1])) {
620 CLAY_error("I don't know how to regenerate access with "
621 "dependences in output dims");
626 char *body
= ebody
->body
->expression
->string
[0];
627 int body_len
= strlen(body
);
628 int start
= ebody
->start
[index
];
629 int len
= ebody
->length
[index
];
630 int is_zero
; // if the line contains only zeros
632 if (start
>= body_len
|| start
+ len
>= body_len
|| (start
== -1 && len
== -1))
636 char end_body
[OSL_MAX_STRING
];
637 int hwm
= OSL_MAX_STRING
;
639 CLAY_malloc(new_body
, char *, OSL_MAX_STRING
* sizeof(char));
641 // copy the beginning of the body
642 if (start
+1 >= OSL_MAX_STRING
)
643 CLAY_error("memcpy: please recompile osl with a higher OSL_MAX_STRING");
644 memcpy(new_body
, body
, start
);
645 new_body
[start
] = '\0';
647 // save the end in a buffer
648 int sz
= body_len
- start
- len
;
649 if (sz
+ 1 >= OSL_MAX_STRING
)
650 CLAY_error("memcpy: please recompile osl with a higher OSL_MAX_STRING");
651 memcpy(end_body
, body
+ start
+ len
, sz
);
655 // copy access name string
656 val
= osl_relation_get_array_id(access
);
657 val
= clay_util_arrays_search(arrays
, val
); // get the index in th array
658 osl_util_safe_strcat(&new_body
, arrays
->names
[val
], &hwm
);
661 // generate each dims
662 for (i
= 1 ; i
< access
->nb_output_dims
; i
++) {
663 row
= clay_util_relation_get_line(access
, i
);
667 osl_util_safe_strcat(&new_body
, "[", &hwm
);
671 k
= 1 + access
->nb_output_dims
;
674 for (j
= 0 ; j
< access
->nb_input_dims
; j
++, k
++) {
675 val
= osl_int_get_si(precision
, access
->m
[row
][k
]);
677 clay_util_name_sprint(&new_body
,
681 scatnames
->names
->string
[j
*2+1]);
687 for (j
= 0 ; j
< access
->nb_parameters
; j
++, k
++) {
688 val
= osl_int_get_si(precision
, access
->m
[row
][k
]);
690 clay_util_name_sprint(&new_body
,
700 val
= osl_int_get_si(precision
, access
->m
[row
][k
]);
701 if (val
!= 0 || is_zero
)
702 clay_util_name_sprint(&new_body
,
708 osl_util_safe_strcat(&new_body
, "]", &hwm
);
711 // length of the generated access
712 ebody
->length
[index
] = strlen(new_body
) - start
;
715 osl_util_safe_strcat(&new_body
, end_body
, &hwm
);
718 free(ebody
->body
->expression
->string
[0]);
719 ebody
->body
->expression
->string
[0] = new_body
;
722 int diff
= ebody
->length
[index
] - len
;
723 for (i
= index
+1 ; i
< ebody
->nb_access
; i
++)
724 if (ebody
->start
[i
] != -1)
725 ebody
->start
[i
] += diff
;
730 * clay_util_arrays_search function:
731 * Search the string which corresponds to id
732 * arrays is an extension of osl
733 * \param[in] arrays An arrays osl structure
734 * \param[in] id The id to search
735 * \return Return the index in the arrays
737 int clay_util_arrays_search(osl_arrays_p arrays
, unsigned int id
) {
739 for (i
= 0 ; i
< arrays
->nb_names
; i
++) {
740 if (arrays
->id
[i
] == id
)
748 * clay_util_foreach_access function:
749 * Execute func on each access which corresponds to access_name
750 * \param[in,out] scop
752 * \param[in] access_name The id to search
753 * \param[in] func The function to execute for each access
754 * The function takes an osl_relation_list_p in
755 * parameter (the elt can be modified) and must
756 * return a define error or CLAY_SUCCESS
757 * \param[in] args args of `func'
758 * \param[in] regenerate_body If 1: after each call to func,
759 * clay_util_body_regenerate_access is also called
760 * \return Return a define error or CLAY_SUCCESS
762 int clay_util_foreach_access(osl_scop_p scop
,
764 unsigned int access_name
,
765 int (*func
)(osl_relation_list_p
, void*),
767 int regenerate_body
) {
769 osl_statement_p stmt
= scop
->statement
;
770 osl_relation_list_p access
;
772 osl_extbody_p ebody
= NULL
;
773 osl_body_p body
= NULL
;
774 osl_generic_p gen
= NULL
;
779 // TODO : global vars ?
781 osl_scatnames_p scatnames
;
782 osl_strings_p params
;
783 arrays
= osl_generic_lookup(scop
->extension
, OSL_URI_ARRAYS
);
784 scatnames
= osl_generic_lookup(scop
->extension
, OSL_URI_SCATNAMES
);
785 params
= osl_generic_lookup(scop
->parameters
, OSL_URI_STRINGS
);
787 if (!arrays
|| !scatnames
|| !params
)
788 CLAY_warning("no arrays or scatnames extension");
790 stmt
= clay_beta_find(scop
->statement
, beta
);
792 return CLAY_ERROR_BETA_NOT_FOUND
;
794 // for each access in the beta, we search the access_name
795 while (stmt
!= NULL
) {
796 if (clay_beta_check(stmt
, beta
)) {
797 access
= stmt
->access
;
803 if (osl_relation_get_array_id(a
) == access_name
) {
806 ebody
= osl_generic_lookup(stmt
->extension
, OSL_URI_EXTBODY
);
808 CLAY_error("extbody uri not found on this statement");
809 fprintf(stderr
, "%s\n",
810 ebody
->body
->expression
->string
[0]);
814 ret
= (*func
)(access
, args
);
815 if (ret
!= CLAY_SUCCESS
) {
816 fprintf(stderr
, "%s\n",
817 ebody
->body
->expression
->string
[0]);
821 // re-generate the body
822 if (regenerate_body
) {
823 clay_util_body_regenerate_access(
832 //synchronize extbody with body
833 body
= osl_generic_lookup(stmt
->extension
, OSL_URI_BODY
);
835 osl_generic_remove(&stmt
->extension
, OSL_URI_BODY
);
836 body
= osl_body_clone(ebody
->body
);
837 gen
= osl_generic_shell(body
, osl_body_interface());
838 osl_generic_add(&stmt
->extension
, gen
);
845 access
= access
->next
;
853 fprintf(stderr
,"[Clay] Warning: access number %d not found\n", access_name
);
860 * clay_util_relation_get_line function:
861 * Because the lines in the scattering matrix may have not ordered, we have to
862 * search the corresponding line. It returns the first line where the value is
863 * different from zero in the `column'. `column' is between 0 and
865 * \param[in] relation
866 * \param[in] column Line to search
867 * \return Return the real line
869 int clay_util_relation_get_line(osl_relation_p relation
, int column
) {
870 if (column
< 0 || column
> relation
->nb_output_dims
)
873 int precision
= relation
->precision
;
874 for (i
= 0 ; i
< relation
->nb_rows
; i
++) {
875 if (!osl_int_zero(precision
, relation
->m
[i
][column
+1])) {
879 return (i
== relation
->nb_rows
? -1 : i
);