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
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
36 #include <isl/id_to_id.h>
37 #include <isl/options.h>
38 #include <isl/printer.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.
49 struct isl_options
*isl
;
50 struct pet_options
*pet
;
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
)
60 ISL_ARG_DEF(options
, struct options
, options_args
)
62 /* Call "fn" on each declared array in "scop" that has an exposed field
65 static void foreach_declared_array(struct pet_scop
*scop
, int exposed
,
66 void (*fn
)(struct pet_array
*array
, void *user
), void *user
)
70 for (i
= 0; i
< scop
->n_array
; ++i
) {
71 struct pet_array
*array
= scop
->arrays
[i
];
73 if (array
->declared
&& array
->exposed
== exposed
)
78 /* foreach_declared_array callback that sets "indent"
80 static void set_indent(struct pet_array
*array
, void *user
)
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
{
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
;
110 isl_multi_pw_aff
*size
;
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
,
132 p
= isl_printer_start_line(p
);
133 p
= isl_printer_print_str(p
, str
);
134 p
= isl_printer_end_line(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
};
151 foreach_declared_array(scop
, 0, &set_indent
, indent
);
153 foreach_declared_array(scop
, 1, &print_array
, &data
);
156 p
= print_str_on_line(p
, "{");
157 p
= isl_printer_indent(p
, 2);
159 foreach_declared_array(scop
, 0, &print_array
, &data
);
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
,
172 p
= isl_printer_indent(p
, -2);
173 p
= print_str_on_line(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
)
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
;
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
);
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
;
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
);
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
,
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
;
285 struct pet_stmt
*stmt
;
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
);
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
)
312 isl_id_to_ast_expr
*ref2expr
;
314 id
= isl_ast_node_get_annotation(node
);
315 ref2expr
= isl_id_get_user(id
);
321 /* isl_id_to_ast_expr_foreach callback that prints the definitions of the macros
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
);
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
);
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
);
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
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
)
423 isl_schedule
*schedule
;
424 isl_ast_build
*build
;
426 isl_ast_print_options
*print_options
;
427 isl_id_to_id
*id2stmt
;
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
);
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
[])
468 struct options
*options
;
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
,