pet_stmt_build_ast_exprs: handle some nested operations inside accesses
[pet.git] / pet_loopback.c
blobfe22129794c80f4ccad3091c399b13e81e25de89
1 /*
2 * Copyright 2022 Sven Verdoolaege. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following
13 * disclaimer in the documentation and/or other materials provided
14 * with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY SVEN VERDOOLAEGE ''AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SVEN VERDOOLAEGE OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
23 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 * The views and conclusions contained in the software and documentation
29 * are those of the authors and should not be interpreted as
30 * representing official policies, either expressed or implied, of
31 * Sven Verdoolaege.
34 #include <isl/arg.h>
35 #include <isl/id.h>
36 #include <isl/id_to_id.h>
37 #include <isl/options.h>
38 #include <isl/printer.h>
39 #include <isl/val.h>
41 #include <pet.h>
43 /* The command line options of this example application.
45 * "isl" and "pet" are the isl and pet command line options.
46 * "input" is the input C file that needs to be parsed.
48 struct options {
49 struct isl_options *isl;
50 struct pet_options *pet;
51 char *input;
54 ISL_ARGS_START(struct options, options_args)
55 ISL_ARG_CHILD(struct options, isl, "isl", &isl_options_args, "isl options")
56 ISL_ARG_CHILD(struct options, pet, NULL, &pet_options_args, "pet options")
57 ISL_ARG_ARG(struct options, input, "input-C-file", NULL)
58 ISL_ARGS_END
60 ISL_ARG_DEF(options, struct options, options_args)
62 /* Call "fn" on each declared array in "scop" that has an exposed field
63 * equal to "exposed".
65 static void foreach_declared_array(struct pet_scop *scop, int exposed,
66 void (*fn)(struct pet_array *array, void *user), void *user)
68 int i;
70 for (i = 0; i < scop->n_array; ++i) {
71 struct pet_array *array = scop->arrays[i];
73 if (array->declared && array->exposed == exposed)
74 fn(array, user);
78 /* foreach_declared_array callback that sets "indent"
80 static void set_indent(struct pet_array *array, void *user)
82 int *indent = user;
84 *indent = 1;
87 /* Internal data structure for print_array().
88 * "p" is the printer to print on.
89 * "build" is the build for building expressions.
91 struct print_array_data {
92 isl_printer *p;
93 isl_ast_build *build;
96 /* Print a declaration for "array' to data->p.
98 * The size of the array is obtained from the extent.
99 * In particular, it is one more than the largest value
100 * in every dimension.
101 * Use data->build to build an AST expression for this size.
102 * Just in case this AST expression contains any macro calls,
103 * print all the corresponding macro definitions before printing
104 * the actual declaration.
106 static void print_array(struct pet_array *array, void *user)
108 struct print_array_data *data = user;
109 isl_val *one;
110 isl_multi_pw_aff *size;
111 isl_ast_expr *expr;
113 one = isl_val_one(isl_set_get_ctx(array->extent));
114 size = isl_set_max_multi_pw_aff(isl_set_copy(array->extent));
115 size = isl_multi_pw_aff_add_constant_val(size, one);
116 expr = isl_ast_build_access_from_multi_pw_aff(data->build, size);
117 data->p = isl_ast_expr_print_macros(expr, data->p);
118 data->p = isl_printer_start_line(data->p);
119 data->p = isl_printer_print_str(data->p, array->element_type);
120 data->p = isl_printer_print_str(data->p, " ");
121 data->p = isl_printer_print_ast_expr(data->p, expr);
122 data->p = isl_printer_print_str(data->p, ";");
123 data->p = isl_printer_end_line(data->p);
124 isl_ast_expr_free(expr);
127 /* Print "str" to "p" on a separate line.
129 static __isl_give isl_printer *print_str_on_line(__isl_take isl_printer *p,
130 const char *str)
132 p = isl_printer_start_line(p);
133 p = isl_printer_print_str(p, str);
134 p = isl_printer_end_line(p);
136 return p;
139 /* Print declarations for all declared arrays, putting the hidden (non-exposed)
140 * ones in a separate scope. Set "indent" if there are any such arrays and
141 * therefore a separate scope was created.
143 static __isl_give isl_printer *print_declarations(__isl_take isl_printer *p,
144 __isl_keep isl_ast_build *build, struct pet_scop *scop, int *indent)
146 struct print_array_data data = { .p = p, .build = build };
147 int i;
149 *indent = 0;
151 foreach_declared_array(scop, 0, &set_indent, indent);
153 foreach_declared_array(scop, 1, &print_array, &data);
155 if (*indent) {
156 p = print_str_on_line(p, "{");
157 p = isl_printer_indent(p, 2);
159 foreach_declared_array(scop, 0, &print_array, &data);
162 return p;
165 /* Close the scope created by print_declarations() if any,
166 * i.e., if "indent" is set.
168 static __isl_give isl_printer *print_end_declarations(__isl_take isl_printer *p,
169 int indent)
171 if (indent) {
172 p = isl_printer_indent(p, -2);
173 p = print_str_on_line(p, "}");
176 return p;
179 /* Set up a mapping from statement names to the corresponding statements.
180 * Each statement is attached as a user pointer to an identifier
181 * with the same name as the statement.
183 static __isl_give isl_id_to_id *set_up_id2stmt(struct pet_scop *scop)
185 int i;
186 isl_ctx *ctx;
187 isl_id_to_id *id2stmt;
189 ctx = isl_set_get_ctx(scop->context);
190 id2stmt = isl_id_to_id_alloc(ctx, scop->n_stmt);
192 for (i = 0; i < scop->n_stmt; ++i) {
193 struct pet_stmt *stmt = scop->stmts[i];
194 isl_id *tuple_id, *id;
195 const char *name;
197 tuple_id = isl_set_get_tuple_id(stmt->domain);
198 name = isl_id_get_name(tuple_id);
199 id = isl_id_alloc(ctx, name, stmt);
200 id2stmt = isl_id_to_id_set(id2stmt, tuple_id, id);
203 return id2stmt;
206 /* Return the pet_stmt corresponding to "node", assuming it is a user node
207 * in an AST generated by isl_ast_build_node_from_schedule and
208 * looking up such statements in "id2stmt".
210 * A user node in an AST generated by isl_ast_build_node_from_schedule
211 * performs a call to the statement. That is, the statement name
212 * is the first argument of the associated call expression.
214 * Extract this statement name and then look it up in "id2stmt".
216 static struct pet_stmt *node_stmt(__isl_keep isl_ast_node *node,
217 isl_id_to_id *id2stmt)
219 isl_ast_expr *expr, *arg;
220 isl_id *id;
221 struct pet_stmt *stmt;
223 expr = isl_ast_node_user_get_expr(node);
224 arg = isl_ast_expr_get_op_arg(expr, 0);
225 isl_ast_expr_free(expr);
226 id = isl_ast_expr_get_id(arg);
227 isl_ast_expr_free(arg);
229 id = isl_id_to_id_get(id2stmt, id);
230 stmt = isl_id_get_user(id);
231 isl_id_free(id);
233 return stmt;
236 /* pet_stmt_build_ast_exprs callback for transforming
237 * the index expression "index" of the reference with identifier "ref_id".
239 * In particular, pullback "index" over the function in "user".
241 static __isl_give isl_multi_pw_aff *pullback_index(
242 __isl_take isl_multi_pw_aff *index, __isl_keep isl_id *ref_id,
243 void *user)
245 isl_pw_multi_aff *fn = user;
247 fn = isl_pw_multi_aff_copy(fn);
248 return isl_multi_pw_aff_pullback_pw_multi_aff(index, fn);
251 /* isl_id_set_free_user callback for freeing
252 * a user pointer of type isl_id_to_ast_expr.
254 static void free_isl_id_to_ast_expr(void *user)
256 isl_id_to_ast_expr *id_to_ast_expr = user;
258 isl_id_to_ast_expr_free(id_to_ast_expr);
261 /* This callback is called on each leaf node of the AST generated
262 * by isl_ast_build_node_from_schedule.
263 * "node" is the generated leaf node.
264 * "build" is the build within which the leaf node is generated.
266 * Obtain the schedule at the point where the leaf node is generated.
267 * This is known to apply to a single statement, the one for which
268 * the leaf node is being generated.
269 * It is also known to map statement instances to unique elements
270 * in the target space. This means the inverse mapping is single-valued and
271 * can be converted to a function. Use this function to reformulate
272 * all index expressions to refer to the schedule dimensions
273 * when generating AST expressions for all accesses
274 * in pet_stmt_build_ast_exprs.
276 * Attach the generated AST expressions, keyed off the corresponding
277 * reference identifiers, to the AST node as an annotation.
278 * This annotation will be retrieved in peek_ref2expr().
280 static __isl_give isl_ast_node *at_domain(__isl_take isl_ast_node *node,
281 __isl_keep isl_ast_build *build, void *user)
283 isl_id_to_id *id2stmt = user;
284 isl_id *id;
285 struct pet_stmt *stmt;
286 isl_map *schedule;
287 isl_pw_multi_aff *reverse;
288 isl_id_to_ast_expr *ref2expr;
290 stmt = node_stmt(node, id2stmt);
292 schedule = isl_map_from_union_map(isl_ast_build_get_schedule(build));
293 reverse = isl_pw_multi_aff_from_map(isl_map_reverse(schedule));
294 ref2expr = pet_stmt_build_ast_exprs(stmt, build,
295 &pullback_index, reverse, NULL, NULL);
296 isl_pw_multi_aff_free(reverse);
298 id = isl_id_alloc(isl_ast_node_get_ctx(node), NULL, ref2expr);
299 id = isl_id_set_free_user(id, &free_isl_id_to_ast_expr);
300 node = isl_ast_node_set_annotation(node, id);
301 return node;
304 /* Return the generated AST expressions, keyed off the corresponding
305 * reference identifiers, that were attached to "node"
306 * as an annotation in at_domain().
308 static __isl_keep isl_id_to_ast_expr *peek_ref2expr(
309 __isl_keep isl_ast_node *node)
311 isl_id *id;
312 isl_id_to_ast_expr *ref2expr;
314 id = isl_ast_node_get_annotation(node);
315 ref2expr = isl_id_get_user(id);
316 isl_id_free(id);
318 return ref2expr;
321 /* isl_id_to_ast_expr_foreach callback that prints the definitions of the macros
322 * called by "expr".
324 static isl_stat expr_print_macros(__isl_take isl_id *id,
325 __isl_take isl_ast_expr *expr, void *user)
327 isl_printer **p = user;
329 *p = isl_ast_expr_print_macros(expr, *p);
331 isl_id_free(id);
332 isl_ast_expr_free(expr);
334 return isl_stat_non_null(*p);
337 /* If "node" is a user node, then print the definitions of the macros
338 * that get called in the AST expressions attached to the node.
340 static isl_bool node_print_macros(__isl_keep isl_ast_node *node, void *user)
342 isl_id_to_ast_expr *ref2expr;
344 if (isl_ast_node_get_type(node) != isl_ast_node_user)
345 return isl_bool_true;
347 ref2expr = peek_ref2expr(node);
348 if (isl_id_to_ast_expr_foreach(ref2expr, &expr_print_macros, user) < 0)
349 return isl_bool_error;
351 return isl_bool_false;
354 /* Print the definitions of the macros that get called by "node".
355 * This includes any macros that get called in the AST expressions
356 * attached to the user nodes.
358 static __isl_give isl_printer *print_macros(__isl_take isl_printer *p,
359 __isl_keep isl_ast_node *node)
361 if (isl_ast_node_foreach_descendant_top_down(node,
362 &node_print_macros, &p) < 0)
363 return isl_printer_free(p);
364 p = isl_ast_node_print_macros(node, p);
365 return p;
368 /* Print the body of the statement corresponding to user node "node" to "p".
370 static __isl_give isl_printer *print_user(__isl_take isl_printer *p,
371 __isl_take isl_ast_print_options *options,
372 __isl_keep isl_ast_node *node, void *user)
374 isl_id_to_id *id2stmt = user;
375 struct pet_stmt *stmt;
376 isl_id_to_ast_expr *ref2expr;
378 stmt = node_stmt(node, id2stmt);
379 ref2expr = peek_ref2expr(node);
381 p = pet_stmt_print_body(stmt, p, ref2expr);
383 isl_ast_print_options_free(options);
385 return p;
388 /* This function is called for each each scop detected in the input file and
389 * is expected to write (a transformed version of) the scop "scop"
390 * to the printer "p".
391 * "user" is the value passed to pet_transform_C_source.
393 * This particular callback does not perform any transformation and
394 * simply prints out the original scop.
395 * "user" is set to NULL.
397 * First build a map from statement names to the corresponding statements.
398 * This will be used to recover the statements from their names
399 * in at_domain() and print_user().
401 * Then create an isl_ast_build that will be used to build all AST nodes and
402 * expressions. Set a callback that will be called
403 * by isl_ast_build_node_from_schedule for each leaf node.
404 * This callback takes care of creating AST expressions
405 * for all accesses in the corresponding statement and attaches
406 * them to the node.
408 * Generate an AST using the original schedule and print it
409 * using print_user() for printing statement bodies.
411 * Before printing the AST itself, print out the declarations
412 * of any variables that are declared inside the scop, as well as
413 * the definitions of any macros that are used in the generated AST or
414 * any of the generated AST expressions.
415 * Finally, close any scope that may have been opened
416 * to print variable declarations.
418 static __isl_give isl_printer *transform(__isl_take isl_printer *p,
419 struct pet_scop *scop, void *user)
421 int indent;
422 isl_ctx *ctx;
423 isl_schedule *schedule;
424 isl_ast_build *build;
425 isl_ast_node *node;
426 isl_ast_print_options *print_options;
427 isl_id_to_id *id2stmt;
429 if (!scop || !p)
430 return isl_printer_free(p);
432 ctx = isl_printer_get_ctx(p);
433 schedule = isl_schedule_copy(scop->schedule);
434 id2stmt = set_up_id2stmt(scop);
435 build = isl_ast_build_alloc(ctx);
436 build = isl_ast_build_set_at_each_domain(build, &at_domain, id2stmt);
437 node = isl_ast_build_node_from_schedule(build, schedule);
438 print_options = isl_ast_print_options_alloc(ctx);
439 print_options = isl_ast_print_options_set_print_user(print_options,
440 &print_user, id2stmt);
441 p = print_declarations(p, build, scop, &indent);
442 p = print_macros(p, node);
443 p = isl_ast_node_print(node, p, print_options);
444 p = print_end_declarations(p, indent);
445 isl_ast_node_free(node);
446 isl_ast_build_free(build);
447 isl_id_to_id_free(id2stmt);
448 pet_scop_free(scop);
450 return p;
453 /* This is an example application that uses pet to parse a C file and
454 * prints out the code generated from the extracted polyhedral model
455 * without any transformation.
457 * First set some default options
458 * - only print required macro definitions once
459 * - encapsulate dynamic control into the extracted statements
461 * Then transform the specified input file, transforming
462 * each scop using the dummy transform() function and
463 * writing the result to stdout.
465 int main(int argc, char *argv[])
467 isl_ctx *ctx;
468 struct options *options;
469 int r;
471 options = options_new_with_defaults();
472 ctx = isl_ctx_alloc_with_options(&options_args, options);
473 isl_options_set_ast_print_macro_once(ctx, 1);
474 pet_options_set_encapsulate_dynamic_control(ctx, 1);
475 argc = options_parse(options, argc, argv, ISL_ARG_ALL);
477 r = pet_transform_C_source(ctx, options->input, stdout,
478 &transform, NULL);
480 isl_ctx_free(ctx);
481 return r;