8 #include <isl/constraint.h>
12 #include "c2pdg_options.h"
14 const char dot_yaml
[] = ".yaml";
16 static __isl_give isl_map
*isl_map_equal_dims(__isl_take isl_space
*dim
,
17 enum isl_dim_type type1
, int pos1
, enum isl_dim_type type2
, int pos2
)
19 isl_map
*map
= isl_map_universe(dim
);
20 return isl_map_equate(map
, type1
, pos1
, type2
, pos2
);
23 static __isl_give isl_map
*isl_map_opposite_dims(__isl_take isl_space
*dim
,
24 enum isl_dim_type type1
, int pos1
, enum isl_dim_type type2
, int pos2
)
26 isl_map
*map
= isl_map_universe(dim
);
27 return isl_map_oppose(map
, type1
, pos1
, type2
, pos2
);
30 static pdg::call_or_access
*convert_expr(pdg::PDG
*pdg
,
31 pdg::statement
*stat
, struct pet_expr
*expr
,
32 std::map
<isl_id
*, pdg::array
*> &id2array
, __isl_keep isl_map
*trans
);
34 /* Convert the access pet_expr to a pdg::access and return it.
35 * We assume that the pet_expr accesses data in a single space
36 * (in particular, we do not support accesses to different fields
37 * of a struct) and that it is either a write or a read (and not both).
38 * The pdg::access is also added to the list of accesses in "stat".
39 * The pointer to the pdg::array is taken from "id2array" based
40 * on the tuple id on the access relation.
42 * The domain of the access relation is transformed using "trans".
43 * If the access has any arguments then the domain of the access relation
44 * is a wrapped mapping from the iteration space to the space of
45 * argument values. We only need to change the domain of this wrapped
46 * mapping, so we extend the input transformation with an identity mapping
47 * on the space of argument values.
49 static pdg::access
*extract_access(pdg::PDG
*pdg
, pdg::statement
*stat
,
50 struct pet_expr
*expr
, std::map
<isl_id
*, pdg::array
*> &id2array
,
51 __isl_keep isl_map
*trans
)
53 pdg::access
*access
= new pdg::access
;
61 n
= pet_expr_get_n_arg(expr
);
62 for (int i
= 0; i
< n
; ++i
) {
63 pdg::call_or_access
*coa
;
65 arg
= pet_expr_get_arg(expr
, i
);
66 coa
= convert_expr(pdg
, stat
, arg
, id2array
, trans
);
68 access
->nested
.push_back(coa
);
71 if (pet_expr_access_is_write(expr
)) {
72 access
->type
= pdg::access::write
;
73 umap
= pet_expr_access_get_dependent_may_write(expr
);
75 access
->type
= pdg::access::read
;
76 umap
= pet_expr_access_get_dependent_may_read(expr
);
79 assert(isl_union_map_n_map(umap
) == 1);
81 map
= isl_map_from_union_map(umap
);
82 trans
= isl_map_copy(trans
);
84 dim
= isl_space_domain(isl_map_get_space(map
));
85 if (!isl_space_is_wrapping(dim
))
89 dim
= isl_space_unwrap(dim
);
90 dim
= isl_space_range(dim
);
91 dim
= isl_space_map_from_set(dim
);
92 id
= isl_map_identity(dim
);
93 trans
= isl_map_product(trans
, id
);
96 map
= isl_map_apply_domain(map
, trans
);
97 if (isl_map_has_tuple_id(map
, isl_dim_out
))
98 id
= isl_map_get_tuple_id(map
, isl_dim_out
);
99 n_index
= isl_map_dim(map
, isl_dim_out
);
100 for (int i
= n_index
- 1; i
>= 0; --i
)
101 if (!isl_map_involves_dims(map
, isl_dim_out
, i
, 1))
102 map
= isl_map_project_out(map
, isl_dim_out
, i
, 1);
106 map
= isl_map_set_tuple_id(map
, isl_dim_out
, id
);
107 access
->map
= new pdg::IslMap(map
);
109 if (!pet_expr_is_affine(expr
)) {
110 id
= pet_expr_access_get_id(expr
);
111 access
->array
= id2array
[id
];
115 stat
->accesses
.push_back(access
);
120 /* Convert the integer pet_expr to a pdg::access and return this pdg::access.
121 * The pdg::access is also added to the list of accesses in "stat".
123 * The domain of the access relation derived from the integer value
124 * is equal to the range space of "trans".
126 static pdg::access
*extract_access_from_int(pdg::PDG
*pdg
, pdg::statement
*stat
,
127 struct pet_expr
*expr
, __isl_keep isl_map
*trans
)
129 pdg::access
*access
= new pdg::access
;
135 access
->type
= pdg::access::read
;
137 v
= pet_expr_int_get_val(expr
);
138 space
= isl_space_range(isl_map_get_space(trans
));
139 aff
= isl_aff_val_on_domain(isl_local_space_from_space(space
), v
);
140 pa
= isl_pw_aff_from_aff(aff
);
142 access
->map
= new pdg::IslMap(isl_map_from_pw_aff(pa
));
144 stat
->accesses
.push_back(access
);
149 static pdg::call_or_access
*convert_expr(pdg::PDG
*pdg
,
150 pdg::statement
*stat
, struct pet_expr
*expr
,
151 std::map
<isl_id
*, pdg::array
*> &id2array
, __isl_keep isl_map
*trans
)
153 pdg::call_or_access
*coa
= new pdg::call_or_access
;
154 enum pet_expr_type type
;
156 type
= pet_expr_get_type(expr
);
157 if (type
== pet_expr_access
) {
158 coa
->type
= pdg::call_or_access::t_access
;
159 coa
->access
= extract_access(pdg
, stat
, expr
, id2array
, trans
);
160 } else if (type
== pet_expr_int
) {
161 coa
->type
= pdg::call_or_access::t_access
;
162 coa
->access
= extract_access_from_int(pdg
, stat
, expr
, trans
);
166 coa
->type
= pdg::call_or_access::t_call
;
167 coa
->call
= new pdg::function_call
;
169 if (type
== pet_expr_call
)
170 coa
->call
->name
= new str(pet_expr_call_get_name(expr
));
171 else if (type
== pet_expr_double
) {
172 char *s
= pet_expr_double_get_str(expr
);
173 coa
->call
->name
= new str(s
);
175 } else if (type
== pet_expr_op
&&
176 pet_expr_op_get_type(expr
) == pet_op_cond
)
177 coa
->call
->name
= new str("#test");
180 new str(pet_op_str(pet_expr_op_get_type(expr
)));
182 n
= pet_expr_get_n_arg(expr
);
183 for (int i
= 0; i
< n
; ++i
) {
184 pdg::call_or_access
*child
;
186 arg
= pet_expr_get_arg(expr
, i
);
187 child
= convert_expr(pdg
, stat
, arg
, id2array
, trans
);
189 coa
->call
->arguments
.push_back(child
);
196 static pdg::function_call
*extract_top_function(pdg::PDG
*pdg
,
197 pdg::statement
*stat
, struct pet_expr
*expr
,
198 std::map
<isl_id
*, pdg::array
*> &id2array
, __isl_keep isl_map
*trans
)
200 pdg::function_call
*call
;
201 pdg::call_or_access
*coa
= convert_expr(pdg
, stat
, expr
, id2array
, trans
);
203 if (coa
->type
== pdg::call_or_access::t_access
) {
204 call
= new pdg::function_call
;
205 call
->arguments
.push_back(coa
);
206 call
->name
= new str(string(""));
215 /* Extract a function call for the top-level function called from "tree".
216 * We currently assume the tree is an expression statement and
217 * extract the function call from the expression.
219 static pdg::function_call
*extract_top_function(pdg::PDG
*pdg
,
220 pdg::statement
*stat
, __isl_keep pet_tree
*tree
,
221 std::map
<isl_id
*, pdg::array
*> &id2array
, __isl_keep isl_map
*trans
)
223 pdg::function_call
*call
;
226 assert(pet_tree_get_type(tree
) == pet_tree_expr
);
227 expr
= pet_tree_expr_get_expr(tree
);
228 call
= extract_top_function(pdg
, stat
, expr
, id2array
, trans
);
234 /* For each of the filters ("arguments") of "stmt", check if the user
235 * has specified any bounds on the values of the corresponding array
236 * elements. If so, apply those bounds to the filter in the iteration
238 * Return the (possibly) restricted iteration domain.
240 static __isl_give isl_set
*apply_filter_bounds(__isl_take isl_set
*dom
,
241 struct pet_stmt
*stmt
, std::map
<isl_id
*, pdg::array
*> &id2array
)
245 if (!isl_set_is_wrapping(dom
))
248 map
= isl_set_unwrap(dom
);
250 for (int i
= 0; i
< stmt
->n_arg
; ++i
) {
253 pet_expr
*expr
= stmt
->args
[i
];
256 if (pet_expr_get_type(expr
) != pet_expr_access
)
259 id
= pet_expr_access_get_id(expr
);
260 array
= id2array
[id
];
263 if (!array
->value_bounds
)
265 bound
= array
->value_bounds
->get_isl_set();
266 bound
= isl_set_insert_dims(bound
, isl_dim_set
, 0, i
);
267 bound
= isl_set_add_dims(bound
, isl_dim_set
,
268 stmt
->n_arg
- (i
+ 1));
269 map
= isl_map_intersect_range(map
, bound
);
272 return isl_map_wrap(map
);
275 /* Given a filter access function encoded in "coa" corresponding to
276 * filter "pos" in "domain", extend the function to a filter access
277 * relation by exploiting the implications in "scop".
278 * In particular, if a statement depends on all previous iterations
279 * of some (other) statement (not) having executed, then it is marked
280 * by pet as only depending on the latest iteration of that statement.
281 * The implicit dependence on all previous iterations is encoded
282 * in the implications in "scop".
284 * If the given filter has a fixed filter value that matches the satisfied
285 * field of one of the implications and if that implication also references
286 * the same virtual array, then the corresponding extension is applied
287 * to the original filter access function to extend it to the complete
288 * filter access relation.
289 * Otherwise, we leave the filter access function untouched.
291 static void apply_implications(pdg::call_or_access
*coa
, struct pet_scop
*scop
,
292 __isl_keep isl_set
*domain
, int pos
)
298 int is_int
, satisfied
;
300 if (scop
->n_implication
== 0)
303 ext_domain
= isl_set_unwrap(isl_set_copy(domain
));
304 v
= isl_map_plain_get_val_if_fixed(ext_domain
, isl_dim_out
, pos
);
305 is_int
= isl_val_is_int(v
);
307 satisfied
= isl_val_get_num_si(v
);
309 isl_map_free(ext_domain
);
313 assert(coa
->type
== pdg::call_or_access::t_access
);
314 map
= coa
->access
->map
->map
;
315 map_id
= isl_map_get_tuple_id(map
, isl_dim_out
);
317 for (int i
= 0; i
< scop
->n_implication
; ++i
) {
318 struct pet_implication
*pi
;
321 pi
= scop
->implications
[i
];
323 if (pi
->satisfied
!= satisfied
)
325 pi_id
= isl_map_get_tuple_id(pi
->extension
, isl_dim_in
);
330 map
= isl_map_apply_range(map
, isl_map_copy(pi
->extension
));
334 coa
->access
->map
->map
= map
;
339 /* Does "expr" perform a (possibly compound) assignment?
341 static int is_assignment(__isl_keep pet_expr
*expr
)
346 if (pet_expr_get_type(expr
) != pet_expr_op
)
348 if (pet_expr_op_get_type(expr
) > pet_op_assign
)
350 arg
= pet_expr_get_arg(expr
, 0);
351 is_access
= pet_expr_get_type(arg
) == pet_expr_access
;
357 /* Does "expr" perform an increment or decrement operation?
359 static int is_inc_dec(__isl_keep pet_expr
*expr
)
364 if (pet_expr_get_type(expr
) != pet_expr_op
)
366 if (!pet_op_is_inc_dec(pet_expr_op_get_type(expr
)))
368 arg
= pet_expr_get_arg(expr
, 0);
369 is_access
= pet_expr_get_type(arg
) == pet_expr_access
;
375 /* Is "tree" an expression statement that satisfies "fn"?
377 static int is_expr_tree(__isl_keep pet_tree
*tree
,
378 int (*fn
)(__isl_keep pet_expr
*expr
))
383 if (pet_tree_get_type(tree
) != pet_tree_expr
)
385 expr
= pet_tree_expr_get_expr(tree
);
392 /* Does "tree" perform a (possibly compound) assignment?
394 static int is_assignment(__isl_keep pet_tree
*tree
)
396 return is_expr_tree(tree
, &is_assignment
);
399 /* Does "tree" perform an increment or decrement operation?
401 static int is_inc_dec(__isl_keep pet_tree
*tree
)
403 return is_expr_tree(tree
, &is_inc_dec
);
406 /* Create and return a pdg::node correspondingto stmt.
407 * Since isa uses the "prefix" field in a pdg::node to represent the
408 * original schedule, we try to extract such a prefix from the schedule.
409 * If one of the original for loops had a decrement, then the corresponding
410 * part of the schedule will be of the form [i] -> [-i]. Since such
411 * an inverse cannot be represented by "prefix", we invert the corresponding
412 * dimension of the iteration domain instead, taking care to also
413 * change the domains of all access relations accordingly.
415 * If 'stmt" has any arguments, then stmt->domain is a wrapped map
416 * and the transformation on the iteration domain only applies
417 * to the domain of that map. We therefore need to combine that
418 * transformation with an identity mapping on the range before
419 * applying it to the domain.
420 * Any arguments found are also converted into filters on the node
421 * domain. The original filter access functions are extended to
422 * filter access relations using the implications in "scop".
424 static pdg::node
*extract_node(pdg::PDG
*pdg
, struct pet_scop
*scop
,
425 struct pet_stmt
*stmt
, std::map
<isl_id
*, pdg::array
*> &id2array
)
427 pdg::node
*node
= new pdg::node
;
428 pdg::statement
*stat
;
434 isl_map
*trans
, *trans_dom
;
436 space
= isl_set_get_space(stmt
->domain
);
437 if (isl_space_is_wrapping(space
))
438 space
= isl_space_domain(isl_space_unwrap(space
));
439 trans
= isl_map_universe(isl_space_map_from_set(space
));
441 n_out
= isl_map_dim(stmt
->schedule
, isl_dim_out
);
442 for (int i
= 0, j
= 0; i
< n_out
; ++i
) {
443 v
= isl_map_plain_get_val_if_fixed(stmt
->schedule
,
445 if (!isl_val_is_nan(v
))
446 node
->prefix
.push_back(isl_val_get_num_si(v
));
450 dim
= isl_map_get_space(stmt
->schedule
);
451 test
= isl_map_equal_dims(dim
, isl_dim_in
, j
,
453 if (isl_map_is_subset(stmt
->schedule
, test
)) {
454 trans
= isl_map_equate(trans
, isl_dim_in
, j
,
458 dim
= isl_map_get_space(stmt
->schedule
);
459 test
= isl_map_opposite_dims(dim
, isl_dim_in
, j
,
461 assert(isl_map_is_subset(stmt
->schedule
, test
));
462 trans
= isl_map_oppose(trans
, isl_dim_in
, j
,
467 node
->prefix
.push_back(-1);
472 dom
= isl_set_copy(stmt
->domain
);
473 trans_dom
= isl_map_copy(trans
);
474 if (isl_set_is_wrapping(dom
)) {
475 isl_space
*space
= isl_set_get_space(dom
);
477 space
= isl_space_range(isl_space_unwrap(space
));
478 id
= isl_map_identity(isl_space_map_from_set(space
));
479 trans_dom
= isl_map_product(trans_dom
, id
);
481 dom
= isl_set_apply(dom
, trans_dom
);
482 dom
= apply_filter_bounds(dom
, stmt
, id2array
);
483 node
->source
= new pdg::IslSet(dom
);
484 node
->name
= new str(isl_map_get_tuple_name(stmt
->schedule
,
487 stat
= new pdg::statement
;
488 node
->statement
= stat
;
490 stat
->line
= pet_loc_get_line(stmt
->loc
);
492 for (int i
= 0; i
< stmt
->n_arg
; ++i
) {
493 pdg::call_or_access
*coa
;
494 coa
= convert_expr(pdg
, stat
, stmt
->args
[i
], id2array
, trans
);
495 apply_implications(coa
, scop
, node
->source
->set
, i
);
496 node
->filters
.push_back(coa
);
499 if (is_assignment(stmt
->body
)) {
501 pet_expr
*expr
, *arg
;
502 expr
= pet_tree_expr_get_expr(stmt
->body
);
503 if (pet_expr_op_get_type(expr
) != pet_op_assign
) {
504 arg
= pet_expr_get_arg(expr
, 0);
505 access
= extract_access(pdg
, stat
,
506 arg
, id2array
, trans
);
508 access
->type
= pdg::access::read
;
510 arg
= pet_expr_get_arg(expr
, 1);
511 stat
->top_function
= extract_top_function(pdg
, stat
,
512 arg
, id2array
, trans
);
514 arg
= pet_expr_get_arg(expr
, 0);
515 access
= extract_access(pdg
, stat
, arg
, id2array
, trans
);
517 stat
->top_outputs
.push_back(access
);
519 } else if (is_inc_dec(stmt
->body
)) {
521 pet_expr
*expr
, *arg
;
522 expr
= pet_tree_expr_get_expr(stmt
->body
);
523 arg
= pet_expr_get_arg(expr
, 0);
524 access
= extract_access(pdg
, stat
, arg
, id2array
, trans
);
525 access
->type
= pdg::access::read
;
526 stat
->top_function
= new pdg::function_call
;
527 stat
->top_function
->name
=
528 new str(pet_op_str(pet_expr_op_get_type(expr
)));
529 access
= extract_access(pdg
, stat
, arg
, id2array
, trans
);
530 stat
->top_outputs
.push_back(access
);
534 stat
->top_function
= extract_top_function(pdg
, stat
,
535 stmt
->body
, id2array
, trans
);
538 for (int i
= 0; i
< stat
->accesses
.size(); ++i
)
539 if (stat
->accesses
[i
]->type
== pdg::access::read
)
540 stat
->accesses
[i
]->nr
= nr
++;
541 for (int i
= 0; i
< stat
->accesses
.size(); ++i
)
542 if (stat
->accesses
[i
]->type
!= pdg::access::read
)
543 stat
->accesses
[i
]->nr
= nr
++;
550 /* Create and return a pdg::array corresponding to pa and add it
553 static pdg::array
*extract_array(pdg::PDG
*pdg
, struct pet_array
*pa
,
554 std::map
<isl_id
*, pdg::array
*> &id2array
)
556 isl_id
*id
= isl_set_get_tuple_id(pa
->extent
);
557 pdg::array
*array
= new pdg::array
;
558 int dim
= isl_set_dim(pa
->extent
, isl_dim_set
);
560 array
->name
= new str(isl_id_get_name(id
));
561 array
->element_type
= new str(pa
->element_type
);
562 for (int j
= 0; j
< dim
; ++j
) {
567 dim
= isl_set_get_space(pa
->extent
);
568 obj
= isl_aff_zero_on_domain(isl_local_space_from_space(dim
));
569 obj
= isl_aff_add_coefficient_si(obj
, isl_dim_in
, j
, 1);
570 v
= isl_set_max_val(pa
->extent
, obj
);
572 if (isl_val_is_int(v
))
573 array
->dims
.push_back(isl_val_get_num_si(v
) + 1);
575 array
->dims
.push_back(-1);
578 if (pa
->value_bounds
)
579 array
->value_bounds
=
580 new pdg::IslSet(isl_set_copy(pa
->value_bounds
));
581 array
->uniquely_defined
= pa
->uniquely_defined
;
583 id2array
[id
] = array
;
590 /* Assign a default value to parameter "p", which corresponds
591 * to the parameter at position "i" in scop->context.
593 * If the parameter appears in scop->context_value and if
594 * it has a fixed value, then use that value.
595 * Otherwise find the minimual value of "p" in scop->context.
596 * It there is no minimal value, then we currently leave p->value unset.
598 static void set_default_parameter_value(pdg::parameter
*p
,
599 __isl_keep isl_id
*id
, struct pet_scop
*scop
, int i
)
606 pos
= isl_set_find_dim_by_id(scop
->context_value
, isl_dim_param
, id
);
608 v
= isl_set_plain_get_val_if_fixed(scop
->context_value
,
610 if (isl_val_is_int(v
)) {
611 p
->value
= new integer(isl_val_get_num_si(v
));
618 space
= isl_set_get_space(scop
->context
);
619 obj
= isl_aff_zero_on_domain(isl_local_space_from_space(space
));
620 obj
= isl_aff_add_coefficient_si(obj
, isl_dim_param
, i
, 1);
621 v
= isl_set_min_val(scop
->context
, obj
);
623 if (isl_val_is_int(v
))
624 p
->value
= new integer(isl_val_get_num_si(v
));
629 int main(int argc
, char **argv
)
632 struct options
*options
= options_new_with_defaults();
633 struct pet_scop
*scop
;
640 std::map
<isl_id
*, pdg::array
*> id2array
;
642 ctx
= isl_ctx_alloc_with_options(&options_args
, options
);
644 fprintf(stderr
, "Unable to allocate ctx\n");
648 pdg
= new pdg::PDG(ctx
);
649 pdg
->add_history_line("c2pdg", argc
, argv
);
650 pdg
->placement
= new str(string("original"));
652 pet_options_set_autodetect(ctx
, 1);
653 argc
= options_parse(options
, argc
, argv
, ISL_ARG_ALL
);
655 len
= strlen(options
->input
);
656 if (len
> 2 && !strcmp(options
->input
+ len
- 2, ".c"))
658 output_name
= isl_alloc_array(ctx
, char, len
+ sizeof(dot_yaml
));
660 memcpy(output_name
, options
->input
, len
);
661 memcpy(output_name
+ len
, dot_yaml
, sizeof(dot_yaml
));
662 output
= fopen(output_name
, "w");
665 scop
= pet_scop_extract_from_C_source(ctx
, options
->input
,
667 scop
= pet_scop_align_params(scop
);
670 output_name
[len
] = '\0';
671 slash
= strrchr(output_name
, '/');
674 pdg
->name
= new str(output_name
);
676 nparam
= isl_set_dim(scop
->context
, isl_dim_param
);
677 for (int i
= 0; i
< nparam
; ++i
) {
678 isl_id
*id
= isl_set_get_dim_id(scop
->context
, isl_dim_param
, i
);
679 pdg::parameter
*p
= new pdg::parameter
;
681 p
->name
= new str(isl_id_get_name(id
));
682 set_default_parameter_value(p
, id
, scop
, i
);
684 pdg
->params
.push_back(p
);
688 pdg
->context
= new pdg::IslSet(isl_set_copy(scop
->context
));
690 for (int i
= 0; i
< scop
->n_array
; ++i
) {
692 array
= extract_array(pdg
, scop
->arrays
[i
], id2array
);
693 pdg
->arrays
.push_back(array
);
696 for (int i
= 0, nodenr
= 0; i
< scop
->n_stmt
; ++i
) {
698 if (isl_set_is_empty(scop
->stmts
[i
]->domain
))
700 node
= extract_node(pdg
, scop
, scop
->stmts
[i
], id2array
);
702 pdg
->nodes
.push_back(node
);