brintos

brintos / llvm-project-archived public Read only

0
0
Text · 186.1 KiB · 774dfab Raw
5942 lines · c
1/*2 * Copyright 2012-2014 Ecole Normale Superieure3 * Copyright 2014      INRIA Rocquencourt4 *5 * Use of this software is governed by the MIT license6 *7 * Written by Sven Verdoolaege,8 * Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France9 * and Inria Paris - Rocquencourt, Domaine de Voluceau - Rocquencourt,10 * B.P. 105 - 78153 Le Chesnay, France11 */12 13#include <limits.h>14#include <isl/id.h>15#include <isl/val.h>16#include <isl/space.h>17#include <isl/aff.h>18#include <isl/constraint.h>19#include <isl/set.h>20#include <isl/ilp.h>21#include <isl/union_set.h>22#include <isl/union_map.h>23#include <isl/schedule_node.h>24#include <isl/options.h>25#include <isl_sort.h>26#include <isl_tarjan.h>27#include <isl_ast_private.h>28#include <isl_ast_build_expr.h>29#include <isl_ast_build_private.h>30#include <isl_ast_graft_private.h>31 32/* Try and reduce the number of disjuncts in the representation of "set",33 * without dropping explicit representations of local variables.34 */35static __isl_give isl_set *isl_set_coalesce_preserve(__isl_take isl_set *set)36{37	isl_ctx *ctx;38	int save_preserve;39 40	if (!set)41		return NULL;42 43	ctx = isl_set_get_ctx(set);44	save_preserve = isl_options_get_coalesce_preserve_locals(ctx);45	isl_options_set_coalesce_preserve_locals(ctx, 1);46	set = isl_set_coalesce(set);47	isl_options_set_coalesce_preserve_locals(ctx, save_preserve);48	return set;49}50 51/* Data used in generate_domain.52 *53 * "build" is the input build.54 * "list" collects the results.55 */56struct isl_generate_domain_data {57	isl_ast_build *build;58 59	isl_ast_graft_list *list;60};61 62static __isl_give isl_ast_graft_list *generate_next_level(63	__isl_take isl_union_map *executed,64	__isl_take isl_ast_build *build);65static __isl_give isl_ast_graft_list *generate_code(66	__isl_take isl_union_map *executed, __isl_take isl_ast_build *build,67	int internal);68 69/* Generate an AST for a single domain based on70 * the (non single valued) inverse schedule "executed".71 *72 * We extend the schedule with the iteration domain73 * and continue generating through a call to generate_code.74 *75 * In particular, if executed has the form76 *77 *	S -> D78 *79 * then we continue generating code on80 *81 *	[S -> D] -> D82 *83 * The extended inverse schedule is clearly single valued84 * ensuring that the nested generate_code will not reach this function,85 * but will instead create calls to all elements of D that need86 * to be executed from the current schedule domain.87 */88static isl_stat generate_non_single_valued(__isl_take isl_map *executed,89	struct isl_generate_domain_data *data)90{91	isl_map *identity;92	isl_ast_build *build;93	isl_ast_graft_list *list;94 95	build = isl_ast_build_copy(data->build);96 97	identity = isl_set_identity(isl_map_range(isl_map_copy(executed)));98	executed = isl_map_domain_product(executed, identity);99	build = isl_ast_build_set_single_valued(build, 1);100 101	list = generate_code(isl_union_map_from_map(executed), build, 1);102 103	data->list = isl_ast_graft_list_concat(data->list, list);104 105	return isl_stat_ok;106}107 108/* Call the at_each_domain callback, if requested by the user,109 * after recording the current inverse schedule in the build.110 */111static __isl_give isl_ast_graft *at_each_domain(__isl_take isl_ast_graft *graft,112	__isl_keep isl_map *executed, __isl_keep isl_ast_build *build)113{114	if (!graft || !build)115		return isl_ast_graft_free(graft);116	if (!build->at_each_domain)117		return graft;118 119	build = isl_ast_build_copy(build);120	build = isl_ast_build_set_executed(build,121			isl_union_map_from_map(isl_map_copy(executed)));122	if (!build)123		return isl_ast_graft_free(graft);124 125	graft->node = build->at_each_domain(graft->node,126					build, build->at_each_domain_user);127	isl_ast_build_free(build);128 129	if (!graft->node)130		graft = isl_ast_graft_free(graft);131 132	return graft;133}134 135/* Generate a call expression for the single executed136 * domain element "map" and put a guard around it based its (simplified)137 * domain.  "executed" is the original inverse schedule from which "map"138 * has been derived.  In particular, "map" is either identical to "executed"139 * or it is the result of gisting "executed" with respect to the build domain.140 * "executed" is only used if there is an at_each_domain callback.141 *142 * At this stage, any pending constraints in the build can no longer143 * be simplified with respect to any enforced constraints since144 * the call node does not have any enforced constraints.145 * Since all pending constraints not covered by any enforced constraints146 * will be added as a guard to the graft in create_node_scaled,147 * even in the eliminated case, the pending constraints148 * can be considered to have been generated by outer constructs.149 *150 * If the user has set an at_each_domain callback, it is called151 * on the constructed call expression node.152 */153static isl_stat add_domain(__isl_take isl_map *executed,154	__isl_take isl_map *map, struct isl_generate_domain_data *data)155{156	isl_ast_build *build;157	isl_ast_graft *graft;158	isl_ast_graft_list *list;159	isl_set *guard, *pending;160 161	build = isl_ast_build_copy(data->build);162	pending = isl_ast_build_get_pending(build);163	build = isl_ast_build_replace_pending_by_guard(build, pending);164 165	guard = isl_map_domain(isl_map_copy(map));166	guard = isl_set_compute_divs(guard);167	guard = isl_set_coalesce_preserve(guard);168	guard = isl_set_gist(guard, isl_ast_build_get_generated(build));169	guard = isl_ast_build_specialize(build, guard);170 171	graft = isl_ast_graft_alloc_domain(map, build);172	graft = at_each_domain(graft, executed, build);173	isl_ast_build_free(build);174	isl_map_free(executed);175	graft = isl_ast_graft_add_guard(graft, guard, data->build);176 177	list = isl_ast_graft_list_from_ast_graft(graft);178	data->list = isl_ast_graft_list_concat(data->list, list);179 180	return isl_stat_ok;181}182 183/* Generate an AST for a single domain based on184 * the inverse schedule "executed" and add it to data->list.185 *186 * If there is more than one domain element associated to the current187 * schedule "time", then we need to continue the generation process188 * in generate_non_single_valued.189 * Note that the inverse schedule being single-valued may depend190 * on constraints that are only available in the original context191 * domain specified by the user.  We therefore first introduce192 * some of the constraints of data->build->domain.  In particular,193 * we intersect with a single-disjunct approximation of this set.194 * We perform this approximation to avoid further splitting up195 * the executed relation, possibly introducing a disjunctive guard196 * on the statement.197 *198 * On the other hand, we only perform the test after having taken the gist199 * of the domain as the resulting map is the one from which the call200 * expression is constructed.  Using this map to construct the call201 * expression usually yields simpler results in cases where the original202 * map is not obviously single-valued.203 * If the original map is obviously single-valued, then the gist204 * operation is skipped.205 *206 * Because we perform the single-valuedness test on the gisted map,207 * we may in rare cases fail to recognize that the inverse schedule208 * is single-valued.  This becomes problematic if this happens209 * from the recursive call through generate_non_single_valued210 * as we would then end up in an infinite recursion.211 * We therefore check if we are inside a call to generate_non_single_valued212 * and revert to the ungisted map if the gisted map turns out not to be213 * single-valued.214 *215 * Otherwise, call add_domain to generate a call expression (with guard) and216 * to call the at_each_domain callback, if any.217 */218static isl_stat generate_domain(__isl_take isl_map *executed, void *user)219{220	struct isl_generate_domain_data *data = user;221	isl_set *domain;222	isl_map *map = NULL;223	int empty, sv;224 225	domain = isl_ast_build_get_domain(data->build);226	domain = isl_set_from_basic_set(isl_set_simple_hull(domain));227	executed = isl_map_intersect_domain(executed, domain);228	empty = isl_map_is_empty(executed);229	if (empty < 0)230		goto error;231	if (empty) {232		isl_map_free(executed);233		return isl_stat_ok;234	}235 236	sv = isl_map_plain_is_single_valued(executed);237	if (sv < 0)238		goto error;239	if (sv)240		return add_domain(executed, isl_map_copy(executed), data);241 242	executed = isl_map_coalesce(executed);243	map = isl_map_copy(executed);244	map = isl_ast_build_compute_gist_map_domain(data->build, map);245	sv = isl_map_is_single_valued(map);246	if (sv < 0)247		goto error;248	if (!sv) {249		isl_map_free(map);250		if (data->build->single_valued)251			map = isl_map_copy(executed);252		else253			return generate_non_single_valued(executed, data);254	}255 256	return add_domain(executed, map, data);257error:258	isl_map_free(map);259	isl_map_free(executed);260	return isl_stat_error;261}262 263/* Call build->create_leaf to a create "leaf" node in the AST,264 * encapsulate the result in an isl_ast_graft and return the result265 * as a 1-element list.266 *267 * Note that the node returned by the user may be an entire tree.268 *269 * Since the node itself cannot enforce any constraints, we turn270 * all pending constraints into guards and add them to the resulting271 * graft to ensure that they will be generated.272 *273 * Before we pass control to the user, we first clear some information274 * from the build that is (presumbably) only meaningful275 * for the current code generation.276 * This includes the create_leaf callback itself, so we make a copy277 * of the build first.278 */279static __isl_give isl_ast_graft_list *call_create_leaf(280	__isl_take isl_union_map *executed, __isl_take isl_ast_build *build)281{282	isl_set *guard;283	isl_ast_node *node;284	isl_ast_graft *graft;285	isl_ast_build *user_build;286 287	guard = isl_ast_build_get_pending(build);288	user_build = isl_ast_build_copy(build);289	user_build = isl_ast_build_replace_pending_by_guard(user_build,290							isl_set_copy(guard));291	user_build = isl_ast_build_set_executed(user_build, executed);292	user_build = isl_ast_build_clear_local_info(user_build);293	if (!user_build)294		node = NULL;295	else296		node = build->create_leaf(user_build, build->create_leaf_user);297	graft = isl_ast_graft_alloc(node, build);298	graft = isl_ast_graft_add_guard(graft, guard, build);299	isl_ast_build_free(build);300	return isl_ast_graft_list_from_ast_graft(graft);301}302 303static __isl_give isl_ast_graft_list *build_ast_from_child(304	__isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,305	__isl_take isl_union_map *executed);306 307/* Generate an AST after having handled the complete schedule308 * of this call to the code generator or the complete band309 * if we are generating an AST from a schedule tree.310 *311 * If we are inside a band node, then move on to the child of the band.312 *313 * If the user has specified a create_leaf callback, control314 * is passed to the user in call_create_leaf.315 *316 * Otherwise, we generate one or more calls for each individual317 * domain in generate_domain.318 */319static __isl_give isl_ast_graft_list *generate_inner_level(320	__isl_take isl_union_map *executed, __isl_take isl_ast_build *build)321{322	isl_ctx *ctx;323	struct isl_generate_domain_data data = { build };324 325	if (!build || !executed)326		goto error;327 328	if (isl_ast_build_has_schedule_node(build)) {329		isl_schedule_node *node;330		node = isl_ast_build_get_schedule_node(build);331		build = isl_ast_build_reset_schedule_node(build);332		return build_ast_from_child(build, node, executed);333	}334 335	if (build->create_leaf)336		return call_create_leaf(executed, build);337 338	ctx = isl_union_map_get_ctx(executed);339	data.list = isl_ast_graft_list_alloc(ctx, 0);340	if (isl_union_map_foreach_map(executed, &generate_domain, &data) < 0)341		data.list = isl_ast_graft_list_free(data.list);342 343	if (0)344error:		data.list = NULL;345	isl_ast_build_free(build);346	isl_union_map_free(executed);347	return data.list;348}349 350/* Call the before_each_for callback, if requested by the user.351 */352static __isl_give isl_ast_node *before_each_for(__isl_take isl_ast_node *node,353	__isl_keep isl_ast_build *build)354{355	isl_id *id;356 357	if (!node || !build)358		return isl_ast_node_free(node);359	if (!build->before_each_for)360		return node;361	id = build->before_each_for(build, build->before_each_for_user);362	node = isl_ast_node_set_annotation(node, id);363	return node;364}365 366/* Call the after_each_for callback, if requested by the user.367 */368static __isl_give isl_ast_graft *after_each_for(__isl_take isl_ast_graft *graft,369	__isl_keep isl_ast_build *build)370{371	if (!graft || !build)372		return isl_ast_graft_free(graft);373	if (!build->after_each_for)374		return graft;375	graft->node = build->after_each_for(graft->node, build,376						build->after_each_for_user);377	if (!graft->node)378		return isl_ast_graft_free(graft);379	return graft;380}381 382/* Plug in all the know values of the current and outer dimensions383 * in the domain of "executed".  In principle, we only need to plug384 * in the known value of the current dimension since the values of385 * outer dimensions have been plugged in already.386 * However, it turns out to be easier to just plug in all known values.387 */388static __isl_give isl_union_map *plug_in_values(389	__isl_take isl_union_map *executed, __isl_keep isl_ast_build *build)390{391	return isl_ast_build_substitute_values_union_map_domain(build,392								    executed);393}394 395/* Check if the constraint "c" is a lower bound on dimension "pos",396 * an upper bound, or independent of dimension "pos".397 */398static int constraint_type(isl_constraint *c, int pos)399{400	if (isl_constraint_is_lower_bound(c, isl_dim_set, pos))401		return 1;402	if (isl_constraint_is_upper_bound(c, isl_dim_set, pos))403		return 2;404	return 0;405}406 407/* Compare the types of the constraints "a" and "b",408 * resulting in constraints that are independent of "depth"409 * to be sorted before the lower bounds on "depth", which in410 * turn are sorted before the upper bounds on "depth".411 */412static int cmp_constraint(__isl_keep isl_constraint *a,413	__isl_keep isl_constraint *b, void *user)414{415	int *depth = user;416	int t1 = constraint_type(a, *depth);417	int t2 = constraint_type(b, *depth);418 419	return t1 - t2;420}421 422/* Extract a lower bound on dimension "pos" from constraint "c".423 *424 * If the constraint is of the form425 *426 *	a x + f(...) >= 0427 *428 * then we essentially return429 *430 *	l = ceil(-f(...)/a)431 *432 * However, if the current dimension is strided, then we need to make433 * sure that the lower bound we construct is of the form434 *435 *	f + s a436 *437 * with f the offset and s the stride.438 * We therefore compute439 *440 *	f + s * ceil((l - f)/s)441 */442static __isl_give isl_aff *lower_bound(__isl_keep isl_constraint *c,443	int pos, __isl_keep isl_ast_build *build)444{445	isl_aff *aff;446 447	aff = isl_constraint_get_bound(c, isl_dim_set, pos);448	aff = isl_aff_ceil(aff);449 450	if (isl_ast_build_has_stride(build, pos)) {451		isl_aff *offset;452		isl_val *stride;453 454		offset = isl_ast_build_get_offset(build, pos);455		stride = isl_ast_build_get_stride(build, pos);456 457		aff = isl_aff_sub(aff, isl_aff_copy(offset));458		aff = isl_aff_scale_down_val(aff, isl_val_copy(stride));459		aff = isl_aff_ceil(aff);460		aff = isl_aff_scale_val(aff, stride);461		aff = isl_aff_add(aff, offset);462	}463 464	aff = isl_ast_build_compute_gist_aff(build, aff);465 466	return aff;467}468 469/* Return the exact lower bound (or upper bound if "upper" is set)470 * of "domain" as a piecewise affine expression.471 *472 * If we are computing a lower bound (of a strided dimension), then473 * we need to make sure it is of the form474 *475 *	f + s a476 *477 * where f is the offset and s is the stride.478 * We therefore need to include the stride constraint before computing479 * the minimum.480 */481static __isl_give isl_pw_aff *exact_bound(__isl_keep isl_set *domain,482	__isl_keep isl_ast_build *build, int upper)483{484	isl_set *stride;485	isl_map *it_map;486	isl_pw_aff *pa;487	isl_pw_multi_aff *pma;488 489	domain = isl_set_copy(domain);490	if (!upper) {491		stride = isl_ast_build_get_stride_constraint(build);492		domain = isl_set_intersect(domain, stride);493	}494	it_map = isl_ast_build_map_to_iterator(build, domain);495	if (upper)496		pma = isl_map_lexmax_pw_multi_aff(it_map);497	else498		pma = isl_map_lexmin_pw_multi_aff(it_map);499	pa = isl_pw_multi_aff_get_pw_aff(pma, 0);500	isl_pw_multi_aff_free(pma);501	pa = isl_ast_build_compute_gist_pw_aff(build, pa);502	pa = isl_pw_aff_coalesce(pa);503 504	return pa;505}506 507/* Callback for sorting the isl_pw_aff_list passed to reduce_list and508 * remove_redundant_lower_bounds.509 */510static int reduce_list_cmp(__isl_keep isl_pw_aff *a, __isl_keep isl_pw_aff *b,511	void *user)512{513	return isl_pw_aff_plain_cmp(a, b);514}515 516/* Given a list of lower bounds "list", remove those that are redundant517 * with respect to the other bounds in "list" and the domain of "build".518 *519 * We first sort the bounds in the same way as they would be sorted520 * by set_for_node_expressions so that we can try and remove the last521 * bounds first.522 *523 * For a lower bound to be effective, there needs to be at least524 * one domain element for which it is larger than all other lower bounds.525 * For each lower bound we therefore intersect the domain with526 * the conditions that it is larger than all other bounds and527 * check whether the result is empty.  If so, the bound can be removed.528 */529static __isl_give isl_pw_aff_list *remove_redundant_lower_bounds(530	__isl_take isl_pw_aff_list *list, __isl_keep isl_ast_build *build)531{532	int i, j;533	isl_size n;534	isl_set *domain;535 536	list = isl_pw_aff_list_sort(list, &reduce_list_cmp, NULL);537 538	n = isl_pw_aff_list_n_pw_aff(list);539	if (n < 0)540		return isl_pw_aff_list_free(list);541	if (n <= 1)542		return list;543 544	domain = isl_ast_build_get_domain(build);545 546	for (i = n - 1; i >= 0; --i) {547		isl_pw_aff *pa_i;548		isl_set *domain_i;549		int empty;550 551		domain_i = isl_set_copy(domain);552		pa_i = isl_pw_aff_list_get_pw_aff(list, i);553 554		for (j = 0; j < n; ++j) {555			isl_pw_aff *pa_j;556			isl_set *better;557 558			if (j == i)559				continue;560 561			pa_j = isl_pw_aff_list_get_pw_aff(list, j);562			better = isl_pw_aff_gt_set(isl_pw_aff_copy(pa_i), pa_j);563			domain_i = isl_set_intersect(domain_i, better);564		}565 566		empty = isl_set_is_empty(domain_i);567 568		isl_set_free(domain_i);569		isl_pw_aff_free(pa_i);570 571		if (empty < 0)572			goto error;573		if (!empty)574			continue;575		list = isl_pw_aff_list_drop(list, i, 1);576		n--;577	}578 579	isl_set_free(domain);580 581	return list;582error:583	isl_set_free(domain);584	return isl_pw_aff_list_free(list);585}586 587/* Extract a lower bound on dimension "pos" from each constraint588 * in "constraints" and return the list of lower bounds.589 * If "constraints" has zero elements, then we extract a lower bound590 * from "domain" instead.591 *592 * If the current dimension is strided, then the lower bound593 * is adjusted by lower_bound to match the stride information.594 * This modification may make one or more lower bounds redundant595 * with respect to the other lower bounds.  We therefore check596 * for this condition and remove the redundant lower bounds.597 */598static __isl_give isl_pw_aff_list *lower_bounds(599	__isl_keep isl_constraint_list *constraints, int pos,600	__isl_keep isl_set *domain, __isl_keep isl_ast_build *build)601{602	isl_ctx *ctx;603	isl_pw_aff_list *list;604	int i;605	isl_size n;606 607	if (!build)608		return NULL;609 610	n = isl_constraint_list_n_constraint(constraints);611	if (n < 0)612		return NULL;613	if (n == 0) {614		isl_pw_aff *pa;615		pa = exact_bound(domain, build, 0);616		return isl_pw_aff_list_from_pw_aff(pa);617	}618 619	ctx = isl_ast_build_get_ctx(build);620	list = isl_pw_aff_list_alloc(ctx,n);621 622	for (i = 0; i < n; ++i) {623		isl_aff *aff;624		isl_constraint *c;625 626		c = isl_constraint_list_get_constraint(constraints, i);627		aff = lower_bound(c, pos, build);628		isl_constraint_free(c);629		list = isl_pw_aff_list_add(list, isl_pw_aff_from_aff(aff));630	}631 632	if (isl_ast_build_has_stride(build, pos))633		list = remove_redundant_lower_bounds(list, build);634 635	return list;636}637 638/* Extract an upper bound on dimension "pos" from each constraint639 * in "constraints" and return the list of upper bounds.640 * If "constraints" has zero elements, then we extract an upper bound641 * from "domain" instead.642 */643static __isl_give isl_pw_aff_list *upper_bounds(644	__isl_keep isl_constraint_list *constraints, int pos,645	__isl_keep isl_set *domain, __isl_keep isl_ast_build *build)646{647	isl_ctx *ctx;648	isl_pw_aff_list *list;649	int i;650	isl_size n;651 652	n = isl_constraint_list_n_constraint(constraints);653	if (n < 0)654		return NULL;655	if (n == 0) {656		isl_pw_aff *pa;657		pa = exact_bound(domain, build, 1);658		return isl_pw_aff_list_from_pw_aff(pa);659	}660 661	ctx = isl_ast_build_get_ctx(build);662	list = isl_pw_aff_list_alloc(ctx,n);663 664	for (i = 0; i < n; ++i) {665		isl_aff *aff;666		isl_constraint *c;667 668		c = isl_constraint_list_get_constraint(constraints, i);669		aff = isl_constraint_get_bound(c, isl_dim_set, pos);670		isl_constraint_free(c);671		aff = isl_aff_floor(aff);672		list = isl_pw_aff_list_add(list, isl_pw_aff_from_aff(aff));673	}674 675	return list;676}677 678/* Return an isl_ast_expr that performs the reduction of type "type"679 * on AST expressions corresponding to the elements in "list".680 *681 * The list is assumed to contain at least one element.682 * If the list contains exactly one element, then the returned isl_ast_expr683 * simply computes that affine expression.684 * If the list contains more than one element, then we sort it685 * using a fairly arbitrary but hopefully reasonably stable order.686 */687static __isl_give isl_ast_expr *reduce_list(enum isl_ast_expr_op_type type,688	__isl_keep isl_pw_aff_list *list, __isl_keep isl_ast_build *build)689{690	int i;691	isl_size n;692	isl_ctx *ctx;693	isl_ast_expr *expr;694 695	n = isl_pw_aff_list_n_pw_aff(list);696	if (n < 0)697		return NULL;698 699	if (n == 1)700		return isl_ast_build_expr_from_pw_aff_internal(build,701				isl_pw_aff_list_get_pw_aff(list, 0));702 703	ctx = isl_pw_aff_list_get_ctx(list);704	expr = isl_ast_expr_alloc_op(ctx, type, n);705 706	list = isl_pw_aff_list_copy(list);707	list = isl_pw_aff_list_sort(list, &reduce_list_cmp, NULL);708	if (!list)709		return isl_ast_expr_free(expr);710 711	for (i = 0; i < n; ++i) {712		isl_ast_expr *expr_i;713 714		expr_i = isl_ast_build_expr_from_pw_aff_internal(build,715				isl_pw_aff_list_get_pw_aff(list, i));716		expr = isl_ast_expr_op_add_arg(expr, expr_i);717	}718 719	isl_pw_aff_list_free(list);720	return expr;721}722 723/* Add guards implied by the "generated constraints",724 * but not (necessarily) enforced by the generated AST to "guard".725 * In particular, if there is any stride constraints,726 * then add the guard implied by those constraints.727 * If we have generated a degenerate loop, then add the guard728 * implied by "bounds" on the outer dimensions, i.e., the guard729 * that ensures that the single value actually exists.730 * Since there may also be guards implied by a combination731 * of these constraints, we first combine them before732 * deriving the implied constraints.733 */734static __isl_give isl_set *add_implied_guards(__isl_take isl_set *guard,735	int degenerate, __isl_keep isl_basic_set *bounds,736	__isl_keep isl_ast_build *build)737{738	isl_size depth;739	isl_bool has_stride;740	isl_space *space;741	isl_set *dom, *set;742 743	depth = isl_ast_build_get_depth(build);744	has_stride = isl_ast_build_has_stride(build, depth);745	if (depth < 0 || has_stride < 0)746		return isl_set_free(guard);747	if (!has_stride && !degenerate)748		return guard;749 750	space = isl_basic_set_get_space(bounds);751	dom = isl_set_universe(space);752 753	if (degenerate) {754		bounds = isl_basic_set_copy(bounds);755		bounds = isl_basic_set_drop_constraints_not_involving_dims(756					bounds, isl_dim_set, depth, 1);757		set = isl_set_from_basic_set(bounds);758		dom = isl_set_intersect(dom, set);759	}760 761	if (has_stride) {762		set = isl_ast_build_get_stride_constraint(build);763		dom = isl_set_intersect(dom, set);764	}765 766	dom = isl_set_eliminate(dom, isl_dim_set, depth, 1);767	dom = isl_ast_build_compute_gist(build, dom);768	guard = isl_set_intersect(guard, dom);769 770	return guard;771}772 773/* Update "graft" based on "sub_build" for the degenerate case.774 *775 * "build" is the build in which graft->node was created776 * "sub_build" contains information about the current level itself,777 * including the single value attained.778 *779 * We set the initialization part of the for loop to the single780 * value attained by the current dimension.781 * The increment and condition are not strictly needed as they are known782 * to be "1" and "iterator <= value" respectively.783 */784static __isl_give isl_ast_graft *refine_degenerate(785	__isl_take isl_ast_graft *graft, __isl_keep isl_ast_build *build,786	__isl_keep isl_ast_build *sub_build)787{788	isl_pw_aff *value;789	isl_ast_expr *init;790 791	if (!graft || !sub_build)792		return isl_ast_graft_free(graft);793 794	value = isl_pw_aff_copy(sub_build->value);795 796	init = isl_ast_build_expr_from_pw_aff_internal(build, value);797	graft->node = isl_ast_node_for_set_init(graft->node, init);798	if (!graft->node)799		return isl_ast_graft_free(graft);800 801	return graft;802}803 804/* Return the intersection of constraints in "list" as a set.805 */806static __isl_give isl_set *intersect_constraints(807	__isl_keep isl_constraint_list *list)808{809	int i;810	isl_size n;811	isl_basic_set *bset;812 813	n = isl_constraint_list_n_constraint(list);814	if (n < 0)815		return NULL;816	if (n < 1)817		isl_die(isl_constraint_list_get_ctx(list), isl_error_internal,818			"expecting at least one constraint", return NULL);819 820	bset = isl_basic_set_from_constraint(821				isl_constraint_list_get_constraint(list, 0));822	for (i = 1; i < n; ++i) {823		isl_basic_set *bset_i;824 825		bset_i = isl_basic_set_from_constraint(826				isl_constraint_list_get_constraint(list, i));827		bset = isl_basic_set_intersect(bset, bset_i);828	}829 830	return isl_set_from_basic_set(bset);831}832 833/* Compute the constraints on the outer dimensions enforced by834 * graft->node and add those constraints to graft->enforced,835 * in case the upper bound is expressed as a set "upper".836 *837 * In particular, if l(...) is a lower bound in "lower", and838 *839 *	-a i + f(...) >= 0		or	a i <= f(...)840 *841 * is an upper bound ocnstraint on the current dimension i,842 * then the for loop enforces the constraint843 *844 *	-a l(...) + f(...) >= 0		or	a l(...) <= f(...)845 *846 * We therefore simply take each lower bound in turn, plug it into847 * the upper bounds and compute the intersection over all lower bounds.848 *849 * If a lower bound is a rational expression, then850 * isl_basic_set_preimage_multi_aff will force this rational851 * expression to have only integer values.  However, the loop852 * itself does not enforce this integrality constraint.  We therefore853 * use the ceil of the lower bounds instead of the lower bounds themselves.854 * Other constraints will make sure that the for loop is only executed855 * when each of the lower bounds attains an integral value.856 * In particular, potentially rational values only occur in857 * lower_bound if the offset is a (seemingly) rational expression,858 * but then outer conditions will make sure that this rational expression859 * only attains integer values.860 */861static __isl_give isl_ast_graft *set_enforced_from_set(862	__isl_take isl_ast_graft *graft,863	__isl_keep isl_pw_aff_list *lower, int pos, __isl_keep isl_set *upper)864{865	isl_space *space;866	isl_basic_set *enforced;867	isl_pw_multi_aff *pma;868	int i;869	isl_size n;870 871	n = isl_pw_aff_list_n_pw_aff(lower);872	if (!graft || n < 0)873		return isl_ast_graft_free(graft);874 875	space = isl_set_get_space(upper);876	enforced = isl_basic_set_universe(isl_space_copy(space));877 878	space = isl_space_map_from_set(space);879	pma = isl_pw_multi_aff_identity(space);880 881	for (i = 0; i < n; ++i) {882		isl_pw_aff *pa;883		isl_set *enforced_i;884		isl_basic_set *hull;885		isl_pw_multi_aff *pma_i;886 887		pa = isl_pw_aff_list_get_pw_aff(lower, i);888		pa = isl_pw_aff_ceil(pa);889		pma_i = isl_pw_multi_aff_copy(pma);890		pma_i = isl_pw_multi_aff_set_pw_aff(pma_i, pos, pa);891		enforced_i = isl_set_copy(upper);892		enforced_i = isl_set_preimage_pw_multi_aff(enforced_i, pma_i);893		hull = isl_set_simple_hull(enforced_i);894		enforced = isl_basic_set_intersect(enforced, hull);895	}896 897	isl_pw_multi_aff_free(pma);898 899	graft = isl_ast_graft_enforce(graft, enforced);900 901	return graft;902}903 904/* Compute the constraints on the outer dimensions enforced by905 * graft->node and add those constraints to graft->enforced,906 * in case the upper bound is expressed as907 * a list of affine expressions "upper".908 *909 * The enforced condition is that each lower bound expression is less910 * than or equal to each upper bound expression.911 */912static __isl_give isl_ast_graft *set_enforced_from_list(913	__isl_take isl_ast_graft *graft,914	__isl_keep isl_pw_aff_list *lower, __isl_keep isl_pw_aff_list *upper)915{916	isl_set *cond;917	isl_basic_set *enforced;918 919	lower = isl_pw_aff_list_copy(lower);920	upper = isl_pw_aff_list_copy(upper);921	cond = isl_pw_aff_list_le_set(lower, upper);922	enforced = isl_set_simple_hull(cond);923	graft = isl_ast_graft_enforce(graft, enforced);924 925	return graft;926}927 928/* Does "aff" have a negative constant term?929 */930static isl_bool aff_constant_is_negative(__isl_keep isl_set *set,931	__isl_keep isl_aff *aff, void *user)932{933	isl_bool is_neg;934	isl_val *v;935 936	v = isl_aff_get_constant_val(aff);937	is_neg = isl_val_is_neg(v);938	isl_val_free(v);939 940	return is_neg;941}942 943/* Does "pa" have a negative constant term over its entire domain?944 */945static isl_bool pw_aff_constant_is_negative(__isl_keep isl_pw_aff *pa,946	void *user)947{948	return isl_pw_aff_every_piece(pa, &aff_constant_is_negative, NULL);949}950 951/* Does each element in "list" have a negative constant term?952 */953static int list_constant_is_negative(__isl_keep isl_pw_aff_list *list)954{955	return isl_pw_aff_list_every(list, &pw_aff_constant_is_negative, NULL);956}957 958/* Add 1 to each of the elements in "list", where each of these elements959 * is defined over the internal schedule space of "build".960 */961static __isl_give isl_pw_aff_list *list_add_one(962	__isl_take isl_pw_aff_list *list, __isl_keep isl_ast_build *build)963{964	int i;965	isl_size n;966	isl_space *space;967	isl_aff *aff;968	isl_pw_aff *one;969 970	n = isl_pw_aff_list_n_pw_aff(list);971	if (n < 0)972		return isl_pw_aff_list_free(list);973 974	space = isl_ast_build_get_space(build, 1);975	aff = isl_aff_zero_on_domain(isl_local_space_from_space(space));976	aff = isl_aff_add_constant_si(aff, 1);977	one = isl_pw_aff_from_aff(aff);978 979	for (i = 0; i < n; ++i) {980		isl_pw_aff *pa;981		pa = isl_pw_aff_list_get_pw_aff(list, i);982		pa = isl_pw_aff_add(pa, isl_pw_aff_copy(one));983		list = isl_pw_aff_list_set_pw_aff(list, i, pa);984	}985 986	isl_pw_aff_free(one);987 988	return list;989}990 991/* Set the condition part of the for node graft->node in case992 * the upper bound is represented as a list of piecewise affine expressions.993 *994 * In particular, set the condition to995 *996 *	iterator <= min(list of upper bounds)997 *998 * If each of the upper bounds has a negative constant term, then999 * set the condition to1000 *1001 *	iterator < min(list of (upper bound + 1)s)1002 *1003 */1004static __isl_give isl_ast_graft *set_for_cond_from_list(1005	__isl_take isl_ast_graft *graft, __isl_keep isl_pw_aff_list *list,1006	__isl_keep isl_ast_build *build)1007{1008	int neg;1009	isl_ast_expr *bound, *iterator, *cond;1010	enum isl_ast_expr_op_type type = isl_ast_expr_op_le;1011 1012	if (!graft || !list)1013		return isl_ast_graft_free(graft);1014 1015	neg = list_constant_is_negative(list);1016	if (neg < 0)1017		return isl_ast_graft_free(graft);1018	list = isl_pw_aff_list_copy(list);1019	if (neg) {1020		list = list_add_one(list, build);1021		type = isl_ast_expr_op_lt;1022	}1023 1024	bound = reduce_list(isl_ast_expr_op_min, list, build);1025	iterator = isl_ast_expr_copy(graft->node->u.f.iterator);1026	cond = isl_ast_expr_alloc_binary(type, iterator, bound);1027	graft->node = isl_ast_node_for_set_cond(graft->node, cond);1028 1029	isl_pw_aff_list_free(list);1030	if (!graft->node)1031		return isl_ast_graft_free(graft);1032	return graft;1033}1034 1035/* Set the condition part of the for node graft->node in case1036 * the upper bound is represented as a set.1037 */1038static __isl_give isl_ast_graft *set_for_cond_from_set(1039	__isl_take isl_ast_graft *graft, __isl_keep isl_set *set,1040	__isl_keep isl_ast_build *build)1041{1042	isl_ast_expr *cond;1043 1044	if (!graft)1045		return NULL;1046 1047	cond = isl_ast_build_expr_from_set_internal(build, isl_set_copy(set));1048	graft->node = isl_ast_node_for_set_cond(graft->node, cond);1049	if (!graft->node)1050		return isl_ast_graft_free(graft);1051	return graft;1052}1053 1054/* Construct an isl_ast_expr for the increment (i.e., stride) of1055 * the current dimension.1056 */1057static __isl_give isl_ast_expr *for_inc(__isl_keep isl_ast_build *build)1058{1059	isl_size depth;1060	isl_val *v;1061	isl_ctx *ctx;1062 1063	depth = isl_ast_build_get_depth(build);1064	if (depth < 0)1065		return NULL;1066	ctx = isl_ast_build_get_ctx(build);1067 1068	if (!isl_ast_build_has_stride(build, depth))1069		return isl_ast_expr_alloc_int_si(ctx, 1);1070 1071	v = isl_ast_build_get_stride(build, depth);1072	return isl_ast_expr_from_val(v);1073}1074 1075/* Should we express the loop condition as1076 *1077 *	iterator <= min(list of upper bounds)1078 *1079 * or as a conjunction of constraints?1080 *1081 * The first is constructed from a list of upper bounds.1082 * The second is constructed from a set.1083 *1084 * If there are no upper bounds in "constraints", then this could mean1085 * that "domain" simply doesn't have an upper bound or that we didn't1086 * pick any upper bound.  In the first case, we want to generate the1087 * loop condition as a(n empty) conjunction of constraints1088 * In the second case, we will compute1089 * a single upper bound from "domain" and so we use the list form.1090 *1091 * If there are upper bounds in "constraints",1092 * then we use the list form iff the atomic_upper_bound option is set.1093 */1094static int use_upper_bound_list(isl_ctx *ctx, int n_upper,1095	__isl_keep isl_set *domain, int depth)1096{1097	if (n_upper > 0)1098		return isl_options_get_ast_build_atomic_upper_bound(ctx);1099	else1100		return isl_set_dim_has_upper_bound(domain, isl_dim_set, depth);1101}1102 1103/* Fill in the expressions of the for node in graft->node.1104 *1105 * In particular,1106 * - set the initialization part of the loop to the maximum of the lower bounds1107 * - extract the increment from the stride of the current dimension1108 * - construct the for condition either based on a list of upper bounds1109 *	or on a set of upper bound constraints.1110 */1111static __isl_give isl_ast_graft *set_for_node_expressions(1112	__isl_take isl_ast_graft *graft, __isl_keep isl_pw_aff_list *lower,1113	int use_list, __isl_keep isl_pw_aff_list *upper_list,1114	__isl_keep isl_set *upper_set, __isl_keep isl_ast_build *build)1115{1116	isl_ast_expr *init;1117 1118	if (!graft)1119		return NULL;1120 1121	init = reduce_list(isl_ast_expr_op_max, lower, build);1122	graft->node = isl_ast_node_for_set_init(graft->node, init);1123	graft->node = isl_ast_node_for_set_inc(graft->node, for_inc(build));1124 1125	if (!graft->node)1126		graft = isl_ast_graft_free(graft);1127 1128	if (use_list)1129		graft = set_for_cond_from_list(graft, upper_list, build);1130	else1131		graft = set_for_cond_from_set(graft, upper_set, build);1132 1133	return graft;1134}1135 1136/* Update "graft" based on "bounds" and "domain" for the generic,1137 * non-degenerate, case.1138 *1139 * "c_lower" and "c_upper" contain the lower and upper bounds1140 * that the loop node should express.1141 * "domain" is the subset of the intersection of the constraints1142 * for which some code is executed.1143 *1144 * There may be zero lower bounds or zero upper bounds in "constraints"1145 * in case the list of constraints was created1146 * based on the atomic option or based on separation with explicit bounds.1147 * In that case, we use "domain" to derive lower and/or upper bounds.1148 *1149 * We first compute a list of one or more lower bounds.1150 *1151 * Then we decide if we want to express the condition as1152 *1153 *	iterator <= min(list of upper bounds)1154 *1155 * or as a conjunction of constraints.1156 *1157 * The set of enforced constraints is then computed either based on1158 * a list of upper bounds or on a set of upper bound constraints.1159 * We do not compute any enforced constraints if we were forced1160 * to compute a lower or upper bound using exact_bound.  The domains1161 * of the resulting expressions may imply some bounds on outer dimensions1162 * that we do not want to appear in the enforced constraints since1163 * they are not actually enforced by the corresponding code.1164 *1165 * Finally, we fill in the expressions of the for node.1166 */1167static __isl_give isl_ast_graft *refine_generic_bounds(1168	__isl_take isl_ast_graft *graft,1169	__isl_take isl_constraint_list *c_lower,1170	__isl_take isl_constraint_list *c_upper,1171	__isl_keep isl_set *domain, __isl_keep isl_ast_build *build)1172{1173	isl_size depth;1174	isl_ctx *ctx;1175	isl_pw_aff_list *lower;1176	int use_list;1177	isl_set *upper_set = NULL;1178	isl_pw_aff_list *upper_list = NULL;1179	isl_size n_lower, n_upper;1180 1181	depth = isl_ast_build_get_depth(build);1182	if (!graft || !c_lower || !c_upper || depth < 0)1183		goto error;1184 1185	ctx = isl_ast_graft_get_ctx(graft);1186 1187	n_lower = isl_constraint_list_n_constraint(c_lower);1188	n_upper = isl_constraint_list_n_constraint(c_upper);1189	if (n_lower < 0 || n_upper < 0)1190		goto error;1191 1192	use_list = use_upper_bound_list(ctx, n_upper, domain, depth);1193 1194	lower = lower_bounds(c_lower, depth, domain, build);1195 1196	if (use_list)1197		upper_list = upper_bounds(c_upper, depth, domain, build);1198	else if (n_upper > 0)1199		upper_set = intersect_constraints(c_upper);1200	else1201		upper_set = isl_set_universe(isl_set_get_space(domain));1202 1203	if (n_lower == 0 || n_upper == 0)1204		;1205	else if (use_list)1206		graft = set_enforced_from_list(graft, lower, upper_list);1207	else1208		graft = set_enforced_from_set(graft, lower, depth, upper_set);1209 1210	graft = set_for_node_expressions(graft, lower, use_list, upper_list,1211					upper_set, build);1212 1213	isl_pw_aff_list_free(lower);1214	isl_pw_aff_list_free(upper_list);1215	isl_set_free(upper_set);1216	isl_constraint_list_free(c_lower);1217	isl_constraint_list_free(c_upper);1218 1219	return graft;1220error:1221	isl_constraint_list_free(c_lower);1222	isl_constraint_list_free(c_upper);1223	return isl_ast_graft_free(graft);1224}1225 1226/* Internal data structure used inside count_constraints to keep1227 * track of the number of constraints that are independent of dimension "pos",1228 * the lower bounds in "pos" and the upper bounds in "pos".1229 */1230struct isl_ast_count_constraints_data {1231	int pos;1232 1233	int n_indep;1234	int n_lower;1235	int n_upper;1236};1237 1238/* Increment data->n_indep, data->lower or data->upper depending1239 * on whether "c" is independent of dimensions data->pos,1240 * a lower bound or an upper bound.1241 */1242static isl_stat count_constraints(__isl_take isl_constraint *c, void *user)1243{1244	struct isl_ast_count_constraints_data *data = user;1245 1246	if (isl_constraint_is_lower_bound(c, isl_dim_set, data->pos))1247		data->n_lower++;1248	else if (isl_constraint_is_upper_bound(c, isl_dim_set, data->pos))1249		data->n_upper++;1250	else1251		data->n_indep++;1252 1253	isl_constraint_free(c);1254 1255	return isl_stat_ok;1256}1257 1258/* Update "graft" based on "bounds" and "domain" for the generic,1259 * non-degenerate, case.1260 *1261 * "list" respresent the list of bounds that need to be encoded by1262 * the for loop.  Only the constraints that involve the iterator1263 * are relevant here.  The other constraints are taken care of by1264 * the caller and are included in the generated constraints of "build".1265 * "domain" is the subset of the intersection of the constraints1266 * for which some code is executed.1267 * "build" is the build in which graft->node was created.1268 *1269 * We separate lower bounds, upper bounds and constraints that1270 * are independent of the loop iterator.1271 *1272 * The actual for loop bounds are generated in refine_generic_bounds.1273 */1274static __isl_give isl_ast_graft *refine_generic_split(1275	__isl_take isl_ast_graft *graft, __isl_take isl_constraint_list *list,1276	__isl_keep isl_set *domain, __isl_keep isl_ast_build *build)1277{1278	struct isl_ast_count_constraints_data data;1279	isl_size depth;1280	isl_constraint_list *lower;1281	isl_constraint_list *upper;1282 1283	depth = isl_ast_build_get_depth(build);1284	if (depth < 0)1285		list = isl_constraint_list_free(list);1286	if (!list)1287		return isl_ast_graft_free(graft);1288 1289	data.pos = depth;1290 1291	list = isl_constraint_list_sort(list, &cmp_constraint, &data.pos);1292	if (!list)1293		return isl_ast_graft_free(graft);1294 1295	data.n_indep = data.n_lower = data.n_upper = 0;1296	if (isl_constraint_list_foreach(list, &count_constraints, &data) < 0) {1297		isl_constraint_list_free(list);1298		return isl_ast_graft_free(graft);1299	}1300 1301	lower = isl_constraint_list_drop(list, 0, data.n_indep);1302	upper = isl_constraint_list_copy(lower);1303	lower = isl_constraint_list_drop(lower, data.n_lower, data.n_upper);1304	upper = isl_constraint_list_drop(upper, 0, data.n_lower);1305 1306	return refine_generic_bounds(graft, lower, upper, domain, build);1307}1308 1309/* Update "graft" based on "bounds" and "domain" for the generic,1310 * non-degenerate, case.1311 *1312 * "bounds" respresent the bounds that need to be encoded by1313 * the for loop (or a guard around the for loop).1314 * "domain" is the subset of "bounds" for which some code is executed.1315 * "build" is the build in which graft->node was created.1316 *1317 * We break up "bounds" into a list of constraints and continue with1318 * refine_generic_split.1319 */1320static __isl_give isl_ast_graft *refine_generic(1321	__isl_take isl_ast_graft *graft,1322	__isl_keep isl_basic_set *bounds, __isl_keep isl_set *domain,1323	__isl_keep isl_ast_build *build)1324{1325	isl_constraint_list *list;1326 1327	if (!build || !graft)1328		return isl_ast_graft_free(graft);1329 1330	list = isl_basic_set_get_constraint_list(bounds);1331 1332	graft = refine_generic_split(graft, list, domain, build);1333 1334	return graft;1335}1336 1337/* Create a for node for the current level.1338 *1339 * Mark the for node degenerate if "degenerate" is set.1340 */1341static __isl_give isl_ast_node *create_for(__isl_keep isl_ast_build *build,1342	int degenerate)1343{1344	isl_size depth;1345	isl_id *id;1346	isl_ast_node *node;1347 1348	depth = isl_ast_build_get_depth(build);1349	if (depth < 0)1350		return NULL;1351 1352	id = isl_ast_build_get_iterator_id(build, depth);1353	node = isl_ast_node_alloc_for(id);1354	if (degenerate)1355		node = isl_ast_node_for_mark_degenerate(node);1356 1357	return node;1358}1359 1360/* If the ast_build_exploit_nested_bounds option is set, then return1361 * the constraints enforced by all elements in "list".1362 * Otherwise, return the universe.1363 */1364static __isl_give isl_basic_set *extract_shared_enforced(1365	__isl_keep isl_ast_graft_list *list, __isl_keep isl_ast_build *build)1366{1367	isl_ctx *ctx;1368	isl_space *space;1369 1370	if (!list)1371		return NULL;1372 1373	ctx = isl_ast_graft_list_get_ctx(list);1374	if (isl_options_get_ast_build_exploit_nested_bounds(ctx))1375		return isl_ast_graft_list_extract_shared_enforced(list, build);1376 1377	space = isl_ast_build_get_space(build, 1);1378	return isl_basic_set_universe(space);1379}1380 1381/* Return the pending constraints of "build" that are not already taken1382 * care of (by a combination of "enforced" and the generated constraints1383 * of "build").1384 */1385static __isl_give isl_set *extract_pending(__isl_keep isl_ast_build *build,1386	__isl_keep isl_basic_set *enforced)1387{1388	isl_set *guard, *context;1389 1390	guard = isl_ast_build_get_pending(build);1391	context = isl_set_from_basic_set(isl_basic_set_copy(enforced));1392	context = isl_set_intersect(context,1393					isl_ast_build_get_generated(build));1394	return isl_set_gist(guard, context);1395}1396 1397/* Create an AST node for the current dimension based on1398 * the schedule domain "bounds" and return the node encapsulated1399 * in an isl_ast_graft.1400 *1401 * "executed" is the current inverse schedule, taking into account1402 * the bounds in "bounds"1403 * "domain" is the domain of "executed", with inner dimensions projected out.1404 * It may be a strict subset of "bounds" in case "bounds" was created1405 * based on the atomic option or based on separation with explicit bounds.1406 *1407 * "domain" may satisfy additional equalities that result1408 * from intersecting "executed" with "bounds" in add_node.1409 * It may also satisfy some global constraints that were dropped out because1410 * we performed separation with explicit bounds.1411 * The very first step is then to copy these constraints to "bounds".1412 *1413 * Since we may be calling before_each_for and after_each_for1414 * callbacks, we record the current inverse schedule in the build.1415 *1416 * We consider three builds,1417 * "build" is the one in which the current level is created,1418 * "body_build" is the build in which the next level is created,1419 * "sub_build" is essentially the same as "body_build", except that1420 * the depth has not been increased yet.1421 *1422 * "build" already contains information (in strides and offsets)1423 * about the strides at the current level, but this information is not1424 * reflected in the build->domain.1425 * We first add this information and the "bounds" to the sub_build->domain.1426 * isl_ast_build_set_loop_bounds adds the stride information and1427 * checks whether the current dimension attains1428 * only a single value and whether this single value can be represented using1429 * a single affine expression.1430 * In the first case, the current level is considered "degenerate".1431 * In the second, sub-case, the current level is considered "eliminated".1432 * Eliminated levels don't need to be reflected in the AST since we can1433 * simply plug in the affine expression.  For degenerate, but non-eliminated,1434 * levels, we do introduce a for node, but mark is as degenerate so that1435 * it can be printed as an assignment of the single value to the loop1436 * "iterator".1437 *1438 * If the current level is eliminated, we explicitly plug in the value1439 * for the current level found by isl_ast_build_set_loop_bounds in the1440 * inverse schedule.  This ensures that if we are working on a slice1441 * of the domain based on information available in the inverse schedule1442 * and the build domain, that then this information is also reflected1443 * in the inverse schedule.  This operation also eliminates the current1444 * dimension from the inverse schedule making sure no inner dimensions depend1445 * on the current dimension.  Otherwise, we create a for node, marking1446 * it degenerate if appropriate.  The initial for node is still incomplete1447 * and will be completed in either refine_degenerate or refine_generic.1448 *1449 * We then generate a sequence of grafts for the next level,1450 * create a surrounding graft for the current level and insert1451 * the for node we created (if the current level is not eliminated).1452 * Before creating a graft for the current level, we first extract1453 * hoistable constraints from the child guards and combine them1454 * with the pending constraints in the build.  These constraints1455 * are used to simplify the child guards and then added to the guard1456 * of the current graft to ensure that they will be generated.1457 * If the hoisted guard is a disjunction, then we use it directly1458 * to gist the guards on the children before intersect it with the1459 * pending constraints.  We do so because this disjunction is typically1460 * identical to the guards on the children such that these guards1461 * can be effectively removed completely.  After the intersection,1462 * the gist operation would have a harder time figuring this out.1463 *1464 * Finally, we set the bounds of the for loop in either1465 * refine_degenerate or refine_generic.1466 * We do so in a context where the pending constraints of the build1467 * have been replaced by the guard of the current graft.1468 */1469static __isl_give isl_ast_graft *create_node_scaled(1470	__isl_take isl_union_map *executed,1471	__isl_take isl_basic_set *bounds, __isl_take isl_set *domain,1472	__isl_take isl_ast_build *build)1473{1474	isl_size depth;1475	int degenerate;1476	isl_bool eliminated;1477	isl_size n;1478	isl_basic_set *hull;1479	isl_basic_set *enforced;1480	isl_set *guard, *hoisted;1481	isl_ast_node *node = NULL;1482	isl_ast_graft *graft;1483	isl_ast_graft_list *children;1484	isl_ast_build *sub_build;1485	isl_ast_build *body_build;1486 1487	domain = isl_ast_build_eliminate_divs(build, domain);1488	domain = isl_set_detect_equalities(domain);1489	hull = isl_set_unshifted_simple_hull(isl_set_copy(domain));1490	bounds = isl_basic_set_intersect(bounds, hull);1491	build = isl_ast_build_set_executed(build, isl_union_map_copy(executed));1492 1493	depth = isl_ast_build_get_depth(build);1494	if (depth < 0)1495		build = isl_ast_build_free(build);1496	sub_build = isl_ast_build_copy(build);1497	bounds = isl_basic_set_remove_redundancies(bounds);1498	bounds = isl_ast_build_specialize_basic_set(sub_build, bounds);1499	sub_build = isl_ast_build_set_loop_bounds(sub_build,1500						isl_basic_set_copy(bounds));1501	degenerate = isl_ast_build_has_value(sub_build);1502	eliminated = isl_ast_build_has_affine_value(sub_build, depth);1503	if (degenerate < 0 || eliminated < 0)1504		executed = isl_union_map_free(executed);1505	if (!degenerate)1506		bounds = isl_ast_build_compute_gist_basic_set(build, bounds);1507	sub_build = isl_ast_build_set_pending_generated(sub_build,1508						isl_basic_set_copy(bounds));1509	if (eliminated)1510		executed = plug_in_values(executed, sub_build);1511	else1512		node = create_for(build, degenerate);1513 1514	body_build = isl_ast_build_copy(sub_build);1515	body_build = isl_ast_build_increase_depth(body_build);1516	if (!eliminated)1517		node = before_each_for(node, body_build);1518	children = generate_next_level(executed,1519				    isl_ast_build_copy(body_build));1520 1521	enforced = extract_shared_enforced(children, build);1522	guard = extract_pending(sub_build, enforced);1523	hoisted = isl_ast_graft_list_extract_hoistable_guard(children, build);1524	n = isl_set_n_basic_set(hoisted);1525	if (n < 0)1526		children = isl_ast_graft_list_free(children);1527	if (n > 1)1528		children = isl_ast_graft_list_gist_guards(children,1529						    isl_set_copy(hoisted));1530	guard = isl_set_intersect(guard, hoisted);1531	if (!eliminated)1532		guard = add_implied_guards(guard, degenerate, bounds, build);1533 1534	graft = isl_ast_graft_alloc_from_children(children,1535			    isl_set_copy(guard), enforced, build, sub_build);1536 1537	if (!eliminated) {1538		isl_ast_build *for_build;1539 1540		graft = isl_ast_graft_insert_for(graft, node);1541		for_build = isl_ast_build_copy(build);1542		for_build = isl_ast_build_replace_pending_by_guard(for_build,1543							isl_set_copy(guard));1544		if (degenerate)1545			graft = refine_degenerate(graft, for_build, sub_build);1546		else1547			graft = refine_generic(graft, bounds,1548					domain, for_build);1549		isl_ast_build_free(for_build);1550	}1551	isl_set_free(guard);1552	if (!eliminated)1553		graft = after_each_for(graft, body_build);1554 1555	isl_ast_build_free(body_build);1556	isl_ast_build_free(sub_build);1557	isl_ast_build_free(build);1558	isl_basic_set_free(bounds);1559	isl_set_free(domain);1560 1561	return graft;1562}1563 1564/* Internal data structure for checking if all constraints involving1565 * the input dimension "depth" are such that the other coefficients1566 * are multiples of "m", reducing "m" if they are not.1567 * If "m" is reduced all the way down to "1", then the check has failed1568 * and we break out of the iteration.1569 */1570struct isl_check_scaled_data {1571	int depth;1572	isl_val *m;1573};1574 1575/* If constraint "c" involves the input dimension data->depth,1576 * then make sure that all the other coefficients are multiples of data->m,1577 * reducing data->m if needed.1578 * Break out of the iteration if data->m has become equal to "1".1579 */1580static isl_stat constraint_check_scaled(__isl_take isl_constraint *c,1581	void *user)1582{1583	struct isl_check_scaled_data *data = user;1584	int i, j;1585	isl_size n;1586	enum isl_dim_type t[] = { isl_dim_param, isl_dim_in, isl_dim_out,1587				    isl_dim_div };1588 1589	if (!isl_constraint_involves_dims(c, isl_dim_in, data->depth, 1)) {1590		isl_constraint_free(c);1591		return isl_stat_ok;1592	}1593 1594	for (i = 0; i < 4; ++i) {1595		n = isl_constraint_dim(c, t[i]);1596		if (n < 0)1597			break;1598		for (j = 0; j < n; ++j) {1599			isl_val *d;1600 1601			if (t[i] == isl_dim_in && j == data->depth)1602				continue;1603			if (!isl_constraint_involves_dims(c, t[i], j, 1))1604				continue;1605			d = isl_constraint_get_coefficient_val(c, t[i], j);1606			data->m = isl_val_gcd(data->m, d);1607			if (isl_val_is_one(data->m))1608				break;1609		}1610		if (j < n)1611			break;1612	}1613 1614	isl_constraint_free(c);1615 1616	return i < 4 ? isl_stat_error : isl_stat_ok;1617}1618 1619/* For each constraint of "bmap" that involves the input dimension data->depth,1620 * make sure that all the other coefficients are multiples of data->m,1621 * reducing data->m if needed.1622 * Break out of the iteration if data->m has become equal to "1".1623 */1624static isl_stat basic_map_check_scaled(__isl_take isl_basic_map *bmap,1625	void *user)1626{1627	isl_stat r;1628 1629	r = isl_basic_map_foreach_constraint(bmap,1630						&constraint_check_scaled, user);1631	isl_basic_map_free(bmap);1632 1633	return r;1634}1635 1636/* For each constraint of "map" that involves the input dimension data->depth,1637 * make sure that all the other coefficients are multiples of data->m,1638 * reducing data->m if needed.1639 * Break out of the iteration if data->m has become equal to "1".1640 */1641static isl_stat map_check_scaled(__isl_take isl_map *map, void *user)1642{1643	isl_stat r;1644 1645	r = isl_map_foreach_basic_map(map, &basic_map_check_scaled, user);1646	isl_map_free(map);1647 1648	return r;1649}1650 1651/* Create an AST node for the current dimension based on1652 * the schedule domain "bounds" and return the node encapsulated1653 * in an isl_ast_graft.1654 *1655 * "executed" is the current inverse schedule, taking into account1656 * the bounds in "bounds"1657 * "domain" is the domain of "executed", with inner dimensions projected out.1658 *1659 *1660 * Before moving on to the actual AST node construction in create_node_scaled,1661 * we first check if the current dimension is strided and if we can scale1662 * down this stride.  Note that we only do this if the ast_build_scale_strides1663 * option is set.1664 *1665 * In particular, let the current dimension take on values1666 *1667 *	f + s a1668 *1669 * with a an integer.  We check if we can find an integer m that (obviously)1670 * divides both f and s.1671 *1672 * If so, we check if the current dimension only appears in constraints1673 * where the coefficients of the other variables are multiples of m.1674 * We perform this extra check to avoid the risk of introducing1675 * divisions by scaling down the current dimension.1676 *1677 * If so, we scale the current dimension down by a factor of m.1678 * That is, we plug in1679 *1680 *	i = m i'							(1)1681 *1682 * Note that in principle we could always scale down strided loops1683 * by plugging in1684 *1685 *	i = f + s i'1686 *1687 * but this may result in i' taking on larger values than the original i,1688 * due to the shift by "f".1689 * By constrast, the scaling in (1) can only reduce the (absolute) value "i".1690 */1691static __isl_give isl_ast_graft *create_node(__isl_take isl_union_map *executed,1692	__isl_take isl_basic_set *bounds, __isl_take isl_set *domain,1693	__isl_take isl_ast_build *build)1694{1695	struct isl_check_scaled_data data;1696	isl_size depth;1697	isl_ctx *ctx;1698	isl_aff *offset;1699	isl_val *d;1700 1701	ctx = isl_ast_build_get_ctx(build);1702	if (!isl_options_get_ast_build_scale_strides(ctx))1703		return create_node_scaled(executed, bounds, domain, build);1704 1705	depth = isl_ast_build_get_depth(build);1706	if (depth < 0)1707		build = isl_ast_build_free(build);1708	data.depth = depth;1709	if (!isl_ast_build_has_stride(build, data.depth))1710		return create_node_scaled(executed, bounds, domain, build);1711 1712	offset = isl_ast_build_get_offset(build, data.depth);1713	data.m = isl_ast_build_get_stride(build, data.depth);1714	if (!data.m)1715		offset = isl_aff_free(offset);1716	offset = isl_aff_scale_down_val(offset, isl_val_copy(data.m));1717	d = isl_aff_get_denominator_val(offset);1718	if (!d)1719		executed = isl_union_map_free(executed);1720 1721	if (executed && isl_val_is_divisible_by(data.m, d))1722		data.m = isl_val_div(data.m, d);1723	else {1724		data.m = isl_val_set_si(data.m, 1);1725		isl_val_free(d);1726	}1727 1728	if (!isl_val_is_one(data.m)) {1729		if (isl_union_map_foreach_map(executed, &map_check_scaled,1730						&data) < 0 &&1731		    !isl_val_is_one(data.m))1732			executed = isl_union_map_free(executed);1733	}1734 1735	if (!isl_val_is_one(data.m)) {1736		isl_space *space;1737		isl_multi_aff *ma;1738		isl_aff *aff;1739		isl_map *map;1740		isl_union_map *umap;1741 1742		space = isl_ast_build_get_space(build, 1);1743		space = isl_space_map_from_set(space);1744		ma = isl_multi_aff_identity(space);1745		aff = isl_multi_aff_get_aff(ma, data.depth);1746		aff = isl_aff_scale_val(aff, isl_val_copy(data.m));1747		ma = isl_multi_aff_set_aff(ma, data.depth, aff);1748 1749		bounds = isl_basic_set_preimage_multi_aff(bounds,1750						isl_multi_aff_copy(ma));1751		domain = isl_set_preimage_multi_aff(domain,1752						isl_multi_aff_copy(ma));1753		map = isl_map_reverse(isl_map_from_multi_aff(ma));1754		umap = isl_union_map_from_map(map);1755		executed = isl_union_map_apply_domain(executed,1756						isl_union_map_copy(umap));1757		build = isl_ast_build_scale_down(build, isl_val_copy(data.m),1758						umap);1759	}1760	isl_aff_free(offset);1761	isl_val_free(data.m);1762 1763	return create_node_scaled(executed, bounds, domain, build);1764}1765 1766/* Add the basic set to the list that "user" points to.1767 */1768static isl_stat collect_basic_set(__isl_take isl_basic_set *bset, void *user)1769{1770	isl_basic_set_list **list = user;1771 1772	*list = isl_basic_set_list_add(*list, bset);1773 1774	return isl_stat_ok;1775}1776 1777/* Extract the basic sets of "set" and collect them in an isl_basic_set_list.1778 */1779static __isl_give isl_basic_set_list *isl_basic_set_list_from_set(1780	__isl_take isl_set *set)1781{1782	isl_size n;1783	isl_ctx *ctx;1784	isl_basic_set_list *list;1785 1786	n = isl_set_n_basic_set(set);1787	if (n < 0)1788		set = isl_set_free(set);1789	if (!set)1790		return NULL;1791 1792	ctx = isl_set_get_ctx(set);1793 1794	list = isl_basic_set_list_alloc(ctx, n);1795	if (isl_set_foreach_basic_set(set, &collect_basic_set, &list) < 0)1796		list = isl_basic_set_list_free(list);1797 1798	isl_set_free(set);1799	return list;1800}1801 1802/* Generate code for the schedule domain "bounds"1803 * and add the result to "list".1804 *1805 * We mainly detect strides here and check if the bounds do not1806 * conflict with the current build domain1807 * and then pass over control to create_node.1808 *1809 * "bounds" reflects the bounds on the current dimension and possibly1810 * some extra conditions on outer dimensions.1811 * It does not, however, include any divs involving the current dimension,1812 * so it does not capture any stride constraints.1813 * We therefore need to compute that part of the schedule domain that1814 * intersects with "bounds" and derive the strides from the result.1815 */1816static __isl_give isl_ast_graft_list *add_node(1817	__isl_take isl_ast_graft_list *list, __isl_take isl_union_map *executed,1818	__isl_take isl_basic_set *bounds, __isl_take isl_ast_build *build)1819{1820	isl_ast_graft *graft;1821	isl_set *domain = NULL;1822	isl_union_set *uset;1823	int empty, disjoint;1824 1825	uset = isl_union_set_from_basic_set(isl_basic_set_copy(bounds));1826	executed = isl_union_map_intersect_domain(executed, uset);1827	empty = isl_union_map_is_empty(executed);1828	if (empty < 0)1829		goto error;1830	if (empty)1831		goto done;1832 1833	uset = isl_union_map_domain(isl_union_map_copy(executed));1834	domain = isl_set_from_union_set(uset);1835	domain = isl_ast_build_specialize(build, domain);1836 1837	domain = isl_set_compute_divs(domain);1838	domain = isl_ast_build_eliminate_inner(build, domain);1839	disjoint = isl_set_is_disjoint(domain, build->domain);1840	if (disjoint < 0)1841		goto error;1842	if (disjoint)1843		goto done;1844 1845	build = isl_ast_build_detect_strides(build, isl_set_copy(domain));1846 1847	graft = create_node(executed, bounds, domain,1848				isl_ast_build_copy(build));1849	list = isl_ast_graft_list_add(list, graft);1850	isl_ast_build_free(build);1851	return list;1852error:1853	list = isl_ast_graft_list_free(list);1854done:1855	isl_set_free(domain);1856	isl_basic_set_free(bounds);1857	isl_union_map_free(executed);1858	isl_ast_build_free(build);1859	return list;1860}1861 1862/* Does any element of i follow or coincide with any element of j1863 * at the current depth for equal values of the outer dimensions?1864 */1865static isl_bool domain_follows_at_depth(__isl_keep isl_basic_set *i,1866	__isl_keep isl_basic_set *j, void *user)1867{1868	int depth = *(int *) user;1869	isl_basic_map *test;1870	isl_bool empty;1871	int l;1872 1873	test = isl_basic_map_from_domain_and_range(isl_basic_set_copy(i),1874						    isl_basic_set_copy(j));1875	for (l = 0; l < depth; ++l)1876		test = isl_basic_map_equate(test, isl_dim_in, l,1877						isl_dim_out, l);1878	test = isl_basic_map_order_ge(test, isl_dim_in, depth,1879					isl_dim_out, depth);1880	empty = isl_basic_map_is_empty(test);1881	isl_basic_map_free(test);1882 1883	return isl_bool_not(empty);1884}1885 1886/* Split up each element of "list" into a part that is related to "bset"1887 * according to "gt" and a part that is not.1888 * Return a list that consist of "bset" and all the pieces.1889 */1890static __isl_give isl_basic_set_list *add_split_on(1891	__isl_take isl_basic_set_list *list, __isl_take isl_basic_set *bset,1892	__isl_keep isl_basic_map *gt)1893{1894	int i;1895	isl_size n;1896	isl_basic_set_list *res;1897 1898	n = isl_basic_set_list_n_basic_set(list);1899	if (n < 0)1900		bset = isl_basic_set_free(bset);1901 1902	gt = isl_basic_map_copy(gt);1903	gt = isl_basic_map_intersect_domain(gt, isl_basic_set_copy(bset));1904	res = isl_basic_set_list_from_basic_set(bset);1905	for (i = 0; res && i < n; ++i) {1906		isl_basic_set *bset;1907		isl_set *set1, *set2;1908		isl_basic_map *bmap;1909		int empty;1910 1911		bset = isl_basic_set_list_get_basic_set(list, i);1912		bmap = isl_basic_map_copy(gt);1913		bmap = isl_basic_map_intersect_range(bmap, bset);1914		bset = isl_basic_map_range(bmap);1915		empty = isl_basic_set_is_empty(bset);1916		if (empty < 0)1917			res = isl_basic_set_list_free(res);1918		if (empty)  {1919			isl_basic_set_free(bset);1920			bset = isl_basic_set_list_get_basic_set(list, i);1921			res = isl_basic_set_list_add(res, bset);1922			continue;1923		}1924 1925		res = isl_basic_set_list_add(res, isl_basic_set_copy(bset));1926		set1 = isl_set_from_basic_set(bset);1927		bset = isl_basic_set_list_get_basic_set(list, i);1928		set2 = isl_set_from_basic_set(bset);1929		set1 = isl_set_subtract(set2, set1);1930		set1 = isl_set_make_disjoint(set1);1931 1932		res = isl_basic_set_list_concat(res,1933					    isl_basic_set_list_from_set(set1));1934	}1935	isl_basic_map_free(gt);1936	isl_basic_set_list_free(list);1937	return res;1938}1939 1940static __isl_give isl_ast_graft_list *generate_sorted_domains(1941	__isl_keep isl_basic_set_list *domain_list,1942	__isl_keep isl_union_map *executed,1943	__isl_keep isl_ast_build *build);1944 1945/* Internal data structure for add_nodes.1946 *1947 * "executed" and "build" are extra arguments to be passed to add_node.1948 * "list" collects the results.1949 */1950struct isl_add_nodes_data {1951	isl_union_map *executed;1952	isl_ast_build *build;1953 1954	isl_ast_graft_list *list;1955};1956 1957/* Generate code for the schedule domains in "scc"1958 * and add the results to "list".1959 *1960 * The domains in "scc" form a strongly connected component in the ordering.1961 * If the number of domains in "scc" is larger than 1, then this means1962 * that we cannot determine a valid ordering for the domains in the component.1963 * This should be fairly rare because the individual domains1964 * have been made disjoint first.1965 * The problem is that the domains may be integrally disjoint but not1966 * rationally disjoint.  For example, we may have domains1967 *1968 *	{ [i,i] : 0 <= i <= 1 }		and	{ [i,1-i] : 0 <= i <= 1 }1969 *1970 * These two domains have an empty intersection, but their rational1971 * relaxations do intersect.  It is impossible to order these domains1972 * in the second dimension because the first should be ordered before1973 * the second for outer dimension equal to 0, while it should be ordered1974 * after for outer dimension equal to 1.1975 *1976 * This may happen in particular in case of unrolling since the domain1977 * of each slice is replaced by its simple hull.1978 *1979 * For each basic set i in "scc" and for each of the following basic sets j,1980 * we split off that part of the basic set i that shares the outer dimensions1981 * with j and lies before j in the current dimension.1982 * We collect all the pieces in a new list that replaces "scc".1983 *1984 * While the elements in "scc" should be disjoint, we double-check1985 * this property to avoid running into an infinite recursion in case1986 * they intersect due to some internal error.1987 */1988static isl_stat add_nodes(__isl_take isl_basic_set_list *scc, void *user)1989{1990	struct isl_add_nodes_data *data = user;1991	int i;1992	isl_size depth;1993	isl_size n;1994	isl_basic_set *bset, *first;1995	isl_basic_set_list *list;1996	isl_space *space;1997	isl_basic_map *gt;1998 1999	n = isl_basic_set_list_n_basic_set(scc);2000	if (n < 0)2001		goto error;2002	bset = isl_basic_set_list_get_basic_set(scc, 0);2003	if (n == 1) {2004		isl_basic_set_list_free(scc);2005		data->list = add_node(data->list,2006				isl_union_map_copy(data->executed), bset,2007				isl_ast_build_copy(data->build));2008		return data->list ? isl_stat_ok : isl_stat_error;2009	}2010 2011	depth = isl_ast_build_get_depth(data->build);2012	if (depth < 0)2013		bset = isl_basic_set_free(bset);2014	space = isl_basic_set_get_space(bset);2015	space = isl_space_map_from_set(space);2016	gt = isl_basic_map_universe(space);2017	for (i = 0; i < depth; ++i)2018		gt = isl_basic_map_equate(gt, isl_dim_in, i, isl_dim_out, i);2019	gt = isl_basic_map_order_gt(gt, isl_dim_in, depth, isl_dim_out, depth);2020 2021	first = isl_basic_set_copy(bset);2022	list = isl_basic_set_list_from_basic_set(bset);2023	for (i = 1; i < n; ++i) {2024		int disjoint;2025 2026		bset = isl_basic_set_list_get_basic_set(scc, i);2027 2028		disjoint = isl_basic_set_is_disjoint(bset, first);2029		if (disjoint < 0)2030			list = isl_basic_set_list_free(list);2031		else if (!disjoint)2032			isl_die(isl_basic_set_list_get_ctx(scc),2033				isl_error_internal,2034				"basic sets in scc are assumed to be disjoint",2035				list = isl_basic_set_list_free(list));2036 2037		list = add_split_on(list, bset, gt);2038	}2039	isl_basic_set_free(first);2040	isl_basic_map_free(gt);2041	isl_basic_set_list_free(scc);2042	scc = list;2043	data->list = isl_ast_graft_list_concat(data->list,2044		    generate_sorted_domains(scc, data->executed, data->build));2045	isl_basic_set_list_free(scc);2046 2047	return data->list ? isl_stat_ok : isl_stat_error;2048error:2049	isl_basic_set_list_free(scc);2050	return isl_stat_error;2051}2052 2053/* Sort the domains in "domain_list" according to the execution order2054 * at the current depth (for equal values of the outer dimensions),2055 * generate code for each of them, collecting the results in a list.2056 * If no code is generated (because the intersection of the inverse schedule2057 * with the domains turns out to be empty), then an empty list is returned.2058 *2059 * The caller is responsible for ensuring that the basic sets in "domain_list"2060 * are pair-wise disjoint.  It can, however, in principle happen that2061 * two basic sets should be ordered one way for one value of the outer2062 * dimensions and the other way for some other value of the outer dimensions.2063 * We therefore play safe and look for strongly connected components.2064 * The function add_nodes takes care of handling non-trivial components.2065 */2066static __isl_give isl_ast_graft_list *generate_sorted_domains(2067	__isl_keep isl_basic_set_list *domain_list,2068	__isl_keep isl_union_map *executed, __isl_keep isl_ast_build *build)2069{2070	isl_ctx *ctx;2071	struct isl_add_nodes_data data;2072	isl_size depth;2073	isl_size n;2074 2075	n = isl_basic_set_list_n_basic_set(domain_list);2076	if (n < 0)2077		return NULL;2078 2079	ctx = isl_basic_set_list_get_ctx(domain_list);2080	data.list = isl_ast_graft_list_alloc(ctx, n);2081	if (n == 0)2082		return data.list;2083	if (n == 1)2084		return add_node(data.list, isl_union_map_copy(executed),2085			isl_basic_set_list_get_basic_set(domain_list, 0),2086			isl_ast_build_copy(build));2087 2088	depth = isl_ast_build_get_depth(build);2089	data.executed = executed;2090	data.build = build;2091	if (depth < 0 || isl_basic_set_list_foreach_scc(domain_list,2092					&domain_follows_at_depth, &depth,2093					&add_nodes, &data) < 0)2094		data.list = isl_ast_graft_list_free(data.list);2095 2096	return data.list;2097}2098 2099/* Do i and j share any values for the outer dimensions?2100 */2101static isl_bool shared_outer(__isl_keep isl_basic_set *i,2102	__isl_keep isl_basic_set *j, void *user)2103{2104	int depth = *(int *) user;2105	isl_basic_map *test;2106	isl_bool empty;2107	int l;2108 2109	test = isl_basic_map_from_domain_and_range(isl_basic_set_copy(i),2110						    isl_basic_set_copy(j));2111	for (l = 0; l < depth; ++l)2112		test = isl_basic_map_equate(test, isl_dim_in, l,2113						isl_dim_out, l);2114	empty = isl_basic_map_is_empty(test);2115	isl_basic_map_free(test);2116 2117	return isl_bool_not(empty);2118}2119 2120/* Internal data structure for generate_sorted_domains_wrap.2121 *2122 * "n" is the total number of basic sets2123 * "executed" and "build" are extra arguments to be passed2124 *	to generate_sorted_domains.2125 *2126 * "single" is set to 1 by generate_sorted_domains_wrap if there2127 * is only a single component.2128 * "list" collects the results.2129 */2130struct isl_ast_generate_parallel_domains_data {2131	isl_size n;2132	isl_union_map *executed;2133	isl_ast_build *build;2134 2135	int single;2136	isl_ast_graft_list *list;2137};2138 2139/* Call generate_sorted_domains on "scc", fuse the result into a list2140 * with either zero or one graft and collect the these single element2141 * lists into data->list.2142 *2143 * If there is only one component, i.e., if the number of basic sets2144 * in the current component is equal to the total number of basic sets,2145 * then data->single is set to 1 and the result of generate_sorted_domains2146 * is not fused.2147 */2148static isl_stat generate_sorted_domains_wrap(__isl_take isl_basic_set_list *scc,2149	void *user)2150{2151	struct isl_ast_generate_parallel_domains_data *data = user;2152	isl_ast_graft_list *list;2153	isl_size n;2154 2155	n = isl_basic_set_list_n_basic_set(scc);2156	if (n < 0)2157		scc = isl_basic_set_list_free(scc);2158	list = generate_sorted_domains(scc, data->executed, data->build);2159	data->single = n == data->n;2160	if (!data->single)2161		list = isl_ast_graft_list_fuse(list, data->build);2162	if (!data->list)2163		data->list = list;2164	else2165		data->list = isl_ast_graft_list_concat(data->list, list);2166 2167	isl_basic_set_list_free(scc);2168	if (!data->list)2169		return isl_stat_error;2170 2171	return isl_stat_ok;2172}2173 2174/* Look for any (weakly connected) components in the "domain_list"2175 * of domains that share some values of the outer dimensions.2176 * That is, domains in different components do not share any values2177 * of the outer dimensions.  This means that these components2178 * can be freely reordered.2179 * Within each of the components, we sort the domains according2180 * to the execution order at the current depth.2181 *2182 * If there is more than one component, then generate_sorted_domains_wrap2183 * fuses the result of each call to generate_sorted_domains2184 * into a list with either zero or one graft and collects these (at most)2185 * single element lists into a bigger list. This means that the elements of the2186 * final list can be freely reordered.  In particular, we sort them2187 * according to an arbitrary but fixed ordering to ease merging of2188 * graft lists from different components.2189 */2190static __isl_give isl_ast_graft_list *generate_parallel_domains(2191	__isl_keep isl_basic_set_list *domain_list,2192	__isl_keep isl_union_map *executed, __isl_keep isl_ast_build *build)2193{2194	isl_size depth;2195	struct isl_ast_generate_parallel_domains_data data;2196 2197	data.n = isl_basic_set_list_n_basic_set(domain_list);2198	if (data.n < 0)2199		return NULL;2200 2201	if (data.n <= 1)2202		return generate_sorted_domains(domain_list, executed, build);2203 2204	depth = isl_ast_build_get_depth(build);2205	if (depth < 0)2206		return NULL;2207	data.list = NULL;2208	data.executed = executed;2209	data.build = build;2210	data.single = 0;2211	if (isl_basic_set_list_foreach_scc(domain_list, &shared_outer, &depth,2212					    &generate_sorted_domains_wrap,2213					    &data) < 0)2214		data.list = isl_ast_graft_list_free(data.list);2215 2216	if (!data.single)2217		data.list = isl_ast_graft_list_sort_guard(data.list);2218 2219	return data.list;2220}2221 2222/* Internal data for separate_domain.2223 *2224 * "explicit" is set if we only want to use explicit bounds.2225 *2226 * "domain" collects the separated domains.2227 */2228struct isl_separate_domain_data {2229	isl_ast_build *build;2230	int explicit;2231	isl_set *domain;2232};2233 2234/* Extract implicit bounds on the current dimension for the executed "map".2235 *2236 * The domain of "map" may involve inner dimensions, so we2237 * need to eliminate them.2238 */2239static __isl_give isl_set *implicit_bounds(__isl_take isl_map *map,2240	__isl_keep isl_ast_build *build)2241{2242	isl_set *domain;2243 2244	domain = isl_map_domain(map);2245	domain = isl_ast_build_eliminate(build, domain);2246 2247	return domain;2248}2249 2250/* Extract explicit bounds on the current dimension for the executed "map".2251 *2252 * Rather than eliminating the inner dimensions as in implicit_bounds,2253 * we simply drop any constraints involving those inner dimensions.2254 * The idea is that most bounds that are implied by constraints on the2255 * inner dimensions will be enforced by for loops and not by explicit guards.2256 * There is then no need to separate along those bounds.2257 */2258static __isl_give isl_set *explicit_bounds(__isl_take isl_map *map,2259	__isl_keep isl_ast_build *build)2260{2261	isl_set *domain;2262	isl_size depth;2263	isl_size dim;2264 2265	depth = isl_ast_build_get_depth(build);2266	dim = isl_map_dim(map, isl_dim_out);2267	if (depth < 0 || dim < 0)2268		return isl_map_domain(isl_map_free(map));2269	map = isl_map_drop_constraints_involving_dims(map, isl_dim_out, 0, dim);2270 2271	domain = isl_map_domain(map);2272	dim = isl_set_dim(domain, isl_dim_set);2273	domain = isl_set_detect_equalities(domain);2274	domain = isl_set_drop_constraints_involving_dims(domain,2275				isl_dim_set, depth + 1, dim - (depth + 1));2276	domain = isl_set_remove_divs_involving_dims(domain,2277				isl_dim_set, depth, 1);2278	domain = isl_set_remove_unknown_divs(domain);2279 2280	return domain;2281}2282 2283/* Split data->domain into pieces that intersect with the range of "map"2284 * and pieces that do not intersect with the range of "map"2285 * and then add that part of the range of "map" that does not intersect2286 * with data->domain.2287 */2288static isl_stat separate_domain(__isl_take isl_map *map, void *user)2289{2290	struct isl_separate_domain_data *data = user;2291	isl_set *domain;2292	isl_set *d1, *d2;2293 2294	if (data->explicit)2295		domain = explicit_bounds(map, data->build);2296	else2297		domain = implicit_bounds(map, data->build);2298 2299	domain = isl_set_coalesce(domain);2300	domain = isl_set_make_disjoint(domain);2301	d1 = isl_set_subtract(isl_set_copy(domain), isl_set_copy(data->domain));2302	d2 = isl_set_subtract(isl_set_copy(data->domain), isl_set_copy(domain));2303	data->domain = isl_set_intersect(data->domain, domain);2304	data->domain = isl_set_union(data->domain, d1);2305	data->domain = isl_set_union(data->domain, d2);2306 2307	return isl_stat_ok;2308}2309 2310/* Separate the schedule domains of "executed".2311 *2312 * That is, break up the domain of "executed" into basic sets,2313 * such that for each basic set S, every element in S is associated with2314 * the same domain spaces.2315 *2316 * "space" is the (single) domain space of "executed".2317 */2318static __isl_give isl_set *separate_schedule_domains(2319	__isl_take isl_space *space, __isl_take isl_union_map *executed,2320	__isl_keep isl_ast_build *build)2321{2322	struct isl_separate_domain_data data = { build };2323	isl_ctx *ctx;2324 2325	ctx = isl_ast_build_get_ctx(build);2326	data.explicit = isl_options_get_ast_build_separation_bounds(ctx) ==2327				    ISL_AST_BUILD_SEPARATION_BOUNDS_EXPLICIT;2328	data.domain = isl_set_empty(space);2329	if (isl_union_map_foreach_map(executed, &separate_domain, &data) < 0)2330		data.domain = isl_set_free(data.domain);2331 2332	isl_union_map_free(executed);2333	return data.domain;2334}2335 2336/* Temporary data used during the search for a lower bound for unrolling.2337 *2338 * "build" is the build in which the unrolling will be performed2339 * "domain" is the original set for which to find a lower bound2340 * "depth" is the dimension for which to find a lower boudn2341 * "expansion" is the expansion that needs to be applied to "domain"2342 * in the unrolling that will be performed2343 *2344 * "lower" is the best lower bound found so far.  It is NULL if we have not2345 * found any yet.2346 * "n" is the corresponding size.  If lower is NULL, then the value of n2347 * is undefined.2348 * "n_div" is the maximal number of integer divisions in the first2349 * unrolled iteration (after expansion).  It is set to -1 if it hasn't2350 * been computed yet.2351 */2352struct isl_find_unroll_data {2353	isl_ast_build *build;2354	isl_set *domain;2355	int depth;2356	isl_basic_map *expansion;2357 2358	isl_aff *lower;2359	int *n;2360	int n_div;2361};2362 2363/* Return the constraint2364 *2365 *	i_"depth" = aff + offset2366 */2367static __isl_give isl_constraint *at_offset(int depth, __isl_keep isl_aff *aff,2368	int offset)2369{2370	aff = isl_aff_copy(aff);2371	aff = isl_aff_add_coefficient_si(aff, isl_dim_in, depth, -1);2372	aff = isl_aff_add_constant_si(aff, offset);2373	return isl_equality_from_aff(aff);2374}2375 2376/* Update *user to the number of integer divisions in the first element2377 * of "ma", if it is larger than the current value.2378 */2379static isl_stat update_n_div(__isl_take isl_set *set,2380	__isl_take isl_multi_aff *ma, void *user)2381{2382	isl_aff *aff;2383	int *n = user;2384	isl_size n_div;2385 2386	aff = isl_multi_aff_get_aff(ma, 0);2387	n_div = isl_aff_dim(aff, isl_dim_div);2388	isl_aff_free(aff);2389	isl_multi_aff_free(ma);2390	isl_set_free(set);2391 2392	if (n_div > *n)2393		*n = n_div;2394 2395	return n_div >= 0 ? isl_stat_ok : isl_stat_error;2396}2397 2398/* Get the number of integer divisions in the expression for the iterator2399 * value at the first slice in the unrolling based on lower bound "lower",2400 * taking into account the expansion that needs to be performed on this slice.2401 */2402static int get_expanded_n_div(struct isl_find_unroll_data *data,2403	__isl_keep isl_aff *lower)2404{2405	isl_constraint *c;2406	isl_set *set;2407	isl_map *it_map, *expansion;2408	isl_pw_multi_aff *pma;2409	int n;2410 2411	c = at_offset(data->depth, lower, 0);2412	set = isl_set_copy(data->domain);2413	set = isl_set_add_constraint(set, c);2414	expansion = isl_map_from_basic_map(isl_basic_map_copy(data->expansion));2415	set = isl_set_apply(set, expansion);2416	it_map = isl_ast_build_map_to_iterator(data->build, set);2417	pma = isl_pw_multi_aff_from_map(it_map);2418	n = 0;2419	if (isl_pw_multi_aff_foreach_piece(pma, &update_n_div, &n) < 0)2420		n = -1;2421	isl_pw_multi_aff_free(pma);2422 2423	return n;2424}2425 2426/* Is the lower bound "lower" with corresponding iteration count "n"2427 * better than the one stored in "data"?2428 * If there is no upper bound on the iteration count ("n" is infinity) or2429 * if the count is too large, then we cannot use this lower bound.2430 * Otherwise, if there was no previous lower bound or2431 * if the iteration count of the new lower bound is smaller than2432 * the iteration count of the previous lower bound, then we consider2433 * the new lower bound to be better.2434 * If the iteration count is the same, then compare the number2435 * of integer divisions that would be needed to express2436 * the iterator value at the first slice in the unrolling2437 * according to the lower bound.  If we end up computing this2438 * number, then store the lowest value in data->n_div.2439 */2440static int is_better_lower_bound(struct isl_find_unroll_data *data,2441	__isl_keep isl_aff *lower, __isl_keep isl_val *n)2442{2443	int cmp;2444	int n_div;2445 2446	if (!n)2447		return -1;2448	if (isl_val_is_infty(n))2449		return 0;2450	if (isl_val_cmp_si(n, INT_MAX) > 0)2451		return 0;2452	if (!data->lower)2453		return 1;2454	cmp = isl_val_cmp_si(n, *data->n);2455	if (cmp < 0)2456		return 1;2457	if (cmp > 0)2458		return 0;2459	if (data->n_div < 0)2460		data->n_div = get_expanded_n_div(data, data->lower);2461	if (data->n_div < 0)2462		return -1;2463	if (data->n_div == 0)2464		return 0;2465	n_div = get_expanded_n_div(data, lower);2466	if (n_div < 0)2467		return -1;2468	if (n_div >= data->n_div)2469		return 0;2470	data->n_div = n_div;2471 2472	return 1;2473}2474 2475/* Check if we can use "c" as a lower bound and if it is better than2476 * any previously found lower bound.2477 *2478 * If "c" does not involve the dimension at the current depth,2479 * then we cannot use it.2480 * Otherwise, let "c" be of the form2481 *2482 *	i >= f(j)/a2483 *2484 * We compute the maximal value of2485 *2486 *	-ceil(f(j)/a)) + i + 12487 *2488 * over the domain.  If there is such a value "n", then we know2489 *2490 *	-ceil(f(j)/a)) + i + 1 <= n2491 *2492 * or2493 *2494 *	i < ceil(f(j)/a)) + n2495 *2496 * meaning that we can use ceil(f(j)/a)) as a lower bound for unrolling.2497 * We just need to check if we have found any lower bound before and2498 * if the new lower bound is better (smaller n or fewer integer divisions)2499 * than the previously found lower bounds.2500 */2501static isl_stat update_unrolling_lower_bound(struct isl_find_unroll_data *data,2502	__isl_keep isl_constraint *c)2503{2504	isl_aff *aff, *lower;2505	isl_val *max;2506	int better;2507 2508	if (!isl_constraint_is_lower_bound(c, isl_dim_set, data->depth))2509		return isl_stat_ok;2510 2511	lower = isl_constraint_get_bound(c, isl_dim_set, data->depth);2512	lower = isl_aff_ceil(lower);2513	aff = isl_aff_copy(lower);2514	aff = isl_aff_neg(aff);2515	aff = isl_aff_add_coefficient_si(aff, isl_dim_in, data->depth, 1);2516	aff = isl_aff_add_constant_si(aff, 1);2517	max = isl_set_max_val(data->domain, aff);2518	isl_aff_free(aff);2519 2520	better = is_better_lower_bound(data, lower, max);2521	if (better < 0 || !better) {2522		isl_val_free(max);2523		isl_aff_free(lower);2524		return better < 0 ? isl_stat_error : isl_stat_ok;2525	}2526 2527	isl_aff_free(data->lower);2528	data->lower = lower;2529	*data->n = isl_val_get_num_si(max);2530	isl_val_free(max);2531 2532	return isl_stat_ok;2533}2534 2535/* Check if we can use "c" as a lower bound and if it is better than2536 * any previously found lower bound.2537 */2538static isl_stat constraint_find_unroll(__isl_take isl_constraint *c, void *user)2539{2540	struct isl_find_unroll_data *data;2541	isl_stat r;2542 2543	data = (struct isl_find_unroll_data *) user;2544	r = update_unrolling_lower_bound(data, c);2545	isl_constraint_free(c);2546 2547	return r;2548}2549 2550/* Look for a lower bound l(i) on the dimension at "depth"2551 * and a size n such that "domain" is a subset of2552 *2553 *	{ [i] : l(i) <= i_d < l(i) + n }2554 *2555 * where d is "depth" and l(i) depends only on earlier dimensions.2556 * Furthermore, try and find a lower bound such that n is as small as possible.2557 * In particular, "n" needs to be finite.2558 * "build" is the build in which the unrolling will be performed.2559 * "expansion" is the expansion that needs to be applied to "domain"2560 * in the unrolling that will be performed.2561 *2562 * Inner dimensions have been eliminated from "domain" by the caller.2563 *2564 * We first construct a collection of lower bounds on the input set2565 * by computing its simple hull.  We then iterate through them,2566 * discarding those that we cannot use (either because they do not2567 * involve the dimension at "depth" or because they have no corresponding2568 * upper bound, meaning that "n" would be unbounded) and pick out the2569 * best from the remaining ones.2570 *2571 * If we cannot find a suitable lower bound, then we consider that2572 * to be an error.2573 */2574static __isl_give isl_aff *find_unroll_lower_bound(2575	__isl_keep isl_ast_build *build, __isl_keep isl_set *domain,2576	int depth, __isl_keep isl_basic_map *expansion, int *n)2577{2578	struct isl_find_unroll_data data =2579			{ build, domain, depth, expansion, NULL, n, -1 };2580	isl_basic_set *hull;2581 2582	hull = isl_set_simple_hull(isl_set_copy(domain));2583 2584	if (isl_basic_set_foreach_constraint(hull,2585					    &constraint_find_unroll, &data) < 0)2586		goto error;2587 2588	isl_basic_set_free(hull);2589 2590	if (!data.lower)2591		isl_die(isl_set_get_ctx(domain), isl_error_invalid,2592			"cannot find lower bound for unrolling", return NULL);2593 2594	return data.lower;2595error:2596	isl_basic_set_free(hull);2597	return isl_aff_free(data.lower);2598}2599 2600/* Call "fn" on each iteration of the current dimension of "domain".2601 * If "init" is not NULL, then it is called with the number of2602 * iterations before any call to "fn".2603 * Return -1 on failure.2604 *2605 * Since we are going to be iterating over the individual values,2606 * we first check if there are any strides on the current dimension.2607 * If there is, we rewrite the current dimension i as2608 *2609 *		i = stride i' + offset2610 *2611 * and then iterate over individual values of i' instead.2612 *2613 * We then look for a lower bound on i' and a size such that the domain2614 * is a subset of2615 *2616 *	{ [j,i'] : l(j) <= i' < l(j) + n }2617 *2618 * and then take slices of the domain at values of i'2619 * between l(j) and l(j) + n - 1.2620 *2621 * We compute the unshifted simple hull of each slice to ensure that2622 * we have a single basic set per offset.  The slicing constraint2623 * may get simplified away before the unshifted simple hull is taken2624 * and may therefore in some rare cases disappear from the result.2625 * We therefore explicitly add the constraint back after computing2626 * the unshifted simple hull to ensure that the basic sets2627 * remain disjoint.  The constraints that are dropped by taking the hull2628 * will be taken into account at the next level, as in the case of the2629 * atomic option.2630 *2631 * Finally, we map i' back to i and call "fn".2632 */2633static int foreach_iteration(__isl_take isl_set *domain,2634	__isl_keep isl_ast_build *build, int (*init)(int n, void *user),2635	int (*fn)(__isl_take isl_basic_set *bset, void *user), void *user)2636{2637	int i, n;2638	isl_bool empty;2639	isl_size depth;2640	isl_multi_aff *expansion;2641	isl_basic_map *bmap;2642	isl_aff *lower = NULL;2643	isl_ast_build *stride_build;2644 2645	depth = isl_ast_build_get_depth(build);2646	if (depth < 0)2647		domain = isl_set_free(domain);2648 2649	domain = isl_ast_build_eliminate_inner(build, domain);2650	domain = isl_set_intersect(domain, isl_ast_build_get_domain(build));2651	stride_build = isl_ast_build_copy(build);2652	stride_build = isl_ast_build_detect_strides(stride_build,2653							isl_set_copy(domain));2654	expansion = isl_ast_build_get_stride_expansion(stride_build);2655 2656	domain = isl_set_preimage_multi_aff(domain,2657					    isl_multi_aff_copy(expansion));2658	domain = isl_ast_build_eliminate_divs(stride_build, domain);2659	isl_ast_build_free(stride_build);2660 2661	bmap = isl_basic_map_from_multi_aff(expansion);2662 2663	empty = isl_set_is_empty(domain);2664	if (empty < 0) {2665		n = -1;2666	} else if (empty) {2667		n = 0;2668	} else {2669		lower = find_unroll_lower_bound(build, domain, depth, bmap, &n);2670		if (!lower)2671			n = -1;2672	}2673	if (n >= 0 && init && init(n, user) < 0)2674		n = -1;2675	for (i = 0; i < n; ++i) {2676		isl_set *set;2677		isl_basic_set *bset;2678		isl_constraint *slice;2679 2680		slice = at_offset(depth, lower, i);2681		set = isl_set_copy(domain);2682		set = isl_set_add_constraint(set, isl_constraint_copy(slice));2683		bset = isl_set_unshifted_simple_hull(set);2684		bset = isl_basic_set_add_constraint(bset, slice);2685		bset = isl_basic_set_apply(bset, isl_basic_map_copy(bmap));2686 2687		if (fn(bset, user) < 0)2688			break;2689	}2690 2691	isl_aff_free(lower);2692	isl_set_free(domain);2693	isl_basic_map_free(bmap);2694 2695	return n < 0 || i < n ? -1 : 0;2696}2697 2698/* Data structure for storing the results and the intermediate objects2699 * of compute_domains.2700 *2701 * "list" is the main result of the function and contains a list2702 * of disjoint basic sets for which code should be generated.2703 *2704 * "executed" and "build" are inputs to compute_domains.2705 * "schedule_domain" is the domain of "executed".2706 *2707 * "option" contains the domains at the current depth that should by2708 * atomic, separated or unrolled.  These domains are as specified by2709 * the user, except that inner dimensions have been eliminated and2710 * that they have been made pair-wise disjoint.2711 *2712 * "sep_class" contains the user-specified split into separation classes2713 * specialized to the current depth.2714 * "done" contains the union of the separation domains that have already2715 * been handled.2716 */2717struct isl_codegen_domains {2718	isl_basic_set_list *list;2719 2720	isl_union_map *executed;2721	isl_ast_build *build;2722	isl_set *schedule_domain;2723 2724	isl_set *option[4];2725 2726	isl_map *sep_class;2727	isl_set *done;2728};2729 2730/* Internal data structure for do_unroll.2731 *2732 * "domains" stores the results of compute_domains.2733 * "class_domain" is the original class domain passed to do_unroll.2734 * "unroll_domain" collects the unrolled iterations.2735 */2736struct isl_ast_unroll_data {2737	struct isl_codegen_domains *domains;2738	isl_set *class_domain;2739	isl_set *unroll_domain;2740};2741 2742/* Given an iteration of an unrolled domain represented by "bset",2743 * add it to data->domains->list.2744 * Since we may have dropped some constraints, we intersect with2745 * the class domain again to ensure that each element in the list2746 * is disjoint from the other class domains.2747 */2748static int do_unroll_iteration(__isl_take isl_basic_set *bset, void *user)2749{2750	struct isl_ast_unroll_data *data = user;2751	isl_set *set;2752	isl_basic_set_list *list;2753 2754	set = isl_set_from_basic_set(bset);2755	data->unroll_domain = isl_set_union(data->unroll_domain,2756					    isl_set_copy(set));2757	set = isl_set_intersect(set, isl_set_copy(data->class_domain));2758	set = isl_set_make_disjoint(set);2759	list = isl_basic_set_list_from_set(set);2760	data->domains->list = isl_basic_set_list_concat(data->domains->list,2761							list);2762 2763	return 0;2764}2765 2766/* Extend domains->list with a list of basic sets, one for each value2767 * of the current dimension in "domain" and remove the corresponding2768 * sets from the class domain.  Return the updated class domain.2769 * The divs that involve the current dimension have not been projected out2770 * from this domain.2771 *2772 * We call foreach_iteration to iterate over the individual values and2773 * in do_unroll_iteration we collect the individual basic sets in2774 * domains->list and their union in data->unroll_domain, which is then2775 * used to update the class domain.2776 */2777static __isl_give isl_set *do_unroll(struct isl_codegen_domains *domains,2778	__isl_take isl_set *domain, __isl_take isl_set *class_domain)2779{2780	struct isl_ast_unroll_data data;2781 2782	if (!domain)2783		return isl_set_free(class_domain);2784	if (!class_domain)2785		return isl_set_free(domain);2786 2787	data.domains = domains;2788	data.class_domain = class_domain;2789	data.unroll_domain = isl_set_empty(isl_set_get_space(domain));2790 2791	if (foreach_iteration(domain, domains->build, NULL,2792				&do_unroll_iteration, &data) < 0)2793		data.unroll_domain = isl_set_free(data.unroll_domain);2794 2795	class_domain = isl_set_subtract(class_domain, data.unroll_domain);2796 2797	return class_domain;2798}2799 2800/* Add domains to domains->list for each individual value of the current2801 * dimension, for that part of the schedule domain that lies in the2802 * intersection of the option domain and the class domain.2803 * Remove the corresponding sets from the class domain and2804 * return the updated class domain.2805 *2806 * We first break up the unroll option domain into individual pieces2807 * and then handle each of them separately.  The unroll option domain2808 * has been made disjoint in compute_domains_init_options,2809 *2810 * Note that we actively want to combine different pieces of the2811 * schedule domain that have the same value at the current dimension.2812 * We therefore need to break up the unroll option domain before2813 * intersecting with class and schedule domain, hoping that the2814 * unroll option domain specified by the user is relatively simple.2815 */2816static __isl_give isl_set *compute_unroll_domains(2817	struct isl_codegen_domains *domains, __isl_take isl_set *class_domain)2818{2819	isl_set *unroll_domain;2820	isl_basic_set_list *unroll_list;2821	int i;2822	isl_size n;2823	isl_bool empty;2824 2825	empty = isl_set_is_empty(domains->option[isl_ast_loop_unroll]);2826	if (empty < 0)2827		return isl_set_free(class_domain);2828	if (empty)2829		return class_domain;2830 2831	unroll_domain = isl_set_copy(domains->option[isl_ast_loop_unroll]);2832	unroll_list = isl_basic_set_list_from_set(unroll_domain);2833 2834	n = isl_basic_set_list_n_basic_set(unroll_list);2835	if (n < 0)2836		class_domain = isl_set_free(class_domain);2837	for (i = 0; i < n; ++i) {2838		isl_basic_set *bset;2839 2840		bset = isl_basic_set_list_get_basic_set(unroll_list, i);2841		unroll_domain = isl_set_from_basic_set(bset);2842		unroll_domain = isl_set_intersect(unroll_domain,2843						    isl_set_copy(class_domain));2844		unroll_domain = isl_set_intersect(unroll_domain,2845					isl_set_copy(domains->schedule_domain));2846 2847		empty = isl_set_is_empty(unroll_domain);2848		if (empty >= 0 && empty) {2849			isl_set_free(unroll_domain);2850			continue;2851		}2852 2853		class_domain = do_unroll(domains, unroll_domain, class_domain);2854	}2855 2856	isl_basic_set_list_free(unroll_list);2857 2858	return class_domain;2859}2860 2861/* Try and construct a single basic set that includes the intersection of2862 * the schedule domain, the atomic option domain and the class domain.2863 * Add the resulting basic set(s) to domains->list and remove them2864 * from class_domain.  Return the updated class domain.2865 *2866 * We construct a single domain rather than trying to combine2867 * the schedule domains of individual domains because we are working2868 * within a single component so that non-overlapping schedule domains2869 * should already have been separated.2870 * We do however need to make sure that this single domains is a subset2871 * of the class domain so that it would not intersect with any other2872 * class domains.  This means that we may end up splitting up the atomic2873 * domain in case separation classes are being used.2874 *2875 * "domain" is the intersection of the schedule domain and the class domain,2876 * with inner dimensions projected out.2877 */2878static __isl_give isl_set *compute_atomic_domain(2879	struct isl_codegen_domains *domains, __isl_take isl_set *class_domain)2880{2881	isl_basic_set *bset;2882	isl_basic_set_list *list;2883	isl_set *domain, *atomic_domain;2884	int empty;2885 2886	domain = isl_set_copy(domains->option[isl_ast_loop_atomic]);2887	domain = isl_set_intersect(domain, isl_set_copy(class_domain));2888	domain = isl_set_intersect(domain,2889				isl_set_copy(domains->schedule_domain));2890	empty = isl_set_is_empty(domain);2891	if (empty < 0)2892		class_domain = isl_set_free(class_domain);2893	if (empty) {2894		isl_set_free(domain);2895		return class_domain;2896	}2897 2898	domain = isl_ast_build_eliminate(domains->build, domain);2899	domain = isl_set_coalesce_preserve(domain);2900	bset = isl_set_unshifted_simple_hull(domain);2901	domain = isl_set_from_basic_set(bset);2902	atomic_domain = isl_set_copy(domain);2903	domain = isl_set_intersect(domain, isl_set_copy(class_domain));2904	class_domain = isl_set_subtract(class_domain, atomic_domain);2905	domain = isl_set_make_disjoint(domain);2906	list = isl_basic_set_list_from_set(domain);2907	domains->list = isl_basic_set_list_concat(domains->list, list);2908 2909	return class_domain;2910}2911 2912/* Split up the schedule domain into uniform basic sets,2913 * in the sense that each element in a basic set is associated to2914 * elements of the same domains, and add the result to domains->list.2915 * Do this for that part of the schedule domain that lies in the2916 * intersection of "class_domain" and the separate option domain.2917 *2918 * "class_domain" may or may not include the constraints2919 * of the schedule domain, but this does not make a difference2920 * since we are going to intersect it with the domain of the inverse schedule.2921 * If it includes schedule domain constraints, then they may involve2922 * inner dimensions, but we will eliminate them in separation_domain.2923 */2924static int compute_separate_domain(struct isl_codegen_domains *domains,2925	__isl_keep isl_set *class_domain)2926{2927	isl_space *space;2928	isl_set *domain;2929	isl_union_map *executed;2930	isl_basic_set_list *list;2931	int empty;2932 2933	domain = isl_set_copy(domains->option[isl_ast_loop_separate]);2934	domain = isl_set_intersect(domain, isl_set_copy(class_domain));2935	executed = isl_union_map_copy(domains->executed);2936	executed = isl_union_map_intersect_domain(executed,2937				    isl_union_set_from_set(domain));2938	empty = isl_union_map_is_empty(executed);2939	if (empty < 0 || empty) {2940		isl_union_map_free(executed);2941		return empty < 0 ? -1 : 0;2942	}2943 2944	space = isl_set_get_space(class_domain);2945	domain = separate_schedule_domains(space, executed, domains->build);2946 2947	list = isl_basic_set_list_from_set(domain);2948	domains->list = isl_basic_set_list_concat(domains->list, list);2949 2950	return 0;2951}2952 2953/* Split up the domain at the current depth into disjoint2954 * basic sets for which code should be generated separately2955 * for the given separation class domain.2956 *2957 * If any separation classes have been defined, then "class_domain"2958 * is the domain of the current class and does not refer to inner dimensions.2959 * Otherwise, "class_domain" is the universe domain.2960 *2961 * We first make sure that the class domain is disjoint from2962 * previously considered class domains.2963 *2964 * The separate domains can be computed directly from the "class_domain".2965 *2966 * The unroll, atomic and remainder domains need the constraints2967 * from the schedule domain.2968 *2969 * For unrolling, the actual schedule domain is needed (with divs that2970 * may refer to the current dimension) so that stride detection can be2971 * performed.2972 *2973 * For atomic and remainder domains, inner dimensions and divs involving2974 * the current dimensions should be eliminated.2975 * In case we are working within a separation class, we need to intersect2976 * the result with the current "class_domain" to ensure that the domains2977 * are disjoint from those generated from other class domains.2978 *2979 * The domain that has been made atomic may be larger than specified2980 * by the user since it needs to be representable as a single basic set.2981 * This possibly larger domain is removed from class_domain by2982 * compute_atomic_domain.  It is computed first so that the extended domain2983 * would not overlap with any domains computed before.2984 * Similary, the unrolled domains may have some constraints removed and2985 * may therefore also be larger than specified by the user.2986 *2987 * If anything is left after handling separate, unroll and atomic,2988 * we split it up into basic sets and append the basic sets to domains->list.2989 */2990static isl_stat compute_partial_domains(struct isl_codegen_domains *domains,2991	__isl_take isl_set *class_domain)2992{2993	isl_basic_set_list *list;2994	isl_set *domain;2995 2996	class_domain = isl_set_subtract(class_domain,2997					isl_set_copy(domains->done));2998	domains->done = isl_set_union(domains->done,2999					isl_set_copy(class_domain));3000 3001	class_domain = compute_atomic_domain(domains, class_domain);3002	class_domain = compute_unroll_domains(domains, class_domain);3003 3004	domain = isl_set_copy(class_domain);3005 3006	if (compute_separate_domain(domains, domain) < 0)3007		goto error;3008	domain = isl_set_subtract(domain,3009			isl_set_copy(domains->option[isl_ast_loop_separate]));3010 3011	domain = isl_set_intersect(domain,3012				isl_set_copy(domains->schedule_domain));3013 3014	domain = isl_ast_build_eliminate(domains->build, domain);3015	domain = isl_set_intersect(domain, isl_set_copy(class_domain));3016 3017	domain = isl_set_coalesce_preserve(domain);3018	domain = isl_set_make_disjoint(domain);3019 3020	list = isl_basic_set_list_from_set(domain);3021	domains->list = isl_basic_set_list_concat(domains->list, list);3022 3023	isl_set_free(class_domain);3024 3025	return isl_stat_ok;3026error:3027	isl_set_free(domain);3028	isl_set_free(class_domain);3029	return isl_stat_error;3030}3031 3032/* Split up the domain at the current depth into disjoint3033 * basic sets for which code should be generated separately3034 * for the separation class identified by "pnt".3035 *3036 * We extract the corresponding class domain from domains->sep_class,3037 * eliminate inner dimensions and pass control to compute_partial_domains.3038 */3039static isl_stat compute_class_domains(__isl_take isl_point *pnt, void *user)3040{3041	struct isl_codegen_domains *domains = user;3042	isl_set *class_set;3043	isl_set *domain;3044	int disjoint;3045 3046	class_set = isl_set_from_point(pnt);3047	domain = isl_map_domain(isl_map_intersect_range(3048				isl_map_copy(domains->sep_class), class_set));3049	domain = isl_ast_build_compute_gist(domains->build, domain);3050	domain = isl_ast_build_eliminate(domains->build, domain);3051 3052	disjoint = isl_set_plain_is_disjoint(domain, domains->schedule_domain);3053	if (disjoint < 0)3054		return isl_stat_error;3055	if (disjoint) {3056		isl_set_free(domain);3057		return isl_stat_ok;3058	}3059 3060	return compute_partial_domains(domains, domain);3061}3062 3063/* Extract the domains at the current depth that should be atomic,3064 * separated or unrolled and store them in option.3065 *3066 * The domains specified by the user might overlap, so we make3067 * them disjoint by subtracting earlier domains from later domains.3068 */3069static void compute_domains_init_options(isl_set *option[4],3070	__isl_keep isl_ast_build *build)3071{3072	enum isl_ast_loop_type type, type2;3073	isl_set *unroll;3074 3075	for (type = isl_ast_loop_atomic;3076	    type <= isl_ast_loop_separate; ++type) {3077		option[type] = isl_ast_build_get_option_domain(build, type);3078		for (type2 = isl_ast_loop_atomic; type2 < type; ++type2)3079			option[type] = isl_set_subtract(option[type],3080						isl_set_copy(option[type2]));3081	}3082 3083	unroll = option[isl_ast_loop_unroll];3084	unroll = isl_set_coalesce(unroll);3085	unroll = isl_set_make_disjoint(unroll);3086	option[isl_ast_loop_unroll] = unroll;3087}3088 3089/* Split up the domain at the current depth into disjoint3090 * basic sets for which code should be generated separately,3091 * based on the user-specified options.3092 * Return the list of disjoint basic sets.3093 *3094 * There are three kinds of domains that we need to keep track of.3095 * - the "schedule domain" is the domain of "executed"3096 * - the "class domain" is the domain corresponding to the currrent3097 *	separation class3098 * - the "option domain" is the domain corresponding to one of the options3099 *	atomic, unroll or separate3100 *3101 * We first consider the individial values of the separation classes3102 * and split up the domain for each of them separately.3103 * Finally, we consider the remainder.  If no separation classes were3104 * specified, then we call compute_partial_domains with the universe3105 * "class_domain".  Otherwise, we take the "schedule_domain" as "class_domain",3106 * with inner dimensions removed.  We do this because we want to3107 * avoid computing the complement of the class domains (i.e., the difference3108 * between the universe and domains->done).3109 */3110static __isl_give isl_basic_set_list *compute_domains(3111	__isl_keep isl_union_map *executed, __isl_keep isl_ast_build *build)3112{3113	struct isl_codegen_domains domains;3114	isl_ctx *ctx;3115	isl_set *domain;3116	isl_union_set *schedule_domain;3117	isl_set *classes;3118	isl_space *space;3119	int n_param;3120	enum isl_ast_loop_type type;3121	isl_bool empty;3122 3123	if (!executed)3124		return NULL;3125 3126	ctx = isl_union_map_get_ctx(executed);3127	domains.list = isl_basic_set_list_alloc(ctx, 0);3128 3129	schedule_domain = isl_union_map_domain(isl_union_map_copy(executed));3130	domain = isl_set_from_union_set(schedule_domain);3131 3132	compute_domains_init_options(domains.option, build);3133 3134	domains.sep_class = isl_ast_build_get_separation_class(build);3135	classes = isl_map_range(isl_map_copy(domains.sep_class));3136	n_param = isl_set_dim(classes, isl_dim_param);3137	if (n_param < 0)3138		classes = isl_set_free(classes);3139	classes = isl_set_project_out(classes, isl_dim_param, 0, n_param);3140 3141	space = isl_set_get_space(domain);3142	domains.build = build;3143	domains.schedule_domain = isl_set_copy(domain);3144	domains.executed = executed;3145	domains.done = isl_set_empty(space);3146 3147	if (isl_set_foreach_point(classes, &compute_class_domains, &domains) < 0)3148		domains.list = isl_basic_set_list_free(domains.list);3149	isl_set_free(classes);3150 3151	empty = isl_set_is_empty(domains.done);3152	if (empty < 0) {3153		domains.list = isl_basic_set_list_free(domains.list);3154		domain = isl_set_free(domain);3155	} else if (empty) {3156		isl_set_free(domain);3157		domain = isl_set_universe(isl_set_get_space(domains.done));3158	} else {3159		domain = isl_ast_build_eliminate(build, domain);3160	}3161	if (compute_partial_domains(&domains, domain) < 0)3162		domains.list = isl_basic_set_list_free(domains.list);3163 3164	isl_set_free(domains.schedule_domain);3165	isl_set_free(domains.done);3166	isl_map_free(domains.sep_class);3167	for (type = isl_ast_loop_atomic; type <= isl_ast_loop_separate; ++type)3168		isl_set_free(domains.option[type]);3169 3170	return domains.list;3171}3172 3173/* Generate code for a single component, after shifting (if any)3174 * has been applied, in case the schedule was specified as a union map.3175 *3176 * We first split up the domain at the current depth into disjoint3177 * basic sets based on the user-specified options.3178 * Then we generated code for each of them and concatenate the results.3179 */3180static __isl_give isl_ast_graft_list *generate_shifted_component_flat(3181	__isl_take isl_union_map *executed, __isl_take isl_ast_build *build)3182{3183	isl_basic_set_list *domain_list;3184	isl_ast_graft_list *list = NULL;3185 3186	domain_list = compute_domains(executed, build);3187	list = generate_parallel_domains(domain_list, executed, build);3188 3189	isl_basic_set_list_free(domain_list);3190	isl_union_map_free(executed);3191	isl_ast_build_free(build);3192 3193	return list;3194}3195 3196/* Generate code for a single component, after shifting (if any)3197 * has been applied, in case the schedule was specified as a schedule tree3198 * and the separate option was specified.3199 *3200 * We perform separation on the domain of "executed" and then generate3201 * an AST for each of the resulting disjoint basic sets.3202 */3203static __isl_give isl_ast_graft_list *generate_shifted_component_tree_separate(3204	__isl_take isl_union_map *executed, __isl_take isl_ast_build *build)3205{3206	isl_space *space;3207	isl_set *domain;3208	isl_basic_set_list *domain_list;3209	isl_ast_graft_list *list;3210 3211	space = isl_ast_build_get_space(build, 1);3212	domain = separate_schedule_domains(space,3213					isl_union_map_copy(executed), build);3214	domain_list = isl_basic_set_list_from_set(domain);3215 3216	list = generate_parallel_domains(domain_list, executed, build);3217 3218	isl_basic_set_list_free(domain_list);3219	isl_union_map_free(executed);3220	isl_ast_build_free(build);3221 3222	return list;3223}3224 3225/* Internal data structure for generate_shifted_component_tree_unroll.3226 *3227 * "executed" and "build" are inputs to generate_shifted_component_tree_unroll.3228 * "list" collects the constructs grafts.3229 */3230struct isl_ast_unroll_tree_data {3231	isl_union_map *executed;3232	isl_ast_build *build;3233	isl_ast_graft_list *list;3234};3235 3236/* Initialize data->list to a list of "n" elements.3237 */3238static int init_unroll_tree(int n, void *user)3239{3240	struct isl_ast_unroll_tree_data *data = user;3241	isl_ctx *ctx;3242 3243	ctx = isl_ast_build_get_ctx(data->build);3244	data->list = isl_ast_graft_list_alloc(ctx, n);3245 3246	return 0;3247}3248 3249/* Given an iteration of an unrolled domain represented by "bset",3250 * generate the corresponding AST and add the result to data->list.3251 */3252static int do_unroll_tree_iteration(__isl_take isl_basic_set *bset, void *user)3253{3254	struct isl_ast_unroll_tree_data *data = user;3255 3256	data->list = add_node(data->list, isl_union_map_copy(data->executed),3257				bset, isl_ast_build_copy(data->build));3258 3259	return 0;3260}3261 3262/* Generate code for a single component, after shifting (if any)3263 * has been applied, in case the schedule was specified as a schedule tree3264 * and the unroll option was specified.3265 *3266 * We call foreach_iteration to iterate over the individual values and3267 * construct and collect the corresponding grafts in do_unroll_tree_iteration.3268 */3269static __isl_give isl_ast_graft_list *generate_shifted_component_tree_unroll(3270	__isl_take isl_union_map *executed, __isl_take isl_set *domain,3271	__isl_take isl_ast_build *build)3272{3273	struct isl_ast_unroll_tree_data data = { executed, build, NULL };3274 3275	if (foreach_iteration(domain, build, &init_unroll_tree,3276				&do_unroll_tree_iteration, &data) < 0)3277		data.list = isl_ast_graft_list_free(data.list);3278 3279	isl_union_map_free(executed);3280	isl_ast_build_free(build);3281 3282	return data.list;3283}3284 3285/* Does "domain" involve a disjunction that is purely based on3286 * constraints involving only outer dimension?3287 *3288 * In particular, is there a disjunction such that the constraints3289 * involving the current and later dimensions are the same over3290 * all the disjuncts?3291 */3292static isl_bool has_pure_outer_disjunction(__isl_keep isl_set *domain,3293	__isl_keep isl_ast_build *build)3294{3295	isl_basic_set *hull;3296	isl_set *shared, *inner;3297	isl_bool equal;3298	isl_size depth;3299	isl_size n;3300	isl_size dim;3301 3302	n = isl_set_n_basic_set(domain);3303	if (n < 0)3304		return isl_bool_error;3305	if (n <= 1)3306		return isl_bool_false;3307	dim = isl_set_dim(domain, isl_dim_set);3308	depth = isl_ast_build_get_depth(build);3309	if (dim < 0 || depth < 0)3310		return isl_bool_error;3311 3312	inner = isl_set_copy(domain);3313	inner = isl_set_drop_constraints_not_involving_dims(inner,3314					    isl_dim_set, depth, dim - depth);3315	hull = isl_set_plain_unshifted_simple_hull(isl_set_copy(inner));3316	shared = isl_set_from_basic_set(hull);3317	equal = isl_set_plain_is_equal(inner, shared);3318	isl_set_free(inner);3319	isl_set_free(shared);3320 3321	return equal;3322}3323 3324/* Generate code for a single component, after shifting (if any)3325 * has been applied, in case the schedule was specified as a schedule tree.3326 * In particular, handle the base case where there is either no isolated3327 * set or we are within the isolated set (in which case "isolated" is set)3328 * or the iterations that precede or follow the isolated set.3329 *3330 * The schedule domain is broken up or combined into basic sets3331 * according to the AST generation option specified in the current3332 * schedule node, which may be either atomic, separate, unroll or3333 * unspecified.  If the option is unspecified, then we currently simply3334 * split the schedule domain into disjoint basic sets.3335 *3336 * In case the separate option is specified, the AST generation is3337 * handled by generate_shifted_component_tree_separate.3338 * In the other cases, we need the global schedule domain.3339 * In the unroll case, the AST generation is then handled by3340 * generate_shifted_component_tree_unroll which needs the actual3341 * schedule domain (with divs that may refer to the current dimension)3342 * so that stride detection can be performed.3343 * In the atomic or unspecified case, inner dimensions and divs involving3344 * the current dimensions should be eliminated.3345 * The result is then either combined into a single basic set or3346 * split up into disjoint basic sets.3347 * Finally an AST is generated for each basic set and the results are3348 * concatenated.3349 *3350 * If the schedule domain involves a disjunction that is purely based on3351 * constraints involving only outer dimension, then it is treated as3352 * if atomic was specified.  This ensures that only a single loop3353 * is generated instead of a sequence of identical loops with3354 * different guards.3355 */3356static __isl_give isl_ast_graft_list *generate_shifted_component_tree_base(3357	__isl_take isl_union_map *executed, __isl_take isl_ast_build *build,3358	int isolated)3359{3360	isl_bool outer_disjunction;3361	isl_union_set *schedule_domain;3362	isl_set *domain;3363	isl_basic_set_list *domain_list;3364	isl_ast_graft_list *list;3365	enum isl_ast_loop_type type;3366 3367	type = isl_ast_build_get_loop_type(build, isolated);3368	if (type < 0)3369		goto error;3370 3371	if (type == isl_ast_loop_separate)3372		return generate_shifted_component_tree_separate(executed,3373								build);3374 3375	schedule_domain = isl_union_map_domain(isl_union_map_copy(executed));3376	domain = isl_set_from_union_set(schedule_domain);3377 3378	if (type == isl_ast_loop_unroll)3379		return generate_shifted_component_tree_unroll(executed, domain,3380								build);3381 3382	domain = isl_ast_build_eliminate(build, domain);3383	domain = isl_set_coalesce_preserve(domain);3384 3385	outer_disjunction = has_pure_outer_disjunction(domain, build);3386	if (outer_disjunction < 0)3387		domain = isl_set_free(domain);3388 3389	if (outer_disjunction || type == isl_ast_loop_atomic) {3390		isl_basic_set *hull;3391		hull = isl_set_unshifted_simple_hull(domain);3392		domain_list = isl_basic_set_list_from_basic_set(hull);3393	} else {3394		domain = isl_set_make_disjoint(domain);3395		domain_list = isl_basic_set_list_from_set(domain);3396	}3397 3398	list = generate_parallel_domains(domain_list, executed, build);3399 3400	isl_basic_set_list_free(domain_list);3401	isl_union_map_free(executed);3402	isl_ast_build_free(build);3403 3404	return list;3405error:3406	isl_union_map_free(executed);3407	isl_ast_build_free(build);3408	return NULL;3409}3410 3411/* Extract out the disjunction imposed by "domain" on the outer3412 * schedule dimensions.3413 *3414 * In particular, remove all inner dimensions from "domain" (including3415 * the current dimension) and then remove the constraints that are shared3416 * by all disjuncts in the result.3417 */3418static __isl_give isl_set *extract_disjunction(__isl_take isl_set *domain,3419	__isl_keep isl_ast_build *build)3420{3421	isl_set *hull;3422	isl_size depth;3423	isl_size dim;3424 3425	domain = isl_ast_build_specialize(build, domain);3426	depth = isl_ast_build_get_depth(build);3427	dim = isl_set_dim(domain, isl_dim_set);3428	if (depth < 0 || dim < 0)3429		return isl_set_free(domain);3430	domain = isl_set_eliminate(domain, isl_dim_set, depth, dim - depth);3431	domain = isl_set_remove_unknown_divs(domain);3432	hull = isl_set_copy(domain);3433	hull = isl_set_from_basic_set(isl_set_unshifted_simple_hull(hull));3434	domain = isl_set_gist(domain, hull);3435 3436	return domain;3437}3438 3439/* Add "guard" to the grafts in "list".3440 * "build" is the outer AST build, while "sub_build" includes "guard"3441 * in its generated domain.3442 *3443 * First combine the grafts into a single graft and then add the guard.3444 * If the list is empty, or if some error occurred, then simply return3445 * the list.3446 */3447static __isl_give isl_ast_graft_list *list_add_guard(3448	__isl_take isl_ast_graft_list *list, __isl_keep isl_set *guard,3449	__isl_keep isl_ast_build *build, __isl_keep isl_ast_build *sub_build)3450{3451	isl_ast_graft *graft;3452	isl_size n;3453 3454	list = isl_ast_graft_list_fuse(list, sub_build);3455 3456	n = isl_ast_graft_list_n_ast_graft(list);3457	if (n < 0)3458		return isl_ast_graft_list_free(list);3459	if (n != 1)3460		return list;3461 3462	graft = isl_ast_graft_list_get_ast_graft(list, 0);3463	graft = isl_ast_graft_add_guard(graft, isl_set_copy(guard), build);3464	list = isl_ast_graft_list_set_ast_graft(list, 0, graft);3465 3466	return list;3467}3468 3469/* Generate code for a single component, after shifting (if any)3470 * has been applied, in case the schedule was specified as a schedule tree.3471 * In particular, do so for the specified subset of the schedule domain.3472 *3473 * If we are outside of the isolated part, then "domain" may include3474 * a disjunction.  Explicitly generate this disjunction at this point3475 * instead of relying on the disjunction getting hoisted back up3476 * to this level.3477 */3478static __isl_give isl_ast_graft_list *generate_shifted_component_tree_part(3479	__isl_keep isl_union_map *executed, __isl_take isl_set *domain,3480	__isl_keep isl_ast_build *build, int isolated)3481{3482	isl_union_set *uset;3483	isl_ast_graft_list *list;3484	isl_ast_build *sub_build;3485	int empty;3486 3487	uset = isl_union_set_from_set(isl_set_copy(domain));3488	executed = isl_union_map_copy(executed);3489	executed = isl_union_map_intersect_domain(executed, uset);3490	empty = isl_union_map_is_empty(executed);3491	if (empty < 0)3492		goto error;3493	if (empty) {3494		isl_ctx *ctx;3495		isl_union_map_free(executed);3496		isl_set_free(domain);3497		ctx = isl_ast_build_get_ctx(build);3498		return isl_ast_graft_list_alloc(ctx, 0);3499	}3500 3501	sub_build = isl_ast_build_copy(build);3502	if (!isolated) {3503		domain = extract_disjunction(domain, build);3504		sub_build = isl_ast_build_restrict_generated(sub_build,3505							isl_set_copy(domain));3506	}3507	list = generate_shifted_component_tree_base(executed,3508				isl_ast_build_copy(sub_build), isolated);3509	if (!isolated)3510		list = list_add_guard(list, domain, build, sub_build);3511	isl_ast_build_free(sub_build);3512	isl_set_free(domain);3513	return list;3514error:3515	isl_union_map_free(executed);3516	isl_set_free(domain);3517	return NULL;3518}3519 3520/* Generate code for a single component, after shifting (if any)3521 * has been applied, in case the schedule was specified as a schedule tree.3522 * In particular, do so for the specified sequence of subsets3523 * of the schedule domain, "before", "isolated", "after" and "other",3524 * where only the "isolated" part is considered to be isolated.3525 */3526static __isl_give isl_ast_graft_list *generate_shifted_component_parts(3527	__isl_take isl_union_map *executed, __isl_take isl_set *before,3528	__isl_take isl_set *isolated, __isl_take isl_set *after,3529	__isl_take isl_set *other, __isl_take isl_ast_build *build)3530{3531	isl_ast_graft_list *list, *res;3532 3533	res = generate_shifted_component_tree_part(executed, before, build, 0);3534	list = generate_shifted_component_tree_part(executed, isolated,3535						    build, 1);3536	res = isl_ast_graft_list_concat(res, list);3537	list = generate_shifted_component_tree_part(executed, after, build, 0);3538	res = isl_ast_graft_list_concat(res, list);3539	list = generate_shifted_component_tree_part(executed, other, build, 0);3540	res = isl_ast_graft_list_concat(res, list);3541 3542	isl_union_map_free(executed);3543	isl_ast_build_free(build);3544 3545	return res;3546}3547 3548/* Does "set" intersect "first", but not "second"?3549 */3550static isl_bool only_intersects_first(__isl_keep isl_set *set,3551	__isl_keep isl_set *first, __isl_keep isl_set *second)3552{3553	isl_bool disjoint;3554 3555	disjoint = isl_set_is_disjoint(set, first);3556	if (disjoint < 0)3557		return isl_bool_error;3558	if (disjoint)3559		return isl_bool_false;3560 3561	return isl_set_is_disjoint(set, second);3562}3563 3564/* Generate code for a single component, after shifting (if any)3565 * has been applied, in case the schedule was specified as a schedule tree.3566 * In particular, do so in case of isolation where there is3567 * only an "isolated" part and an "after" part.3568 * "dead1" and "dead2" are freed by this function in order to simplify3569 * the caller.3570 *3571 * The "before" and "other" parts are set to empty sets.3572 */3573static __isl_give isl_ast_graft_list *generate_shifted_component_only_after(3574	__isl_take isl_union_map *executed, __isl_take isl_set *isolated,3575	__isl_take isl_set *after, __isl_take isl_ast_build *build,3576	__isl_take isl_set *dead1, __isl_take isl_set *dead2)3577{3578	isl_set *empty;3579 3580	empty = isl_set_empty(isl_set_get_space(after));3581	isl_set_free(dead1);3582	isl_set_free(dead2);3583	return generate_shifted_component_parts(executed, isl_set_copy(empty),3584						isolated, after, empty, build);3585}3586 3587/* Generate code for a single component, after shifting (if any)3588 * has been applied, in case the schedule was specified as a schedule tree.3589 *3590 * We first check if the user has specified an isolated schedule domain3591 * and that we are not already outside of this isolated schedule domain.3592 * If so, we break up the schedule domain into iterations that3593 * precede the isolated domain, the isolated domain itself,3594 * the iterations that follow the isolated domain and3595 * the remaining iterations (those that are incomparable3596 * to the isolated domain).3597 * We generate an AST for each piece and concatenate the results.3598 *3599 * If the isolated domain is not convex, then it is replaced3600 * by a convex superset to ensure that the sets of preceding and3601 * following iterations are properly defined and, in particular,3602 * that there are no intermediate iterations that do not belong3603 * to the isolated domain.3604 *3605 * In the special case where at least one element of the schedule3606 * domain that does not belong to the isolated domain needs3607 * to be scheduled after this isolated domain, but none of those3608 * elements need to be scheduled before, break up the schedule domain3609 * in only two parts, the isolated domain, and a part that will be3610 * scheduled after the isolated domain.3611 *3612 * If no isolated set has been specified, then we generate an3613 * AST for the entire inverse schedule.3614 */3615static __isl_give isl_ast_graft_list *generate_shifted_component_tree(3616	__isl_take isl_union_map *executed, __isl_take isl_ast_build *build)3617{3618	int i;3619	isl_size depth;3620	int empty, has_isolate;3621	isl_space *space;3622	isl_union_set *schedule_domain;3623	isl_set *domain;3624	isl_basic_set *hull;3625	isl_set *isolated, *before, *after, *test;3626	isl_map *gt, *lt;3627	isl_bool pure;3628 3629	build = isl_ast_build_extract_isolated(build);3630	has_isolate = isl_ast_build_has_isolated(build);3631	if (has_isolate < 0)3632		executed = isl_union_map_free(executed);3633	else if (!has_isolate)3634		return generate_shifted_component_tree_base(executed, build, 0);3635 3636	schedule_domain = isl_union_map_domain(isl_union_map_copy(executed));3637	domain = isl_set_from_union_set(schedule_domain);3638 3639	isolated = isl_ast_build_get_isolated(build);3640	isolated = isl_set_intersect(isolated, isl_set_copy(domain));3641	test = isl_ast_build_specialize(build, isl_set_copy(isolated));3642	empty = isl_set_is_empty(test);3643	isl_set_free(test);3644	if (empty < 0)3645		goto error;3646	if (empty) {3647		isl_set_free(isolated);3648		isl_set_free(domain);3649		return generate_shifted_component_tree_base(executed, build, 0);3650	}3651	depth = isl_ast_build_get_depth(build);3652	if (depth < 0)3653		goto error;3654 3655	isolated = isl_ast_build_eliminate(build, isolated);3656	hull = isl_set_unshifted_simple_hull(isolated);3657	isolated = isl_set_from_basic_set(hull);3658 3659	space = isl_space_map_from_set(isl_set_get_space(isolated));3660	gt = isl_map_universe(space);3661	for (i = 0; i < depth; ++i)3662		gt = isl_map_equate(gt, isl_dim_in, i, isl_dim_out, i);3663	gt = isl_map_order_gt(gt, isl_dim_in, depth, isl_dim_out, depth);3664	lt = isl_map_reverse(isl_map_copy(gt));3665	before = isl_set_apply(isl_set_copy(isolated), gt);3666	after = isl_set_apply(isl_set_copy(isolated), lt);3667 3668	domain = isl_set_subtract(domain, isl_set_copy(isolated));3669	pure = only_intersects_first(domain, after, before);3670	if (pure < 0)3671		executed = isl_union_map_free(executed);3672	else if (pure)3673		return generate_shifted_component_only_after(executed, isolated,3674						domain, build, before, after);3675	domain = isl_set_subtract(domain, isl_set_copy(before));3676	domain = isl_set_subtract(domain, isl_set_copy(after));3677	after = isl_set_subtract(after, isl_set_copy(isolated));3678	after = isl_set_subtract(after, isl_set_copy(before));3679	before = isl_set_subtract(before, isl_set_copy(isolated));3680 3681	return generate_shifted_component_parts(executed, before, isolated,3682						after, domain, build);3683error:3684	isl_set_free(domain);3685	isl_set_free(isolated);3686	isl_union_map_free(executed);3687	isl_ast_build_free(build);3688	return NULL;3689}3690 3691/* Generate code for a single component, after shifting (if any)3692 * has been applied.3693 *3694 * Call generate_shifted_component_tree or generate_shifted_component_flat3695 * depending on whether the schedule was specified as a schedule tree.3696 */3697static __isl_give isl_ast_graft_list *generate_shifted_component(3698	__isl_take isl_union_map *executed, __isl_take isl_ast_build *build)3699{3700	if (isl_ast_build_has_schedule_node(build))3701		return generate_shifted_component_tree(executed, build);3702	else3703		return generate_shifted_component_flat(executed, build);3704}3705 3706struct isl_set_map_pair {3707	isl_set *set;3708	isl_map *map;3709};3710 3711/* Given an array "domain" of isl_set_map_pairs and an array "order"3712 * of indices into the "domain" array,3713 * return the union of the "map" fields of the elements3714 * indexed by the first "n" elements of "order".3715 */3716static __isl_give isl_union_map *construct_component_executed(3717	struct isl_set_map_pair *domain, int *order, int n)3718{3719	int i;3720	isl_map *map;3721	isl_union_map *executed;3722 3723	map = isl_map_copy(domain[order[0]].map);3724	executed = isl_union_map_from_map(map);3725	for (i = 1; i < n; ++i) {3726		map = isl_map_copy(domain[order[i]].map);3727		executed = isl_union_map_add_map(executed, map);3728	}3729 3730	return executed;3731}3732 3733/* Generate code for a single component, after shifting (if any)3734 * has been applied.3735 *3736 * The component inverse schedule is specified as the "map" fields3737 * of the elements of "domain" indexed by the first "n" elements of "order".3738 */3739static __isl_give isl_ast_graft_list *generate_shifted_component_from_list(3740	struct isl_set_map_pair *domain, int *order, int n,3741	__isl_take isl_ast_build *build)3742{3743	isl_union_map *executed;3744 3745	executed = construct_component_executed(domain, order, n);3746	return generate_shifted_component(executed, build);3747}3748 3749/* Does set dimension "pos" of "set" have an obviously fixed value?3750 */3751static int dim_is_fixed(__isl_keep isl_set *set, int pos)3752{3753	int fixed;3754	isl_val *v;3755 3756	v = isl_set_plain_get_val_if_fixed(set, isl_dim_set, pos);3757	if (!v)3758		return -1;3759	fixed = !isl_val_is_nan(v);3760	isl_val_free(v);3761 3762	return fixed;3763}3764 3765/* Given an array "domain" of isl_set_map_pairs and an array "order"3766 * of indices into the "domain" array,3767 * do all (except for at most one) of the "set" field of the elements3768 * indexed by the first "n" elements of "order" have a fixed value3769 * at position "depth"?3770 */3771static int at_most_one_non_fixed(struct isl_set_map_pair *domain,3772	int *order, int n, int depth)3773{3774	int i;3775	int non_fixed = -1;3776 3777	for (i = 0; i < n; ++i) {3778		int f;3779 3780		f = dim_is_fixed(domain[order[i]].set, depth);3781		if (f < 0)3782			return -1;3783		if (f)3784			continue;3785		if (non_fixed >= 0)3786			return 0;3787		non_fixed = i;3788	}3789 3790	return 1;3791}3792 3793/* Given an array "domain" of isl_set_map_pairs and an array "order"3794 * of indices into the "domain" array,3795 * eliminate the inner dimensions from the "set" field of the elements3796 * indexed by the first "n" elements of "order", provided the current3797 * dimension does not have a fixed value.3798 *3799 * Return the index of the first element in "order" with a corresponding3800 * "set" field that does not have an (obviously) fixed value.3801 */3802static int eliminate_non_fixed(struct isl_set_map_pair *domain,3803	int *order, int n, int depth, __isl_keep isl_ast_build *build)3804{3805	int i;3806	int base = -1;3807 3808	for (i = n - 1; i >= 0; --i) {3809		int f;3810		f = dim_is_fixed(domain[order[i]].set, depth);3811		if (f < 0)3812			return -1;3813		if (f)3814			continue;3815		domain[order[i]].set = isl_ast_build_eliminate_inner(build,3816							domain[order[i]].set);3817		base = i;3818	}3819 3820	return base;3821}3822 3823/* Given an array "domain" of isl_set_map_pairs and an array "order"3824 * of indices into the "domain" array,3825 * find the element of "domain" (amongst those indexed by the first "n"3826 * elements of "order") with the "set" field that has the smallest3827 * value for the current iterator.3828 *3829 * Note that the domain with the smallest value may depend on the parameters3830 * and/or outer loop dimension.  Since the result of this function is only3831 * used as heuristic, we only make a reasonable attempt at finding the best3832 * domain, one that should work in case a single domain provides the smallest3833 * value for the current dimension over all values of the parameters3834 * and outer dimensions.3835 *3836 * In particular, we compute the smallest value of the first domain3837 * and replace it by that of any later domain if that later domain3838 * has a smallest value that is smaller for at least some value3839 * of the parameters and outer dimensions.3840 */3841static int first_offset(struct isl_set_map_pair *domain, int *order, int n,3842	__isl_keep isl_ast_build *build)3843{3844	int i;3845	isl_map *min_first;3846	int first = 0;3847 3848	min_first = isl_ast_build_map_to_iterator(build,3849					isl_set_copy(domain[order[0]].set));3850	min_first = isl_map_lexmin(min_first);3851 3852	for (i = 1; i < n; ++i) {3853		isl_map *min, *test;3854		int empty;3855 3856		min = isl_ast_build_map_to_iterator(build,3857					isl_set_copy(domain[order[i]].set));3858		min = isl_map_lexmin(min);3859		test = isl_map_copy(min);3860		test = isl_map_apply_domain(isl_map_copy(min_first), test);3861		test = isl_map_order_lt(test, isl_dim_in, 0, isl_dim_out, 0);3862		empty = isl_map_is_empty(test);3863		isl_map_free(test);3864		if (empty >= 0 && !empty) {3865			isl_map_free(min_first);3866			first = i;3867			min_first = min;3868		} else3869			isl_map_free(min);3870 3871		if (empty < 0)3872			break;3873	}3874 3875	isl_map_free(min_first);3876 3877	return i < n ? -1 : first;3878}3879 3880/* Construct a shifted inverse schedule based on the original inverse schedule,3881 * the stride and the offset.3882 *3883 * The original inverse schedule is specified as the "map" fields3884 * of the elements of "domain" indexed by the first "n" elements of "order".3885 *3886 * "stride" and "offset" are such that the difference3887 * between the values of the current dimension of domain "i"3888 * and the values of the current dimension for some reference domain are3889 * equal to3890 *3891 *	stride * integer + offset[i]3892 *3893 * Moreover, 0 <= offset[i] < stride.3894 *3895 * For each domain, we create a map3896 *3897 *	{ [..., j, ...] -> [..., j - offset[i], offset[i], ....] }3898 *3899 * where j refers to the current dimension and the other dimensions are3900 * unchanged, and apply this map to the original schedule domain.3901 *3902 * For example, for the original schedule3903 *3904 *	{ A[i] -> [2i]: 0 <= i < 10; B[i] -> [2i+1] : 0 <= i < 10 }3905 *3906 * and assuming the offset is 0 for the A domain and 1 for the B domain,3907 * we apply the mapping3908 *3909 *	{ [j] -> [j, 0] }3910 *3911 * to the schedule of the "A" domain and the mapping3912 *3913 *	{ [j - 1] -> [j, 1] }3914 *3915 * to the schedule of the "B" domain.3916 *3917 *3918 * Note that after the transformation, the differences between pairs3919 * of values of the current dimension over all domains are multiples3920 * of stride and that we have therefore exposed the stride.3921 *3922 *3923 * To see that the mapping preserves the lexicographic order,3924 * first note that each of the individual maps above preserves the order.3925 * If the value of the current iterator is j1 in one domain and j2 in another,3926 * then if j1 = j2, we know that the same map is applied to both domains3927 * and the order is preserved.3928 * Otherwise, let us assume, without loss of generality, that j1 < j2.3929 * If c1 >= c2 (with c1 and c2 the corresponding offsets), then3930 *3931 *	j1 - c1 < j2 - c23932 *3933 * and the order is preserved.3934 * If c1 < c2, then we know3935 *3936 *	0 <= c2 - c1 < s3937 *3938 * We also have3939 *3940 *	j2 - j1 = n * s + r3941 *3942 * with n >= 0 and 0 <= r < s.3943 * In other words, r = c2 - c1.3944 * If n > 0, then3945 *3946 *	j1 - c1 < j2 - c23947 *3948 * If n = 0, then3949 *3950 *	j1 - c1 = j2 - c23951 *3952 * and so3953 *3954 *	(j1 - c1, c1) << (j2 - c2, c2)3955 *3956 * with "<<" the lexicographic order, proving that the order is preserved3957 * in all cases.3958 */3959static __isl_give isl_union_map *construct_shifted_executed(3960	struct isl_set_map_pair *domain, int *order, int n,3961	__isl_keep isl_val *stride, __isl_keep isl_multi_val *offset,3962	__isl_keep isl_ast_build *build)3963{3964	int i;3965	isl_union_map *executed;3966	isl_space *space;3967	isl_map *map;3968	isl_size depth;3969	isl_constraint *c;3970 3971	depth = isl_ast_build_get_depth(build);3972	if (depth < 0)3973		return NULL;3974	space = isl_ast_build_get_space(build, 1);3975	executed = isl_union_map_empty(isl_space_copy(space));3976	space = isl_space_map_from_set(space);3977	map = isl_map_identity(isl_space_copy(space));3978	map = isl_map_eliminate(map, isl_dim_out, depth, 1);3979	map = isl_map_insert_dims(map, isl_dim_out, depth + 1, 1);3980	space = isl_space_insert_dims(space, isl_dim_out, depth + 1, 1);3981 3982	c = isl_constraint_alloc_equality(isl_local_space_from_space(space));3983	c = isl_constraint_set_coefficient_si(c, isl_dim_in, depth, 1);3984	c = isl_constraint_set_coefficient_si(c, isl_dim_out, depth, -1);3985 3986	for (i = 0; i < n; ++i) {3987		isl_map *map_i;3988		isl_val *v;3989 3990		v = isl_multi_val_get_val(offset, i);3991		if (!v)3992			break;3993		map_i = isl_map_copy(map);3994		map_i = isl_map_fix_val(map_i, isl_dim_out, depth + 1,3995					isl_val_copy(v));3996		v = isl_val_neg(v);3997		c = isl_constraint_set_constant_val(c, v);3998		map_i = isl_map_add_constraint(map_i, isl_constraint_copy(c));3999 4000		map_i = isl_map_apply_domain(isl_map_copy(domain[order[i]].map),4001						map_i);4002		executed = isl_union_map_add_map(executed, map_i);4003	}4004 4005	isl_constraint_free(c);4006	isl_map_free(map);4007 4008	if (i < n)4009		executed = isl_union_map_free(executed);4010 4011	return executed;4012}4013 4014/* Generate code for a single component, after exposing the stride,4015 * given that the schedule domain is "shifted strided".4016 *4017 * The component inverse schedule is specified as the "map" fields4018 * of the elements of "domain" indexed by the first "n" elements of "order".4019 *4020 * The schedule domain being "shifted strided" means that the differences4021 * between the values of the current dimension of domain "i"4022 * and the values of the current dimension for some reference domain are4023 * equal to4024 *4025 *	stride * integer + offset[i]4026 *4027 * We first look for the domain with the "smallest" value for the current4028 * dimension and adjust the offsets such that the offset of the "smallest"4029 * domain is equal to zero.  The other offsets are reduced modulo stride.4030 *4031 * Based on this information, we construct a new inverse schedule in4032 * construct_shifted_executed that exposes the stride.4033 * Since this involves the introduction of a new schedule dimension,4034 * the build needs to be changed accordingly.4035 * After computing the AST, the newly introduced dimension needs4036 * to be removed again from the list of grafts.  We do this by plugging4037 * in a mapping that represents the new schedule domain in terms of the4038 * old schedule domain.4039 */4040static __isl_give isl_ast_graft_list *generate_shift_component(4041	struct isl_set_map_pair *domain, int *order, int n,4042	__isl_keep isl_val *stride, __isl_keep isl_multi_val *offset,4043	__isl_take isl_ast_build *build)4044{4045	isl_ast_graft_list *list;4046	int first;4047	isl_size depth;4048	isl_val *val;4049	isl_multi_val *mv;4050	isl_space *space;4051	isl_multi_aff *ma, *zero;4052	isl_union_map *executed;4053 4054	depth = isl_ast_build_get_depth(build);4055 4056	first = first_offset(domain, order, n, build);4057	if (depth < 0 || first < 0)4058		goto error;4059 4060	mv = isl_multi_val_copy(offset);4061	val = isl_multi_val_get_val(offset, first);4062	val = isl_val_neg(val);4063	mv = isl_multi_val_add_val(mv, val);4064	mv = isl_multi_val_mod_val(mv, isl_val_copy(stride));4065 4066	executed = construct_shifted_executed(domain, order, n, stride, mv,4067						build);4068	space = isl_ast_build_get_space(build, 1);4069	space = isl_space_map_from_set(space);4070	ma = isl_multi_aff_identity(isl_space_copy(space));4071	space = isl_space_from_domain(isl_space_domain(space));4072	space = isl_space_add_dims(space, isl_dim_out, 1);4073	zero = isl_multi_aff_zero(space);4074	ma = isl_multi_aff_range_splice(ma, depth + 1, zero);4075	build = isl_ast_build_insert_dim(build, depth + 1);4076	list = generate_shifted_component(executed, build);4077 4078	list = isl_ast_graft_list_preimage_multi_aff(list, ma);4079 4080	isl_multi_val_free(mv);4081 4082	return list;4083error:4084	isl_ast_build_free(build);4085	return NULL;4086}4087 4088/* Does any node in the schedule tree rooted at the current schedule node4089 * of "build" depend on outer schedule nodes?4090 */4091static int has_anchored_subtree(__isl_keep isl_ast_build *build)4092{4093	isl_schedule_node *node;4094	int dependent = 0;4095 4096	node = isl_ast_build_get_schedule_node(build);4097	dependent = isl_schedule_node_is_subtree_anchored(node);4098	isl_schedule_node_free(node);4099 4100	return dependent;4101}4102 4103/* Generate code for a single component.4104 *4105 * The component inverse schedule is specified as the "map" fields4106 * of the elements of "domain" indexed by the first "n" elements of "order".4107 *4108 * This function may modify the "set" fields of "domain".4109 *4110 * Before proceeding with the actual code generation for the component,4111 * we first check if there are any "shifted" strides, meaning that4112 * the schedule domains of the individual domains are all strided,4113 * but that they have different offsets, resulting in the union4114 * of schedule domains not being strided anymore.4115 *4116 * The simplest example is the schedule4117 *4118 *	{ A[i] -> [2i]: 0 <= i < 10; B[i] -> [2i+1] : 0 <= i < 10 }4119 *4120 * Both schedule domains are strided, but their union is not.4121 * This function detects such cases and then rewrites the schedule to4122 *4123 *	{ A[i] -> [2i, 0]: 0 <= i < 10; B[i] -> [2i, 1] : 0 <= i < 10 }4124 *4125 * In the new schedule, the schedule domains have the same offset (modulo4126 * the stride), ensuring that the union of schedule domains is also strided.4127 *4128 *4129 * If there is only a single domain in the component, then there is4130 * nothing to do.   Similarly, if the current schedule dimension has4131 * a fixed value for almost all domains then there is nothing to be done.4132 * In particular, we need at least two domains where the current schedule4133 * dimension does not have a fixed value.4134 * Finally, in case of a schedule map input,4135 * if any of the options refer to the current schedule dimension,4136 * then we bail out as well.  It would be possible to reformulate the options4137 * in terms of the new schedule domain, but that would introduce constraints4138 * that separate the domains in the options and that is something we would4139 * like to avoid.4140 * In the case of a schedule tree input, we bail out if any of4141 * the descendants of the current schedule node refer to outer4142 * schedule nodes in any way.4143 *4144 *4145 * To see if there is any shifted stride, we look at the differences4146 * between the values of the current dimension in pairs of domains4147 * for equal values of outer dimensions.  These differences should be4148 * of the form4149 *4150 *	m x + r4151 *4152 * with "m" the stride and "r" a constant.  Note that we cannot perform4153 * this analysis on individual domains as the lower bound in each domain4154 * may depend on parameters or outer dimensions and so the current dimension4155 * itself may not have a fixed remainder on division by the stride.4156 *4157 * In particular, we compare the first domain that does not have an4158 * obviously fixed value for the current dimension to itself and all4159 * other domains and collect the offsets and the gcd of the strides.4160 * If the gcd becomes one, then we failed to find shifted strides.4161 * If the gcd is zero, then the differences were all fixed, meaning4162 * that some domains had non-obviously fixed values for the current dimension.4163 * If all the offsets are the same (for those domains that do not have4164 * an obviously fixed value for the current dimension), then we do not4165 * apply the transformation.4166 * If none of the domains were skipped, then there is nothing to do.4167 * If some of them were skipped, then if we apply separation, the schedule4168 * domain should get split in pieces with a (non-shifted) stride.4169 *4170 * Otherwise, we apply a shift to expose the stride in4171 * generate_shift_component.4172 */4173static __isl_give isl_ast_graft_list *generate_component(4174	struct isl_set_map_pair *domain, int *order, int n,4175	__isl_take isl_ast_build *build)4176{4177	int i, d;4178	isl_size depth;4179	isl_ctx *ctx;4180	isl_map *map;4181	isl_set *deltas;4182	isl_val *gcd = NULL;4183	isl_multi_val *mv;4184	int fixed, skip;4185	int base;4186	isl_ast_graft_list *list;4187	int res = 0;4188 4189	depth = isl_ast_build_get_depth(build);4190	if (depth < 0)4191		goto error;4192 4193	skip = n == 1;4194	if (skip >= 0 && !skip)4195		skip = at_most_one_non_fixed(domain, order, n, depth);4196	if (skip >= 0 && !skip) {4197		if (isl_ast_build_has_schedule_node(build))4198			skip = has_anchored_subtree(build);4199		else4200			skip = isl_ast_build_options_involve_depth(build);4201	}4202	if (skip < 0)4203		goto error;4204	if (skip)4205		return generate_shifted_component_from_list(domain,4206							    order, n, build);4207 4208	base = eliminate_non_fixed(domain, order, n, depth, build);4209	if (base < 0)4210		goto error;4211 4212	ctx = isl_ast_build_get_ctx(build);4213 4214	mv = isl_multi_val_zero(isl_space_set_alloc(ctx, 0, n));4215 4216	fixed = 1;4217	for (i = 0; i < n; ++i) {4218		isl_val *r, *m;4219 4220		map = isl_map_from_domain_and_range(4221					isl_set_copy(domain[order[base]].set),4222					isl_set_copy(domain[order[i]].set));4223		for (d = 0; d < depth; ++d)4224			map = isl_map_equate(map, isl_dim_in, d,4225						    isl_dim_out, d);4226		deltas = isl_map_deltas(map);4227		res = isl_set_dim_residue_class_val(deltas, depth, &m, &r);4228		isl_set_free(deltas);4229		if (res < 0)4230			break;4231 4232		if (i == 0)4233			gcd = m;4234		else4235			gcd = isl_val_gcd(gcd, m);4236		if (isl_val_is_one(gcd)) {4237			isl_val_free(r);4238			break;4239		}4240		mv = isl_multi_val_set_val(mv, i, r);4241 4242		res = dim_is_fixed(domain[order[i]].set, depth);4243		if (res < 0)4244			break;4245		if (res)4246			continue;4247 4248		if (fixed && i > base) {4249			isl_val *a, *b;4250			a = isl_multi_val_get_val(mv, i);4251			b = isl_multi_val_get_val(mv, base);4252			if (isl_val_ne(a, b))4253				fixed = 0;4254			isl_val_free(a);4255			isl_val_free(b);4256		}4257	}4258 4259	if (res < 0 || !gcd) {4260		isl_ast_build_free(build);4261		list = NULL;4262	} else if (i < n || fixed || isl_val_is_zero(gcd)) {4263		list = generate_shifted_component_from_list(domain,4264							    order, n, build);4265	} else {4266		list = generate_shift_component(domain, order, n, gcd, mv,4267						build);4268	}4269 4270	isl_val_free(gcd);4271	isl_multi_val_free(mv);4272 4273	return list;4274error:4275	isl_ast_build_free(build);4276	return NULL;4277}4278 4279/* Store both "map" itself and its domain in the4280 * structure pointed to by *next and advance to the next array element.4281 */4282static isl_stat extract_domain(__isl_take isl_map *map, void *user)4283{4284	struct isl_set_map_pair **next = user;4285 4286	(*next)->map = isl_map_copy(map);4287	(*next)->set = isl_map_domain(map);4288	(*next)++;4289 4290	return isl_stat_ok;4291}4292 4293static isl_bool after_in_tree(__isl_keep isl_union_map *umap,4294	__isl_keep isl_schedule_node *node);4295 4296/* Is any domain element of "umap" scheduled after any of4297 * the corresponding image elements by the tree rooted at4298 * the child of "node"?4299 */4300static isl_bool after_in_child(__isl_keep isl_union_map *umap,4301	__isl_keep isl_schedule_node *node)4302{4303	isl_schedule_node *child;4304	isl_bool after;4305 4306	child = isl_schedule_node_get_child(node, 0);4307	after = after_in_tree(umap, child);4308	isl_schedule_node_free(child);4309 4310	return after;4311}4312 4313/* Is any domain element of "umap" scheduled after any of4314 * the corresponding image elements by the tree rooted at4315 * the band node "node"?4316 *4317 * We first check if any domain element is scheduled after any4318 * of the corresponding image elements by the band node itself.4319 * If not, we restrict "map" to those pairs of element that4320 * are scheduled together by the band node and continue with4321 * the child of the band node.4322 * If there are no such pairs then the map passed to after_in_child4323 * will be empty causing it to return 0.4324 */4325static isl_bool after_in_band(__isl_keep isl_union_map *umap,4326	__isl_keep isl_schedule_node *node)4327{4328	isl_multi_union_pw_aff *mupa;4329	isl_union_map *partial, *test, *gt, *universe, *umap1, *umap2;4330	isl_union_set *domain, *range;4331	isl_space *space;4332	isl_bool empty;4333	isl_bool after;4334	isl_size n;4335 4336	n = isl_schedule_node_band_n_member(node);4337	if (n < 0)4338		return isl_bool_error;4339	if (n == 0)4340		return after_in_child(umap, node);4341 4342	mupa = isl_schedule_node_band_get_partial_schedule(node);4343	space = isl_multi_union_pw_aff_get_space(mupa);4344	partial = isl_union_map_from_multi_union_pw_aff(mupa);4345	test = isl_union_map_copy(umap);4346	test = isl_union_map_apply_domain(test, isl_union_map_copy(partial));4347	test = isl_union_map_apply_range(test, isl_union_map_copy(partial));4348	gt = isl_union_map_from_map(isl_map_lex_gt(space));4349	test = isl_union_map_intersect(test, gt);4350	empty = isl_union_map_is_empty(test);4351	isl_union_map_free(test);4352 4353	if (empty < 0 || !empty) {4354		isl_union_map_free(partial);4355		return isl_bool_not(empty);4356	}4357 4358	universe = isl_union_map_universe(isl_union_map_copy(umap));4359	domain = isl_union_map_domain(isl_union_map_copy(universe));4360	range = isl_union_map_range(universe);4361	umap1 = isl_union_map_copy(partial);4362	umap1 = isl_union_map_intersect_domain(umap1, domain);4363	umap2 = isl_union_map_intersect_domain(partial, range);4364	test = isl_union_map_apply_range(umap1, isl_union_map_reverse(umap2));4365	test = isl_union_map_intersect(test, isl_union_map_copy(umap));4366	after = after_in_child(test, node);4367	isl_union_map_free(test);4368	return after;4369}4370 4371/* Is any domain element of "umap" scheduled after any of4372 * the corresponding image elements by the tree rooted at4373 * the context node "node"?4374 *4375 * The context constraints apply to the schedule domain,4376 * so we cannot apply them directly to "umap", which contains4377 * pairs of statement instances.  Instead, we add them4378 * to the range of the prefix schedule for both domain and4379 * range of "umap".4380 */4381static isl_bool after_in_context(__isl_keep isl_union_map *umap,4382	__isl_keep isl_schedule_node *node)4383{4384	isl_union_map *prefix, *universe, *umap1, *umap2;4385	isl_union_set *domain, *range;4386	isl_set *context;4387	isl_bool after;4388 4389	umap = isl_union_map_copy(umap);4390	context = isl_schedule_node_context_get_context(node);4391	prefix = isl_schedule_node_get_prefix_schedule_union_map(node);4392	universe = isl_union_map_universe(isl_union_map_copy(umap));4393	domain = isl_union_map_domain(isl_union_map_copy(universe));4394	range = isl_union_map_range(universe);4395	umap1 = isl_union_map_copy(prefix);4396	umap1 = isl_union_map_intersect_domain(umap1, domain);4397	umap2 = isl_union_map_intersect_domain(prefix, range);4398	umap1 = isl_union_map_intersect_range(umap1,4399					    isl_union_set_from_set(context));4400	umap1 = isl_union_map_apply_range(umap1, isl_union_map_reverse(umap2));4401	umap = isl_union_map_intersect(umap, umap1);4402 4403	after = after_in_child(umap, node);4404 4405	isl_union_map_free(umap);4406 4407	return after;4408}4409 4410/* Is any domain element of "umap" scheduled after any of4411 * the corresponding image elements by the tree rooted at4412 * the expansion node "node"?4413 *4414 * We apply the expansion to domain and range of "umap" and4415 * continue with its child.4416 */4417static isl_bool after_in_expansion(__isl_keep isl_union_map *umap,4418	__isl_keep isl_schedule_node *node)4419{4420	isl_union_map *expansion;4421	isl_bool after;4422 4423	expansion = isl_schedule_node_expansion_get_expansion(node);4424	umap = isl_union_map_copy(umap);4425	umap = isl_union_map_apply_domain(umap, isl_union_map_copy(expansion));4426	umap = isl_union_map_apply_range(umap, expansion);4427 4428	after = after_in_child(umap, node);4429 4430	isl_union_map_free(umap);4431 4432	return after;4433}4434 4435/* Is any domain element of "umap" scheduled after any of4436 * the corresponding image elements by the tree rooted at4437 * the extension node "node"?4438 *4439 * Since the extension node may add statement instances before or4440 * after the pairs of statement instances in "umap", we return isl_bool_true4441 * to ensure that these pairs are not broken up.4442 */4443static isl_bool after_in_extension(__isl_keep isl_union_map *umap,4444	__isl_keep isl_schedule_node *node)4445{4446	return isl_bool_true;4447}4448 4449/* Is any domain element of "umap" scheduled after any of4450 * the corresponding image elements by the tree rooted at4451 * the filter node "node"?4452 *4453 * We intersect domain and range of "umap" with the filter and4454 * continue with its child.4455 */4456static isl_bool after_in_filter(__isl_keep isl_union_map *umap,4457	__isl_keep isl_schedule_node *node)4458{4459	isl_union_set *filter;4460	isl_bool after;4461 4462	umap = isl_union_map_copy(umap);4463	filter = isl_schedule_node_filter_get_filter(node);4464	umap = isl_union_map_intersect_domain(umap, isl_union_set_copy(filter));4465	umap = isl_union_map_intersect_range(umap, filter);4466 4467	after = after_in_child(umap, node);4468 4469	isl_union_map_free(umap);4470 4471	return after;4472}4473 4474/* Is any domain element of "umap" scheduled after any of4475 * the corresponding image elements by the tree rooted at4476 * the set node "node"?4477 *4478 * This is only the case if this condition holds in any4479 * of the (filter) children of the set node.4480 * In particular, if the domain and the range of "umap"4481 * are contained in different children, then the condition4482 * does not hold.4483 */4484static isl_bool after_in_set(__isl_keep isl_union_map *umap,4485	__isl_keep isl_schedule_node *node)4486{4487	int i;4488	isl_size n;4489 4490	n = isl_schedule_node_n_children(node);4491	if (n < 0)4492		return isl_bool_error;4493	for (i = 0; i < n; ++i) {4494		isl_schedule_node *child;4495		isl_bool after;4496 4497		child = isl_schedule_node_get_child(node, i);4498		after = after_in_tree(umap, child);4499		isl_schedule_node_free(child);4500 4501		if (after < 0 || after)4502			return after;4503	}4504 4505	return isl_bool_false;4506}4507 4508/* Return the filter of child "i" of "node".4509 */4510static __isl_give isl_union_set *child_filter(4511	__isl_keep isl_schedule_node *node, int i)4512{4513	isl_schedule_node *child;4514	isl_union_set *filter;4515 4516	child = isl_schedule_node_get_child(node, i);4517	filter = isl_schedule_node_filter_get_filter(child);4518	isl_schedule_node_free(child);4519 4520	return filter;4521}4522 4523/* Is any domain element of "umap" scheduled after any of4524 * the corresponding image elements by the tree rooted at4525 * the sequence node "node"?4526 *4527 * This happens in particular if any domain element is4528 * contained in a later child than one containing a range element or4529 * if the condition holds within a given child in the sequence.4530 * The later part of the condition is checked by after_in_set.4531 */4532static isl_bool after_in_sequence(__isl_keep isl_union_map *umap,4533	__isl_keep isl_schedule_node *node)4534{4535	int i, j;4536	isl_size n;4537	isl_union_map *umap_i;4538	isl_bool empty;4539	isl_bool after = isl_bool_false;4540 4541	n = isl_schedule_node_n_children(node);4542	if (n < 0)4543		return isl_bool_error;4544	for (i = 1; i < n; ++i) {4545		isl_union_set *filter_i;4546 4547		umap_i = isl_union_map_copy(umap);4548		filter_i = child_filter(node, i);4549		umap_i = isl_union_map_intersect_domain(umap_i, filter_i);4550		empty = isl_union_map_is_empty(umap_i);4551		if (empty < 0)4552			goto error;4553		if (empty) {4554			isl_union_map_free(umap_i);4555			continue;4556		}4557 4558		for (j = 0; j < i; ++j) {4559			isl_union_set *filter_j;4560			isl_union_map *umap_ij;4561 4562			umap_ij = isl_union_map_copy(umap_i);4563			filter_j = child_filter(node, j);4564			umap_ij = isl_union_map_intersect_range(umap_ij,4565								filter_j);4566			empty = isl_union_map_is_empty(umap_ij);4567			isl_union_map_free(umap_ij);4568 4569			if (empty < 0)4570				goto error;4571			if (!empty)4572				after = isl_bool_true;4573			if (after)4574				break;4575		}4576 4577		isl_union_map_free(umap_i);4578		if (after)4579			break;4580	}4581 4582	if (after < 0 || after)4583		return after;4584 4585	return after_in_set(umap, node);4586error:4587	isl_union_map_free(umap_i);4588	return isl_bool_error;4589}4590 4591/* Is any domain element of "umap" scheduled after any of4592 * the corresponding image elements by the tree rooted at "node"?4593 *4594 * If "umap" is empty, then clearly there is no such element.4595 * Otherwise, consider the different types of nodes separately.4596 */4597static isl_bool after_in_tree(__isl_keep isl_union_map *umap,4598	__isl_keep isl_schedule_node *node)4599{4600	isl_bool empty;4601	enum isl_schedule_node_type type;4602 4603	empty = isl_union_map_is_empty(umap);4604	if (empty < 0)4605		return isl_bool_error;4606	if (empty)4607		return isl_bool_false;4608	if (!node)4609		return isl_bool_error;4610 4611	type = isl_schedule_node_get_type(node);4612	switch (type) {4613	case isl_schedule_node_error:4614		return isl_bool_error;4615	case isl_schedule_node_leaf:4616		return isl_bool_false;4617	case isl_schedule_node_band:4618		return after_in_band(umap, node);4619	case isl_schedule_node_domain:4620		isl_die(isl_schedule_node_get_ctx(node), isl_error_internal,4621			"unexpected internal domain node",4622			return isl_bool_error);4623	case isl_schedule_node_context:4624		return after_in_context(umap, node);4625	case isl_schedule_node_expansion:4626		return after_in_expansion(umap, node);4627	case isl_schedule_node_extension:4628		return after_in_extension(umap, node);4629	case isl_schedule_node_filter:4630		return after_in_filter(umap, node);4631	case isl_schedule_node_guard:4632	case isl_schedule_node_mark:4633		return after_in_child(umap, node);4634	case isl_schedule_node_set:4635		return after_in_set(umap, node);4636	case isl_schedule_node_sequence:4637		return after_in_sequence(umap, node);4638	}4639 4640	return isl_bool_true;4641}4642 4643/* Is any domain element of "map1" scheduled after any domain4644 * element of "map2" by the subtree underneath the current band node,4645 * while at the same time being scheduled together by the current4646 * band node, i.e., by "map1" and "map2?4647 *4648 * If the child of the current band node is a leaf, then4649 * no element can be scheduled after any other element.4650 *4651 * Otherwise, we construct a relation between domain elements4652 * of "map1" and domain elements of "map2" that are scheduled4653 * together and then check if the subtree underneath the current4654 * band node determines their relative order.4655 */4656static isl_bool after_in_subtree(__isl_keep isl_ast_build *build,4657	__isl_keep isl_map *map1, __isl_keep isl_map *map2)4658{4659	isl_schedule_node *node;4660	isl_map *map;4661	isl_union_map *umap;4662	isl_bool after;4663 4664	node = isl_ast_build_get_schedule_node(build);4665	if (!node)4666		return isl_bool_error;4667	node = isl_schedule_node_child(node, 0);4668	if (isl_schedule_node_get_type(node) == isl_schedule_node_leaf) {4669		isl_schedule_node_free(node);4670		return isl_bool_false;4671	}4672	map = isl_map_copy(map2);4673	map = isl_map_apply_domain(map, isl_map_copy(map1));4674	umap = isl_union_map_from_map(map);4675	after = after_in_tree(umap, node);4676	isl_union_map_free(umap);4677	isl_schedule_node_free(node);4678	return after;4679}4680 4681/* Internal data for any_scheduled_after.4682 *4683 * "build" is the build in which the AST is constructed.4684 * "depth" is the number of loops that have already been generated4685 * "group_coscheduled" is a local copy of options->ast_build_group_coscheduled4686 * "domain" is an array of set-map pairs corresponding to the different4687 * iteration domains.  The set is the schedule domain, i.e., the domain4688 * of the inverse schedule, while the map is the inverse schedule itself.4689 */4690struct isl_any_scheduled_after_data {4691	isl_ast_build *build;4692	int depth;4693	int group_coscheduled;4694	struct isl_set_map_pair *domain;4695};4696 4697/* Is any element of domain "i" scheduled after any element of domain "j"4698 * (for a common iteration of the first data->depth loops)?4699 *4700 * data->domain[i].set contains the domain of the inverse schedule4701 * for domain "i", i.e., elements in the schedule domain.4702 *4703 * If we are inside a band of a schedule tree and there is a pair4704 * of elements in the two domains that is schedule together by4705 * the current band, then we check if any element of "i" may be schedule4706 * after element of "j" by the descendants of the band node.4707 *4708 * If data->group_coscheduled is set, then we also return 1 if there4709 * is any pair of elements in the two domains that are scheduled together.4710 */4711static isl_bool any_scheduled_after(int i, int j, void *user)4712{4713	struct isl_any_scheduled_after_data *data = user;4714	isl_size dim = isl_set_dim(data->domain[i].set, isl_dim_set);4715	int pos;4716 4717	if (dim < 0)4718		return isl_bool_error;4719 4720	for (pos = data->depth; pos < dim; ++pos) {4721		int follows;4722 4723		follows = isl_set_follows_at(data->domain[i].set,4724						data->domain[j].set, pos);4725 4726		if (follows < -1)4727			return isl_bool_error;4728		if (follows > 0)4729			return isl_bool_true;4730		if (follows < 0)4731			return isl_bool_false;4732	}4733 4734	if (isl_ast_build_has_schedule_node(data->build)) {4735		isl_bool after;4736 4737		after = after_in_subtree(data->build, data->domain[i].map,4738					    data->domain[j].map);4739		if (after < 0 || after)4740			return after;4741	}4742 4743	return isl_bool_ok(data->group_coscheduled);4744}4745 4746/* Look for independent components at the current depth and generate code4747 * for each component separately.  The resulting lists of grafts are4748 * merged in an attempt to combine grafts with identical guards.4749 *4750 * Code for two domains can be generated separately if all the elements4751 * of one domain are scheduled before (or together with) all the elements4752 * of the other domain.  We therefore consider the graph with as nodes4753 * the domains and an edge between two nodes if any element of the first4754 * node is scheduled after any element of the second node.4755 * If the ast_build_group_coscheduled is set, then we also add an edge if4756 * there is any pair of elements in the two domains that are scheduled4757 * together.4758 * Code is then generated (by generate_component)4759 * for each of the strongly connected components in this graph4760 * in their topological order.4761 *4762 * Since the test is performed on the domain of the inverse schedules of4763 * the different domains, we precompute these domains and store4764 * them in data.domain.4765 */4766static __isl_give isl_ast_graft_list *generate_components(4767	__isl_take isl_union_map *executed, __isl_take isl_ast_build *build)4768{4769	int i;4770	isl_ctx *ctx = isl_ast_build_get_ctx(build);4771	isl_size n = isl_union_map_n_map(executed);4772	isl_size depth;4773	struct isl_any_scheduled_after_data data;4774	struct isl_set_map_pair *next;4775	struct isl_tarjan_graph *g = NULL;4776	isl_ast_graft_list *list = NULL;4777	int n_domain = 0;4778 4779	data.domain = NULL;4780	if (n < 0)4781		goto error;4782	data.domain = isl_calloc_array(ctx, struct isl_set_map_pair, n);4783	if (!data.domain)4784		goto error;4785	n_domain = n;4786 4787	next = data.domain;4788	if (isl_union_map_foreach_map(executed, &extract_domain, &next) < 0)4789		goto error;4790 4791	depth = isl_ast_build_get_depth(build);4792	if (depth < 0)4793		goto error;4794	data.build = build;4795	data.depth = depth;4796	data.group_coscheduled = isl_options_get_ast_build_group_coscheduled(ctx);4797	g = isl_tarjan_graph_init(ctx, n, &any_scheduled_after, &data);4798	if (!g)4799		goto error;4800 4801	list = isl_ast_graft_list_alloc(ctx, 0);4802 4803	i = 0;4804	while (list && n) {4805		isl_ast_graft_list *list_c;4806		int first = i;4807 4808		if (g->order[i] == -1)4809			isl_die(ctx, isl_error_internal, "cannot happen",4810				goto error);4811		++i; --n;4812		while (g->order[i] != -1) {4813			++i; --n;4814		}4815 4816		list_c = generate_component(data.domain,4817					    g->order + first, i - first,4818					    isl_ast_build_copy(build));4819		list = isl_ast_graft_list_merge(list, list_c, build);4820 4821		++i;4822	}4823 4824	if (0)4825error:		list = isl_ast_graft_list_free(list);4826	isl_tarjan_graph_free(g);4827	for (i = 0; i < n_domain; ++i) {4828		isl_map_free(data.domain[i].map);4829		isl_set_free(data.domain[i].set);4830	}4831	free(data.domain);4832	isl_union_map_free(executed);4833	isl_ast_build_free(build);4834 4835	return list;4836}4837 4838/* Generate code for the next level (and all inner levels).4839 *4840 * If "executed" is empty, i.e., no code needs to be generated,4841 * then we return an empty list.4842 *4843 * If we have already generated code for all loop levels, then we pass4844 * control to generate_inner_level.4845 *4846 * If "executed" lives in a single space, i.e., if code needs to be4847 * generated for a single domain, then there can only be a single4848 * component and we go directly to generate_shifted_component.4849 * Otherwise, we call generate_components to detect the components4850 * and to call generate_component on each of them separately.4851 */4852static __isl_give isl_ast_graft_list *generate_next_level(4853	__isl_take isl_union_map *executed, __isl_take isl_ast_build *build)4854{4855	isl_size depth;4856	isl_size dim;4857	isl_size n;4858 4859	if (!build || !executed)4860		goto error;4861 4862	if (isl_union_map_is_empty(executed)) {4863		isl_ctx *ctx = isl_ast_build_get_ctx(build);4864		isl_union_map_free(executed);4865		isl_ast_build_free(build);4866		return isl_ast_graft_list_alloc(ctx, 0);4867	}4868 4869	depth = isl_ast_build_get_depth(build);4870	dim = isl_ast_build_dim(build, isl_dim_set);4871	if (depth < 0 || dim < 0)4872		goto error;4873	if (depth >= dim)4874		return generate_inner_level(executed, build);4875 4876	n = isl_union_map_n_map(executed);4877	if (n < 0)4878		goto error;4879	if (n == 1)4880		return generate_shifted_component(executed, build);4881 4882	return generate_components(executed, build);4883error:4884	isl_union_map_free(executed);4885	isl_ast_build_free(build);4886	return NULL;4887}4888 4889/* Internal data structure used by isl_ast_build_node_from_schedule_map.4890 * internal, executed and build are the inputs to generate_code.4891 * list collects the output.4892 */4893struct isl_generate_code_data {4894	int internal;4895	isl_union_map *executed;4896	isl_ast_build *build;4897 4898	isl_ast_graft_list *list;4899};4900 4901/* Given an inverse schedule in terms of the external build schedule, i.e.,4902 *4903 *	[E -> S] -> D4904 *4905 * with E the external build schedule and S the additional schedule "space",4906 * reformulate the inverse schedule in terms of the internal schedule domain,4907 * i.e., return4908 *4909 *	[I -> S] -> D4910 *4911 * We first obtain a mapping4912 *4913 *	I -> E4914 *4915 * take the inverse and the product with S -> S, resulting in4916 *4917 *	[I -> S] -> [E -> S]4918 *4919 * Applying the map to the input produces the desired result.4920 */4921static __isl_give isl_union_map *internal_executed(4922	__isl_take isl_union_map *executed, __isl_keep isl_space *space,4923	__isl_keep isl_ast_build *build)4924{4925	isl_map *id, *proj;4926 4927	proj = isl_ast_build_get_schedule_map(build);4928	proj = isl_map_reverse(proj);4929	space = isl_space_map_from_set(isl_space_copy(space));4930	id = isl_map_identity(space);4931	proj = isl_map_product(proj, id);4932	executed = isl_union_map_apply_domain(executed,4933						isl_union_map_from_map(proj));4934	return executed;4935}4936 4937/* Generate an AST that visits the elements in the range of data->executed4938 * in the relative order specified by the corresponding domain element(s)4939 * for those domain elements that belong to "set".4940 * Add the result to data->list.4941 *4942 * The caller ensures that "set" is a universe domain.4943 * "space" is the space of the additional part of the schedule.4944 * It is equal to the space of "set" if build->domain is parametric.4945 * Otherwise, it is equal to the range of the wrapped space of "set".4946 *4947 * If the build space is not parametric and4948 * if isl_ast_build_node_from_schedule_map4949 * was called from an outside user (data->internal not set), then4950 * the (inverse) schedule refers to the external build domain and needs to4951 * be transformed to refer to the internal build domain.4952 *4953 * If the build space is parametric, then we add some of the parameter4954 * constraints to the executed relation.  Adding these constraints4955 * allows for an earlier detection of conflicts in some cases.4956 * However, we do not want to divide the executed relation into4957 * more disjuncts than necessary.  We therefore approximate4958 * the constraints on the parameters by a single disjunct set.4959 *4960 * The build is extended to include the additional part of the schedule.4961 * If the original build space was not parametric, then the options4962 * in data->build refer only to the additional part of the schedule4963 * and they need to be adjusted to refer to the complete AST build4964 * domain.4965 *4966 * After having adjusted inverse schedule and build, we start generating4967 * code with the outer loop of the current code generation4968 * in generate_next_level.4969 *4970 * If the original build space was not parametric, we undo the embedding4971 * on the resulting isl_ast_node_list so that it can be used within4972 * the outer AST build.4973 */4974static isl_stat generate_code_in_space(struct isl_generate_code_data *data,4975	__isl_take isl_set *set, __isl_take isl_space *space)4976{4977	isl_union_map *executed;4978	isl_ast_build *build;4979	isl_ast_graft_list *list;4980	int embed;4981 4982	executed = isl_union_map_copy(data->executed);4983	executed = isl_union_map_intersect_domain(executed,4984						 isl_union_set_from_set(set));4985 4986	embed = !isl_set_is_params(data->build->domain);4987	if (embed && !data->internal)4988		executed = internal_executed(executed, space, data->build);4989	if (!embed) {4990		isl_set *domain;4991		domain = isl_ast_build_get_domain(data->build);4992		domain = isl_set_from_basic_set(isl_set_simple_hull(domain));4993		executed = isl_union_map_intersect_params(executed, domain);4994	}4995 4996	build = isl_ast_build_copy(data->build);4997	build = isl_ast_build_product(build, space);4998 4999	list = generate_next_level(executed, build);5000 5001	list = isl_ast_graft_list_unembed(list, embed);5002 5003	data->list = isl_ast_graft_list_concat(data->list, list);5004 5005	return isl_stat_ok;5006}5007 5008/* Generate an AST that visits the elements in the range of data->executed5009 * in the relative order specified by the corresponding domain element(s)5010 * for those domain elements that belong to "set".5011 * Add the result to data->list.5012 *5013 * The caller ensures that "set" is a universe domain.5014 *5015 * If the build space S is not parametric, then the space of "set"5016 * need to be a wrapped relation with S as domain.  That is, it needs5017 * to be of the form5018 *5019 *	[S -> T]5020 *5021 * Check this property and pass control to generate_code_in_space5022 * passing along T.5023 * If the build space is not parametric, then T is the space of "set".5024 */5025static isl_stat generate_code_set(__isl_take isl_set *set, void *user)5026{5027	struct isl_generate_code_data *data = user;5028	isl_space *space, *build_space;5029	int is_domain;5030 5031	space = isl_set_get_space(set);5032 5033	if (isl_set_is_params(data->build->domain))5034		return generate_code_in_space(data, set, space);5035 5036	build_space = isl_ast_build_get_space(data->build, data->internal);5037	space = isl_space_unwrap(space);5038	is_domain = isl_space_is_domain(build_space, space);5039	isl_space_free(build_space);5040	space = isl_space_range(space);5041 5042	if (is_domain < 0)5043		goto error;5044	if (!is_domain)5045		isl_die(isl_set_get_ctx(set), isl_error_invalid,5046			"invalid nested schedule space", goto error);5047 5048	return generate_code_in_space(data, set, space);5049error:5050	isl_set_free(set);5051	isl_space_free(space);5052	return isl_stat_error;5053}5054 5055/* Generate an AST that visits the elements in the range of "executed"5056 * in the relative order specified by the corresponding domain element(s).5057 *5058 * "build" is an isl_ast_build that has either been constructed by5059 * isl_ast_build_from_context or passed to a callback set by5060 * isl_ast_build_set_create_leaf.5061 * In the first case, the space of the isl_ast_build is typically5062 * a parametric space, although this is currently not enforced.5063 * In the second case, the space is never a parametric space.5064 * If the space S is not parametric, then the domain space(s) of "executed"5065 * need to be wrapped relations with S as domain.5066 *5067 * If the domain of "executed" consists of several spaces, then an AST5068 * is generated for each of them (in arbitrary order) and the results5069 * are concatenated.5070 *5071 * If "internal" is set, then the domain "S" above refers to the internal5072 * schedule domain representation.  Otherwise, it refers to the external5073 * representation, as returned by isl_ast_build_get_schedule_space.5074 *5075 * We essentially run over all the spaces in the domain of "executed"5076 * and call generate_code_set on each of them.5077 */5078static __isl_give isl_ast_graft_list *generate_code(5079	__isl_take isl_union_map *executed, __isl_take isl_ast_build *build,5080	int internal)5081{5082	isl_ctx *ctx;5083	struct isl_generate_code_data data = { 0 };5084	isl_space *space;5085	isl_union_set *schedule_domain;5086	isl_union_map *universe;5087 5088	if (!build)5089		goto error;5090	space = isl_ast_build_get_space(build, 1);5091	space = isl_space_align_params(space,5092				    isl_union_map_get_space(executed));5093	space = isl_space_align_params(space,5094				    isl_union_map_get_space(build->options));5095	build = isl_ast_build_align_params(build, isl_space_copy(space));5096	executed = isl_union_map_align_params(executed, space);5097	if (!executed || !build)5098		goto error;5099 5100	ctx = isl_ast_build_get_ctx(build);5101 5102	data.internal = internal;5103	data.executed = executed;5104	data.build = build;5105	data.list = isl_ast_graft_list_alloc(ctx, 0);5106 5107	universe = isl_union_map_universe(isl_union_map_copy(executed));5108	schedule_domain = isl_union_map_domain(universe);5109	if (isl_union_set_foreach_set(schedule_domain, &generate_code_set,5110					&data) < 0)5111		data.list = isl_ast_graft_list_free(data.list);5112 5113	isl_union_set_free(schedule_domain);5114	isl_union_map_free(executed);5115 5116	isl_ast_build_free(build);5117	return data.list;5118error:5119	isl_union_map_free(executed);5120	isl_ast_build_free(build);5121	return NULL;5122}5123 5124/* Generate an AST that visits the elements in the domain of "schedule"5125 * in the relative order specified by the corresponding image element(s).5126 *5127 * "build" is an isl_ast_build that has either been constructed by5128 * isl_ast_build_from_context or passed to a callback set by5129 * isl_ast_build_set_create_leaf.5130 * In the first case, the space of the isl_ast_build is typically5131 * a parametric space, although this is currently not enforced.5132 * In the second case, the space is never a parametric space.5133 * If the space S is not parametric, then the range space(s) of "schedule"5134 * need to be wrapped relations with S as domain.5135 *5136 * If the range of "schedule" consists of several spaces, then an AST5137 * is generated for each of them (in arbitrary order) and the results5138 * are concatenated.5139 *5140 * We first initialize the local copies of the relevant options.5141 * We do this here rather than when the isl_ast_build is created5142 * because the options may have changed between the construction5143 * of the isl_ast_build and the call to isl_generate_code.5144 *5145 * The main computation is performed on an inverse schedule (with5146 * the schedule domain in the domain and the elements to be executed5147 * in the range) called "executed".5148 */5149__isl_give isl_ast_node *isl_ast_build_node_from_schedule_map(5150	__isl_keep isl_ast_build *build, __isl_take isl_union_map *schedule)5151{5152	isl_ast_graft_list *list;5153	isl_ast_node *node;5154	isl_union_map *executed;5155 5156	build = isl_ast_build_copy(build);5157	build = isl_ast_build_set_single_valued(build, 0);5158	schedule = isl_union_map_coalesce(schedule);5159	schedule = isl_union_map_remove_redundancies(schedule);5160	executed = isl_union_map_reverse(schedule);5161	list = generate_code(executed, isl_ast_build_copy(build), 0);5162	node = isl_ast_node_from_graft_list(list, build);5163	isl_ast_build_free(build);5164 5165	return node;5166}5167 5168/* The old name for isl_ast_build_node_from_schedule_map.5169 * It is being kept for backward compatibility, but5170 * it will be removed in the future.5171 */5172__isl_give isl_ast_node *isl_ast_build_ast_from_schedule(5173	__isl_keep isl_ast_build *build, __isl_take isl_union_map *schedule)5174{5175	return isl_ast_build_node_from_schedule_map(build, schedule);5176}5177 5178/* Generate an AST that visits the elements in the domain of "executed"5179 * in the relative order specified by the leaf node "node".5180 *5181 * The relation "executed" maps the outer generated loop iterators5182 * to the domain elements executed by those iterations.5183 *5184 * Simply pass control to generate_inner_level.5185 * Note that the current build does not refer to any band node, so5186 * that generate_inner_level will not try to visit the child of5187 * the leaf node.5188 *5189 * If multiple statement instances reach a leaf,5190 * then they can be executed in any order.5191 * Group the list of grafts based on shared guards5192 * such that identical guards are only generated once5193 * when the list is eventually passed on to isl_ast_graft_list_fuse.5194 */5195static __isl_give isl_ast_graft_list *build_ast_from_leaf(5196	__isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,5197	__isl_take isl_union_map *executed)5198{5199	isl_ast_graft_list *list;5200 5201	isl_schedule_node_free(node);5202	list = generate_inner_level(executed, isl_ast_build_copy(build));5203	list = isl_ast_graft_list_group_on_guard(list, build);5204	isl_ast_build_free(build);5205 5206	return list;5207}5208 5209/* Check that the band partial schedule "partial" does not filter out5210 * any statement instances, as specified by the range of "executed".5211 */5212static isl_stat check_band_schedule_total_on_instances(5213	__isl_keep isl_multi_union_pw_aff *partial,5214	__isl_keep isl_union_map *executed)5215{5216	isl_bool subset;5217	isl_union_set *domain, *instances;5218 5219	instances = isl_union_map_range(isl_union_map_copy(executed));5220	partial = isl_multi_union_pw_aff_copy(partial);5221	domain = isl_multi_union_pw_aff_domain(partial);5222	subset = isl_union_set_is_subset(instances, domain);5223	isl_union_set_free(domain);5224	isl_union_set_free(instances);5225 5226	if (subset < 0)5227		return isl_stat_error;5228	if (!subset)5229		isl_die(isl_union_map_get_ctx(executed), isl_error_invalid,5230			"band node is not allowed to drop statement instances",5231			return isl_stat_error);5232	return isl_stat_ok;5233}5234 5235/* Generate an AST that visits the elements in the domain of "executed"5236 * in the relative order specified by the band node "node" and its descendants.5237 *5238 * The relation "executed" maps the outer generated loop iterators5239 * to the domain elements executed by those iterations.5240 *5241 * If the band is empty, we continue with its descendants.5242 * Otherwise, we extend the build and the inverse schedule with5243 * the additional space/partial schedule and continue generating5244 * an AST in generate_next_level.5245 * As soon as we have extended the inverse schedule with the additional5246 * partial schedule, we look for equalities that may exists between5247 * the old and the new part.5248 */5249static __isl_give isl_ast_graft_list *build_ast_from_band(5250	__isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,5251	__isl_take isl_union_map *executed)5252{5253	isl_space *space;5254	isl_multi_union_pw_aff *extra;5255	isl_union_map *extra_umap;5256	isl_ast_graft_list *list;5257	isl_size n1, n2;5258	isl_size n;5259 5260	n = isl_schedule_node_band_n_member(node);5261	if (!build || n < 0 || !executed)5262		goto error;5263 5264	if (n == 0)5265		return build_ast_from_child(build, node, executed);5266 5267	extra = isl_schedule_node_band_get_partial_schedule(node);5268	extra = isl_multi_union_pw_aff_align_params(extra,5269				isl_ast_build_get_space(build, 1));5270	space = isl_multi_union_pw_aff_get_space(extra);5271 5272	if (check_band_schedule_total_on_instances(extra, executed) < 0)5273		executed = isl_union_map_free(executed);5274 5275	extra_umap = isl_union_map_from_multi_union_pw_aff(extra);5276	extra_umap = isl_union_map_reverse(extra_umap);5277 5278	executed = isl_union_map_domain_product(executed, extra_umap);5279	executed = isl_union_map_detect_equalities(executed);5280 5281	n1 = isl_ast_build_dim(build, isl_dim_param);5282	build = isl_ast_build_product(build, space);5283	n2 = isl_ast_build_dim(build, isl_dim_param);5284	if (n1 < 0 || n2 < 0)5285		build = isl_ast_build_free(build);5286	else if (n2 > n1)5287		isl_die(isl_ast_build_get_ctx(build), isl_error_invalid,5288			"band node is not allowed to introduce new parameters",5289			build = isl_ast_build_free(build));5290	build = isl_ast_build_set_schedule_node(build, node);5291 5292	list = generate_next_level(executed, build);5293 5294	list = isl_ast_graft_list_unembed(list, 1);5295 5296	return list;5297error:5298	isl_schedule_node_free(node);5299	isl_union_map_free(executed);5300	isl_ast_build_free(build);5301	return NULL;5302}5303 5304/* Hoist a list of grafts (in practice containing a single graft)5305 * from "sub_build" (which includes extra context information)5306 * to "build".5307 *5308 * In particular, project out all additional parameters introduced5309 * by the context node from the enforced constraints and the guard5310 * of the single graft.5311 */5312static __isl_give isl_ast_graft_list *hoist_out_of_context(5313	__isl_take isl_ast_graft_list *list, __isl_keep isl_ast_build *build,5314	__isl_keep isl_ast_build *sub_build)5315{5316	isl_ast_graft *graft;5317	isl_basic_set *enforced;5318	isl_set *guard;5319	isl_size n_param, extra_param;5320 5321	n_param = isl_ast_build_dim(build, isl_dim_param);5322	extra_param = isl_ast_build_dim(sub_build, isl_dim_param);5323	if (n_param < 0 || extra_param < 0)5324		return isl_ast_graft_list_free(list);5325 5326	if (extra_param == n_param)5327		return list;5328 5329	extra_param -= n_param;5330	enforced = isl_ast_graft_list_extract_shared_enforced(list, sub_build);5331	enforced = isl_basic_set_project_out(enforced, isl_dim_param,5332							n_param, extra_param);5333	enforced = isl_basic_set_remove_unknown_divs(enforced);5334	guard = isl_ast_graft_list_extract_hoistable_guard(list, sub_build);5335	guard = isl_set_remove_divs_involving_dims(guard, isl_dim_param,5336							n_param, extra_param);5337	guard = isl_set_project_out(guard, isl_dim_param, n_param, extra_param);5338	guard = isl_set_compute_divs(guard);5339	graft = isl_ast_graft_alloc_from_children(list, guard, enforced,5340							build, sub_build);5341	list = isl_ast_graft_list_from_ast_graft(graft);5342 5343	return list;5344}5345 5346/* Generate an AST that visits the elements in the domain of "executed"5347 * in the relative order specified by the context node "node"5348 * and its descendants.5349 *5350 * The relation "executed" maps the outer generated loop iterators5351 * to the domain elements executed by those iterations.5352 *5353 * The context node may introduce additional parameters as well as5354 * constraints on the outer schedule dimensions or original parameters.5355 *5356 * We add the extra parameters to a new build and the context5357 * constraints to both the build and (as a single disjunct)5358 * to the domain of "executed".  Since the context constraints5359 * are specified in terms of the input schedule, we first need5360 * to map them to the internal schedule domain.5361 *5362 * After constructing the AST from the descendants of "node",5363 * we combine the list of grafts into a single graft within5364 * the new build, in order to be able to exploit the additional5365 * context constraints during this combination.5366 *5367 * Additionally, if the current node is the outermost node in5368 * the schedule tree (apart from the root domain node), we generate5369 * all pending guards, again to be able to exploit the additional5370 * context constraints.  We currently do not do this for internal5371 * context nodes since we may still want to hoist conditions5372 * to outer AST nodes.5373 *5374 * If the context node introduced any new parameters, then they5375 * are removed from the set of enforced constraints and guard5376 * in hoist_out_of_context.5377 */5378static __isl_give isl_ast_graft_list *build_ast_from_context(5379	__isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,5380	__isl_take isl_union_map *executed)5381{5382	isl_set *context;5383	isl_space *space;5384	isl_multi_aff *internal2input;5385	isl_ast_build *sub_build;5386	isl_ast_graft_list *list;5387	isl_size n;5388	isl_size depth;5389 5390	depth = isl_schedule_node_get_tree_depth(node);5391	if (depth < 0)5392		build = isl_ast_build_free(build);5393	space = isl_ast_build_get_space(build, 1);5394	context = isl_schedule_node_context_get_context(node);5395	context = isl_set_align_params(context, space);5396	sub_build = isl_ast_build_copy(build);5397	space = isl_set_get_space(context);5398	sub_build = isl_ast_build_align_params(sub_build, space);5399	internal2input = isl_ast_build_get_internal2input(sub_build);5400	context = isl_set_preimage_multi_aff(context, internal2input);5401	sub_build = isl_ast_build_restrict_generated(sub_build,5402					isl_set_copy(context));5403	context = isl_set_from_basic_set(isl_set_simple_hull(context));5404	executed = isl_union_map_intersect_domain(executed,5405					isl_union_set_from_set(context));5406 5407	list = build_ast_from_child(isl_ast_build_copy(sub_build),5408						node, executed);5409	n = isl_ast_graft_list_n_ast_graft(list);5410	if (n < 0)5411		list = isl_ast_graft_list_free(list);5412 5413	list = isl_ast_graft_list_fuse(list, sub_build);5414	if (depth == 1)5415		list = isl_ast_graft_list_insert_pending_guard_nodes(list,5416								sub_build);5417	if (n >= 1)5418		list = hoist_out_of_context(list, build, sub_build);5419 5420	isl_ast_build_free(build);5421	isl_ast_build_free(sub_build);5422 5423	return list;5424}5425 5426/* Generate an AST that visits the elements in the domain of "executed"5427 * in the relative order specified by the expansion node "node" and5428 * its descendants.5429 *5430 * The relation "executed" maps the outer generated loop iterators5431 * to the domain elements executed by those iterations.5432 *5433 * We expand the domain elements by the expansion and5434 * continue with the descendants of the node.5435 */5436static __isl_give isl_ast_graft_list *build_ast_from_expansion(5437	__isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,5438	__isl_take isl_union_map *executed)5439{5440	isl_union_map *expansion;5441	isl_size n1, n2;5442 5443	expansion = isl_schedule_node_expansion_get_expansion(node);5444	expansion = isl_union_map_align_params(expansion,5445				isl_union_map_get_space(executed));5446 5447	n1 = isl_union_map_dim(executed, isl_dim_param);5448	executed = isl_union_map_apply_range(executed, expansion);5449	n2 = isl_union_map_dim(executed, isl_dim_param);5450	if (n1 < 0 || n2 < 0)5451		goto error;5452	if (n2 > n1)5453		isl_die(isl_ast_build_get_ctx(build), isl_error_invalid,5454			"expansion node is not allowed to introduce "5455			"new parameters", goto error);5456 5457	return build_ast_from_child(build, node, executed);5458error:5459	isl_ast_build_free(build);5460	isl_schedule_node_free(node);5461	isl_union_map_free(executed);5462	return NULL;5463}5464 5465/* Generate an AST that visits the elements in the domain of "executed"5466 * in the relative order specified by the extension node "node" and5467 * its descendants.5468 *5469 * The relation "executed" maps the outer generated loop iterators5470 * to the domain elements executed by those iterations.5471 *5472 * Extend the inverse schedule with the extension applied to current5473 * set of generated constraints.  Since the extension if formulated5474 * in terms of the input schedule, it first needs to be transformed5475 * to refer to the internal schedule.5476 */5477static __isl_give isl_ast_graft_list *build_ast_from_extension(5478	__isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,5479	__isl_take isl_union_map *executed)5480{5481	isl_union_set *schedule_domain;5482	isl_union_map *extension;5483	isl_set *set;5484 5485	set = isl_ast_build_get_generated(build);5486	set = isl_set_from_basic_set(isl_set_simple_hull(set));5487	schedule_domain = isl_union_set_from_set(set);5488 5489	extension = isl_schedule_node_extension_get_extension(node);5490 5491	extension = isl_union_map_preimage_domain_multi_aff(extension,5492			isl_multi_aff_copy(build->internal2input));5493	extension = isl_union_map_intersect_domain(extension, schedule_domain);5494	extension = isl_ast_build_substitute_values_union_map_domain(build,5495								    extension);5496	executed = isl_union_map_union(executed, extension);5497 5498	return build_ast_from_child(build, node, executed);5499}5500 5501/* Generate an AST that visits the elements in the domain of "executed"5502 * in the relative order specified by the filter node "node" and5503 * its descendants.5504 *5505 * The relation "executed" maps the outer generated loop iterators5506 * to the domain elements executed by those iterations.5507 *5508 * We simply intersect the iteration domain (i.e., the range of "executed")5509 * with the filter and continue with the descendants of the node,5510 * unless the resulting inverse schedule is empty, in which5511 * case we return an empty list.5512 *5513 * If the result of the intersection is equal to the original "executed"5514 * relation, then keep the original representation since the intersection5515 * may have unnecessarily broken up the relation into a greater number5516 * of disjuncts.5517 */5518static __isl_give isl_ast_graft_list *build_ast_from_filter(5519	__isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,5520	__isl_take isl_union_map *executed)5521{5522	isl_ctx *ctx;5523	isl_union_set *filter;5524	isl_union_map *orig;5525	isl_ast_graft_list *list;5526	int empty;5527	isl_bool unchanged;5528	isl_size n1, n2;5529 5530	orig = isl_union_map_copy(executed);5531	if (!build || !node || !executed)5532		goto error;5533 5534	filter = isl_schedule_node_filter_get_filter(node);5535	filter = isl_union_set_align_params(filter,5536				isl_union_map_get_space(executed));5537	n1 = isl_union_map_dim(executed, isl_dim_param);5538	executed = isl_union_map_intersect_range(executed, filter);5539	n2 = isl_union_map_dim(executed, isl_dim_param);5540	if (n1 < 0 || n2 < 0)5541		goto error;5542	if (n2 > n1)5543		isl_die(isl_ast_build_get_ctx(build), isl_error_invalid,5544			"filter node is not allowed to introduce "5545			"new parameters", goto error);5546 5547	unchanged = isl_union_map_is_subset(orig, executed);5548	empty = isl_union_map_is_empty(executed);5549	if (unchanged < 0 || empty < 0)5550		goto error;5551	if (unchanged) {5552		isl_union_map_free(executed);5553		return build_ast_from_child(build, node, orig);5554	}5555	isl_union_map_free(orig);5556	if (!empty)5557		return build_ast_from_child(build, node, executed);5558 5559	ctx = isl_ast_build_get_ctx(build);5560	list = isl_ast_graft_list_alloc(ctx, 0);5561	isl_ast_build_free(build);5562	isl_schedule_node_free(node);5563	isl_union_map_free(executed);5564	return list;5565error:5566	isl_ast_build_free(build);5567	isl_schedule_node_free(node);5568	isl_union_map_free(executed);5569	isl_union_map_free(orig);5570	return NULL;5571}5572 5573/* Generate an AST that visits the elements in the domain of "executed"5574 * in the relative order specified by the guard node "node" and5575 * its descendants.5576 *5577 * The relation "executed" maps the outer generated loop iterators5578 * to the domain elements executed by those iterations.5579 *5580 * Ensure that the associated guard is enforced by the outer AST5581 * constructs by adding it to the guard of the graft.5582 * Since we know that we will enforce the guard, we can also include it5583 * in the generated constraints used to construct an AST for5584 * the descendant nodes.5585 */5586static __isl_give isl_ast_graft_list *build_ast_from_guard(5587	__isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,5588	__isl_take isl_union_map *executed)5589{5590	isl_space *space;5591	isl_set *guard, *hoisted;5592	isl_basic_set *enforced;5593	isl_ast_build *sub_build;5594	isl_ast_graft *graft;5595	isl_ast_graft_list *list;5596	isl_size n1, n2, n;5597 5598	space = isl_ast_build_get_space(build, 1);5599	guard = isl_schedule_node_guard_get_guard(node);5600	n1 = isl_space_dim(space, isl_dim_param);5601	guard = isl_set_align_params(guard, space);5602	n2 = isl_set_dim(guard, isl_dim_param);5603	if (n1 < 0 || n2 < 0)5604		guard = isl_set_free(guard);5605	else if (n2 > n1)5606		isl_die(isl_ast_build_get_ctx(build), isl_error_invalid,5607			"guard node is not allowed to introduce "5608			"new parameters", guard = isl_set_free(guard));5609	guard = isl_set_preimage_multi_aff(guard,5610			isl_multi_aff_copy(build->internal2input));5611	guard = isl_ast_build_specialize(build, guard);5612	guard = isl_set_gist(guard, isl_set_copy(build->generated));5613 5614	sub_build = isl_ast_build_copy(build);5615	sub_build = isl_ast_build_restrict_generated(sub_build,5616							isl_set_copy(guard));5617 5618	list = build_ast_from_child(isl_ast_build_copy(sub_build),5619							node, executed);5620 5621	hoisted = isl_ast_graft_list_extract_hoistable_guard(list, sub_build);5622	n = isl_set_n_basic_set(hoisted);5623	if (n < 0)5624		list = isl_ast_graft_list_free(list);5625	if (n > 1)5626		list = isl_ast_graft_list_gist_guards(list,5627						    isl_set_copy(hoisted));5628	guard = isl_set_intersect(guard, hoisted);5629	enforced = extract_shared_enforced(list, build);5630	graft = isl_ast_graft_alloc_from_children(list, guard, enforced,5631						    build, sub_build);5632 5633	isl_ast_build_free(sub_build);5634	isl_ast_build_free(build);5635	return isl_ast_graft_list_from_ast_graft(graft);5636}5637 5638/* Call the before_each_mark callback, if requested by the user.5639 *5640 * Return 0 on success and -1 on error.5641 *5642 * The caller is responsible for recording the current inverse schedule5643 * in "build".5644 */5645static isl_stat before_each_mark(__isl_keep isl_id *mark,5646	__isl_keep isl_ast_build *build)5647{5648	if (!build)5649		return isl_stat_error;5650	if (!build->before_each_mark)5651		return isl_stat_ok;5652	return build->before_each_mark(mark, build,5653					build->before_each_mark_user);5654}5655 5656/* Call the after_each_mark callback, if requested by the user.5657 *5658 * The caller is responsible for recording the current inverse schedule5659 * in "build".5660 */5661static __isl_give isl_ast_graft *after_each_mark(5662	__isl_take isl_ast_graft *graft, __isl_keep isl_ast_build *build)5663{5664	if (!graft || !build)5665		return isl_ast_graft_free(graft);5666	if (!build->after_each_mark)5667		return graft;5668	graft->node = build->after_each_mark(graft->node, build,5669						build->after_each_mark_user);5670	if (!graft->node)5671		return isl_ast_graft_free(graft);5672	return graft;5673}5674 5675 5676/* Generate an AST that visits the elements in the domain of "executed"5677 * in the relative order specified by the mark node "node" and5678 * its descendants.5679 *5680 * The relation "executed" maps the outer generated loop iterators5681 * to the domain elements executed by those iterations.5682 5683 * Since we may be calling before_each_mark and after_each_mark5684 * callbacks, we record the current inverse schedule in the build.5685 *5686 * We generate an AST for the child of the mark node, combine5687 * the graft list into a single graft and then insert the mark5688 * in the AST of that single graft.5689 */5690static __isl_give isl_ast_graft_list *build_ast_from_mark(5691	__isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,5692	__isl_take isl_union_map *executed)5693{5694	isl_id *mark;5695	isl_ast_graft *graft;5696	isl_ast_graft_list *list;5697	isl_size n;5698 5699	build = isl_ast_build_set_executed(build, isl_union_map_copy(executed));5700 5701	mark = isl_schedule_node_mark_get_id(node);5702	if (before_each_mark(mark, build) < 0)5703		node = isl_schedule_node_free(node);5704 5705	list = build_ast_from_child(isl_ast_build_copy(build), node, executed);5706	list = isl_ast_graft_list_fuse(list, build);5707	n = isl_ast_graft_list_n_ast_graft(list);5708	if (n < 0)5709		list = isl_ast_graft_list_free(list);5710	if (n == 0) {5711		isl_id_free(mark);5712	} else {5713		graft = isl_ast_graft_list_get_ast_graft(list, 0);5714		graft = isl_ast_graft_insert_mark(graft, mark);5715		graft = after_each_mark(graft, build);5716		list = isl_ast_graft_list_set_ast_graft(list, 0, graft);5717	}5718	isl_ast_build_free(build);5719 5720	return list;5721}5722 5723static __isl_give isl_ast_graft_list *build_ast_from_schedule_node(5724	__isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,5725	__isl_take isl_union_map *executed);5726 5727/* Generate an AST that visits the elements in the domain of "executed"5728 * in the relative order specified by the sequence (or set) node "node" and5729 * its descendants.5730 *5731 * The relation "executed" maps the outer generated loop iterators5732 * to the domain elements executed by those iterations.5733 *5734 * We simply generate an AST for each of the children and concatenate5735 * the results.5736 */5737static __isl_give isl_ast_graft_list *build_ast_from_sequence(5738	__isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,5739	__isl_take isl_union_map *executed)5740{5741	int i;5742	isl_size n;5743	isl_ctx *ctx;5744	isl_ast_graft_list *list;5745 5746	ctx = isl_ast_build_get_ctx(build);5747	list = isl_ast_graft_list_alloc(ctx, 0);5748 5749	n = isl_schedule_node_n_children(node);5750	if (n < 0)5751		list = isl_ast_graft_list_free(list);5752	for (i = 0; i < n; ++i) {5753		isl_schedule_node *child;5754		isl_ast_graft_list *list_i;5755 5756		child = isl_schedule_node_get_child(node, i);5757		list_i = build_ast_from_schedule_node(isl_ast_build_copy(build),5758					child, isl_union_map_copy(executed));5759		list = isl_ast_graft_list_concat(list, list_i);5760	}5761	isl_ast_build_free(build);5762	isl_schedule_node_free(node);5763	isl_union_map_free(executed);5764 5765	return list;5766}5767 5768/* Generate an AST that visits the elements in the domain of "executed"5769 * in the relative order specified by the node "node" and its descendants.5770 *5771 * The relation "executed" maps the outer generated loop iterators5772 * to the domain elements executed by those iterations.5773 *5774 * The node types are handled in separate functions.5775 * Set nodes are currently treated in the same way as sequence nodes.5776 * The children of a set node may be executed in any order,5777 * including the order of the children.5778 */5779static __isl_give isl_ast_graft_list *build_ast_from_schedule_node(5780	__isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,5781	__isl_take isl_union_map *executed)5782{5783	enum isl_schedule_node_type type;5784 5785	type = isl_schedule_node_get_type(node);5786 5787	switch (type) {5788	case isl_schedule_node_error:5789		goto error;5790	case isl_schedule_node_leaf:5791		return build_ast_from_leaf(build, node, executed);5792	case isl_schedule_node_band:5793		return build_ast_from_band(build, node, executed);5794	case isl_schedule_node_context:5795		return build_ast_from_context(build, node, executed);5796	case isl_schedule_node_domain:5797		isl_die(isl_schedule_node_get_ctx(node), isl_error_unsupported,5798			"unexpected internal domain node", goto error);5799	case isl_schedule_node_expansion:5800		return build_ast_from_expansion(build, node, executed);5801	case isl_schedule_node_extension:5802		return build_ast_from_extension(build, node, executed);5803	case isl_schedule_node_filter:5804		return build_ast_from_filter(build, node, executed);5805	case isl_schedule_node_guard:5806		return build_ast_from_guard(build, node, executed);5807	case isl_schedule_node_mark:5808		return build_ast_from_mark(build, node, executed);5809	case isl_schedule_node_sequence:5810	case isl_schedule_node_set:5811		return build_ast_from_sequence(build, node, executed);5812	}5813 5814	isl_die(isl_ast_build_get_ctx(build), isl_error_internal,5815		"unhandled type", goto error);5816error:5817	isl_union_map_free(executed);5818	isl_schedule_node_free(node);5819	isl_ast_build_free(build);5820 5821	return NULL;5822}5823 5824/* Generate an AST that visits the elements in the domain of "executed"5825 * in the relative order specified by the (single) child of "node" and5826 * its descendants.5827 *5828 * The relation "executed" maps the outer generated loop iterators5829 * to the domain elements executed by those iterations.5830 *5831 * This function is never called on a leaf, set or sequence node,5832 * so the node always has exactly one child.5833 */5834static __isl_give isl_ast_graft_list *build_ast_from_child(5835	__isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,5836	__isl_take isl_union_map *executed)5837{5838	node = isl_schedule_node_child(node, 0);5839	return build_ast_from_schedule_node(build, node, executed);5840}5841 5842/* Generate an AST that visits the elements in the domain of the domain5843 * node "node" in the relative order specified by its descendants.5844 *5845 * An initial inverse schedule is created that maps a zero-dimensional5846 * schedule space to the node domain.5847 * The input "build" is assumed to have a parametric domain and5848 * is replaced by the same zero-dimensional schedule space.5849 *5850 * We also add some of the parameter constraints in the build domain5851 * to the executed relation.  Adding these constraints5852 * allows for an earlier detection of conflicts in some cases.5853 * However, we do not want to divide the executed relation into5854 * more disjuncts than necessary.  We therefore approximate5855 * the constraints on the parameters by a single disjunct set.5856 */5857static __isl_give isl_ast_node *build_ast_from_domain(5858	__isl_take isl_ast_build *build, __isl_take isl_schedule_node *node)5859{5860	isl_ctx *ctx;5861	isl_union_set *domain, *schedule_domain;5862	isl_union_map *executed;5863	isl_space *space;5864	isl_set *set;5865	isl_ast_graft_list *list;5866	isl_ast_node *ast;5867	int is_params;5868 5869	if (!build)5870		goto error;5871 5872	ctx = isl_ast_build_get_ctx(build);5873	space = isl_ast_build_get_space(build, 1);5874	is_params = isl_space_is_params(space);5875	isl_space_free(space);5876	if (is_params < 0)5877		goto error;5878	if (!is_params)5879		isl_die(ctx, isl_error_unsupported,5880			"expecting parametric initial context", goto error);5881 5882	domain = isl_schedule_node_domain_get_domain(node);5883	domain = isl_union_set_coalesce(domain);5884 5885	space = isl_union_set_get_space(domain);5886	space = isl_space_set_from_params(space);5887	build = isl_ast_build_product(build, space);5888 5889	set = isl_ast_build_get_domain(build);5890	set = isl_set_from_basic_set(isl_set_simple_hull(set));5891	schedule_domain = isl_union_set_from_set(set);5892 5893	executed = isl_union_map_from_domain_and_range(schedule_domain, domain);5894	list = build_ast_from_child(isl_ast_build_copy(build), node, executed);5895	ast = isl_ast_node_from_graft_list(list, build);5896	isl_ast_build_free(build);5897 5898	return ast;5899error:5900	isl_schedule_node_free(node);5901	isl_ast_build_free(build);5902	return NULL;5903}5904 5905/* Generate an AST that visits the elements in the domain of "schedule"5906 * in the relative order specified by the schedule tree.5907 *5908 * "build" is an isl_ast_build that has been created using5909 * isl_ast_build_alloc or isl_ast_build_from_context based5910 * on a parametric set.5911 *5912 * The construction starts at the root node of the schedule,5913 * which is assumed to be a domain node.5914 */5915__isl_give isl_ast_node *isl_ast_build_node_from_schedule(5916	__isl_keep isl_ast_build *build, __isl_take isl_schedule *schedule)5917{5918	isl_ctx *ctx;5919	isl_schedule_node *node;5920 5921	if (!build || !schedule)5922		goto error;5923 5924	ctx = isl_ast_build_get_ctx(build);5925 5926	node = isl_schedule_get_root(schedule);5927	if (!node)5928		goto error;5929	isl_schedule_free(schedule);5930 5931	build = isl_ast_build_copy(build);5932	build = isl_ast_build_set_single_valued(build, 0);5933	if (isl_schedule_node_get_type(node) != isl_schedule_node_domain)5934		isl_die(ctx, isl_error_unsupported,5935			"expecting root domain node",5936			build = isl_ast_build_free(build));5937	return build_ast_from_domain(build, node);5938error:5939	isl_schedule_free(schedule);5940	return NULL;5941}5942