[polly] r233567 - Update isl to 285e92aea

Tobias Grosser tobias at grosser.es
Mon Mar 30 10:28:58 PDT 2015


Author: grosser
Date: Mon Mar 30 12:28:57 2015
New Revision: 233567

URL: http://llvm.org/viewvc/llvm-project?rev=233567&view=rev
Log:
Update isl to 285e92aea

This is mostly a set of schedule tree enhancements which are not yet directly
useful to Polly.

Modified:
    polly/trunk/lib/CodeGen/IslCodeGeneration.cpp
    polly/trunk/lib/External/isl/doc/user.pod
    polly/trunk/lib/External/isl/include/isl/ast.h
    polly/trunk/lib/External/isl/include/isl/ast_build.h
    polly/trunk/lib/External/isl/include/isl/ast_type.h
    polly/trunk/lib/External/isl/include/isl/schedule.h
    polly/trunk/lib/External/isl/include/isl/schedule_node.h
    polly/trunk/lib/External/isl/include/isl/schedule_type.h
    polly/trunk/lib/External/isl/isl_ast.c
    polly/trunk/lib/External/isl/isl_ast_build.c
    polly/trunk/lib/External/isl/isl_ast_build_private.h
    polly/trunk/lib/External/isl/isl_ast_codegen.c
    polly/trunk/lib/External/isl/isl_ast_graft.c
    polly/trunk/lib/External/isl/isl_ast_graft_private.h
    polly/trunk/lib/External/isl/isl_ast_private.h
    polly/trunk/lib/External/isl/isl_schedule.c
    polly/trunk/lib/External/isl/isl_schedule_band.c
    polly/trunk/lib/External/isl/isl_schedule_band.h
    polly/trunk/lib/External/isl/isl_schedule_node.c
    polly/trunk/lib/External/isl/isl_schedule_node_private.h
    polly/trunk/lib/External/isl/isl_schedule_read.c
    polly/trunk/lib/External/isl/isl_schedule_tree.c
    polly/trunk/lib/External/isl/isl_schedule_tree.h
    polly/trunk/lib/External/isl/isl_scheduler.c
    polly/trunk/lib/External/isl/isl_test.c

Modified: polly/trunk/lib/CodeGen/IslCodeGeneration.cpp
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/lib/CodeGen/IslCodeGeneration.cpp?rev=233567&r1=233566&r2=233567&view=diff
==============================================================================
--- polly/trunk/lib/CodeGen/IslCodeGeneration.cpp (original)
+++ polly/trunk/lib/CodeGen/IslCodeGeneration.cpp Mon Mar 30 12:28:57 2015
@@ -840,7 +840,9 @@ void IslNodeBuilder::createBlock(__isl_t
 void IslNodeBuilder::create(__isl_take isl_ast_node *Node) {
   switch (isl_ast_node_get_type(Node)) {
   case isl_ast_node_error:
-    llvm_unreachable("code  generation error");
+    llvm_unreachable("code generation error");
+  case isl_ast_node_mark:
+    llvm_unreachable("Mark node unexpected");
   case isl_ast_node_for:
     createFor(Node);
     return;

Modified: polly/trunk/lib/External/isl/doc/user.pod
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/lib/External/isl/doc/user.pod?rev=233567&r1=233566&r2=233567&view=diff
==============================================================================
--- polly/trunk/lib/External/isl/doc/user.pod (original)
+++ polly/trunk/lib/External/isl/doc/user.pod Mon Mar 30 12:28:57 2015
@@ -7235,7 +7235,8 @@ two domain elements is determined by the
 to the root that refers to both elements and that orders them apart.
 Each node in the tree is of one of several types.
 The root node is always of type C<isl_schedule_node_domain>
-and it describes the domain elements to which the schedule applies.
+(or C<isl_schedule_node_extension>)
+and it describes the (extra) domain elements to which the schedule applies.
 The other types of nodes are as follows.
 
 =over
@@ -7249,6 +7250,15 @@ schedule dimensions in the same band ass
 then the two domain elements are ordered according to these two
 different values.
 
+=item C<isl_schedule_node_expansion>
+
+An expansion node maps each of the domain elements that reach the node
+to one or more domain elements.  The image of this mapping forms
+the set of domain elements that reach the child of the expansion node.
+The function that maps each of the expanded domain elements
+to the original domain element from which it was expanded
+is called the contraction.
+
 =item C<isl_schedule_node_filter>
 
 A filter node does not impose any ordering, but rather intersects
@@ -7262,6 +7272,11 @@ set node.
 
 A leaf of the schedule tree.  Leaf nodes do not impose any ordering.
 
+=item C<isl_schedule_node_mark>
+
+A mark node can be used to attach any kind of information to a subtree
+of the schedule tree.
+
 =item C<isl_schedule_node_sequence>
 
 A sequence node has one or more children, each of which is a filter node.
@@ -7280,7 +7295,7 @@ is therefore also immaterial.
 
 =back
 
-The following node type is only supported by the AST generator.
+The following node types are only supported by the AST generator.
 
 =over
 
@@ -7296,6 +7311,40 @@ this space is the unnamed zero-dimension
 Since a context node references the outer band nodes, any tree
 containing a context node is considered to be anchored.
 
+=item C<isl_schedule_node_extension>
+
+An extension node instructs the AST generator to add additional
+domain elements that need to be scheduled.
+The additional domain elements are described by the range of
+the extension map in terms of the outer schedule dimensions,
+i.e., the flat product of the outer band nodes.
+Note that domain elements are added whenever the AST generator
+reaches the extension node, meaning that there are still some
+active domain elements for which an AST needs to be generated.
+The conditions under which some domain elements are still active
+may however not be completely described by the outer AST nodes
+generated at that point.
+
+An extension node may also appear as the root of a schedule tree,
+when it is intended to be inserted into another tree
+using C<isl_schedule_node_graft_before> or C<isl_schedule_node_graft_after>.
+In this case, the domain of the extension node should
+correspond to the flat product of the outer band nodes
+in this other schedule tree at the point where the extension tree
+will be inserted.
+
+=item C<isl_schedule_node_guard>
+
+The guard describes constraints on the parameters and
+the schedule dimensions of outer
+bands that need to be enforced by the outer nodes
+in the generated AST.
+The space of the guard is that of the flat product of the outer
+band nodes.  In particular, if there are no outer band nodes, then
+this space is the unnamed zero-dimensional space.
+Since a guard node references the outer band nodes, any tree
+containing a guard node is considered to be anchored.
+
 =back
 
 Except for the C<isl_schedule_node_context> nodes,
@@ -7357,6 +7406,14 @@ be introduced into the schedule using th
 		__isl_take isl_schedule *schedule,
 		__isl_take isl_set *context)
 
+A top-level guard node (right underneath the domain node) can
+be introduced into the schedule using the following function.
+
+	#include <isl/schedule.h>
+	__isl_give isl_schedule *isl_schedule_insert_guard(
+		__isl_take isl_schedule *schedule,
+		__isl_take isl_set *guard)
+
 A schedule that combines two schedules either in the given
 order or in an arbitrary order, i.e., with an C<isl_schedule_node_sequence>
 or an C<isl_schedule_node_set> node,
@@ -7373,7 +7430,7 @@ can be created using the following funct
 The domains of the two input schedules need to be disjoint.
 
 The following function can be used to restrict the domain
-of a schedule to be a subset of the given union set.
+of a schedule with a domain node as root to be a subset of the given union set.
 This operation may remove nodes in the tree that have become
 redundant.
 
@@ -7398,7 +7455,8 @@ in the given schedule to the given space
 		__isl_take isl_space *space);
 
 The following function allows the user to plug in a given function
-in the iteration domains.
+in the iteration domains.  The input schedule is not allowed to contain
+any expansion nodes.
 
 	#include <isl/schedule.h>
 	__isl_give isl_schedule *
@@ -7417,6 +7475,8 @@ The resulting relation encodes the same
 the schedule by mapping the domain elements to a common schedule space.
 If the schedule_separate_components option is set, then the order
 of the children of a set node is explicitly encoded in the result.
+If the tree contains any expansion nodes, then the relation
+is formulated in terms of the expanded domain elements.
 
 Schedules can be read from input using the following functions.
 
@@ -7448,12 +7508,18 @@ The root of the schedule tree can be obt
 		__isl_keep isl_schedule *schedule);
 
 A pointer to a newly created schedule tree with a single domain
-node can be created using the following function.
+node can be created using the following functions.
 
 	#include <isl/schedule_node.h>
 	__isl_give isl_schedule_node *
 	isl_schedule_node_from_domain(
 		__isl_take isl_union_set *domain);
+	__isl_give isl_schedule_node *
+	isl_schedule_node_from_extension(
+		__isl_take isl_union_map *extension);
+
+C<isl_schedule_node_from_extension> creates a tree with an extension
+node as root.
 
 Schedule nodes can be copied and freed using the following functions.
 
@@ -7743,14 +7809,39 @@ See L</"AST Generation Options (Schedule
 		__isl_keep isl_schedule_node *node);
 
 	#include <isl/schedule_node.h>
+	__isl_give isl_union_map *
+	isl_schedule_node_expansion_get_expansion(
+		__isl_keep isl_schedule_node *node);
+	__isl_give isl_union_pw_multi_aff *
+	isl_schedule_node_expansion_get_contraction(
+		__isl_keep isl_schedule_node *node);
+
+	#include <isl/schedule_node.h>
+	__isl_give isl_union_map *
+	isl_schedule_node_extension_get_extension(
+		__isl_keep isl_schedule_node *node);
+
+	#include <isl/schedule_node.h>
 	__isl_give isl_union_set *
 	isl_schedule_node_filter_get_filter(
 		__isl_keep isl_schedule_node *node);
 
-The following functions can be used to obtain an C<isl_union_pw_multi_aff>
-or C<isl_union_map> representation of partial schedules related to the node.
+	#include <isl/schedule_node.h>
+	__isl_give isl_set *isl_schedule_node_guard_get_guard(
+		__isl_keep isl_schedule_node *node);
+
+	#include <isl/schedule_node.h>
+	__isl_give isl_id *isl_schedule_node_mark_get_id(
+		__isl_keep isl_schedule_node *node);
+
+The following functions can be used to obtain an C<isl_multi_union_pw_aff>,
+an C<isl_union_pw_multi_aff> or C<isl_union_map> representation of
+partial schedules related to the node.
 
 	#include <isl/schedule_node.h>
+	__isl_give isl_multi_union_pw_aff *
+	isl_schedule_node_get_prefix_schedule_multi_union_pw_aff(
+		__isl_keep isl_schedule_node *node);
 	__isl_give isl_union_pw_multi_aff *
 	isl_schedule_node_get_prefix_schedule_union_pw_multi_aff(
 		__isl_keep isl_schedule_node *node);
@@ -7758,17 +7849,46 @@ or C<isl_union_map> representation of pa
 	isl_schedule_node_get_prefix_schedule_union_map(
 		__isl_keep isl_schedule_node *node);
 	__isl_give isl_union_map *
+	isl_schedule_node_get_prefix_schedule_relation(
+		__isl_keep isl_schedule_node *node);
+	__isl_give isl_union_map *
 	isl_schedule_node_get_subtree_schedule_union_map(
 		__isl_keep isl_schedule_node *node);
 
 In particular, the functions
+C<isl_schedule_node_get_prefix_schedule_multi_union_pw_aff>,
 C<isl_schedule_node_get_prefix_schedule_union_pw_multi_aff>
 and C<isl_schedule_node_get_prefix_schedule_union_map>
 return a relative ordering on the domain elements that reach the given
 node determined by its ancestors.
+The function C<isl_schedule_node_get_prefix_schedule_relation>
+additionally includes the domain constraints in the result.
 The function C<isl_schedule_node_get_subtree_schedule_union_map>
 returns a representation of the partial schedule defined by the
 subtree rooted at the given node.
+If the tree contains any expansion nodes, then the subtree schedule
+is formulated in terms of the expanded domain elements.
+The tree passed to functions returning a prefix schedule
+may only contain extension nodes if these would not affect
+the result of these functions.  That is, if one of the ancestors
+is an extension node, then all of the domain elements that were
+added by the extension node need to have been filtered out
+by filter nodes between the extension node and the input node.
+The tree passed to C<isl_schedule_node_get_subtree_schedule_union_map>
+may not contain in extension nodes in the selected subtree.
+
+The expansion/contraction defined by an entire subtree, combining
+the expansions/contractions
+on the expansion nodes in the subtree, can be obtained using
+the following functions.
+
+	#include <isl/schedule_node.h>
+	__isl_give isl_union_map *
+	isl_schedule_node_get_subtree_expansion(
+		__isl_keep isl_schedule_node *node);
+	__isl_give isl_union_pw_multi_aff *
+	isl_schedule_node_get_subtree_contraction(
+		__isl_keep isl_schedule_node *node);
 
 The total number of outer band members of given node, i.e.,
 the shared output dimension of the maps in the result
@@ -7790,6 +7910,13 @@ or the union of universes in the spaces
 	isl_schedule_node_get_universe_domain(
 		__isl_keep isl_schedule_node *node);
 
+The input tree of C<isl_schedule_node_get_domain>
+may only contain extension nodes if these would not affect
+the result of this function.  That is, if one of the ancestors
+is an extension node, then all of the domain elements that were
+added by the extension node need to have been filtered out
+by filter nodes between the extension node and the input node.
+
 The following functions can be used to introduce additional nodes
 in the schedule tree.  The new node is introduced at the point
 in the tree where the C<isl_schedule_node> points to and
@@ -7826,6 +7953,22 @@ two filter nodes are merged into one.
 
 	#include <isl/schedule_node.h>
 	__isl_give isl_schedule_node *
+	isl_schedule_node_insert_guard(
+		__isl_take isl_schedule_node *node,
+		__isl_take isl_set *guard);
+
+This function inserts a new guard node with the given guard constraints.
+
+	#include <isl/schedule_node.h>
+	__isl_give isl_schedule_node *
+	isl_schedule_node_insert_mark(
+		__isl_take isl_schedule_node *node,
+		__isl_take isl_id *mark);
+
+This function inserts a new mark node with the give mark identifier.
+
+	#include <isl/schedule_node.h>
+	__isl_give isl_schedule_node *
 	isl_schedule_node_insert_sequence(
 		__isl_take isl_schedule_node *node,
 		__isl_take isl_union_set_list *filters);
@@ -7837,6 +7980,23 @@ two filter nodes are merged into one.
 These functions insert a new sequence or set node with the given
 filters as children.
 
+	#include <isl/schedule_node.h>
+	__isl_give isl_schedule_node *isl_schedule_node_group(
+		__isl_take isl_schedule_node *node,
+		__isl_take isl_id *group_id);
+
+This function introduces an expansion node in between the current
+node and its parent that expands instances of a space with tuple
+identifier C<group_id> to the original domain elements that reach
+the node.  The group instances are identified by the prefix schedule
+of those domain elements.  The ancestors of the node are adjusted
+to refer to the group instances instead of the original domain
+elements.  The return value points to the same node in the updated
+schedule tree as the input node, i.e., to the child of the newly
+introduced expansion node.  Grouping instances of different statements
+ensures that they will be treated as a single statement by the
+AST generator up to the point of the expansion node.
+
 The partial schedule of a band node can be scaled (down) using
 the following functions.
 
@@ -7902,6 +8062,62 @@ any anchored nodes.
 The result points to the node in the resulting tree that is in the same
 position as the node pointed to by C<node> in the original tree.
 
+	#include <isl/schedule_node.h>
+	__isl_give isl_schedule_node *
+	isl_schedule_node_order_after(
+		__isl_take isl_schedule_node *node,
+		__isl_take isl_union_set *filter);
+
+This function splits the domain elements that reach C<node>
+into those that satisfy C<filter> and those that do not and
+arranges for the elements that do satisfy the filter to be
+executed after those that do not.  The order is imposed by
+a sequence node, possibly reusing the grandparent of C<node>
+on two copies of the subtree attached to the original C<node>.
+Both copies are simplified with respect to their filter.
+
+Return a pointer to the copy of the subtree that does not
+satisfy C<filter>.  If there is no such copy (because all
+reaching domain elements satisfy the filter), then return
+the original pointer.
+
+	#include <isl/schedule_node.h>
+	__isl_give isl_schedule_node *
+	isl_schedule_node_graft_before(
+		__isl_take isl_schedule_node *node,
+		__isl_take isl_schedule_node *graft);
+	__isl_give isl_schedule_node *
+	isl_schedule_node_graft_after(
+		__isl_take isl_schedule_node *node,
+		__isl_take isl_schedule_node *graft);
+
+This function inserts the C<graft> tree into the tree containing C<node>
+such that it is executed before (in case of C<isl_schedule_node_graft_before>)
+or after (in case of C<isl_schedule_node_graft_after>) C<node>.
+The root node of C<graft>
+should be an extension node where the domain of the extension
+is the flat product of all outer band nodes of C<node>.
+The root node may also be a domain node.
+The elements of the domain or the range of the extension may not
+intersect with the domain elements that reach "node".
+The schedule tree of C<graft> may not be anchored.
+
+The schedule tree of C<node> is modified to include an extension node
+corresponding to the root node of C<graft> as a child of the original
+parent of C<node>.  The original node that C<node> points to and the
+child of the root node of C<graft> are attached to this extension node
+through a sequence, with appropriate filters and with the child
+of C<graft> appearing before or after the original C<node>.
+
+If C<node> already appears inside a sequence that is the child of
+an extension node and if the spaces of the new domain elements
+do not overlap with those of the original domain elements,
+then that extension node is extended with the new extension
+rather than introducing a new segment of extension and sequence nodes.
+
+Return a pointer to the same node in the modified tree that
+C<node> pointed to in the original tree.
+
 A representation of the schedule node can be printed using
 
 	#include <isl/schedule_node.h>
@@ -8471,11 +8687,13 @@ The basic properties of an AST node can
 The type of an AST node is one of
 C<isl_ast_node_for>,
 C<isl_ast_node_if>,
-C<isl_ast_node_block> or
+C<isl_ast_node_block>,
+C<isl_ast_node_mark> or
 C<isl_ast_node_user>.
 An C<isl_ast_node_for> represents a for node.
 An C<isl_ast_node_if> represents an if node.
 An C<isl_ast_node_block> represents a compound node.
+An C<isl_ast_node_mark> introduces a mark in the AST.
 An C<isl_ast_node_user> represents an expression statement.
 An expression statement typically corresponds to a domain element, i.e.,
 one of the elements that is visited by the AST.
@@ -8513,6 +8731,15 @@ exactly once.
 	isl_ast_node_block_get_children(
 		__isl_keep isl_ast_node *node);
 
+	__isl_give isl_id *isl_ast_node_mark_get_id(
+		__isl_keep isl_ast_node *node);
+	__isl_give isl_ast_node *isl_ast_node_mark_get_node(
+		__isl_keep isl_ast_node *node);
+
+C<isl_ast_node_mark_get_id> returns the identifier of the mark.
+C<isl_ast_node_mark_get_node> returns the child node that is being marked.
+
+	#include <isl/ast.h>
 	__isl_give isl_ast_expr *isl_ast_node_user_get_expr(
 		__isl_keep isl_ast_node *node);
 
@@ -9571,6 +9798,19 @@ user defined node created using the foll
 			__isl_take isl_ast_node *node,
 			__isl_keep isl_ast_build *build,
 			void *user), void *user);
+	__isl_give isl_ast_build *
+	isl_ast_build_set_before_each_mark(
+		__isl_take isl_ast_build *build,
+		int (*fn)(__isl_keep isl_id *mark,
+			__isl_keep isl_ast_build *build,
+			void *user), void *user);
+	__isl_give isl_ast_build *
+	isl_ast_build_set_after_each_mark(
+		__isl_take isl_ast_build *build,
+		__isl_give isl_ast_node *(*fn)(
+			__isl_take isl_ast_node *node,
+			__isl_keep isl_ast_build *build,
+			void *user), void *user);
 
 The callback set by C<isl_ast_build_set_at_each_domain> will
 be called for each domain AST node.
@@ -9585,7 +9825,15 @@ C<isl_ast_node_set_annotation>) to the c
 In particular, if the user has also specified an C<after_each_for>
 callback, then the annotation can be retrieved from the node passed to
 that callback using C<isl_ast_node_get_annotation>.
-All callbacks should C<NULL> on failure.
+The callbacks set by C<isl_ast_build_set_before_each_mark>
+and C<isl_ast_build_set_after_each_mark> will be called for each
+mark AST node that is created, i.e., for each mark schedule node
+in the input schedule tree.  The first will be called in depth-first
+pre-order, while the second will be called in depth-first post-order.
+Since the callback set by C<isl_ast_build_set_before_each_mark>
+is called before the mark AST node is actually constructed, it is passed
+the identifier of the mark node.
+All callbacks should C<NULL> (or -1) on failure.
 The given C<isl_ast_build> can be used to create new
 C<isl_ast_expr> objects using C<isl_ast_build_expr_from_pw_aff>
 or C<isl_ast_build_call_from_pw_multi_aff>.

Modified: polly/trunk/lib/External/isl/include/isl/ast.h
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/lib/External/isl/include/isl/ast.h?rev=233567&r1=233566&r2=233567&view=diff
==============================================================================
--- polly/trunk/lib/External/isl/include/isl/ast.h (original)
+++ polly/trunk/lib/External/isl/include/isl/ast.h Mon Mar 30 12:28:57 2015
@@ -118,6 +118,10 @@ __isl_give isl_ast_node *isl_ast_node_if
 __isl_give isl_ast_node_list *isl_ast_node_block_get_children(
 	__isl_keep isl_ast_node *node);
 
+__isl_give isl_id *isl_ast_node_mark_get_id(__isl_keep isl_ast_node *node);
+__isl_give isl_ast_node *isl_ast_node_mark_get_node(
+	__isl_keep isl_ast_node *node);
+
 __isl_give isl_ast_expr *isl_ast_node_user_get_expr(
 	__isl_keep isl_ast_node *node);
 

Modified: polly/trunk/lib/External/isl/include/isl/ast_build.h
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/lib/External/isl/include/isl/ast_build.h?rev=233567&r1=233566&r2=233567&view=diff
==============================================================================
--- polly/trunk/lib/External/isl/include/isl/ast_build.h (original)
+++ polly/trunk/lib/External/isl/include/isl/ast_build.h Mon Mar 30 12:28:57 2015
@@ -76,6 +76,14 @@ __isl_give isl_ast_build *isl_ast_build_
 	__isl_take isl_ast_build *build,
 	__isl_give isl_ast_node *(*fn)(__isl_take isl_ast_node *node,
 		__isl_keep isl_ast_build *build, void *user), void *user);
+__isl_give isl_ast_build *isl_ast_build_set_before_each_mark(
+	__isl_take isl_ast_build *build,
+	int (*fn)(__isl_keep isl_id *mark, __isl_keep isl_ast_build *build,
+		void *user), void *user);
+__isl_give isl_ast_build *isl_ast_build_set_after_each_mark(
+	__isl_take isl_ast_build *build,
+	__isl_give isl_ast_node *(*fn)(__isl_take isl_ast_node *node,
+		__isl_keep isl_ast_build *build, void *user), void *user);
 __isl_give isl_ast_build *isl_ast_build_set_create_leaf(
 	__isl_take isl_ast_build *build,
 	__isl_give isl_ast_node *(*fn)(__isl_take isl_ast_build *build,

Modified: polly/trunk/lib/External/isl/include/isl/ast_type.h
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/lib/External/isl/include/isl/ast_type.h?rev=233567&r1=233566&r2=233567&view=diff
==============================================================================
--- polly/trunk/lib/External/isl/include/isl/ast_type.h (original)
+++ polly/trunk/lib/External/isl/include/isl/ast_type.h Mon Mar 30 12:28:57 2015
@@ -55,6 +55,7 @@ enum isl_ast_node_type {
 	isl_ast_node_for = 1,
 	isl_ast_node_if,
 	isl_ast_node_block,
+	isl_ast_node_mark,
 	isl_ast_node_user
 };
 

Modified: polly/trunk/lib/External/isl/include/isl/schedule.h
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/lib/External/isl/include/isl/schedule.h?rev=233567&r1=233566&r2=233567&view=diff
==============================================================================
--- polly/trunk/lib/External/isl/include/isl/schedule.h (original)
+++ polly/trunk/lib/External/isl/include/isl/schedule.h Mon Mar 30 12:28:57 2015
@@ -104,6 +104,8 @@ __isl_give isl_schedule *isl_schedule_in
 __isl_give isl_schedule *isl_schedule_insert_partial_schedule(
 	__isl_take isl_schedule *schedule,
 	__isl_take isl_multi_union_pw_aff *partial);
+__isl_give isl_schedule *isl_schedule_insert_guard(
+	__isl_take isl_schedule *schedule, __isl_take isl_set *guard);
 __isl_give isl_schedule *isl_schedule_sequence(
 	__isl_take isl_schedule *schedule1, __isl_take isl_schedule *schedule2);
 __isl_give isl_schedule *isl_schedule_set(

Modified: polly/trunk/lib/External/isl/include/isl/schedule_node.h
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/lib/External/isl/include/isl/schedule_node.h?rev=233567&r1=233566&r2=233567&view=diff
==============================================================================
--- polly/trunk/lib/External/isl/include/isl/schedule_node.h (original)
+++ polly/trunk/lib/External/isl/include/isl/schedule_node.h Mon Mar 30 12:28:57 2015
@@ -14,6 +14,8 @@ extern "C" {
 
 __isl_give isl_schedule_node *isl_schedule_node_from_domain(
 	__isl_take isl_union_set *domain);
+__isl_give isl_schedule_node *isl_schedule_node_from_extension(
+	__isl_take isl_union_map *extension);
 __isl_give isl_schedule_node *isl_schedule_node_copy(
 	__isl_keep isl_schedule_node *node);
 __isl_null isl_schedule_node *isl_schedule_node_free(
@@ -73,6 +75,9 @@ __isl_give isl_schedule_node *isl_schedu
 
 int isl_schedule_node_is_subtree_anchored(__isl_keep isl_schedule_node *node);
 
+__isl_give isl_schedule_node *isl_schedule_node_group(
+	__isl_take isl_schedule_node *node, __isl_take isl_id *group_id);
+
 __isl_give isl_space *isl_schedule_node_band_get_space(
 	__isl_keep isl_schedule_node *node);
 __isl_give isl_multi_union_pw_aff *isl_schedule_node_band_get_partial_schedule(
@@ -123,21 +128,40 @@ __isl_give isl_set *isl_schedule_node_co
 	__isl_keep isl_schedule_node *node);
 __isl_give isl_union_set *isl_schedule_node_domain_get_domain(
 	__isl_keep isl_schedule_node *node);
+__isl_give isl_union_map *isl_schedule_node_expansion_get_expansion(
+	__isl_keep isl_schedule_node *node);
+__isl_give isl_union_pw_multi_aff *isl_schedule_node_expansion_get_contraction(
+	__isl_keep isl_schedule_node *node);
+__isl_give isl_union_map *isl_schedule_node_extension_get_extension(
+	__isl_keep isl_schedule_node *node);
 __isl_give isl_union_set *isl_schedule_node_filter_get_filter(
 	__isl_keep isl_schedule_node *node);
+__isl_give isl_set *isl_schedule_node_guard_get_guard(
+	__isl_keep isl_schedule_node *node);
+__isl_give isl_id *isl_schedule_node_mark_get_id(
+	__isl_keep isl_schedule_node *node);
 
 int isl_schedule_node_get_schedule_depth(__isl_keep isl_schedule_node *node);
 __isl_give isl_union_set *isl_schedule_node_get_domain(
 	__isl_keep isl_schedule_node *node);
 __isl_give isl_union_set *isl_schedule_node_get_universe_domain(
 	__isl_keep isl_schedule_node *node);
+__isl_give isl_multi_union_pw_aff *
+isl_schedule_node_get_prefix_schedule_multi_union_pw_aff(
+	__isl_keep isl_schedule_node *node);
 __isl_give isl_union_pw_multi_aff *
 isl_schedule_node_get_prefix_schedule_union_pw_multi_aff(
 	__isl_keep isl_schedule_node *node);
 __isl_give isl_union_map *isl_schedule_node_get_prefix_schedule_union_map(
 	__isl_keep isl_schedule_node *node);
+__isl_give isl_union_map *isl_schedule_node_get_prefix_schedule_relation(
+	__isl_keep isl_schedule_node *node);
 __isl_give isl_union_map *isl_schedule_node_get_subtree_schedule_union_map(
 	__isl_keep isl_schedule_node *node);
+__isl_give isl_union_map *isl_schedule_node_get_subtree_expansion(
+	__isl_keep isl_schedule_node *node);
+__isl_give isl_union_pw_multi_aff *isl_schedule_node_get_subtree_contraction(
+	__isl_keep isl_schedule_node *node);
 
 __isl_give isl_schedule_node *isl_schedule_node_insert_context(
 	__isl_take isl_schedule_node *node, __isl_take isl_set *context);
@@ -146,6 +170,10 @@ __isl_give isl_schedule_node *isl_schedu
 	__isl_take isl_multi_union_pw_aff *schedule);
 __isl_give isl_schedule_node *isl_schedule_node_insert_filter(
 	__isl_take isl_schedule_node *node, __isl_take isl_union_set *filter);
+__isl_give isl_schedule_node *isl_schedule_node_insert_guard(
+	__isl_take isl_schedule_node *node, __isl_take isl_set *context);
+__isl_give isl_schedule_node *isl_schedule_node_insert_mark(
+	__isl_take isl_schedule_node *node, __isl_take isl_id *mark);
 __isl_give isl_schedule_node *isl_schedule_node_insert_sequence(
 	__isl_take isl_schedule_node *node,
 	__isl_take isl_union_set_list *filters);
@@ -158,6 +186,16 @@ __isl_give isl_schedule_node *isl_schedu
 __isl_give isl_schedule_node *isl_schedule_node_delete(
 	__isl_take isl_schedule_node *node);
 
+__isl_give isl_schedule_node *isl_schedule_node_order_after(
+	__isl_take isl_schedule_node *node, __isl_take isl_union_set *filter);
+
+__isl_give isl_schedule_node *isl_schedule_node_graft_before(
+	__isl_take isl_schedule_node *node,
+	__isl_take isl_schedule_node *graft);
+__isl_give isl_schedule_node *isl_schedule_node_graft_after(
+	__isl_take isl_schedule_node *node,
+	__isl_take isl_schedule_node *graft);
+
 __isl_give isl_schedule_node *isl_schedule_node_reset_user(
 	__isl_take isl_schedule_node *node);
 __isl_give isl_schedule_node *isl_schedule_node_align_params(

Modified: polly/trunk/lib/External/isl/include/isl/schedule_type.h
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/lib/External/isl/include/isl/schedule_type.h?rev=233567&r1=233566&r2=233567&view=diff
==============================================================================
--- polly/trunk/lib/External/isl/include/isl/schedule_type.h (original)
+++ polly/trunk/lib/External/isl/include/isl/schedule_type.h Mon Mar 30 12:28:57 2015
@@ -10,8 +10,12 @@ enum isl_schedule_node_type {
 	isl_schedule_node_band,
 	isl_schedule_node_context,
 	isl_schedule_node_domain,
+	isl_schedule_node_expansion,
+	isl_schedule_node_extension,
 	isl_schedule_node_filter,
 	isl_schedule_node_leaf,
+	isl_schedule_node_guard,
+	isl_schedule_node_mark,
 	isl_schedule_node_sequence,
 	isl_schedule_node_set
 };

Modified: polly/trunk/lib/External/isl/isl_ast.c
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/lib/External/isl/isl_ast.c?rev=233567&r1=233566&r2=233567&view=diff
==============================================================================
--- polly/trunk/lib/External/isl/isl_ast.c (original)
+++ polly/trunk/lib/External/isl/isl_ast.c Mon Mar 30 12:28:57 2015
@@ -853,6 +853,32 @@ error:
 	return NULL;
 }
 
+/* Create a mark node, marking "node" with "id".
+ */
+__isl_give isl_ast_node *isl_ast_node_alloc_mark(__isl_take isl_id *id,
+	__isl_take isl_ast_node *node)
+{
+	isl_ctx *ctx;
+	isl_ast_node *mark;
+
+	if (!id || !node)
+		goto error;
+
+	ctx = isl_id_get_ctx(id);
+	mark = isl_ast_node_alloc(ctx, isl_ast_node_mark);
+	if (!mark)
+		goto error;
+
+	mark->u.m.mark = id;
+	mark->u.m.node = node;
+
+	return mark;
+error:
+	isl_id_free(id);
+	isl_ast_node_free(node);
+	return NULL;
+}
+
 /* Create a user node evaluating "expr".
  */
 __isl_give isl_ast_node *isl_ast_node_alloc_user(__isl_take isl_ast_expr *expr)
@@ -962,6 +988,12 @@ __isl_give isl_ast_node *isl_ast_node_du
 		if (!dup->u.b.children)
 			return isl_ast_node_free(dup);
 		break;
+	case isl_ast_node_mark:
+		dup->u.m.mark = isl_id_copy(node->u.m.mark);
+		dup->u.m.node = isl_ast_node_copy(node->u.m.node);
+		if (!dup->u.m.mark || !dup->u.m.node)
+			return isl_ast_node_free(dup);
+		break;
 	case isl_ast_node_user:
 		dup->u.e.expr = isl_ast_expr_copy(node->u.e.expr);
 		if (!dup->u.e.expr)
@@ -1009,6 +1041,10 @@ __isl_null isl_ast_node *isl_ast_node_fr
 	case isl_ast_node_block:
 		isl_ast_node_list_free(node->u.b.children);
 		break;
+	case isl_ast_node_mark:
+		isl_id_free(node->u.m.mark);
+		isl_ast_node_free(node->u.m.node);
+		break;
 	case isl_ast_node_user:
 		isl_ast_expr_free(node->u.e.expr);
 		break;
@@ -1230,6 +1266,33 @@ __isl_give isl_ast_expr *isl_ast_node_us
 	return isl_ast_expr_copy(node->u.e.expr);
 }
 
+/* Return the mark identifier of the mark node "node".
+ */
+__isl_give isl_id *isl_ast_node_mark_get_id(__isl_keep isl_ast_node *node)
+{
+	if (!node)
+		return NULL;
+	if (node->type != isl_ast_node_mark)
+		isl_die(isl_ast_node_get_ctx(node), isl_error_invalid,
+			"not a mark node", return NULL);
+
+	return isl_id_copy(node->u.m.mark);
+}
+
+/* Return the node marked by mark node "node".
+ */
+__isl_give isl_ast_node *isl_ast_node_mark_get_node(
+	__isl_keep isl_ast_node *node)
+{
+	if (!node)
+		return NULL;
+	if (node->type != isl_ast_node_mark)
+		isl_die(isl_ast_node_get_ctx(node), isl_error_invalid,
+			"not a mark node", return NULL);
+
+	return isl_ast_node_copy(node->u.m.node);
+}
+
 __isl_give isl_id *isl_ast_node_get_annotation(__isl_keep isl_ast_node *node)
 {
 	return node ? isl_id_copy(node->annotation) : NULL;
@@ -1589,6 +1652,11 @@ static __isl_give isl_printer *print_ast
 			p = isl_printer_print_ast_node(p, node->u.f.body);
 		}
 		break;
+	case isl_ast_node_mark:
+		p = isl_printer_print_str(p, "mark: ");
+		p = isl_printer_print_id(p, node->u.m.mark);
+		p = isl_printer_print_str(p, "node: ");
+		p = isl_printer_print_ast_node(p, node->u.m.node);
 	case isl_ast_node_user:
 		p = isl_printer_print_ast_expr(p, node->u.e.expr);
 		break;
@@ -1609,7 +1677,7 @@ static __isl_give isl_printer *print_ast
 	case isl_ast_node_block:
 		p = isl_printer_print_ast_node_list(p, node->u.b.children);
 		break;
-	default:
+	case isl_ast_node_error:
 		break;
 	}
 	p = isl_printer_print_str(p, ")");
@@ -1624,6 +1692,10 @@ static __isl_give isl_printer *print_ast
  * as well.
  * If the node is an if node with an else, then we print a block
  * to avoid spurious dangling else warnings emitted by some compilers.
+ * If the node is a mark, then in principle, we would have to check
+ * the child of the mark node.  However, even if the child would not
+ * require us to print a block, for readability it is probably best
+ * to print a block anyway.
  * If the ast_always_print_block option has been set, then we print a block.
  */
 static int need_block(__isl_keep isl_ast_node *node)
@@ -1636,6 +1708,8 @@ static int need_block(__isl_keep isl_ast
 		return 1;
 	if (node->type == isl_ast_node_if && node->u.i.else_node)
 		return 1;
+	if (node->type == isl_ast_node_mark)
+		return 1;
 
 	ctx = isl_ast_node_get_ctx(node);
 	return isl_options_get_ast_always_print_block(ctx);
@@ -1842,6 +1916,13 @@ static __isl_give isl_printer *print_ast
 		if (!in_block)
 			p = end_block(p);
 		break;
+	case isl_ast_node_mark:
+		p = isl_printer_start_line(p);
+		p = isl_printer_print_str(p, "// ");
+		p = isl_printer_print_str(p, isl_id_get_name(node->u.m.mark));
+		p = isl_printer_end_line(p);
+		p = print_ast_node_c(p, node->u.m.node, options, 0, in_list);
+		break;
 	case isl_ast_node_user:
 		if (options->print_user)
 			return options->print_user(p,
@@ -2024,6 +2105,9 @@ static int ast_node_required_macros(__is
 		macros = ast_node_list_required_macros(node->u.b.children,
 							macros);
 		break;
+	case isl_ast_node_mark:
+		macros = ast_node_required_macros(node->u.m.node, macros);
+		break;
 	case isl_ast_node_user:
 		macros = ast_expr_required_macros(node->u.e.expr, macros);
 		break;

Modified: polly/trunk/lib/External/isl/isl_ast_build.c
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/lib/External/isl/isl_ast_build.c?rev=233567&r1=233566&r2=233567&view=diff
==============================================================================
--- polly/trunk/lib/External/isl/isl_ast_build.c (original)
+++ polly/trunk/lib/External/isl/isl_ast_build.c Mon Mar 30 12:28:57 2015
@@ -204,6 +204,10 @@ __isl_give isl_ast_build *isl_ast_build_
 	dup->before_each_for_user = build->before_each_for_user;
 	dup->after_each_for = build->after_each_for;
 	dup->after_each_for_user = build->after_each_for_user;
+	dup->before_each_mark = build->before_each_mark;
+	dup->before_each_mark_user = build->before_each_mark_user;
+	dup->after_each_mark = build->after_each_mark;
+	dup->after_each_mark_user = build->after_each_mark_user;
 	dup->create_leaf = build->create_leaf;
 	dup->create_leaf_user = build->create_leaf_user;
 	dup->node = isl_schedule_node_copy(build->node);
@@ -425,6 +429,42 @@ __isl_give isl_ast_build *isl_ast_build_
 	return build;
 }
 
+/* Set the "before_each_mark" callback of "build" to "fn".
+ */
+__isl_give isl_ast_build *isl_ast_build_set_before_each_mark(
+	__isl_take isl_ast_build *build,
+	int (*fn)(__isl_keep isl_id *mark, __isl_keep isl_ast_build *build,
+		void *user), void *user)
+{
+	build = isl_ast_build_cow(build);
+
+	if (!build)
+		return NULL;
+
+	build->before_each_mark = fn;
+	build->before_each_mark_user = user;
+
+	return build;
+}
+
+/* Set the "after_each_mark" callback of "build" to "fn".
+ */
+__isl_give isl_ast_build *isl_ast_build_set_after_each_mark(
+	__isl_take isl_ast_build *build,
+	__isl_give isl_ast_node *(*fn)(__isl_take isl_ast_node *node,
+		__isl_keep isl_ast_build *build, void *user), void *user)
+{
+	build = isl_ast_build_cow(build);
+
+	if (!build)
+		return NULL;
+
+	build->after_each_mark = fn;
+	build->after_each_mark_user = user;
+
+	return build;
+}
+
 /* Set the "create_leaf" callback of "build" to "fn".
  */
 __isl_give isl_ast_build *isl_ast_build_set_create_leaf(
@@ -465,6 +505,10 @@ __isl_give isl_ast_build *isl_ast_build_
 	build->before_each_for_user = NULL;
 	build->after_each_for = NULL;
 	build->after_each_for_user = NULL;
+	build->before_each_mark = NULL;
+	build->before_each_mark_user = NULL;
+	build->after_each_mark = NULL;
+	build->after_each_mark_user = NULL;
 	build->create_leaf = NULL;
 	build->create_leaf_user = NULL;
 

Modified: polly/trunk/lib/External/isl/isl_ast_build_private.h
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/lib/External/isl/isl_ast_build_private.h?rev=233567&r1=233566&r2=233567&view=diff
==============================================================================
--- polly/trunk/lib/External/isl/isl_ast_build_private.h (original)
+++ polly/trunk/lib/External/isl/isl_ast_build_private.h Mon Mar 30 12:28:57 2015
@@ -110,6 +110,12 @@
  * The "after_each_for" callback is called on each for node after
  * its children have been created.
  *
+ * The "before_each_mark" callback is called before we handle the subtree
+ * of an isl_schedule_node_mark node.
+ *
+ * The "after_each_mark" callback is called after we have handled the subtree
+ * of an isl_schedule_node_mark node.
+ *
  * "executed" contains the inverse schedule at this point
  * of the AST generation.
  * It is currently only used in isl_ast_build_get_schedule, which is
@@ -172,6 +178,14 @@ struct isl_ast_build {
 		__isl_keep isl_ast_build *context, void *user);
 	void *after_each_for_user;
 
+	int (*before_each_mark)(__isl_keep isl_id *mark,
+		__isl_keep isl_ast_build *build, void *user);
+	void *before_each_mark_user;
+	__isl_give isl_ast_node *(*after_each_mark)(
+		__isl_take isl_ast_node *node,
+		__isl_keep isl_ast_build *context, void *user);
+	void *after_each_mark_user;
+
 	__isl_give isl_ast_node *(*create_leaf)(
 		__isl_take isl_ast_build *build, void *user);
 	void *create_leaf_user;

Modified: polly/trunk/lib/External/isl/isl_ast_codegen.c
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/lib/External/isl/isl_ast_codegen.c?rev=233567&r1=233566&r2=233567&view=diff
==============================================================================
--- polly/trunk/lib/External/isl/isl_ast_codegen.c (original)
+++ polly/trunk/lib/External/isl/isl_ast_codegen.c Mon Mar 30 12:28:57 2015
@@ -4092,6 +4092,45 @@ static int after_in_context(__isl_keep i
 
 /* Is any domain element of "umap" scheduled after any of
  * the corresponding image elements by the tree rooted at
+ * the expansion node "node"?
+ *
+ * We apply the expansion to domain and range of "umap" and
+ * continue with its child.
+ */
+static int after_in_expansion(__isl_keep isl_union_map *umap,
+	__isl_keep isl_schedule_node *node)
+{
+	isl_union_map *expansion;
+	int after;
+
+	expansion = isl_schedule_node_expansion_get_expansion(node);
+	umap = isl_union_map_copy(umap);
+	umap = isl_union_map_apply_domain(umap, isl_union_map_copy(expansion));
+	umap = isl_union_map_apply_range(umap, expansion);
+
+	after = after_in_child(umap, node);
+
+	isl_union_map_free(umap);
+
+	return after;
+}
+
+/* Is any domain element of "umap" scheduled after any of
+ * the corresponding image elements by the tree rooted at
+ * the extension node "node"?
+ *
+ * Since the extension node may add statement instances before or
+ * after the pairs of statement instances in "umap", we return 1
+ * to ensure that these pairs are not broken up.
+ */
+static int after_in_extension(__isl_keep isl_union_map *umap,
+	__isl_keep isl_schedule_node *node)
+{
+	return 1;
+}
+
+/* Is any domain element of "umap" scheduled after any of
+ * the corresponding image elements by the tree rooted at
  * the filter node "node"?
  *
  * We intersect domain and range of "umap" with the filter and
@@ -4258,8 +4297,15 @@ static int after_in_tree(__isl_keep isl_
 			"unexpected internal domain node", return -1);
 	case isl_schedule_node_context:
 		return after_in_context(umap, node);
+	case isl_schedule_node_expansion:
+		return after_in_expansion(umap, node);
+	case isl_schedule_node_extension:
+		return after_in_extension(umap, node);
 	case isl_schedule_node_filter:
 		return after_in_filter(umap, node);
+	case isl_schedule_node_guard:
+	case isl_schedule_node_mark:
+		return after_in_child(umap, node);
 	case isl_schedule_node_set:
 		return after_in_set(umap, node);
 	case isl_schedule_node_sequence:
@@ -4971,6 +5017,78 @@ static __isl_give isl_ast_graft_list *bu
 }
 
 /* Generate an AST that visits the elements in the domain of "executed"
+ * in the relative order specified by the expansion node "node" and
+ * its descendants.
+ *
+ * The relation "executed" maps the outer generated loop iterators
+ * to the domain elements executed by those iterations.
+ *
+ * We expand the domain elements by the expansion and
+ * continue with the descendants of the node.
+ */
+static __isl_give isl_ast_graft_list *build_ast_from_expansion(
+	__isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,
+	__isl_take isl_union_map *executed)
+{
+	isl_union_map *expansion;
+	unsigned n1, n2;
+
+	expansion = isl_schedule_node_expansion_get_expansion(node);
+	expansion = isl_union_map_align_params(expansion,
+				isl_union_map_get_space(executed));
+
+	n1 = isl_union_map_dim(executed, isl_dim_param);
+	executed = isl_union_map_apply_range(executed, expansion);
+	n2 = isl_union_map_dim(executed, isl_dim_param);
+	if (n2 > n1)
+		isl_die(isl_ast_build_get_ctx(build), isl_error_invalid,
+			"expansion node is not allowed to introduce "
+			"new parameters", goto error);
+
+	return build_ast_from_child(build, node, executed);
+error:
+	isl_ast_build_free(build);
+	isl_schedule_node_free(node);
+	isl_union_map_free(executed);
+	return NULL;
+}
+
+/* Generate an AST that visits the elements in the domain of "executed"
+ * in the relative order specified by the extension node "node" and
+ * its descendants.
+ *
+ * The relation "executed" maps the outer generated loop iterators
+ * to the domain elements executed by those iterations.
+ *
+ * Extend the inverse schedule with the extension applied to current
+ * set of generated constraints.  Since the extension if formulated
+ * in terms of the input schedule, it first needs to be transformed
+ * to refer to the internal schedule.
+ */
+static __isl_give isl_ast_graft_list *build_ast_from_extension(
+	__isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,
+	__isl_take isl_union_map *executed)
+{
+	isl_union_set *schedule_domain;
+	isl_union_map *extension;
+	isl_set *set;
+
+	set = isl_ast_build_get_generated(build);
+	schedule_domain = isl_union_set_from_set(set);
+
+	extension = isl_schedule_node_extension_get_extension(node);
+
+	extension = isl_union_map_preimage_domain_multi_aff(extension,
+			isl_multi_aff_copy(build->internal2input));
+	extension = isl_union_map_intersect_domain(extension, schedule_domain);
+	extension = isl_ast_build_substitute_values_union_map_domain(build,
+								    extension);
+	executed = isl_union_map_union(executed, extension);
+
+	return build_ast_from_child(build, node, executed);
+}
+
+/* Generate an AST that visits the elements in the domain of "executed"
  * in the relative order specified by the filter node "node" and
  * its descendants.
  *
@@ -5025,6 +5143,153 @@ error:
 	return NULL;
 }
 
+/* Generate an AST that visits the elements in the domain of "executed"
+ * in the relative order specified by the guard node "node" and
+ * its descendants.
+ *
+ * The relation "executed" maps the outer generated loop iterators
+ * to the domain elements executed by those iterations.
+ *
+ * Ensure that the associated guard is enforced by the outer AST
+ * constructs by adding it to the guard of the graft.
+ * Since we know that we will enforce the guard, we can also include it
+ * in the generated constraints used to construct an AST for
+ * the descendant nodes.
+ */
+static __isl_give isl_ast_graft_list *build_ast_from_guard(
+	__isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,
+	__isl_take isl_union_map *executed)
+{
+	isl_space *space;
+	isl_set *guard, *hoisted;
+	isl_basic_set *enforced;
+	isl_ast_build *sub_build;
+	isl_ast_graft *graft;
+	isl_ast_graft_list *list;
+	unsigned n1, n2;
+
+	space = isl_ast_build_get_space(build, 1);
+	guard = isl_schedule_node_guard_get_guard(node);
+	n1 = isl_space_dim(space, isl_dim_param);
+	guard = isl_set_align_params(guard, space);
+	n2 = isl_set_dim(guard, isl_dim_param);
+	if (n2 > n1)
+		isl_die(isl_ast_build_get_ctx(build), isl_error_invalid,
+			"guard node is not allowed to introduce "
+			"new parameters", guard = isl_set_free(guard));
+	guard = isl_set_preimage_multi_aff(guard,
+			isl_multi_aff_copy(build->internal2input));
+	guard = isl_ast_build_specialize(build, guard);
+	guard = isl_set_gist(guard, isl_set_copy(build->generated));
+
+	sub_build = isl_ast_build_copy(build);
+	sub_build = isl_ast_build_restrict_generated(sub_build,
+							isl_set_copy(guard));
+
+	list = build_ast_from_child(isl_ast_build_copy(sub_build),
+							node, executed);
+
+	hoisted = isl_ast_graft_list_extract_hoistable_guard(list, sub_build);
+	if (isl_set_n_basic_set(hoisted) > 1)
+		list = isl_ast_graft_list_gist_guards(list,
+						    isl_set_copy(hoisted));
+	guard = isl_set_intersect(guard, hoisted);
+	enforced = extract_shared_enforced(list, build);
+	graft = isl_ast_graft_alloc_from_children(list, guard, enforced,
+						    build, sub_build);
+
+	isl_ast_build_free(sub_build);
+	isl_ast_build_free(build);
+	return isl_ast_graft_list_from_ast_graft(graft);
+}
+
+/* Call the before_each_mark callback, if requested by the user.
+ *
+ * Return 0 on success and -1 on error.
+ *
+ * The caller is responsible for recording the current inverse schedule
+ * in "build".
+ */
+static int before_each_mark(__isl_keep isl_id *mark,
+	__isl_keep isl_ast_build *build)
+{
+	if (!build)
+		return -1;
+	if (!build->before_each_mark)
+		return 0;
+	return build->before_each_mark(mark, build,
+					build->before_each_mark_user);
+}
+
+/* Call the after_each_mark callback, if requested by the user.
+ *
+ * Return 0 on success and -1 on error.
+ *
+ * The caller is responsible for recording the current inverse schedule
+ * in "build".
+ */
+static __isl_give isl_ast_graft *after_each_mark(
+	__isl_take isl_ast_graft *graft, __isl_keep isl_ast_build *build)
+{
+	if (!graft || !build)
+		return isl_ast_graft_free(graft);
+	if (!build->after_each_mark)
+		return graft;
+	graft->node = build->after_each_mark(graft->node, build,
+						build->after_each_mark_user);
+	if (!graft->node)
+		return isl_ast_graft_free(graft);
+	return graft;
+}
+
+
+/* Generate an AST that visits the elements in the domain of "executed"
+ * in the relative order specified by the mark node "node" and
+ * its descendants.
+ *
+ * The relation "executed" maps the outer generated loop iterators
+ * to the domain elements executed by those iterations.
+
+ * Since we may be calling before_each_mark and after_each_mark
+ * callbacks, we record the current inverse schedule in the build.
+ *
+ * We generate an AST for the child of the mark node, combine
+ * the graft list into a single graft and then insert the mark
+ * in the AST of that single graft.
+ */
+static __isl_give isl_ast_graft_list *build_ast_from_mark(
+	__isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,
+	__isl_take isl_union_map *executed)
+{
+	isl_id *mark;
+	isl_ast_graft *graft;
+	isl_ast_graft_list *list;
+	int n;
+
+	build = isl_ast_build_set_executed(build, isl_union_map_copy(executed));
+
+	mark = isl_schedule_node_mark_get_id(node);
+	if (before_each_mark(mark, build) < 0)
+		node = isl_schedule_node_free(node);
+
+	list = build_ast_from_child(isl_ast_build_copy(build), node, executed);
+	list = isl_ast_graft_list_fuse(list, build);
+	n = isl_ast_graft_list_n_ast_graft(list);
+	if (n < 0)
+		list = isl_ast_graft_list_free(list);
+	if (n == 0) {
+		isl_id_free(mark);
+	} else {
+		graft = isl_ast_graft_list_get_ast_graft(list, 0);
+		graft = isl_ast_graft_insert_mark(graft, mark);
+		graft = after_each_mark(graft, build);
+		list = isl_ast_graft_list_set_ast_graft(list, 0, graft);
+	}
+	isl_ast_build_free(build);
+
+	return list;
+}
+
 static __isl_give isl_ast_graft_list *build_ast_from_schedule_node(
 	__isl_take isl_ast_build *build, __isl_take isl_schedule_node *node,
 	__isl_take isl_union_map *executed);
@@ -5104,8 +5369,16 @@ static __isl_give isl_ast_graft_list *bu
 	case isl_schedule_node_domain:
 		isl_die(isl_schedule_node_get_ctx(node), isl_error_unsupported,
 			"unexpected internal domain node", goto error);
+	case isl_schedule_node_expansion:
+		return build_ast_from_expansion(build, node, executed);
+	case isl_schedule_node_extension:
+		return build_ast_from_extension(build, node, executed);
 	case isl_schedule_node_filter:
 		return build_ast_from_filter(build, node, executed);
+	case isl_schedule_node_guard:
+		return build_ast_from_guard(build, node, executed);
+	case isl_schedule_node_mark:
+		return build_ast_from_mark(build, node, executed);
 	case isl_schedule_node_sequence:
 	case isl_schedule_node_set:
 		return build_ast_from_sequence(build, node, executed);

Modified: polly/trunk/lib/External/isl/isl_ast_graft.c
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/lib/External/isl/isl_ast_graft.c?rev=233567&r1=233566&r2=233567&view=diff
==============================================================================
--- polly/trunk/lib/External/isl/isl_ast_graft.c (original)
+++ polly/trunk/lib/External/isl/isl_ast_graft.c Mon Mar 30 12:28:57 2015
@@ -960,6 +960,25 @@ error:
 	return NULL;
 }
 
+/* Insert a mark governing the current graft->node.
+ */
+__isl_give isl_ast_graft *isl_ast_graft_insert_mark(
+	__isl_take isl_ast_graft *graft, __isl_take isl_id *mark)
+{
+	if (!graft)
+		goto error;
+
+	graft->node = isl_ast_node_alloc_mark(mark, graft->node);
+	if (!graft->node)
+		return isl_ast_graft_free(graft);
+
+	return graft;
+error:
+	isl_id_free(mark);
+	isl_ast_graft_free(graft);
+	return NULL;
+}
+
 /* Represent the graft list as an AST node.
  * This operation drops the information about guards in the grafts, so
  * if there are any pending guards, then they are materialized as if nodes.

Modified: polly/trunk/lib/External/isl/isl_ast_graft_private.h
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/lib/External/isl/isl_ast_graft_private.h?rev=233567&r1=233566&r2=233567&view=diff
==============================================================================
--- polly/trunk/lib/External/isl/isl_ast_graft_private.h (original)
+++ polly/trunk/lib/External/isl/isl_ast_graft_private.h Mon Mar 30 12:28:57 2015
@@ -76,6 +76,9 @@ __isl_give isl_ast_graft *isl_ast_graft_
 __isl_give isl_ast_graft *isl_ast_graft_enforce(
 	__isl_take isl_ast_graft *graft, __isl_take isl_basic_set *enforced);
 
+__isl_give isl_ast_graft *isl_ast_graft_insert_mark(
+	__isl_take isl_ast_graft *graft, __isl_take isl_id *mark);
+
 __isl_give isl_ast_graft_list *isl_ast_graft_list_unembed(
 	__isl_take isl_ast_graft_list *list, int product);
 __isl_give isl_ast_graft_list *isl_ast_graft_list_preimage_multi_aff(

Modified: polly/trunk/lib/External/isl/isl_ast_private.h
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/lib/External/isl/isl_ast_private.h?rev=233567&r1=233566&r2=233567&view=diff
==============================================================================
--- polly/trunk/lib/External/isl/isl_ast_private.h (original)
+++ polly/trunk/lib/External/isl/isl_ast_private.h Mon Mar 30 12:28:57 2015
@@ -45,9 +45,10 @@ __isl_give isl_ast_expr *isl_ast_expr_al
 
 #include <isl_list_templ.h>
 
-/* A node is either a block, an if, a for or a user node.
+/* A node is either a block, an if, a for, a user node or a mark node.
  * "else_node" is NULL if the if node does not have an else branch.
  * "cond" and "inc" are NULL for degenerate for nodes.
+ * In case of a mark node, "mark" is the mark and "node" is the marked node.
  */
 struct isl_ast_node {
 	int ref;
@@ -75,6 +76,10 @@ struct isl_ast_node {
 		struct {
 			isl_ast_expr *expr;
 		} e;
+		struct {
+			isl_id *mark;
+			isl_ast_node *node;
+		} m;
 	} u;
 
 	isl_id *annotation;
@@ -86,6 +91,8 @@ __isl_give isl_ast_node *isl_ast_node_fo
 __isl_give isl_ast_node *isl_ast_node_alloc_if(__isl_take isl_ast_expr *guard);
 __isl_give isl_ast_node *isl_ast_node_alloc_block(
 	__isl_take isl_ast_node_list *list);
+__isl_give isl_ast_node *isl_ast_node_alloc_mark(__isl_take isl_id *id,
+	__isl_take isl_ast_node *node);
 __isl_give isl_ast_node *isl_ast_node_from_ast_node_list(
 	__isl_take isl_ast_node_list *list);
 __isl_give isl_ast_node *isl_ast_node_for_set_body(

Modified: polly/trunk/lib/External/isl/isl_schedule.c
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/lib/External/isl/isl_schedule.c?rev=233567&r1=233566&r2=233567&view=diff
==============================================================================
--- polly/trunk/lib/External/isl/isl_schedule.c (original)
+++ polly/trunk/lib/External/isl/isl_schedule.c Mon Mar 30 12:28:57 2015
@@ -24,7 +24,7 @@
 
 /* Return a schedule encapsulating the given schedule tree.
  *
- * We currently only allow schedule trees with a domain as root.
+ * We currently only allow schedule trees with a domain or extension as root.
  *
  * The leaf field is initialized as a leaf node so that it can be
  * used to represent leaves in the constructed schedule.
@@ -36,13 +36,16 @@
 __isl_give isl_schedule *isl_schedule_from_schedule_tree(isl_ctx *ctx,
 	__isl_take isl_schedule_tree *tree)
 {
+	enum isl_schedule_node_type type;
 	isl_schedule *schedule;
 
 	if (!tree)
 		return NULL;
-	if (isl_schedule_tree_get_type(tree) != isl_schedule_node_domain)
+	type = isl_schedule_tree_get_type(tree);
+	if (type != isl_schedule_node_domain &&
+	    type != isl_schedule_node_extension)
 		isl_die(isl_schedule_tree_get_ctx(tree), isl_error_unsupported,
-			"root of schedule tree should be a domain",
+			"root of schedule tree should be a domain or extension",
 			goto error);
 
 	schedule = isl_calloc_type(ctx, isl_schedule);
@@ -429,6 +432,8 @@ static __isl_give isl_schedule_node *pul
 
 /* Compute the pullback of "schedule" by the function represented by "upma".
  * In other words, plug in "upma" in the iteration domains of "schedule".
+ *
+ * The schedule tree is not allowed to contain any expansion nodes.
  */
 __isl_give isl_schedule *isl_schedule_pullback_union_pw_multi_aff(
 	__isl_take isl_schedule *schedule,
@@ -441,6 +446,7 @@ __isl_give isl_schedule *isl_schedule_pu
 }
 
 /* Intersect the domain of the schedule "schedule" with "domain".
+ * The root of "schedule" is required to be a domain node.
  */
 __isl_give isl_schedule *isl_schedule_intersect_domain(
 	__isl_take isl_schedule *schedule, __isl_take isl_union_set *domain)
@@ -453,8 +459,8 @@ __isl_give isl_schedule *isl_schedule_in
 
 	root_type = isl_schedule_tree_get_type(schedule->root);
 	if (root_type != isl_schedule_node_domain)
-		isl_die(isl_schedule_get_ctx(schedule), isl_error_internal,
-			"root node not a domain node", goto error);
+		isl_die(isl_schedule_get_ctx(schedule), isl_error_invalid,
+			"root node must be a domain node", goto error);
 
 	node = isl_schedule_get_root(schedule);
 	isl_schedule_free(schedule);
@@ -734,11 +740,23 @@ static __isl_give isl_band_list *constru
 	case isl_schedule_node_domain:
 		isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
 			"internal domain nodes not allowed", goto error);
+	case isl_schedule_node_expansion:
+		isl_die(isl_schedule_node_get_ctx(node), isl_error_unsupported,
+			"expansion nodes not supported", goto error);
+	case isl_schedule_node_extension:
+		isl_die(isl_schedule_node_get_ctx(node), isl_error_unsupported,
+			"extension nodes not supported", goto error);
 	case isl_schedule_node_filter:
 		filter = isl_schedule_node_filter_get_filter(node);
 		domain = isl_union_set_intersect(domain, filter);
 		node = isl_schedule_node_child(node, 0);
 		return construct_band_list(node, domain, parent);
+	case isl_schedule_node_guard:
+		isl_die(isl_schedule_node_get_ctx(node), isl_error_unsupported,
+			"guard nodes not supported", goto error);
+	case isl_schedule_node_mark:
+		isl_die(isl_schedule_node_get_ctx(node), isl_error_unsupported,
+			"mark nodes not supported", goto error);
 	case isl_schedule_node_set:
 		ctx = isl_schedule_node_get_ctx(node);
 		if (isl_options_get_schedule_separate_components(ctx))
@@ -907,6 +925,25 @@ __isl_give isl_schedule *isl_schedule_in
 	schedule = isl_schedule_node_get_schedule(node);
 	isl_schedule_node_free(node);
 
+	return schedule;
+}
+
+/* Insert a guard node with constraints "guard" between the domain
+ * root node of "schedule" and its single child.
+ * Return a pointer to the updated schedule.
+ */
+__isl_give isl_schedule *isl_schedule_insert_guard(
+	__isl_take isl_schedule *schedule, __isl_take isl_set *guard)
+{
+	isl_schedule_node *node;
+
+	node = isl_schedule_get_root(schedule);
+	isl_schedule_free(schedule);
+	node = isl_schedule_node_child(node, 0);
+	node = isl_schedule_node_insert_guard(node, guard);
+	schedule = isl_schedule_node_get_schedule(node);
+	isl_schedule_node_free(node);
+
 	return schedule;
 }
 

Modified: polly/trunk/lib/External/isl/isl_schedule_band.c
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/lib/External/isl/isl_schedule_band.c?rev=233567&r1=233566&r2=233567&view=diff
==============================================================================
--- polly/trunk/lib/External/isl/isl_schedule_band.c (original)
+++ polly/trunk/lib/External/isl/isl_schedule_band.c Mon Mar 30 12:28:57 2015
@@ -309,6 +309,27 @@ __isl_give isl_space *isl_schedule_band_
 	return isl_multi_union_pw_aff_get_space(band->mupa);
 }
 
+/* Intersect the domain of the band schedule of "band" with "domain".
+ */
+__isl_give isl_schedule_band *isl_schedule_band_intersect_domain(
+	__isl_take isl_schedule_band *band, __isl_take isl_union_set *domain)
+{
+	band = isl_schedule_band_cow(band);
+	if (!band || !domain)
+		goto error;
+
+	band->mupa = isl_multi_union_pw_aff_intersect_domain(band->mupa,
+								domain);
+	if (!band->mupa)
+		return isl_schedule_band_free(band);
+
+	return band;
+error:
+	isl_schedule_band_free(band);
+	isl_union_set_free(domain);
+	return NULL;
+}
+
 /* Return the schedule of the band in isolation.
  */
 __isl_give isl_multi_union_pw_aff *isl_schedule_band_get_partial_schedule(
@@ -317,6 +338,26 @@ __isl_give isl_multi_union_pw_aff *isl_s
 	return band ? isl_multi_union_pw_aff_copy(band->mupa) : NULL;
 }
 
+/* Replace the schedule of "band" by "schedule".
+ */
+__isl_give isl_schedule_band *isl_schedule_band_set_partial_schedule(
+	__isl_take isl_schedule_band *band,
+	__isl_take isl_multi_union_pw_aff *schedule)
+{
+	band = isl_schedule_band_cow(band);
+	if (!band || !schedule)
+		goto error;
+
+	isl_multi_union_pw_aff_free(band->mupa);
+	band->mupa = schedule;
+
+	return band;
+error:
+	isl_schedule_band_free(band);
+	isl_multi_union_pw_aff_free(schedule);
+	return NULL;
+}
+
 /* Return the loop AST generation type for the band member of "band"
  * at position "pos".
  */

Modified: polly/trunk/lib/External/isl/isl_schedule_band.h
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/lib/External/isl/isl_schedule_band.h?rev=233567&r1=233566&r2=233567&view=diff
==============================================================================
--- polly/trunk/lib/External/isl/isl_schedule_band.h (original)
+++ polly/trunk/lib/External/isl/isl_schedule_band.h Mon Mar 30 12:28:57 2015
@@ -58,8 +58,13 @@ int isl_schedule_band_is_anchored(__isl_
 
 __isl_give isl_space *isl_schedule_band_get_space(
 	__isl_keep isl_schedule_band *band);
+__isl_give isl_schedule_band *isl_schedule_band_intersect_domain(
+	__isl_take isl_schedule_band *band, __isl_take isl_union_set *domain);
 __isl_give isl_multi_union_pw_aff *isl_schedule_band_get_partial_schedule(
 	__isl_keep isl_schedule_band *band);
+__isl_give isl_schedule_band *isl_schedule_band_set_partial_schedule(
+	__isl_take isl_schedule_band *band,
+	__isl_take isl_multi_union_pw_aff *schedule);
 enum isl_ast_loop_type isl_schedule_band_member_get_ast_loop_type(
 	__isl_keep isl_schedule_band *band, int pos);
 __isl_give isl_schedule_band *isl_schedule_band_member_set_ast_loop_type(

Modified: polly/trunk/lib/External/isl/isl_schedule_node.c
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/lib/External/isl/isl_schedule_node.c?rev=233567&r1=233566&r2=233567&view=diff
==============================================================================
--- polly/trunk/lib/External/isl/isl_schedule_node.c (original)
+++ polly/trunk/lib/External/isl/isl_schedule_node.c Mon Mar 30 12:28:57 2015
@@ -70,6 +70,29 @@ __isl_give isl_schedule_node *isl_schedu
 	return node;
 }
 
+/* Return a pointer to the root of a schedule tree with as single
+ * node a extension node with the given extension.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_from_extension(
+	__isl_take isl_union_map *extension)
+{
+	isl_ctx *ctx;
+	isl_schedule *schedule;
+	isl_schedule_tree *tree;
+	isl_schedule_node *node;
+
+	if (!extension)
+		return NULL;
+
+	ctx = isl_union_map_get_ctx(extension);
+	tree = isl_schedule_tree_from_extension(extension);
+	schedule = isl_schedule_from_schedule_tree(ctx, tree);
+	node = isl_schedule_get_root(schedule);
+	isl_schedule_free(schedule);
+
+	return node;
+}
+
 /* Return the isl_ctx to which "node" belongs.
  */
 isl_ctx *isl_schedule_node_get_ctx(__isl_keep isl_schedule_node *node)
@@ -298,6 +321,114 @@ struct isl_schedule_node_get_filter_pref
 	isl_multi_union_pw_aff *prefix;
 };
 
+static int collect_filter_prefix(__isl_keep isl_schedule_tree_list *list,
+	int n, struct isl_schedule_node_get_filter_prefix_data *data);
+
+/* Update the filter and prefix information in "data" based on the first "n"
+ * elements in "list" and the expansion tree root "tree".
+ *
+ * We first collect the information from the elements in "list",
+ * initializing the filter based on the domain of the expansion.
+ * Then we map the results to the expanded space and combined them
+ * with the results already in "data".
+ */
+static int collect_filter_prefix_expansion(__isl_take isl_schedule_tree *tree,
+	__isl_keep isl_schedule_tree_list *list, int n,
+	struct isl_schedule_node_get_filter_prefix_data *data)
+{
+	struct isl_schedule_node_get_filter_prefix_data contracted;
+	isl_union_pw_multi_aff *c;
+	isl_union_map *exp, *universe;
+	isl_union_set *filter;
+
+	c = isl_schedule_tree_expansion_get_contraction(tree);
+	exp = isl_schedule_tree_expansion_get_expansion(tree);
+
+	contracted.initialized = 1;
+	contracted.universe_domain = data->universe_domain;
+	contracted.universe_filter = data->universe_filter;
+	contracted.collect_prefix = data->collect_prefix;
+	universe = isl_union_map_universe(isl_union_map_copy(exp));
+	filter = isl_union_map_domain(universe);
+	if (data->collect_prefix) {
+		isl_space *space = isl_union_set_get_space(filter);
+		space = isl_space_set_from_params(space);
+		contracted.prefix = isl_multi_union_pw_aff_zero(space);
+	}
+	contracted.filter = filter;
+
+	if (collect_filter_prefix(list, n, &contracted) < 0)
+		contracted.filter = isl_union_set_free(contracted.filter);
+	if (data->collect_prefix) {
+		isl_multi_union_pw_aff *prefix;
+
+		prefix = contracted.prefix;
+		prefix =
+		    isl_multi_union_pw_aff_pullback_union_pw_multi_aff(prefix,
+						isl_union_pw_multi_aff_copy(c));
+		data->prefix = isl_multi_union_pw_aff_flat_range_product(
+						prefix, data->prefix);
+	}
+	filter = contracted.filter;
+	if (data->universe_domain)
+		filter = isl_union_set_preimage_union_pw_multi_aff(filter,
+						isl_union_pw_multi_aff_copy(c));
+	else
+		filter = isl_union_set_apply(filter, isl_union_map_copy(exp));
+	if (!data->initialized)
+		data->filter = filter;
+	else
+		data->filter = isl_union_set_intersect(filter, data->filter);
+	data->initialized = 1;
+
+	isl_union_pw_multi_aff_free(c);
+	isl_union_map_free(exp);
+	isl_schedule_tree_free(tree);
+
+	return 0;
+}
+
+/* Update the filter information in "data" based on the first "n"
+ * elements in "list" and the extension tree root "tree", in case
+ * data->universe_domain is set and data->collect_prefix is not.
+ *
+ * We collect the universe domain of the elements in "list" and
+ * add it to the universe range of the extension (intersected
+ * with the already collected filter, if any).
+ */
+static int collect_universe_domain_extension(__isl_take isl_schedule_tree *tree,
+	__isl_keep isl_schedule_tree_list *list, int n,
+	struct isl_schedule_node_get_filter_prefix_data *data)
+{
+	struct isl_schedule_node_get_filter_prefix_data data_outer;
+	isl_union_map *extension;
+	isl_union_set *filter;
+
+	data_outer.initialized = 0;
+	data_outer.universe_domain = 1;
+	data_outer.universe_filter = data->universe_filter;
+	data_outer.collect_prefix = 0;
+	data_outer.filter = NULL;
+	data_outer.prefix = NULL;
+
+	if (collect_filter_prefix(list, n, &data_outer) < 0)
+		data_outer.filter = isl_union_set_free(data_outer.filter);
+
+	extension = isl_schedule_tree_extension_get_extension(tree);
+	extension = isl_union_map_universe(extension);
+	filter = isl_union_map_range(extension);
+	if (data_outer.initialized)
+		filter = isl_union_set_union(filter, data_outer.filter);
+	if (data->initialized)
+		filter = isl_union_set_intersect(filter, data->filter);
+
+	data->filter = filter;
+
+	isl_schedule_tree_free(tree);
+
+	return 0;
+}
+
 /* Update "data" based on the tree node "tree" in case "data" has
  * not been initialized yet.
  *
@@ -324,8 +455,16 @@ static int collect_filter_prefix_init(__
 	switch (type) {
 	case isl_schedule_node_error:
 		return -1;
+	case isl_schedule_node_expansion:
+		isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal,
+			"should be handled by caller", return -1);
+	case isl_schedule_node_extension:
+		isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+			"cannot handle extension nodes", return -1);
 	case isl_schedule_node_context:
 	case isl_schedule_node_leaf:
+	case isl_schedule_node_guard:
+	case isl_schedule_node_mark:
 	case isl_schedule_node_sequence:
 	case isl_schedule_node_set:
 		return 0;
@@ -376,6 +515,8 @@ static int collect_filter_prefix_init(__
  * (or its universe).
  * If "tree" is a band with at least one member and data->collect_prefix
  * is set, then we extend data->prefix with the band schedule.
+ * If "tree" is an extension, then we make sure that we are not collecting
+ * information on any extended domain elements.
  */
 static int collect_filter_prefix_update(__isl_keep isl_schedule_tree *tree,
 	struct isl_schedule_node_get_filter_prefix_data *data)
@@ -383,13 +524,32 @@ static int collect_filter_prefix_update(
 	enum isl_schedule_node_type type;
 	isl_multi_union_pw_aff *mupa;
 	isl_union_set *filter;
+	isl_union_map *extension;
+	int empty;
 
 	type = isl_schedule_tree_get_type(tree);
 	switch (type) {
 	case isl_schedule_node_error:
 		return -1;
+	case isl_schedule_node_expansion:
+		isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal,
+			"should be handled by caller", return -1);
+	case isl_schedule_node_extension:
+		extension = isl_schedule_tree_extension_get_extension(tree);
+		extension = isl_union_map_intersect_range(extension,
+					isl_union_set_copy(data->filter));
+		empty = isl_union_map_is_empty(extension);
+		isl_union_map_free(extension);
+		if (empty < 0)
+			return -1;
+		if (empty)
+			break;
+		isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+			"cannot handle extension nodes", return -1);
 	case isl_schedule_node_context:
 	case isl_schedule_node_leaf:
+	case isl_schedule_node_guard:
+	case isl_schedule_node_mark:
 	case isl_schedule_node_sequence:
 	case isl_schedule_node_set:
 		break;
@@ -423,38 +583,56 @@ static int collect_filter_prefix_update(
 	return 0;
 }
 
-/* Collect filter and/or prefix information from the elements
- * in "list" (which represent the ancestors of a node).
+/* Collect filter and/or prefix information from the first "n"
+ * elements in "list" (which represent the ancestors of a node).
  * Store the results in "data".
  *
+ * Extension nodes are only supported if they do not affect the outcome,
+ * i.e., if we are collecting information on non-extended domain elements,
+ * or if we are collecting the universe domain (without prefix).
+ *
  * Return 0 on success and -1 on error.
  *
  * We traverse the list from innermost ancestor (last element)
  * to outermost ancestor (first element), calling collect_filter_prefix_init
  * on each node as long as we have not been able to extract any information
  * yet and collect_filter_prefix_update afterwards.
+ * If we come across an expansion node, then we interrupt the traversal
+ * and call collect_filter_prefix_expansion to restart the traversal
+ * over the remaining ancestors and to combine the results with those
+ * that have already been collected.
+ * If we come across an extension node and we are only computing
+ * the universe domain, then we interrupt the traversal and call
+ * collect_universe_domain_extension to restart the traversal
+ * over the remaining ancestors and to combine the results with those
+ * that have already been collected.
  * On successful return, data->initialized will be set since the outermost
  * ancestor is a domain node, which always results in an initialization.
  */
 static int collect_filter_prefix(__isl_keep isl_schedule_tree_list *list,
-	struct isl_schedule_node_get_filter_prefix_data *data)
+	int n, struct isl_schedule_node_get_filter_prefix_data *data)
 {
-	int i, n;
-
-	data->initialized = 0;
-	data->filter = NULL;
+	int i;
 
 	if (!list)
 		return -1;
 
-	n = isl_schedule_tree_list_n_schedule_tree(list);
 	for (i = n - 1; i >= 0; --i) {
 		isl_schedule_tree *tree;
+		enum isl_schedule_node_type type;
 		int r;
 
 		tree = isl_schedule_tree_list_get_schedule_tree(list, i);
 		if (!tree)
 			return -1;
+		type = isl_schedule_tree_get_type(tree);
+		if (type == isl_schedule_node_expansion)
+			return collect_filter_prefix_expansion(tree, list, i,
+								data);
+		if (type == isl_schedule_node_extension &&
+		    data->universe_domain && !data->collect_prefix)
+			return collect_universe_domain_extension(tree, list, i,
+								data);
 		if (!data->initialized)
 			r = collect_filter_prefix_init(tree, data);
 		else
@@ -469,7 +647,57 @@ static int collect_filter_prefix(__isl_k
 
 /* Return the concatenation of the partial schedules of all outer band
  * nodes of "node" interesected with all outer filters
+ * as an isl_multi_union_pw_aff.
+ * None of the ancestors of "node" may be an extension node, unless
+ * there is also a filter ancestor that filters out all the extended
+ * domain elements.
+ *
+ * If "node" is pointing at the root of the schedule tree, then
+ * there are no domain elements reaching the current node, so
+ * we return an empty result.
+ *
+ * We collect all the filters and partial schedules in collect_filter_prefix
+ * and intersect the domain of the combined schedule with the combined filter.
+ */
+__isl_give isl_multi_union_pw_aff *
+isl_schedule_node_get_prefix_schedule_multi_union_pw_aff(
+	__isl_keep isl_schedule_node *node)
+{
+	int n;
+	isl_space *space;
+	struct isl_schedule_node_get_filter_prefix_data data;
+
+	if (!node)
+		return NULL;
+
+	space = isl_schedule_get_space(node->schedule);
+	space = isl_space_set_from_params(space);
+	if (node->tree == node->schedule->root)
+		return isl_multi_union_pw_aff_zero(space);
+
+	data.initialized = 0;
+	data.universe_domain = 1;
+	data.universe_filter = 0;
+	data.collect_prefix = 1;
+	data.filter = NULL;
+	data.prefix = isl_multi_union_pw_aff_zero(space);
+
+	n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
+	if (collect_filter_prefix(node->ancestors, n, &data) < 0)
+		data.prefix = isl_multi_union_pw_aff_free(data.prefix);
+
+	data.prefix = isl_multi_union_pw_aff_intersect_domain(data.prefix,
+								data.filter);
+
+	return data.prefix;
+}
+
+/* Return the concatenation of the partial schedules of all outer band
+ * nodes of "node" interesected with all outer filters
  * as an isl_union_pw_multi_aff.
+ * None of the ancestors of "node" may be an extension node, unless
+ * there is also a filter ancestor that filters out all the extended
+ * domain elements.
  *
  * If "node" is pointing at the root of the schedule tree, then
  * there are no domain elements reaching the current node, so
@@ -487,6 +715,7 @@ __isl_give isl_union_pw_multi_aff *
 isl_schedule_node_get_prefix_schedule_union_pw_multi_aff(
 	__isl_keep isl_schedule_node *node)
 {
+	int n;
 	isl_space *space;
 	isl_union_pw_multi_aff *prefix;
 	struct isl_schedule_node_get_filter_prefix_data data;
@@ -499,12 +728,15 @@ isl_schedule_node_get_prefix_schedule_un
 		return isl_union_pw_multi_aff_empty(space);
 
 	space = isl_space_set_from_params(space);
+	data.initialized = 0;
 	data.universe_domain = 1;
 	data.universe_filter = 0;
 	data.collect_prefix = 1;
+	data.filter = NULL;
 	data.prefix = isl_multi_union_pw_aff_zero(space);
 
-	if (collect_filter_prefix(node->ancestors, &data) < 0)
+	n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
+	if (collect_filter_prefix(node->ancestors, n, &data) < 0)
 		data.prefix = isl_multi_union_pw_aff_free(data.prefix);
 
 	if (data.prefix &&
@@ -534,11 +766,64 @@ __isl_give isl_union_map *isl_schedule_n
 	return isl_union_map_from_union_pw_multi_aff(upma);
 }
 
+/* Return the concatenation of the partial schedules of all outer band
+ * nodes of "node" intersected with all outer domain constraints.
+ * None of the ancestors of "node" may be an extension node, unless
+ * there is also a filter ancestor that filters out all the extended
+ * domain elements.
+ *
+ * Essentially, this functions intersected the domain of the output
+ * of isl_schedule_node_get_prefix_schedule_union_map with the output
+ * of isl_schedule_node_get_domain, except that it only traverses
+ * the ancestors of "node" once.
+ */
+__isl_give isl_union_map *isl_schedule_node_get_prefix_schedule_relation(
+	__isl_keep isl_schedule_node *node)
+{
+	int n;
+	isl_space *space;
+	isl_union_map *prefix;
+	struct isl_schedule_node_get_filter_prefix_data data;
+
+	if (!node)
+		return NULL;
+
+	space = isl_schedule_get_space(node->schedule);
+	if (node->tree == node->schedule->root)
+		return isl_union_map_empty(space);
+
+	space = isl_space_set_from_params(space);
+	data.initialized = 0;
+	data.universe_domain = 0;
+	data.universe_filter = 0;
+	data.collect_prefix = 1;
+	data.filter = NULL;
+	data.prefix = isl_multi_union_pw_aff_zero(space);
+
+	n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
+	if (collect_filter_prefix(node->ancestors, n, &data) < 0)
+		data.prefix = isl_multi_union_pw_aff_free(data.prefix);
+
+	if (data.prefix &&
+	    isl_multi_union_pw_aff_dim(data.prefix, isl_dim_set) == 0) {
+		isl_multi_union_pw_aff_free(data.prefix);
+		prefix = isl_union_map_from_domain(data.filter);
+	} else {
+		prefix = isl_union_map_from_multi_union_pw_aff(data.prefix);
+		prefix = isl_union_map_intersect_domain(prefix, data.filter);
+	}
+
+	return prefix;
+}
+
 /* Return the domain elements that reach "node".
  *
  * If "node" is pointing at the root of the schedule tree, then
  * there are no domain elements reaching the current node, so
  * we return an empty result.
+ * None of the ancestors of "node" may be an extension node, unless
+ * there is also a filter ancestor that filters out all the extended
+ * domain elements.
  *
  * Otherwise, we collect all filters reaching the node,
  * intersected with the root domain in collect_filter_prefix.
@@ -546,6 +831,7 @@ __isl_give isl_union_map *isl_schedule_n
 __isl_give isl_union_set *isl_schedule_node_get_domain(
 	__isl_keep isl_schedule_node *node)
 {
+	int n;
 	struct isl_schedule_node_get_filter_prefix_data data;
 
 	if (!node)
@@ -558,12 +844,15 @@ __isl_give isl_union_set *isl_schedule_n
 		return isl_union_set_empty(space);
 	}
 
+	data.initialized = 0;
 	data.universe_domain = 0;
 	data.universe_filter = 0;
 	data.collect_prefix = 0;
+	data.filter = NULL;
 	data.prefix = NULL;
 
-	if (collect_filter_prefix(node->ancestors, &data) < 0)
+	n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
+	if (collect_filter_prefix(node->ancestors, n, &data) < 0)
 		data.filter = isl_union_set_free(data.filter);
 
 	return data.filter;
@@ -581,6 +870,7 @@ __isl_give isl_union_set *isl_schedule_n
 __isl_give isl_union_set *isl_schedule_node_get_universe_domain(
 	__isl_keep isl_schedule_node *node)
 {
+	int n;
 	struct isl_schedule_node_get_filter_prefix_data data;
 
 	if (!node)
@@ -593,12 +883,15 @@ __isl_give isl_union_set *isl_schedule_n
 		return isl_union_set_empty(space);
 	}
 
+	data.initialized = 0;
 	data.universe_domain = 1;
 	data.universe_filter = 1;
 	data.collect_prefix = 0;
+	data.filter = NULL;
 	data.prefix = NULL;
 
-	if (collect_filter_prefix(node->ancestors, &data) < 0)
+	n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
+	if (collect_filter_prefix(node->ancestors, n, &data) < 0)
 		data.filter = isl_union_set_free(data.filter);
 
 	return data.filter;
@@ -609,6 +902,11 @@ __isl_give isl_union_set *isl_schedule_n
  * Since isl_schedule_tree_get_subtree_schedule_union_map does not handle
  * trees that do not contain any schedule information, we first
  * move down to the first relevant descendant and handle leaves ourselves.
+ *
+ * If the subtree rooted at "node" contains any expansion nodes, then
+ * the returned subtree schedule is formulated in terms of the expanded
+ * domains.
+ * The subtree is not allowed to contain any extension nodes.
  */
 __isl_give isl_union_map *isl_schedule_node_get_subtree_schedule_union_map(
 	__isl_keep isl_schedule_node *node)
@@ -1592,6 +1890,83 @@ __isl_give isl_union_set *isl_schedule_n
 	return isl_schedule_tree_domain_get_domain(node->tree);
 }
 
+/* Return the expansion map of expansion node "node".
+ */
+__isl_give isl_union_map *isl_schedule_node_expansion_get_expansion(
+	__isl_keep isl_schedule_node *node)
+{
+	if (!node)
+		return NULL;
+
+	return isl_schedule_tree_expansion_get_expansion(node->tree);
+}
+
+/* Return the contraction of expansion node "node".
+ */
+__isl_give isl_union_pw_multi_aff *isl_schedule_node_expansion_get_contraction(
+	__isl_keep isl_schedule_node *node)
+{
+	if (!node)
+		return NULL;
+
+	return isl_schedule_tree_expansion_get_contraction(node->tree);
+}
+
+/* Replace the contraction and the expansion of the expansion node "node"
+ * by "contraction" and "expansion".
+ */
+__isl_give isl_schedule_node *
+isl_schedule_node_expansion_set_contraction_and_expansion(
+	__isl_take isl_schedule_node *node,
+	__isl_take isl_union_pw_multi_aff *contraction,
+	__isl_take isl_union_map *expansion)
+{
+	isl_schedule_tree *tree;
+
+	if (!node || !contraction || !expansion)
+		goto error;
+
+	tree = isl_schedule_tree_copy(node->tree);
+	tree = isl_schedule_tree_expansion_set_contraction_and_expansion(tree,
+							contraction, expansion);
+	return isl_schedule_node_graft_tree(node, tree);
+error:
+	isl_schedule_node_free(node);
+	isl_union_pw_multi_aff_free(contraction);
+	isl_union_map_free(expansion);
+	return NULL;
+}
+
+/* Return the extension of the extension node "node".
+ */
+__isl_give isl_union_map *isl_schedule_node_extension_get_extension(
+	__isl_keep isl_schedule_node *node)
+{
+	if (!node)
+		return NULL;
+
+	return isl_schedule_tree_extension_get_extension(node->tree);
+}
+
+/* Replace the extension of extension node "node" by "extension".
+ */
+__isl_give isl_schedule_node *isl_schedule_node_extension_set_extension(
+	__isl_take isl_schedule_node *node, __isl_take isl_union_map *extension)
+{
+	isl_schedule_tree *tree;
+
+	if (!node || !extension)
+		goto error;
+
+	tree = isl_schedule_tree_copy(node->tree);
+	tree = isl_schedule_tree_extension_set_extension(tree, extension);
+	return isl_schedule_node_graft_tree(node, tree);
+error:
+	isl_schedule_node_free(node);
+	isl_union_map_free(extension);
+	return NULL;
+}
+
 /* Return the filter of the filter node "node".
  */
 __isl_give isl_union_set *isl_schedule_node_filter_get_filter(
@@ -1622,26 +1997,88 @@ error:
 	return NULL;
 }
 
+/* Return the guard of the guard node "node".
+ */
+__isl_give isl_set *isl_schedule_node_guard_get_guard(
+	__isl_keep isl_schedule_node *node)
+{
+	if (!node)
+		return NULL;
+
+	return isl_schedule_tree_guard_get_guard(node->tree);
+}
+
+/* Return the mark identifier of the mark node "node".
+ */
+__isl_give isl_id *isl_schedule_node_mark_get_id(
+	__isl_keep isl_schedule_node *node)
+{
+	if (!node)
+		return NULL;
+
+	return isl_schedule_tree_mark_get_id(node->tree);
+}
+
+/* Replace the child at position "pos" of the sequence node "node"
+ * by the children of sequence root node of "tree".
+ */
+__isl_give isl_schedule_node *isl_schedule_node_sequence_splice(
+	__isl_take isl_schedule_node *node, int pos,
+	__isl_take isl_schedule_tree *tree)
+{
+	isl_schedule_tree *node_tree;
+
+	if (!node || !tree)
+		goto error;
+	if (isl_schedule_node_get_type(node) != isl_schedule_node_sequence)
+		isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+			"not a sequence node", goto error);
+	if (isl_schedule_tree_get_type(tree) != isl_schedule_node_sequence)
+		isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+			"not a sequence node", goto error);
+	node_tree = isl_schedule_node_get_tree(node);
+	node_tree = isl_schedule_tree_sequence_splice(node_tree, pos, tree);
+	node = isl_schedule_node_graft_tree(node, node_tree);
+
+	return node;
+error:
+	isl_schedule_node_free(node);
+	isl_schedule_tree_free(tree);
+	return NULL;
+}
+
 /* Update the ancestors of "node" to point to the tree that "node"
  * now points to.
  * That is, replace the child in the original parent that corresponds
  * to the current tree position by node->tree and continue updating
  * the ancestors in the same way until the root is reached.
  *
+ * If "fn" is not NULL, then it is called on each ancestor as we move up
+ * the tree so that it can modify the ancestor before it is added
+ * to the list of ancestors of the modified node.
+ * The additional "pos" argument records the position
+ * of the "tree" argument in the original schedule tree.
+ *
  * If "node" originally points to a leaf of the schedule tree, then make sure
  * that in the end it points to a leaf in the updated schedule tree.
  */
 static __isl_give isl_schedule_node *update_ancestors(
-	__isl_take isl_schedule_node *node)
+	__isl_take isl_schedule_node *node,
+	__isl_give isl_schedule_tree *(*fn)(__isl_take isl_schedule_tree *tree,
+		__isl_keep isl_schedule_node *pos, void *user), void *user)
 {
 	int i, n;
 	int is_leaf;
 	isl_ctx *ctx;
 	isl_schedule_tree *tree;
+	isl_schedule_node *pos = NULL;
+
+	if (fn)
+		pos = isl_schedule_node_copy(node);
 
 	node = isl_schedule_node_cow(node);
 	if (!node)
-		return NULL;
+		return isl_schedule_node_free(pos);
 
 	ctx = isl_schedule_node_get_ctx(node);
 	n = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
@@ -1654,12 +2091,19 @@ static __isl_give isl_schedule_node *upd
 						    node->ancestors, i);
 		parent = isl_schedule_tree_replace_child(parent,
 						    node->child_pos[i], tree);
+		if (fn) {
+			pos = isl_schedule_node_parent(pos);
+			parent = fn(parent, pos, user);
+		}
 		node->ancestors = isl_schedule_tree_list_set_schedule_tree(
 			    node->ancestors, i, isl_schedule_tree_copy(parent));
 
 		tree = parent;
 	}
 
+	if (fn)
+		isl_schedule_node_free(pos);
+
 	is_leaf = isl_schedule_tree_is_leaf(node->tree);
 	node->schedule = isl_schedule_set_root(node->schedule, tree);
 	if (is_leaf) {
@@ -1693,7 +2137,7 @@ __isl_give isl_schedule_node *isl_schedu
 	isl_schedule_tree_free(pos->tree);
 	pos->tree = tree;
 
-	return update_ancestors(pos);
+	return update_ancestors(pos, NULL, NULL);
 error:
 	isl_schedule_node_free(pos);
 	isl_schedule_tree_free(tree);
@@ -1781,6 +2225,49 @@ __isl_give isl_schedule_node *isl_schedu
 	return node;
 }
 
+/* Insert an expansion node with the given "contraction" and "expansion"
+ * between "node" and its parent.
+ * Return a pointer to the new expansion node.
+ *
+ * Typically the domain and range spaces of the expansion are different.
+ * This means that only one of them can refer to the current domain space
+ * in a consistent tree.  It is up to the caller to ensure that the tree
+ * returns to a consistent state.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_insert_expansion(
+	__isl_take isl_schedule_node *node,
+	__isl_take isl_union_pw_multi_aff *contraction,
+	__isl_take isl_union_map *expansion)
+{
+	isl_schedule_tree *tree;
+
+	if (check_insert(node) < 0)
+		node = isl_schedule_node_free(node);
+
+	tree = isl_schedule_node_get_tree(node);
+	tree = isl_schedule_tree_insert_expansion(tree, contraction, expansion);
+	node = isl_schedule_node_graft_tree(node, tree);
+
+	return node;
+}
+
+/* Insert an extension node with extension "extension" between "node" and
+ * its parent.
+ * Return a pointer to the new extension node.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_insert_extension(
+	__isl_take isl_schedule_node *node,
+	__isl_take isl_union_map *extension)
+{
+	isl_schedule_tree *tree;
+
+	tree = isl_schedule_node_get_tree(node);
+	tree = isl_schedule_tree_insert_extension(tree, extension);
+	node = isl_schedule_node_graft_tree(node, tree);
+
+	return node;
+}
+
 /* Insert a filter node with filter "filter" between "node" and its parent.
  * Return a pointer to the new filter node.
  */
@@ -1799,27 +2286,64 @@ __isl_give isl_schedule_node *isl_schedu
 	return node;
 }
 
-/* Attach the current subtree of "node" to a sequence of filter tree nodes
- * with filters described by "filters", attach this sequence
- * of filter tree nodes as children to a new tree of type "type" and
- * replace the original subtree of "node" by this new tree.
+/* Insert a guard node with guard "guard" between "node" and its parent.
+ * Return a pointer to the new guard node.
  */
-static __isl_give isl_schedule_node *isl_schedule_node_insert_children(
-	__isl_take isl_schedule_node *node,
-	enum isl_schedule_node_type type,
-	__isl_take isl_union_set_list *filters)
+__isl_give isl_schedule_node *isl_schedule_node_insert_guard(
+	__isl_take isl_schedule_node *node, __isl_take isl_set *guard)
 {
-	int i, n;
-	isl_ctx *ctx;
 	isl_schedule_tree *tree;
-	isl_schedule_tree_list *list;
 
 	if (check_insert(node) < 0)
 		node = isl_schedule_node_free(node);
 
-	if (!node || !filters)
-		goto error;
-
+	tree = isl_schedule_node_get_tree(node);
+	tree = isl_schedule_tree_insert_guard(tree, guard);
+	node = isl_schedule_node_graft_tree(node, tree);
+
+	return node;
+}
+
+/* Insert a mark node with mark identifier "mark" between "node" and
+ * its parent.
+ * Return a pointer to the new mark node.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_insert_mark(
+	__isl_take isl_schedule_node *node, __isl_take isl_id *mark)
+{
+	isl_schedule_tree *tree;
+
+	if (check_insert(node) < 0)
+		node = isl_schedule_node_free(node);
+
+	tree = isl_schedule_node_get_tree(node);
+	tree = isl_schedule_tree_insert_mark(tree, mark);
+	node = isl_schedule_node_graft_tree(node, tree);
+
+	return node;
+}
+
+/* Attach the current subtree of "node" to a sequence of filter tree nodes
+ * with filters described by "filters", attach this sequence
+ * of filter tree nodes as children to a new tree of type "type" and
+ * replace the original subtree of "node" by this new tree.
+ */
+static __isl_give isl_schedule_node *isl_schedule_node_insert_children(
+	__isl_take isl_schedule_node *node,
+	enum isl_schedule_node_type type,
+	__isl_take isl_union_set_list *filters)
+{
+	int i, n;
+	isl_ctx *ctx;
+	isl_schedule_tree *tree;
+	isl_schedule_tree_list *list;
+
+	if (check_insert(node) < 0)
+		node = isl_schedule_node_free(node);
+
+	if (!node || !filters)
+		goto error;
+
 	ctx = isl_schedule_node_get_ctx(node);
 	n = isl_union_set_list_n_union_set(filters);
 	list = isl_schedule_tree_list_alloc(ctx, n);
@@ -1955,6 +2479,483 @@ __isl_give isl_schedule_node *isl_schedu
 	return node;
 }
 
+/* Internal data structure for the group_ancestor callback.
+ *
+ * If "finished" is set, then we no longer need to modify
+ * any further ancestors.
+ *
+ * "contraction" and "expansion" represent the expansion
+ * that reflects the grouping.
+ *
+ * "domain" contains the domain elements that reach the position
+ * where the grouping is performed.  That is, it is the range
+ * of the resulting expansion.
+ * "domain_universe" is the universe of "domain".
+ * "group" is the set of group elements, i.e., the domain
+ * of the resulting expansion.
+ * "group_universe" is the universe of "group".
+ *
+ * "sched" is the schedule for the group elements, in pratice
+ * an identity mapping on "group_universe".
+ * "dim" is the dimension of "sched".
+ */
+struct isl_schedule_group_data {
+	int finished;
+
+	isl_union_map *expansion;
+	isl_union_pw_multi_aff *contraction;
+
+	isl_union_set *domain;
+	isl_union_set *domain_universe;
+	isl_union_set *group;
+	isl_union_set *group_universe;
+
+	int dim;
+	isl_multi_aff *sched;
+};
+
+/* Is domain covered by data->domain within data->domain_universe?
+ */
+static int locally_covered_by_domain(__isl_keep isl_union_set *domain,
+	struct isl_schedule_group_data *data)
+{
+	int is_subset;
+	isl_union_set *test;
+
+	test = isl_union_set_copy(domain);
+	test = isl_union_set_intersect(test,
+			    isl_union_set_copy(data->domain_universe));
+	is_subset = isl_union_set_is_subset(test, data->domain);
+	isl_union_set_free(test);
+
+	return is_subset;
+}
+
+/* Update the band tree root "tree" to refer to the group instances
+ * in data->group rather than the original domain elements in data->domain.
+ * "pos" is the position in the original schedule tree where the modified
+ * "tree" will be attached.
+ *
+ * Add the part of the identity schedule on the group instances data->sched
+ * that corresponds to this band node to the band schedule.
+ * If the domain elements that reach the node and that are part
+ * of data->domain_universe are all elements of data->domain (and therefore
+ * replaced by the group instances) then this data->domain_universe
+ * is removed from the domain of the band schedule.
+ */
+static __isl_give isl_schedule_tree *group_band(
+	__isl_take isl_schedule_tree *tree, __isl_keep isl_schedule_node *pos,
+	struct isl_schedule_group_data *data)
+{
+	isl_union_set *domain;
+	isl_multi_aff *ma;
+	isl_multi_union_pw_aff *mupa, *partial;
+	int is_covered;
+	int depth, n, has_id;
+
+	domain = isl_schedule_node_get_domain(pos);
+	is_covered = locally_covered_by_domain(domain, data);
+	if (is_covered >= 0 && is_covered) {
+		domain = isl_union_set_universe(domain);
+		domain = isl_union_set_subtract(domain,
+			    isl_union_set_copy(data->domain_universe));
+		tree = isl_schedule_tree_band_intersect_domain(tree, domain);
+	} else
+		isl_union_set_free(domain);
+	if (is_covered < 0)
+		return isl_schedule_tree_free(tree);
+	depth = isl_schedule_node_get_schedule_depth(pos);
+	n = isl_schedule_tree_band_n_member(tree);
+	ma = isl_multi_aff_copy(data->sched);
+	ma = isl_multi_aff_drop_dims(ma, isl_dim_out, 0, depth);
+	ma = isl_multi_aff_drop_dims(ma, isl_dim_out, n, data->dim - depth - n);
+	mupa = isl_multi_union_pw_aff_from_multi_aff(ma);
+	partial = isl_schedule_tree_band_get_partial_schedule(tree);
+	has_id = isl_multi_union_pw_aff_has_tuple_id(partial, isl_dim_set);
+	if (has_id < 0) {
+		partial = isl_multi_union_pw_aff_free(partial);
+	} else if (has_id) {
+		isl_id *id;
+		id = isl_multi_union_pw_aff_get_tuple_id(partial, isl_dim_set);
+		mupa = isl_multi_union_pw_aff_set_tuple_id(mupa,
+							    isl_dim_set, id);
+	}
+	partial = isl_multi_union_pw_aff_union_add(partial, mupa);
+	tree = isl_schedule_tree_band_set_partial_schedule(tree, partial);
+
+	return tree;
+}
+
+/* Drop the parameters in "uset" that are not also in "space".
+ * "n" is the number of parameters in "space".
+ */
+static __isl_give isl_union_set *union_set_drop_extra_params(
+	__isl_take isl_union_set *uset, __isl_keep isl_space *space, int n)
+{
+	int n2;
+
+	uset = isl_union_set_align_params(uset, isl_space_copy(space));
+	n2 = isl_union_set_dim(uset, isl_dim_param);
+	uset = isl_union_set_project_out(uset, isl_dim_param, n, n2 - n);
+
+	return uset;
+}
+
+/* Update the context tree root "tree" to refer to the group instances
+ * in data->group rather than the original domain elements in data->domain.
+ * "pos" is the position in the original schedule tree where the modified
+ * "tree" will be attached.
+ *
+ * We do not actually need to update "tree" since a context node only
+ * refers to the schedule space.  However, we may need to update "data"
+ * to not refer to any parameters introduced by the context node.
+ */
+static __isl_give isl_schedule_tree *group_context(
+	__isl_take isl_schedule_tree *tree, __isl_keep isl_schedule_node *pos,
+	struct isl_schedule_group_data *data)
+{
+	isl_space *space;
+	isl_union_set *domain;
+	int n1, n2;
+	int involves;
+
+	if (isl_schedule_node_get_tree_depth(pos) == 1)
+		return tree;
+
+	domain = isl_schedule_node_get_universe_domain(pos);
+	space = isl_union_set_get_space(domain);
+	isl_union_set_free(domain);
+
+	n1 = isl_space_dim(space, isl_dim_param);
+	data->expansion = isl_union_map_align_params(data->expansion, space);
+	n2 = isl_union_map_dim(data->expansion, isl_dim_param);
+
+	if (!data->expansion)
+		return isl_schedule_tree_free(tree);
+	if (n1 == n2)
+		return tree;
+
+	involves = isl_union_map_involves_dims(data->expansion,
+				isl_dim_param, n1, n2 - n1);
+	if (involves < 0)
+		return isl_schedule_tree_free(tree);
+	if (involves)
+		isl_die(isl_schedule_node_get_ctx(pos), isl_error_invalid,
+			"grouping cannot only refer to global parameters",
+			return isl_schedule_tree_free(tree));
+
+	data->expansion = isl_union_map_project_out(data->expansion,
+				isl_dim_param, n1, n2 - n1);
+	space = isl_union_map_get_space(data->expansion);
+
+	data->contraction = isl_union_pw_multi_aff_align_params(
+				data->contraction, isl_space_copy(space));
+	n2 = isl_union_pw_multi_aff_dim(data->contraction, isl_dim_param);
+	data->contraction = isl_union_pw_multi_aff_drop_dims(data->contraction,
+				isl_dim_param, n1, n2 - n1);
+
+	data->domain = union_set_drop_extra_params(data->domain, space, n1);
+	data->domain_universe =
+		union_set_drop_extra_params(data->domain_universe, space, n1);
+	data->group = union_set_drop_extra_params(data->group, space, n1);
+	data->group_universe =
+		union_set_drop_extra_params(data->group_universe, space, n1);
+
+	data->sched = isl_multi_aff_align_params(data->sched,
+				isl_space_copy(space));
+	n2 = isl_multi_aff_dim(data->sched, isl_dim_param);
+	data->sched = isl_multi_aff_drop_dims(data->sched,
+				isl_dim_param, n1, n2 - n1);
+
+	isl_space_free(space);
+
+	return tree;
+}
+
+/* Update the domain tree root "tree" to refer to the group instances
+ * in data->group rather than the original domain elements in data->domain.
+ * "pos" is the position in the original schedule tree where the modified
+ * "tree" will be attached.
+ *
+ * We first double-check that all grouped domain elements are actually
+ * part of the root domain and then replace those elements by the group
+ * instances.
+ */
+static __isl_give isl_schedule_tree *group_domain(
+	__isl_take isl_schedule_tree *tree, __isl_keep isl_schedule_node *pos,
+	struct isl_schedule_group_data *data)
+{
+	isl_union_set *domain;
+	int is_subset;
+
+	domain = isl_schedule_tree_domain_get_domain(tree);
+	is_subset = isl_union_set_is_subset(data->domain, domain);
+	isl_union_set_free(domain);
+	if (is_subset < 0)
+		return isl_schedule_tree_free(tree);
+	if (!is_subset)
+		isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal,
+			"grouped domain should be part of outer domain",
+			return isl_schedule_tree_free(tree));
+	domain = isl_schedule_tree_domain_get_domain(tree);
+	domain = isl_union_set_subtract(domain,
+				isl_union_set_copy(data->domain));
+	domain = isl_union_set_union(domain, isl_union_set_copy(data->group));
+	tree = isl_schedule_tree_domain_set_domain(tree, domain);
+
+	return tree;
+}
+
+/* Update the expansion tree root "tree" to refer to the group instances
+ * in data->group rather than the original domain elements in data->domain.
+ * "pos" is the position in the original schedule tree where the modified
+ * "tree" will be attached.
+ *
+ * Let G_1 -> D_1 be the expansion of "tree" and G_2 -> D_2 the newly
+ * introduced expansion in a descendant of "tree".
+ * We first double-check that D_2 is a subset of D_1.
+ * Then we remove D_2 from the range of G_1 -> D_1 and add the mapping
+ * G_1 -> D_1 . D_2 -> G_2.
+ * Simmilarly, we restrict the domain of the contraction to the universe
+ * of the range of the updated expansion and add G_2 -> D_2 . D_1 -> G_1,
+ * attempting to remove the domain constraints of this additional part.
+ */
+static __isl_give isl_schedule_tree *group_expansion(
+	__isl_take isl_schedule_tree *tree, __isl_keep isl_schedule_node *pos,
+	struct isl_schedule_group_data *data)
+{
+	isl_union_set *domain;
+	isl_union_map *expansion, *umap;
+	isl_union_pw_multi_aff *contraction, *upma;
+	int is_subset;
+
+	expansion = isl_schedule_tree_expansion_get_expansion(tree);
+	domain = isl_union_map_range(expansion);
+	is_subset = isl_union_set_is_subset(data->domain, domain);
+	isl_union_set_free(domain);
+	if (is_subset < 0)
+		return isl_schedule_tree_free(tree);
+	if (!is_subset)
+		isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal,
+			"grouped domain should be part "
+			"of outer expansion domain",
+			return isl_schedule_tree_free(tree));
+	expansion = isl_schedule_tree_expansion_get_expansion(tree);
+	umap = isl_union_map_from_union_pw_multi_aff(
+			isl_union_pw_multi_aff_copy(data->contraction));
+	umap = isl_union_map_apply_range(expansion, umap);
+	expansion = isl_schedule_tree_expansion_get_expansion(tree);
+	expansion = isl_union_map_subtract_range(expansion,
+				isl_union_set_copy(data->domain));
+	expansion = isl_union_map_union(expansion, umap);
+	umap = isl_union_map_universe(isl_union_map_copy(expansion));
+	domain = isl_union_map_range(umap);
+	contraction = isl_schedule_tree_expansion_get_contraction(tree);
+	umap = isl_union_map_from_union_pw_multi_aff(contraction);
+	umap = isl_union_map_apply_range(isl_union_map_copy(data->expansion),
+					umap);
+	upma = isl_union_pw_multi_aff_from_union_map(umap);
+	contraction = isl_schedule_tree_expansion_get_contraction(tree);
+	contraction = isl_union_pw_multi_aff_intersect_domain(contraction,
+								domain);
+	domain = isl_union_pw_multi_aff_domain(
+				isl_union_pw_multi_aff_copy(upma));
+	upma = isl_union_pw_multi_aff_gist(upma, domain);
+	contraction = isl_union_pw_multi_aff_union_add(contraction, upma);
+	tree = isl_schedule_tree_expansion_set_contraction_and_expansion(tree,
+							contraction, expansion);
+
+	return tree;
+}
+
+/* Update the tree root "tree" to refer to the group instances
+ * in data->group rather than the original domain elements in data->domain.
+ * "pos" is the position in the original schedule tree where the modified
+ * "tree" will be attached.
+ *
+ * If we have come across a domain or expansion node before (data->finished
+ * is set), then we no longer need perform any modifications.
+ *
+ * If "tree" is a filter, then we add data->group_universe to the filter.
+ * We also remove data->domain_universe from the filter if all the domain
+ * elements in this universe that reach the filter node are part of
+ * the elements that are being grouped by data->expansion.
+ * If "tree" is a band, domain or expansion, then it is handled
+ * in a separate function.
+ */
+static __isl_give isl_schedule_tree *group_ancestor(
+	__isl_take isl_schedule_tree *tree, __isl_keep isl_schedule_node *pos,
+	void *user)
+{
+	struct isl_schedule_group_data *data = user;
+	isl_union_set *domain;
+	int is_covered;
+
+	if (!tree || !pos)
+		return isl_schedule_tree_free(tree);
+
+	if (data->finished)
+		return tree;
+
+	switch (isl_schedule_tree_get_type(tree)) {
+	case isl_schedule_node_error:
+		return isl_schedule_tree_free(tree);
+	case isl_schedule_node_extension:
+		isl_die(isl_schedule_tree_get_ctx(tree), isl_error_unsupported,
+			"grouping not allowed in extended tree",
+			return isl_schedule_tree_free(tree));
+	case isl_schedule_node_band:
+		tree = group_band(tree, pos, data);
+		break;
+	case isl_schedule_node_context:
+		tree = group_context(tree, pos, data);
+		break;
+	case isl_schedule_node_domain:
+		tree = group_domain(tree, pos, data);
+		data->finished = 1;
+		break;
+	case isl_schedule_node_filter:
+		domain = isl_schedule_node_get_domain(pos);
+		is_covered = locally_covered_by_domain(domain, data);
+		isl_union_set_free(domain);
+		if (is_covered < 0)
+			return isl_schedule_tree_free(tree);
+		domain = isl_schedule_tree_filter_get_filter(tree);
+		if (is_covered)
+			domain = isl_union_set_subtract(domain,
+				    isl_union_set_copy(data->domain_universe));
+		domain = isl_union_set_union(domain,
+				    isl_union_set_copy(data->group_universe));
+		tree = isl_schedule_tree_filter_set_filter(tree, domain);
+		break;
+	case isl_schedule_node_expansion:
+		tree = group_expansion(tree, pos, data);
+		data->finished = 1;
+		break;
+	case isl_schedule_node_leaf:
+	case isl_schedule_node_guard:
+	case isl_schedule_node_mark:
+	case isl_schedule_node_sequence:
+	case isl_schedule_node_set:
+		break;
+	}
+
+	return tree;
+}
+
+/* Group the domain elements that reach "node" into instances
+ * of a single statement with identifier "group_id".
+ * In particular, group the domain elements according to their
+ * prefix schedule.
+ *
+ * That is, introduce an expansion node with as contraction
+ * the prefix schedule (with the target space replaced by "group_id")
+ * and as expansion the inverse of this contraction (with its range
+ * intersected with the domain elements that reach "node").
+ * The outer nodes are then modified to refer to the group instances
+ * instead of the original domain elements.
+ *
+ * No instance of "group_id" is allowed to reach "node" prior
+ * to the grouping.
+ * No ancestor of "node" is allowed to be an extension node.
+ *
+ * Return a pointer to original node in tree, i.e., the child
+ * of the newly introduced expansion node.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_group(
+	__isl_take isl_schedule_node *node, __isl_take isl_id *group_id)
+{
+	struct isl_schedule_group_data data = { 0 };
+	isl_space *space;
+	isl_union_set *domain;
+	isl_union_pw_multi_aff *contraction;
+	isl_union_map *expansion;
+	int disjoint;
+
+	if (!node || !group_id)
+		goto error;
+	if (check_insert(node) < 0)
+		goto error;
+
+	domain = isl_schedule_node_get_domain(node);
+	data.domain = isl_union_set_copy(domain);
+	data.domain_universe = isl_union_set_copy(domain);
+	data.domain_universe = isl_union_set_universe(data.domain_universe);
+
+	data.dim = isl_schedule_node_get_schedule_depth(node);
+	if (data.dim == 0) {
+		isl_ctx *ctx;
+		isl_set *set;
+		isl_union_set *group;
+		isl_union_map *univ;
+
+		ctx = isl_schedule_node_get_ctx(node);
+		space = isl_space_set_alloc(ctx, 0, 0);
+		space = isl_space_set_tuple_id(space, isl_dim_set, group_id);
+		set = isl_set_universe(isl_space_copy(space));
+		group = isl_union_set_from_set(set);
+		expansion = isl_union_map_from_domain_and_range(domain, group);
+		univ = isl_union_map_universe(isl_union_map_copy(expansion));
+		contraction = isl_union_pw_multi_aff_from_union_map(univ);
+		expansion = isl_union_map_reverse(expansion);
+	} else {
+		isl_multi_union_pw_aff *prefix;
+		isl_union_set *univ;
+
+		prefix =
+		isl_schedule_node_get_prefix_schedule_multi_union_pw_aff(node);
+		prefix = isl_multi_union_pw_aff_set_tuple_id(prefix,
+							isl_dim_set, group_id);
+		space = isl_multi_union_pw_aff_get_space(prefix);
+		contraction = isl_union_pw_multi_aff_from_multi_union_pw_aff(
+							prefix);
+		univ = isl_union_set_universe(isl_union_set_copy(domain));
+		contraction =
+		    isl_union_pw_multi_aff_intersect_domain(contraction, univ);
+		expansion = isl_union_map_from_union_pw_multi_aff(
+				    isl_union_pw_multi_aff_copy(contraction));
+		expansion = isl_union_map_reverse(expansion);
+		expansion = isl_union_map_intersect_range(expansion, domain);
+	}
+	space = isl_space_map_from_set(space);
+	data.sched = isl_multi_aff_identity(space);
+	data.group = isl_union_map_domain(isl_union_map_copy(expansion));
+	data.group = isl_union_set_coalesce(data.group);
+	data.group_universe = isl_union_set_copy(data.group);
+	data.group_universe = isl_union_set_universe(data.group_universe);
+	data.expansion = isl_union_map_copy(expansion);
+	data.contraction = isl_union_pw_multi_aff_copy(contraction);
+	node = isl_schedule_node_insert_expansion(node, contraction, expansion);
+
+	disjoint = isl_union_set_is_disjoint(data.domain_universe,
+					    data.group_universe);
+
+	node = update_ancestors(node, &group_ancestor, &data);
+
+	isl_union_set_free(data.domain);
+	isl_union_set_free(data.domain_universe);
+	isl_union_set_free(data.group);
+	isl_union_set_free(data.group_universe);
+	isl_multi_aff_free(data.sched);
+	isl_union_map_free(data.expansion);
+	isl_union_pw_multi_aff_free(data.contraction);
+
+	node = isl_schedule_node_child(node, 0);
+
+	if (!node || disjoint < 0)
+		return isl_schedule_node_free(node);
+	if (!disjoint)
+		isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+			"group instances already reach node",
+			isl_schedule_node_free(node));
+
+	return node;
+error:
+	isl_schedule_node_free(node);
+	isl_id_free(group_id);
+	return NULL;
+}
+
 /* Compute the gist of the given band node with respect to "context".
  */
 __isl_give isl_schedule_node *isl_schedule_node_band_gist(
@@ -1968,18 +2969,86 @@ __isl_give isl_schedule_node *isl_schedu
 }
 
 /* Internal data structure for isl_schedule_node_gist.
- * "filters" contains an element for each outer filter node
- * with respect to the current position, each representing
- * the intersection of the previous element and the filter on the filter node.
+ * "n_expansion" is the number of outer expansion nodes
+ * with respect to the current position
+ * "filters" contains an element for each outer filter, expansion or
+ * extension node with respect to the current position, each representing
+ * the intersection of the previous element and the filter on the filter node
+ * or the expansion/extension of the previous element.
  * The first element in the original context passed to isl_schedule_node_gist.
  */
 struct isl_node_gist_data {
+	int n_expansion;
 	isl_union_set_list *filters;
 };
 
+/* Enter the expansion node "node" during a isl_schedule_node_gist traversal.
+ *
+ * In particular, add an extra element to data->filters containing
+ * the expansion of the previous element and replace the expansion
+ * and contraction on "node" by the gist with respect to these filters.
+ * Also keep track of the fact that we have entered another expansion.
+ */
+static __isl_give isl_schedule_node *gist_enter_expansion(
+	__isl_take isl_schedule_node *node, struct isl_node_gist_data *data)
+{
+	int n;
+	isl_union_set *inner;
+	isl_union_map *expansion;
+	isl_union_pw_multi_aff *contraction;
+
+	data->n_expansion++;
+
+	n = isl_union_set_list_n_union_set(data->filters);
+	inner = isl_union_set_list_get_union_set(data->filters, n - 1);
+	expansion = isl_schedule_node_expansion_get_expansion(node);
+	inner = isl_union_set_apply(inner, expansion);
+
+	contraction = isl_schedule_node_expansion_get_contraction(node);
+	contraction = isl_union_pw_multi_aff_gist(contraction,
+						isl_union_set_copy(inner));
+
+	data->filters = isl_union_set_list_add(data->filters, inner);
+
+	inner = isl_union_set_list_get_union_set(data->filters, n - 1);
+	expansion = isl_schedule_node_expansion_get_expansion(node);
+	expansion = isl_union_map_gist_domain(expansion, inner);
+	node = isl_schedule_node_expansion_set_contraction_and_expansion(node,
+						contraction, expansion);
+
+	return node;
+}
+
+/* Enter the extension node "node" during a isl_schedule_node_gist traversal.
+ *
+ * In particular, add an extra element to data->filters containing
+ * the union of the previous element with the additional domain elements
+ * introduced by the extension.
+ */
+static __isl_give isl_schedule_node *gist_enter_extension(
+	__isl_take isl_schedule_node *node, struct isl_node_gist_data *data)
+{
+	int n;
+	isl_union_set *inner, *extra;
+	isl_union_map *extension;
+
+	n = isl_union_set_list_n_union_set(data->filters);
+	inner = isl_union_set_list_get_union_set(data->filters, n - 1);
+	extension = isl_schedule_node_extension_get_extension(node);
+	extra = isl_union_map_range(extension);
+	inner = isl_union_set_union(inner, extra);
+
+	data->filters = isl_union_set_list_add(data->filters, inner);
+
+	return node;
+}
+
 /* Can we finish gisting at this node?
  * That is, is the filter on the current filter node a subset of
  * the original context passed to isl_schedule_node_gist?
+ * If we have gone through any expansions, then we cannot perform
+ * this test since the current domain elements are incomparable
+ * to the domain elements in the original context.
  */
 static int gist_done(__isl_keep isl_schedule_node *node,
 	struct isl_node_gist_data *data)
@@ -1987,6 +3056,9 @@ static int gist_done(__isl_keep isl_sche
 	isl_union_set *filter, *outer;
 	int subset;
 
+	if (data->n_expansion != 0)
+		return 0;
+
 	filter = isl_schedule_node_filter_get_filter(node);
 	outer = isl_union_set_list_get_union_set(data->filters, 0);
 	subset = isl_union_set_is_subset(filter, outer);
@@ -2018,6 +3090,12 @@ static int gist_done(__isl_keep isl_sche
  * If the new element in the "filters" list is empty, then no elements
  * can reach the descendants of the current filter node.  The subtree
  * underneath the filter node is therefore removed.
+ *
+ * Each expansion node we come across is handled by
+ * gist_enter_expansion.
+ *
+ * Each extension node we come across is handled by
+ * gist_enter_extension.
  */
 static __isl_give isl_schedule_node *gist_enter(
 	__isl_take isl_schedule_node *node, void *user)
@@ -2032,10 +3110,18 @@ static __isl_give isl_schedule_node *gis
 		switch (isl_schedule_node_get_type(node)) {
 		case isl_schedule_node_error:
 			return isl_schedule_node_free(node);
+		case isl_schedule_node_expansion:
+			node = gist_enter_expansion(node, data);
+			continue;
+		case isl_schedule_node_extension:
+			node = gist_enter_extension(node, data);
+			continue;
 		case isl_schedule_node_band:
 		case isl_schedule_node_context:
 		case isl_schedule_node_domain:
+		case isl_schedule_node_guard:
 		case isl_schedule_node_leaf:
+		case isl_schedule_node_mark:
 		case isl_schedule_node_sequence:
 		case isl_schedule_node_set:
 			continue;
@@ -2080,6 +3166,13 @@ static __isl_give isl_schedule_node *gis
  * the node.  There is no need to compute any gist here, since we
  * already did that when we entered the node.
  *
+ * If the current node is an expansion, then we decrement
+ * the number of outer expansions and remove the element
+ * in data->filters that was added by gist_enter_expansion.
+ *
+ * If the current node is an extension, then remove the element
+ * in data->filters that was added by gist_enter_extension.
+ *
  * If the current node is a band node, then we compute the gist of
  * the band node with respect to the intersection of the original context
  * and the intermediate filters.
@@ -2105,6 +3198,9 @@ static __isl_give isl_schedule_node *gis
 	switch (isl_schedule_node_get_type(node)) {
 	case isl_schedule_node_error:
 		return isl_schedule_node_free(node);
+	case isl_schedule_node_expansion:
+		data->n_expansion--;
+	case isl_schedule_node_extension:
 	case isl_schedule_node_filter:
 		n = isl_union_set_list_n_union_set(data->filters);
 		data->filters = isl_union_set_list_drop(data->filters,
@@ -2153,7 +3249,9 @@ static __isl_give isl_schedule_node *gis
 		break;
 	case isl_schedule_node_context:
 	case isl_schedule_node_domain:
+	case isl_schedule_node_guard:
 	case isl_schedule_node_leaf:
+	case isl_schedule_node_mark:
 		break;
 	}
 
@@ -2176,6 +3274,7 @@ __isl_give isl_schedule_node *isl_schedu
 {
 	struct isl_node_gist_data data;
 
+	data.n_expansion = 0;
 	data.filters = isl_union_set_list_from_union_set(context);
 	node = traverse(node, &gist_enter, &gist_leave, &data);
 	isl_union_set_list_free(data.filters);
@@ -2228,6 +3327,837 @@ error:
 	return NULL;
 }
 
+/* Internal data structure for isl_schedule_node_get_subtree_expansion.
+ * "expansions" contains a list of accumulated expansions
+ * for each outer expansion, set or sequence node.  The first element
+ * in the list is an identity mapping on the reaching domain elements.
+ * "res" collects the results.
+ */
+struct isl_subtree_expansion_data {
+	isl_union_map_list *expansions;
+	isl_union_map *res;
+};
+
+/* Callback for "traverse" to enter a node and to move
+ * to the deepest initial subtree that should be traversed
+ * by isl_schedule_node_get_subtree_expansion.
+ *
+ * Whenever we come across an expansion node, the last element
+ * of data->expansions is combined with the expansion
+ * on the expansion node.
+ *
+ * Whenever we come across a filter node that is the child
+ * of a set or sequence node, data->expansions is extended
+ * with a new element that restricts the previous element
+ * to the elements selected by the filter.
+ * The previous element can then be reused while backtracking.
+ */
+static __isl_give isl_schedule_node *subtree_expansion_enter(
+	__isl_take isl_schedule_node *node, void *user)
+{
+	struct isl_subtree_expansion_data *data = user;
+
+	do {
+		enum isl_schedule_node_type type;
+		isl_union_set *filter;
+		isl_union_map *inner, *expansion;
+		int n;
+
+		switch (isl_schedule_node_get_type(node)) {
+		case isl_schedule_node_error:
+			return isl_schedule_node_free(node);
+		case isl_schedule_node_filter:
+			type = isl_schedule_node_get_parent_type(node);
+			if (type != isl_schedule_node_set &&
+			    type != isl_schedule_node_sequence)
+				break;
+			filter = isl_schedule_node_filter_get_filter(node);
+			n = isl_union_map_list_n_union_map(data->expansions);
+			inner =
+			    isl_union_map_list_get_union_map(data->expansions,
+								n - 1);
+			inner = isl_union_map_intersect_range(inner, filter);
+			data->expansions =
+			    isl_union_map_list_add(data->expansions, inner);
+			break;
+		case isl_schedule_node_expansion:
+			n = isl_union_map_list_n_union_map(data->expansions);
+			expansion =
+				isl_schedule_node_expansion_get_expansion(node);
+			inner =
+			    isl_union_map_list_get_union_map(data->expansions,
+								n - 1);
+			inner = isl_union_map_apply_range(inner, expansion);
+			data->expansions =
+			    isl_union_map_list_set_union_map(data->expansions,
+								n - 1, inner);
+			break;
+		case isl_schedule_node_band:
+		case isl_schedule_node_context:
+		case isl_schedule_node_domain:
+		case isl_schedule_node_extension:
+		case isl_schedule_node_guard:
+		case isl_schedule_node_leaf:
+		case isl_schedule_node_mark:
+		case isl_schedule_node_sequence:
+		case isl_schedule_node_set:
+			break;
+		}
+	} while (isl_schedule_node_has_children(node) &&
+		(node = isl_schedule_node_first_child(node)) != NULL);
+
+	return node;
+}
+
+/* Callback for "traverse" to leave a node for
+ * isl_schedule_node_get_subtree_expansion.
+ *
+ * If we come across a filter node that is the child
+ * of a set or sequence node, then we remove the element
+ * of data->expansions that was added in subtree_expansion_enter.
+ *
+ * If we reach a leaf node, then the accumulated expansion is
+ * added to data->res.
+ */
+static __isl_give isl_schedule_node *subtree_expansion_leave(
+	__isl_take isl_schedule_node *node, void *user)
+{
+	struct isl_subtree_expansion_data *data = user;
+	int n;
+	isl_union_map *inner;
+	enum isl_schedule_node_type type;
+
+	switch (isl_schedule_node_get_type(node)) {
+	case isl_schedule_node_error:
+		return isl_schedule_node_free(node);
+	case isl_schedule_node_filter:
+		type = isl_schedule_node_get_parent_type(node);
+		if (type != isl_schedule_node_set &&
+		    type != isl_schedule_node_sequence)
+			break;
+		n = isl_union_map_list_n_union_map(data->expansions);
+		data->expansions = isl_union_map_list_drop(data->expansions,
+							n - 1, 1);
+		break;
+	case isl_schedule_node_leaf:
+		n = isl_union_map_list_n_union_map(data->expansions);
+		inner = isl_union_map_list_get_union_map(data->expansions,
+							n - 1);
+		data->res = isl_union_map_union(data->res, inner);
+		break;
+	case isl_schedule_node_band:
+	case isl_schedule_node_context:
+	case isl_schedule_node_domain:
+	case isl_schedule_node_expansion:
+	case isl_schedule_node_extension:
+	case isl_schedule_node_guard:
+	case isl_schedule_node_mark:
+	case isl_schedule_node_sequence:
+	case isl_schedule_node_set:
+		break;
+	}
+
+	return node;
+}
+
+/* Return a mapping from the domain elements that reach "node"
+ * to the corresponding domain elements in the leaves of the subtree
+ * rooted at "node" obtained by composing the intermediate expansions.
+ *
+ * We start out with an identity mapping between the domain elements
+ * that reach "node" and compose it with all the expansions
+ * on a path from "node" to a leaf while traversing the subtree.
+ * Within the children of an a sequence or set node, the
+ * accumulated expansion is restricted to the elements selected
+ * by the filter child.
+ */
+__isl_give isl_union_map *isl_schedule_node_get_subtree_expansion(
+	__isl_keep isl_schedule_node *node)
+{
+	struct isl_subtree_expansion_data data;
+	isl_space *space;
+	isl_union_set *domain;
+	isl_union_map *expansion;
+
+	if (!node)
+		return NULL;
+
+	domain = isl_schedule_node_get_universe_domain(node);
+	space = isl_union_set_get_space(domain);
+	expansion = isl_union_set_identity(domain);
+	data.res = isl_union_map_empty(space);
+	data.expansions = isl_union_map_list_from_union_map(expansion);
+
+	node = isl_schedule_node_copy(node);
+	node = traverse(node, &subtree_expansion_enter,
+			&subtree_expansion_leave, &data);
+	if (!node)
+		data.res = isl_union_map_free(data.res);
+	isl_schedule_node_free(node);
+
+	isl_union_map_list_free(data.expansions);
+
+	return data.res;
+}
+
+/* Internal data structure for isl_schedule_node_get_subtree_contraction.
+ * "contractions" contains a list of accumulated contractions
+ * for each outer expansion, set or sequence node.  The first element
+ * in the list is an identity mapping on the reaching domain elements.
+ * "res" collects the results.
+ */
+struct isl_subtree_contraction_data {
+	isl_union_pw_multi_aff_list *contractions;
+	isl_union_pw_multi_aff *res;
+};
+
+/* Callback for "traverse" to enter a node and to move
+ * to the deepest initial subtree that should be traversed
+ * by isl_schedule_node_get_subtree_contraction.
+ *
+ * Whenever we come across an expansion node, the last element
+ * of data->contractions is combined with the contraction
+ * on the expansion node.
+ *
+ * Whenever we come across a filter node that is the child
+ * of a set or sequence node, data->contractions is extended
+ * with a new element that restricts the previous element
+ * to the elements selected by the filter.
+ * The previous element can then be reused while backtracking.
+ */
+static __isl_give isl_schedule_node *subtree_contraction_enter(
+	__isl_take isl_schedule_node *node, void *user)
+{
+	struct isl_subtree_contraction_data *data = user;
+
+	do {
+		enum isl_schedule_node_type type;
+		isl_union_set *filter;
+		isl_union_pw_multi_aff *inner, *contraction;
+		int n;
+
+		switch (isl_schedule_node_get_type(node)) {
+		case isl_schedule_node_error:
+			return isl_schedule_node_free(node);
+		case isl_schedule_node_filter:
+			type = isl_schedule_node_get_parent_type(node);
+			if (type != isl_schedule_node_set &&
+			    type != isl_schedule_node_sequence)
+				break;
+			filter = isl_schedule_node_filter_get_filter(node);
+			n = isl_union_pw_multi_aff_list_n_union_pw_multi_aff(
+						data->contractions);
+			inner =
+			    isl_union_pw_multi_aff_list_get_union_pw_multi_aff(
+						data->contractions, n - 1);
+			inner = isl_union_pw_multi_aff_intersect_domain(inner,
+								filter);
+			data->contractions =
+			    isl_union_pw_multi_aff_list_add(data->contractions,
+								inner);
+			break;
+		case isl_schedule_node_expansion:
+			n = isl_union_pw_multi_aff_list_n_union_pw_multi_aff(
+						data->contractions);
+			contraction =
+			    isl_schedule_node_expansion_get_contraction(node);
+			inner =
+			    isl_union_pw_multi_aff_list_get_union_pw_multi_aff(
+						data->contractions, n - 1);
+			inner =
+			    isl_union_pw_multi_aff_pullback_union_pw_multi_aff(
+						inner, contraction);
+			data->contractions =
+			    isl_union_pw_multi_aff_list_set_union_pw_multi_aff(
+					data->contractions, n - 1, inner);
+			break;
+		case isl_schedule_node_band:
+		case isl_schedule_node_context:
+		case isl_schedule_node_domain:
+		case isl_schedule_node_extension:
+		case isl_schedule_node_guard:
+		case isl_schedule_node_leaf:
+		case isl_schedule_node_mark:
+		case isl_schedule_node_sequence:
+		case isl_schedule_node_set:
+			break;
+		}
+	} while (isl_schedule_node_has_children(node) &&
+		(node = isl_schedule_node_first_child(node)) != NULL);
+
+	return node;
+}
+
+/* Callback for "traverse" to leave a node for
+ * isl_schedule_node_get_subtree_contraction.
+ *
+ * If we come across a filter node that is the child
+ * of a set or sequence node, then we remove the element
+ * of data->contractions that was added in subtree_contraction_enter.
+ *
+ * If we reach a leaf node, then the accumulated contraction is
+ * added to data->res.
+ */
+static __isl_give isl_schedule_node *subtree_contraction_leave(
+	__isl_take isl_schedule_node *node, void *user)
+{
+	struct isl_subtree_contraction_data *data = user;
+	int n;
+	isl_union_pw_multi_aff *inner;
+	enum isl_schedule_node_type type;
+
+	switch (isl_schedule_node_get_type(node)) {
+	case isl_schedule_node_error:
+		return isl_schedule_node_free(node);
+	case isl_schedule_node_filter:
+		type = isl_schedule_node_get_parent_type(node);
+		if (type != isl_schedule_node_set &&
+		    type != isl_schedule_node_sequence)
+			break;
+		n = isl_union_pw_multi_aff_list_n_union_pw_multi_aff(
+						data->contractions);
+		data->contractions =
+			isl_union_pw_multi_aff_list_drop(data->contractions,
+							n - 1, 1);
+		break;
+	case isl_schedule_node_leaf:
+		n = isl_union_pw_multi_aff_list_n_union_pw_multi_aff(
+						data->contractions);
+		inner = isl_union_pw_multi_aff_list_get_union_pw_multi_aff(
+						data->contractions, n - 1);
+		data->res = isl_union_pw_multi_aff_union_add(data->res, inner);
+		break;
+	case isl_schedule_node_band:
+	case isl_schedule_node_context:
+	case isl_schedule_node_domain:
+	case isl_schedule_node_expansion:
+	case isl_schedule_node_extension:
+	case isl_schedule_node_guard:
+	case isl_schedule_node_mark:
+	case isl_schedule_node_sequence:
+	case isl_schedule_node_set:
+		break;
+	}
+
+	return node;
+}
+
+/* Return a mapping from the domain elements in the leaves of the subtree
+ * rooted at "node" to the corresponding domain elements that reach "node"
+ * obtained by composing the intermediate contractions.
+ *
+ * We start out with an identity mapping between the domain elements
+ * that reach "node" and compose it with all the contractions
+ * on a path from "node" to a leaf while traversing the subtree.
+ * Within the children of an a sequence or set node, the
+ * accumulated contraction is restricted to the elements selected
+ * by the filter child.
+ */
+__isl_give isl_union_pw_multi_aff *isl_schedule_node_get_subtree_contraction(
+	__isl_keep isl_schedule_node *node)
+{
+	struct isl_subtree_contraction_data data;
+	isl_space *space;
+	isl_union_set *domain;
+	isl_union_pw_multi_aff *contraction;
+
+	if (!node)
+		return NULL;
+
+	domain = isl_schedule_node_get_universe_domain(node);
+	space = isl_union_set_get_space(domain);
+	contraction = isl_union_set_identity_union_pw_multi_aff(domain);
+	data.res = isl_union_pw_multi_aff_empty(space);
+	data.contractions =
+	    isl_union_pw_multi_aff_list_from_union_pw_multi_aff(contraction);
+
+	node = isl_schedule_node_copy(node);
+	node = traverse(node, &subtree_contraction_enter,
+			&subtree_contraction_leave, &data);
+	if (!node)
+		data.res = isl_union_pw_multi_aff_free(data.res);
+	isl_schedule_node_free(node);
+
+	isl_union_pw_multi_aff_list_free(data.contractions);
+
+	return data.res;
+}
+
+/* Do the nearest "n" ancestors of "node" have the types given in "types"
+ * (starting at the parent of "node")?
+ */
+static int has_ancestors(__isl_keep isl_schedule_node *node,
+	int n, enum isl_schedule_node_type *types)
+{
+	int i, n_ancestor;
+
+	if (!node)
+		return -1;
+
+	n_ancestor = isl_schedule_tree_list_n_schedule_tree(node->ancestors);
+	if (n_ancestor < n)
+		return 0;
+
+	for (i = 0; i < n; ++i) {
+		isl_schedule_tree *tree;
+		int correct_type;
+
+		tree = isl_schedule_tree_list_get_schedule_tree(node->ancestors,
+							    n_ancestor - 1 - i);
+		if (!tree)
+			return -1;
+		correct_type = isl_schedule_tree_get_type(tree) == types[i];
+		isl_schedule_tree_free(tree);
+		if (!correct_type)
+			return 0;
+	}
+
+	return 1;
+}
+
+/* Given a node "node" that appears in an extension (i.e., it is the child
+ * of a filter in a sequence inside an extension node), are the spaces
+ * of the extension specified by "extension" disjoint from those
+ * of both the original extension and the domain elements that reach
+ * that original extension?
+ */
+static int is_disjoint_extension(__isl_keep isl_schedule_node *node,
+	__isl_keep isl_union_map *extension)
+{
+	isl_union_map *old;
+	isl_union_set *domain;
+	int empty;
+
+	node = isl_schedule_node_copy(node);
+	node = isl_schedule_node_parent(node);
+	node = isl_schedule_node_parent(node);
+	node = isl_schedule_node_parent(node);
+	old = isl_schedule_node_extension_get_extension(node);
+	domain = isl_schedule_node_get_universe_domain(node);
+	isl_schedule_node_free(node);
+	old = isl_union_map_universe(old);
+	domain = isl_union_set_union(domain, isl_union_map_range(old));
+	extension = isl_union_map_copy(extension);
+	extension = isl_union_map_intersect_range(extension, domain);
+	empty = isl_union_map_is_empty(extension);
+	isl_union_map_free(extension);
+
+	return empty;
+}
+
+/* Given a node "node" that is governed by an extension node, extend
+ * that extension node with "extension".
+ *
+ * In particular, "node" is the child of a filter in a sequence that
+ * is in turn a child of an extension node.  Extend that extension node
+ * with "extension".
+ *
+ * Return a pointer to the parent of the original node (i.e., a filter).
+ */
+static __isl_give isl_schedule_node *extend_extension(
+	__isl_take isl_schedule_node *node, __isl_take isl_union_map *extension)
+{
+	int pos;
+	int disjoint;
+	isl_union_map *node_extension;
+
+	node = isl_schedule_node_parent(node);
+	pos = isl_schedule_node_get_child_position(node);
+	node = isl_schedule_node_parent(node);
+	node = isl_schedule_node_parent(node);
+	node_extension = isl_schedule_node_extension_get_extension(node);
+	disjoint = isl_union_map_is_disjoint(extension, node_extension);
+	extension = isl_union_map_union(extension, node_extension);
+	node = isl_schedule_node_extension_set_extension(node, extension);
+	node = isl_schedule_node_child(node, 0);
+	node = isl_schedule_node_child(node, pos);
+
+	if (disjoint < 0)
+		return isl_schedule_node_free(node);
+	if (!node)
+		return NULL;
+	if (!disjoint)
+		isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+			"extension domain should be disjoint from earlier "
+			"extensions", return isl_schedule_node_free(node));
+
+	return node;
+}
+
+/* Return the universe of "uset" if this universe is disjoint from "ref".
+ * Otherwise, return "uset".
+ *
+ * Also check if "uset" itself is disjoint from "ref", reporting
+ * an error if it is not.
+ */
+static __isl_give isl_union_set *replace_by_universe_if_disjoint(
+	__isl_take isl_union_set *uset, __isl_keep isl_union_set *ref)
+{
+	int disjoint;
+	isl_union_set *universe;
+
+	disjoint = isl_union_set_is_disjoint(uset, ref);
+	if (disjoint < 0)
+		return isl_union_set_free(uset);
+	if (!disjoint)
+		isl_die(isl_union_set_get_ctx(uset), isl_error_invalid,
+			"extension domain should be disjoint from "
+			"current domain", return isl_union_set_free(uset));
+
+	universe = isl_union_set_universe(isl_union_set_copy(uset));
+	disjoint = isl_union_set_is_disjoint(universe, ref);
+	if (disjoint >= 0 && disjoint) {
+		isl_union_set_free(uset);
+		return universe;
+	}
+	isl_union_set_free(universe);
+
+	if (disjoint < 0)
+		return isl_union_set_free(uset);
+	return uset;
+}
+
+/* Insert an extension node on top of "node" with extension "extension".
+ * In addition, insert a filter that separates node from the extension
+ * between the extension node and "node".
+ * Return a pointer to the inserted filter node.
+ *
+ * If "node" already appears in an extension (i.e., if it is the child
+ * of a filter in a sequence inside an extension node), then extend that
+ * extension with "extension" instead.
+ * In this case, a pointer to the original filter node is returned.
+ * Note that if some of the elements in the new extension live in the
+ * same space as those of the original extension or the domain elements
+ * reaching the original extension, then we insert a new extension anyway.
+ * Otherwise, we would have to adjust the filters in the sequence child
+ * of the extension to ensure that the elements in the new extension
+ * are filtered out.
+ */
+static __isl_give isl_schedule_node *insert_extension(
+	__isl_take isl_schedule_node *node, __isl_take isl_union_map *extension)
+{
+	enum isl_schedule_node_type ancestors[] =
+		{ isl_schedule_node_filter, isl_schedule_node_sequence,
+		  isl_schedule_node_extension };
+	isl_union_set *domain;
+	isl_union_set *filter;
+	int in_ext;
+
+	in_ext = has_ancestors(node, 3, ancestors);
+	if (in_ext < 0)
+		goto error;
+	if (in_ext) {
+		int disjoint;
+
+		disjoint = is_disjoint_extension(node, extension);
+		if (disjoint < 0)
+			goto error;
+		if (disjoint)
+			return extend_extension(node, extension);
+	}
+
+	filter = isl_schedule_node_get_domain(node);
+	domain = isl_union_map_range(isl_union_map_copy(extension));
+	filter = replace_by_universe_if_disjoint(filter, domain);
+	isl_union_set_free(domain);
+
+	node = isl_schedule_node_insert_filter(node, filter);
+	node = isl_schedule_node_insert_extension(node, extension);
+	node = isl_schedule_node_child(node, 0);
+	return node;
+error:
+	isl_schedule_node_free(node);
+	isl_union_map_free(extension);
+	return NULL;
+}
+
+/* Replace the subtree that "node" points to by "tree" (which has
+ * a sequence root with two children), except if the parent of "node"
+ * is a sequence as well, in which case "tree" is spliced at the position
+ * of "node" in its parent.
+ * Return a pointer to the child of the "tree_pos" (filter) child of "tree"
+ * in the updated schedule tree.
+ */
+static __isl_give isl_schedule_node *graft_or_splice(
+	__isl_take isl_schedule_node *node, __isl_take isl_schedule_tree *tree,
+	int tree_pos)
+{
+	int pos;
+
+	if (isl_schedule_node_get_parent_type(node) ==
+	    isl_schedule_node_sequence) {
+		pos = isl_schedule_node_get_child_position(node);
+		node = isl_schedule_node_parent(node);
+		node = isl_schedule_node_sequence_splice(node, pos, tree);
+	} else {
+		pos = 0;
+		node = isl_schedule_node_graft_tree(node, tree);
+	}
+	node = isl_schedule_node_child(node, pos + tree_pos);
+	node = isl_schedule_node_child(node, 0);
+
+	return node;
+}
+
+/* Insert a node "graft" into the schedule tree of "node" such that it
+ * is executed before (if "before" is set) or after (if "before" is not set)
+ * the node that "node" points to.
+ * The root of "graft" is an extension node.
+ * Return a pointer to the node that "node" pointed to.
+ *
+ * We first insert an extension node on top of "node" (or extend
+ * the extension node if there already is one), with a filter on "node"
+ * separating it from the extension.
+ * We then insert a filter in the graft to separate it from the original
+ * domain elements and combine the original and new tree in a sequence.
+ * If we have extended an extension node, then the children of this
+ * sequence are spliced in the sequence of the extended extension
+ * at the position where "node" appears in the original extension.
+ * Otherwise, the sequence pair is attached to the new extension node.
+ */
+static __isl_give isl_schedule_node *graft_extension(
+	__isl_take isl_schedule_node *node, __isl_take isl_schedule_node *graft,
+	int before)
+{
+	isl_union_map *extension;
+	isl_union_set *graft_domain;
+	isl_union_set *node_domain;
+	isl_schedule_tree *tree, *tree_graft;
+
+	extension = isl_schedule_node_extension_get_extension(graft);
+	graft_domain = isl_union_map_range(isl_union_map_copy(extension));
+	node_domain = isl_schedule_node_get_universe_domain(node);
+	node = insert_extension(node, extension);
+
+	graft_domain = replace_by_universe_if_disjoint(graft_domain,
+							node_domain);
+	isl_union_set_free(node_domain);
+
+	tree = isl_schedule_node_get_tree(node);
+	if (!isl_schedule_node_has_children(graft)) {
+		tree_graft = isl_schedule_tree_from_filter(graft_domain);
+	} else {
+		graft = isl_schedule_node_child(graft, 0);
+		tree_graft = isl_schedule_node_get_tree(graft);
+		tree_graft = isl_schedule_tree_insert_filter(tree_graft,
+								graft_domain);
+	}
+	if (before)
+		tree = isl_schedule_tree_sequence_pair(tree_graft, tree);
+	else
+		tree = isl_schedule_tree_sequence_pair(tree, tree_graft);
+	node = graft_or_splice(node, tree, before);
+
+	isl_schedule_node_free(graft);
+
+	return node;
+}
+
+/* Replace the root domain node of "node" by an extension node suitable
+ * for insertion at "pos".
+ * That is, create an extension node that maps the outer band nodes
+ * at "pos" to the domain of the root node of "node" and attach
+ * the child of this root node to the extension node.
+ */
+static __isl_give isl_schedule_node *extension_from_domain(
+	__isl_take isl_schedule_node *node, __isl_keep isl_schedule_node *pos)
+{
+	isl_union_set *universe;
+	isl_union_set *domain;
+	isl_union_map *ext;
+	int depth;
+	int anchored;
+	isl_space *space;
+	isl_schedule_node *res;
+	isl_schedule_tree *tree;
+
+	anchored = isl_schedule_node_is_subtree_anchored(node);
+	if (anchored < 0)
+		return isl_schedule_node_free(node);
+	if (anchored)
+		isl_die(isl_schedule_node_get_ctx(node), isl_error_unsupported,
+			"cannot graft anchored tree with domain root",
+			return isl_schedule_node_free(node));
+
+	depth = isl_schedule_node_get_schedule_depth(pos);
+	domain = isl_schedule_node_domain_get_domain(node);
+	space = isl_union_set_get_space(domain);
+	space = isl_space_set_from_params(space);
+	space = isl_space_add_dims(space, isl_dim_set, depth);
+	universe = isl_union_set_from_set(isl_set_universe(space));
+	ext = isl_union_map_from_domain_and_range(universe, domain);
+	res = isl_schedule_node_from_extension(ext);
+	node = isl_schedule_node_child(node, 0);
+	if (!node)
+		return isl_schedule_node_free(res);
+	if (!isl_schedule_tree_is_leaf(node->tree)) {
+		tree = isl_schedule_node_get_tree(node);
+		res = isl_schedule_node_child(res, 0);
+		res = isl_schedule_node_graft_tree(res, tree);
+		res = isl_schedule_node_parent(res);
+	}
+	isl_schedule_node_free(node);
+
+	return res;
+}
+
+/* Insert a node "graft" into the schedule tree of "node" such that it
+ * is executed before (if "before" is set) or after (if "before" is not set)
+ * the node that "node" points to.
+ * The root of "graft" may be either a domain or an extension node.
+ * In the latter case, the domain of the extension needs to correspond
+ * to the outer band nodes of "node".
+ * The elements of the domain or the range of the extension may not
+ * intersect with the domain elements that reach "node".
+ * The schedule tree of "graft" may not be anchored.
+ *
+ * The schedule tree of "node" is modified to include an extension node
+ * corresponding to the root node of "graft" as a child of the original
+ * parent of "node".  The original node that "node" points to and the
+ * child of the root node of "graft" are attached to this extension node
+ * through a sequence, with appropriate filters and with the child
+ * of "graft" appearing before or after the original "node".
+ *
+ * If "node" already appears inside a sequence that is the child of
+ * an extension node and if the spaces of the new domain elements
+ * do not overlap with those of the original domain elements,
+ * then that extension node is extended with the new extension
+ * rather than introducing a new segment of extension and sequence nodes.
+ *
+ * Return a pointer to the same node in the modified tree that
+ * "node" pointed to in the original tree.
+ */
+static __isl_give isl_schedule_node *isl_schedule_node_graft_before_or_after(
+	__isl_take isl_schedule_node *node, __isl_take isl_schedule_node *graft,
+	int before)
+{
+	if (!node || !graft)
+		goto error;
+	if (check_insert(node) < 0)
+		goto error;
+
+	if (isl_schedule_node_get_type(graft) == isl_schedule_node_domain)
+		graft = extension_from_domain(graft, node);
+
+	if (isl_schedule_node_get_type(graft) != isl_schedule_node_extension)
+		isl_die(isl_schedule_node_get_ctx(node), isl_error_invalid,
+			"expecting domain or extension as root of graft",
+			goto error);
+
+	return graft_extension(node, graft, before);
+error:
+	isl_schedule_node_free(node);
+	isl_schedule_node_free(graft);
+	return NULL;
+}
+
+/* Insert a node "graft" into the schedule tree of "node" such that it
+ * is executed before the node that "node" points to.
+ * The root of "graft" may be either a domain or an extension node.
+ * In the latter case, the domain of the extension needs to correspond
+ * to the outer band nodes of "node".
+ * The elements of the domain or the range of the extension may not
+ * intersect with the domain elements that reach "node".
+ * The schedule tree of "graft" may not be anchored.
+ *
+ * Return a pointer to the same node in the modified tree that
+ * "node" pointed to in the original tree.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_graft_before(
+	__isl_take isl_schedule_node *node, __isl_take isl_schedule_node *graft)
+{
+	return isl_schedule_node_graft_before_or_after(node, graft, 1);
+}
+
+/* Insert a node "graft" into the schedule tree of "node" such that it
+ * is executed after the node that "node" points to.
+ * The root of "graft" may be either a domain or an extension node.
+ * In the latter case, the domain of the extension needs to correspond
+ * to the outer band nodes of "node".
+ * The elements of the domain or the range of the extension may not
+ * intersect with the domain elements that reach "node".
+ * The schedule tree of "graft" may not be anchored.
+ *
+ * Return a pointer to the same node in the modified tree that
+ * "node" pointed to in the original tree.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_graft_after(
+	__isl_take isl_schedule_node *node,
+	__isl_take isl_schedule_node *graft)
+{
+	return isl_schedule_node_graft_before_or_after(node, graft, 0);
+}
+
+/* Split the domain elements that reach "node" into those that satisfy
+ * "filter" and those that do not.  Arrange for the first subset to be
+ * executed after the second subset.
+ * Return a pointer to the tree corresponding to the second subset,
+ * except when this subset is empty in which case the original pointer
+ * is returned.
+ * If both subsets are non-empty, then a sequence node is introduced
+ * to impose the order.  If the grandparent of the original node was
+ * itself a sequence, then the original child is replaced by two children
+ * in this sequence instead.
+ * The children in the sequence are copies of the original subtree,
+ * simplified with respect to their filters.
+ */
+__isl_give isl_schedule_node *isl_schedule_node_order_after(
+	__isl_take isl_schedule_node *node, __isl_take isl_union_set *filter)
+{
+	enum isl_schedule_node_type ancestors[] =
+		{ isl_schedule_node_filter, isl_schedule_node_sequence };
+	isl_union_set *node_domain, *node_filter = NULL;
+	isl_schedule_node *node2;
+	isl_schedule_tree *tree1, *tree2;
+	int empty1, empty2;
+	int in_seq;
+
+	if (!node || !filter)
+		goto error;
+	if (check_insert(node) < 0)
+		goto error;
+
+	in_seq = has_ancestors(node, 2, ancestors);
+	if (in_seq < 0)
+		goto error;
+	if (in_seq)
+		node = isl_schedule_node_parent(node);
+	node_domain = isl_schedule_node_get_domain(node);
+	filter = isl_union_set_gist(filter, isl_union_set_copy(node_domain));
+	node_filter = isl_union_set_copy(node_domain);
+	node_filter = isl_union_set_subtract(node_filter,
+						isl_union_set_copy(filter));
+	node_filter = isl_union_set_gist(node_filter, node_domain);
+	empty1 = isl_union_set_is_empty(filter);
+	empty2 = isl_union_set_is_empty(node_filter);
+	if (empty1 < 0 || empty2 < 0)
+		goto error;
+	if (empty1 || empty2) {
+		isl_union_set_free(filter);
+		isl_union_set_free(node_filter);
+		return node;
+	}
+
+	node2 = isl_schedule_node_copy(node);
+	node = isl_schedule_node_gist(node, isl_union_set_copy(node_filter));
+	node2 = isl_schedule_node_gist(node2, isl_union_set_copy(filter));
+	tree1 = isl_schedule_node_get_tree(node);
+	tree2 = isl_schedule_node_get_tree(node2);
+	isl_schedule_node_free(node2);
+	tree1 = isl_schedule_tree_insert_filter(tree1, node_filter);
+	tree2 = isl_schedule_tree_insert_filter(tree2, filter);
+	tree1 = isl_schedule_tree_sequence_pair(tree1, tree2);
+
+	node = graft_or_splice(node, tree1, 0);
+
+	return node;
+error:
+	isl_schedule_node_free(node);
+	isl_union_set_free(filter);
+	isl_union_set_free(node_filter);
+	return NULL;
+}
+
 /* Reset the user pointer on all identifiers of parameters and tuples
  * in the schedule node "node".
  */
@@ -2261,6 +4191,7 @@ __isl_give isl_schedule_node *isl_schedu
  * by the function represented by "upma".
  * In other words, plug in "upma" in the iteration domains
  * of schedule node "node".
+ * We currently do not handle expansion nodes.
  *
  * Note that this is only a helper function for
  * isl_schedule_pullback_union_pw_multi_aff.  In order to maintain consistency,

Modified: polly/trunk/lib/External/isl/isl_schedule_node_private.h
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/lib/External/isl/isl_schedule_node_private.h?rev=233567&r1=233566&r2=233567&view=diff
==============================================================================
--- polly/trunk/lib/External/isl/isl_schedule_node_private.h (original)
+++ polly/trunk/lib/External/isl/isl_schedule_node_private.h Mon Mar 30 12:28:57 2015
@@ -45,4 +45,12 @@ __isl_give isl_schedule_node *isl_schedu
 __isl_give isl_schedule_node *isl_schedule_node_domain_intersect_domain(
 	__isl_take isl_schedule_node *node, __isl_take isl_union_set *domain);
 
+__isl_give isl_schedule_node *isl_schedule_node_insert_expansion(
+	__isl_take isl_schedule_node *node,
+	__isl_take isl_union_pw_multi_aff *contraction,
+	__isl_take isl_union_map *expansion);
+__isl_give isl_schedule_node *isl_schedule_node_insert_extension(
+	__isl_take isl_schedule_node *node,
+	__isl_take isl_union_map *extension);
+
 #endif

Modified: polly/trunk/lib/External/isl/isl_schedule_read.c
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/lib/External/isl/isl_schedule_read.c?rev=233567&r1=233566&r2=233567&view=diff
==============================================================================
--- polly/trunk/lib/External/isl/isl_schedule_read.c (original)
+++ polly/trunk/lib/External/isl/isl_schedule_read.c Mon Mar 30 12:28:57 2015
@@ -13,9 +13,14 @@ enum isl_schedule_key {
 	isl_schedule_key_child,
 	isl_schedule_key_coincident,
 	isl_schedule_key_context,
+	isl_schedule_key_contraction,
 	isl_schedule_key_domain,
+	isl_schedule_key_expansion,
+	isl_schedule_key_extension,
 	isl_schedule_key_filter,
+	isl_schedule_key_guard,
 	isl_schedule_key_leaf,
+	isl_schedule_key_mark,
 	isl_schedule_key_options,
 	isl_schedule_key_permutable,
 	isl_schedule_key_schedule,
@@ -48,12 +53,22 @@ static enum isl_schedule_key extract_key
 		key = isl_schedule_key_coincident;
 	else if (!strcmp(name, "context"))
 		key = isl_schedule_key_context;
+	else if (!strcmp(name, "contraction"))
+		key = isl_schedule_key_contraction;
 	else if (!strcmp(name, "domain"))
 		key = isl_schedule_key_domain;
+	else if (!strcmp(name, "expansion"))
+		key = isl_schedule_key_expansion;
+	else if (!strcmp(name, "extension"))
+		key = isl_schedule_key_extension;
 	else if (!strcmp(name, "filter"))
 		key = isl_schedule_key_filter;
+	else if (!strcmp(name, "guard"))
+		key = isl_schedule_key_guard;
 	else if (!strcmp(name, "leaf"))
 		key = isl_schedule_key_leaf;
+	else if (!strcmp(name, "mark"))
+		key = isl_schedule_key_mark;
 	else if (!strcmp(name, "options"))
 		key = isl_schedule_key_options;
 	else if (!strcmp(name, "schedule"))
@@ -192,6 +207,132 @@ error:
 	return NULL;
 }
 
+/* Read a subtree with expansion root node from "s".
+ */
+static __isl_give isl_schedule_tree *read_expansion(isl_stream *s)
+{
+	isl_ctx *ctx;
+	isl_union_pw_multi_aff *contraction = NULL;
+	isl_union_map *expansion = NULL;
+	isl_schedule_tree *tree = NULL;
+	int more;
+
+	ctx = isl_stream_get_ctx(s);
+
+	do {
+		struct isl_token *tok;
+		enum isl_schedule_key key;
+		char *str;
+
+		key = get_key(s);
+		if (isl_stream_yaml_next(s) < 0)
+			goto error;
+
+		switch (key) {
+		case isl_schedule_key_contraction:
+			isl_union_pw_multi_aff_free(contraction);
+			tok = isl_stream_next_token(s);
+			str = isl_token_get_str(ctx, tok);
+			contraction = isl_union_pw_multi_aff_read_from_str(ctx,
+									str);
+			free(str);
+			isl_token_free(tok);
+			if (!contraction)
+				goto error;
+			break;
+		case isl_schedule_key_expansion:
+			isl_union_map_free(expansion);
+			tok = isl_stream_next_token(s);
+			str = isl_token_get_str(ctx, tok);
+			expansion = isl_union_map_read_from_str(ctx, str);
+			free(str);
+			isl_token_free(tok);
+			if (!expansion)
+				goto error;
+			break;
+		case isl_schedule_key_child:
+			isl_schedule_tree_free(tree);
+			tree = isl_stream_read_schedule_tree(s);
+			if (!tree)
+				goto error;
+			break;
+		default:
+			isl_die(ctx, isl_error_invalid, "unexpected key",
+				goto error);
+		}
+	} while ((more = isl_stream_yaml_next(s)) > 0);
+
+	if (more < 0)
+		goto error;
+
+	if (!contraction)
+		isl_die(ctx, isl_error_invalid, "missing contraction",
+			goto error);
+	if (!expansion)
+		isl_die(ctx, isl_error_invalid, "missing expansion",
+			goto error);
+
+	if (!tree)
+		return isl_schedule_tree_from_expansion(contraction, expansion);
+	return isl_schedule_tree_insert_expansion(tree, contraction, expansion);
+error:
+	isl_schedule_tree_free(tree);
+	isl_union_pw_multi_aff_free(contraction);
+	isl_union_map_free(expansion);
+	return NULL;
+}
+
+/* Read a subtree with extension root node from "s".
+ */
+static __isl_give isl_schedule_tree *read_extension(isl_stream *s)
+{
+	isl_union_map *extension = NULL;
+	isl_schedule_tree *tree;
+	isl_ctx *ctx;
+	struct isl_token *tok;
+	enum isl_schedule_key key;
+	char *str;
+	int more;
+
+	ctx = isl_stream_get_ctx(s);
+
+	key = get_key(s);
+
+	if (isl_stream_yaml_next(s) < 0)
+		return NULL;
+
+	tok = isl_stream_next_token(s);
+	if (!tok) {
+		isl_stream_error(s, NULL, "unexpected EOF");
+		return NULL;
+	}
+	str = isl_token_get_str(ctx, tok);
+	extension = isl_union_map_read_from_str(ctx, str);
+	free(str);
+	isl_token_free(tok);
+
+	more = isl_stream_yaml_next(s);
+	if (more < 0)
+		goto error;
+	if (!more) {
+		tree = isl_schedule_tree_from_extension(extension);
+	} else {
+		key = get_key(s);
+		if (key != isl_schedule_key_child)
+			isl_die(ctx, isl_error_invalid, "expecting child",
+				goto error);
+		if (isl_stream_yaml_next(s) < 0)
+			goto error;
+		tree = isl_stream_read_schedule_tree(s);
+		tree = isl_schedule_tree_insert_extension(tree, extension);
+	}
+
+	return tree;
+error:
+	isl_union_map_free(extension);
+	return NULL;
+}
+
 /* Read a subtree with filter root node from "s".
  */
 static __isl_give isl_schedule_tree *read_filter(__isl_keep isl_stream *s)
@@ -243,6 +384,109 @@ error:
 	return NULL;
 }
 
+/* Read a subtree with guard root node from "s".
+ */
+static __isl_give isl_schedule_tree *read_guard(isl_stream *s)
+{
+	isl_set *guard = NULL;
+	isl_schedule_tree *tree;
+	isl_ctx *ctx;
+	struct isl_token *tok;
+	enum isl_schedule_key key;
+	char *str;
+	int more;
+
+	ctx = isl_stream_get_ctx(s);
+
+	key = get_key(s);
+
+	if (isl_stream_yaml_next(s) < 0)
+		return NULL;
+
+	tok = isl_stream_next_token(s);
+	if (!tok) {
+		isl_stream_error(s, NULL, "unexpected EOF");
+		return NULL;
+	}
+	str = isl_token_get_str(ctx, tok);
+	guard = isl_set_read_from_str(ctx, str);
+	free(str);
+	isl_token_free(tok);
+
+	more = isl_stream_yaml_next(s);
+	if (more < 0)
+		goto error;
+	if (!more) {
+		tree = isl_schedule_tree_from_guard(guard);
+	} else {
+		key = get_key(s);
+		if (key != isl_schedule_key_child)
+			isl_die(ctx, isl_error_invalid, "expecting child",
+				goto error);
+		if (isl_stream_yaml_next(s) < 0)
+			goto error;
+		tree = isl_stream_read_schedule_tree(s);
+		tree = isl_schedule_tree_insert_guard(tree, guard);
+	}
+
+	return tree;
+error:
+	isl_set_free(guard);
+	return NULL;
+}
+
+/* Read a subtree with mark root node from "s".
+ */
+static __isl_give isl_schedule_tree *read_mark(isl_stream *s)
+{
+	isl_id *mark;
+	isl_schedule_tree *tree;
+	isl_ctx *ctx;
+	struct isl_token *tok;
+	enum isl_schedule_key key;
+	char *str;
+	int more;
+
+	ctx = isl_stream_get_ctx(s);
+
+	key = get_key(s);
+
+	if (isl_stream_yaml_next(s) < 0)
+		return NULL;
+
+	tok = isl_stream_next_token(s);
+	if (!tok) {
+		isl_stream_error(s, NULL, "unexpected EOF");
+		return NULL;
+	}
+	str = isl_token_get_str(ctx, tok);
+	mark = isl_id_alloc(ctx, str, NULL);
+	free(str);
+	isl_token_free(tok);
+
+	more = isl_stream_yaml_next(s);
+	if (more < 0)
+		goto error;
+	if (!more) {
+		isl_die(ctx, isl_error_invalid, "expecting child",
+			goto error);
+	} else {
+		key = get_key(s);
+		if (key != isl_schedule_key_child)
+			isl_die(ctx, isl_error_invalid, "expecting child",
+				goto error);
+		if (isl_stream_yaml_next(s) < 0)
+			goto error;
+		tree = isl_stream_read_schedule_tree(s);
+		tree = isl_schedule_tree_insert_mark(tree, mark);
+	}
+
+	return tree;
+error:
+	isl_id_free(mark);
+	return NULL;
+}
+
 /* Read a sequence of integers from "s" (representing the coincident
  * property of a band node).
  */
@@ -480,13 +724,26 @@ static __isl_give isl_schedule_tree *isl
 	case isl_schedule_key_domain:
 		tree = read_domain(s);
 		break;
+	case isl_schedule_key_contraction:
+	case isl_schedule_key_expansion:
+		tree = read_expansion(s);
+		break;
+	case isl_schedule_key_extension:
+		tree = read_extension(s);
+		break;
 	case isl_schedule_key_filter:
 		tree = read_filter(s);
 		break;
+	case isl_schedule_key_guard:
+		tree = read_guard(s);
+		break;
 	case isl_schedule_key_leaf:
 		isl_token_free(isl_stream_next_token(s));
 		tree = isl_schedule_tree_leaf(isl_stream_get_ctx(s));
 		break;
+	case isl_schedule_key_mark:
+		tree = read_mark(s);
+		break;
 	case isl_schedule_key_sequence:
 		tree = read_sequence(s);
 		break;

Modified: polly/trunk/lib/External/isl/isl_schedule_tree.c
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/lib/External/isl/isl_schedule_tree.c?rev=233567&r1=233566&r2=233567&view=diff
==============================================================================
--- polly/trunk/lib/External/isl/isl_schedule_tree.c (original)
+++ polly/trunk/lib/External/isl/isl_schedule_tree.c Mon Mar 30 12:28:57 2015
@@ -95,11 +95,33 @@ __isl_take isl_schedule_tree *isl_schedu
 		if (!dup->domain)
 			return isl_schedule_tree_free(dup);
 		break;
+	case isl_schedule_node_expansion:
+		dup->contraction =
+			isl_union_pw_multi_aff_copy(tree->contraction);
+		dup->expansion = isl_union_map_copy(tree->expansion);
+		if (!dup->contraction || !dup->expansion)
+			return isl_schedule_tree_free(dup);
+		break;
+	case isl_schedule_node_extension:
+		dup->extension = isl_union_map_copy(tree->extension);
+		if (!dup->extension)
+			return isl_schedule_tree_free(dup);
+		break;
 	case isl_schedule_node_filter:
 		dup->filter = isl_union_set_copy(tree->filter);
 		if (!dup->filter)
 			return isl_schedule_tree_free(dup);
 		break;
+	case isl_schedule_node_guard:
+		dup->guard = isl_set_copy(tree->guard);
+		if (!dup->guard)
+			return isl_schedule_tree_free(dup);
+		break;
+	case isl_schedule_node_mark:
+		dup->mark = isl_id_copy(tree->mark);
+		if (!dup->mark)
+			return isl_schedule_tree_free(dup);
+		break;
 	case isl_schedule_node_leaf:
 	case isl_schedule_node_sequence:
 	case isl_schedule_node_set:
@@ -180,9 +202,22 @@ __isl_null isl_schedule_tree *isl_schedu
 	case isl_schedule_node_domain:
 		isl_union_set_free(tree->domain);
 		break;
+	case isl_schedule_node_expansion:
+		isl_union_pw_multi_aff_free(tree->contraction);
+		isl_union_map_free(tree->expansion);
+		break;
+	case isl_schedule_node_extension:
+		isl_union_map_free(tree->extension);
+		break;
 	case isl_schedule_node_filter:
 		isl_union_set_free(tree->filter);
 		break;
+	case isl_schedule_node_guard:
+		isl_set_free(tree->guard);
+		break;
+	case isl_schedule_node_mark:
+		isl_id_free(tree->mark);
+		break;
 	case isl_schedule_node_sequence:
 	case isl_schedule_node_set:
 	case isl_schedule_node_error:
@@ -280,6 +315,62 @@ error:
 	return NULL;
 }
 
+/* Create a new expansion schedule tree with the given contraction and
+ * expansion and no children.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_from_expansion(
+	__isl_take isl_union_pw_multi_aff *contraction,
+	__isl_take isl_union_map *expansion)
+{
+	isl_ctx *ctx;
+	isl_schedule_tree *tree;
+
+	if (!contraction || !expansion)
+		goto error;
+
+	ctx = isl_union_map_get_ctx(expansion);
+	tree = isl_schedule_tree_alloc(ctx, isl_schedule_node_expansion);
+	if (!tree)
+		goto error;
+
+	tree->contraction = contraction;
+	tree->expansion = expansion;
+
+	return tree;
+error:
+	isl_union_pw_multi_aff_free(contraction);
+	isl_union_map_free(expansion);
+	return NULL;
+}
+
+/* Create a new extension schedule tree with the given extension and
+ * no children.
+ * Since the domain of the extension refers to the outer schedule dimension,
+ * the tree is anchored.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_from_extension(
+	__isl_take isl_union_map *extension)
+{
+	isl_ctx *ctx;
+	isl_schedule_tree *tree;
+
+	if (!extension)
+		return NULL;
+
+	ctx = isl_union_map_get_ctx(extension);
+	tree = isl_schedule_tree_alloc(ctx, isl_schedule_node_extension);
+	if (!tree)
+		goto error;
+
+	tree->extension = extension;
+	tree->anchored = 1;
+
+	return tree;
+error:
+	isl_union_map_free(extension);
+	return NULL;
+}
+
 /* Create a new filter schedule tree with the given filter and no children.
  */
 __isl_give isl_schedule_tree *isl_schedule_tree_from_filter(
@@ -304,6 +395,58 @@ error:
 	return NULL;
 }
 
+/* Create a new guard schedule tree with the given guard and no children.
+ * Since the guard references the outer schedule dimension,
+ * the tree is anchored.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_from_guard(
+	__isl_take isl_set *guard)
+{
+	isl_ctx *ctx;
+	isl_schedule_tree *tree;
+
+	if (!guard)
+		return NULL;
+
+	ctx = isl_set_get_ctx(guard);
+	tree = isl_schedule_tree_alloc(ctx, isl_schedule_node_guard);
+	if (!tree)
+		goto error;
+
+	tree->guard = guard;
+	tree->anchored = 1;
+
+	return tree;
+error:
+	isl_set_free(guard);
+	return NULL;
+}
+
+/* Create a new mark schedule tree with the given mark identifier and
+ * no children.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_from_mark(
+	__isl_take isl_id *mark)
+{
+	isl_ctx *ctx;
+	isl_schedule_tree *tree;
+
+	if (!mark)
+		return NULL;
+
+	ctx = isl_id_get_ctx(mark);
+	tree = isl_schedule_tree_alloc(ctx, isl_schedule_node_mark);
+	if (!tree)
+		goto error;
+
+	tree->mark = mark;
+
+	return tree;
+error:
+	isl_id_free(mark);
+	return NULL;
+}
+
 /* Does "tree" have any node that depends on its position
  * in the complete schedule tree?
  */
@@ -315,7 +458,7 @@ int isl_schedule_tree_is_subtree_anchore
 /* Does the root node of "tree" depend on its position in the complete
  * schedule tree?
  * Band nodes may be anchored depending on the associated AST build options.
- * Context nodes are always anchored.
+ * Context, extension and guard nodes are always anchored.
  */
 int isl_schedule_tree_is_anchored(__isl_keep isl_schedule_tree *tree)
 {
@@ -328,10 +471,14 @@ int isl_schedule_tree_is_anchored(__isl_
 	case isl_schedule_node_band:
 		return isl_schedule_band_is_anchored(tree->band);
 	case isl_schedule_node_context:
+	case isl_schedule_node_extension:
+	case isl_schedule_node_guard:
 		return 1;
 	case isl_schedule_node_domain:
+	case isl_schedule_node_expansion:
 	case isl_schedule_node_filter:
 	case isl_schedule_node_leaf:
+	case isl_schedule_node_mark:
 	case isl_schedule_node_sequence:
 	case isl_schedule_node_set:
 		return 0;
@@ -444,6 +591,19 @@ error:
 	return NULL;
 }
 
+/* Construct a tree with a sequence root node and as children
+ * "tree1" and "tree2".
+ * If the root of one (or both) of the input trees is itself a sequence,
+ * then the tree is replaced by its children.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_sequence_pair(
+	__isl_take isl_schedule_tree *tree1,
+	__isl_take isl_schedule_tree *tree2)
+{
+	return isl_schedule_tree_from_pair(isl_schedule_node_sequence,
+						tree1, tree2);
+}
+
 /* Return the isl_ctx to which "tree" belongs.
  */
 isl_ctx *isl_schedule_tree_get_ctx(__isl_keep isl_schedule_tree *tree)
@@ -486,9 +646,26 @@ int isl_schedule_tree_plain_is_equal(__i
 	case isl_schedule_node_domain:
 		equal = isl_union_set_is_equal(tree1->domain, tree2->domain);
 		break;
+	case isl_schedule_node_expansion:
+		equal = isl_union_map_is_equal(tree1->expansion,
+						tree2->expansion);
+		if (equal >= 0 && equal)
+			equal = isl_union_pw_multi_aff_plain_is_equal(
+				    tree1->contraction, tree2->contraction);
+		break;
+	case isl_schedule_node_extension:
+		equal = isl_union_map_is_equal(tree1->extension,
+						tree2->extension);
+		break;
 	case isl_schedule_node_filter:
 		equal = isl_union_set_is_equal(tree1->filter, tree2->filter);
 		break;
+	case isl_schedule_node_guard:
+		equal = isl_set_is_equal(tree1->guard, tree2->guard);
+		break;
+	case isl_schedule_node_mark:
+		equal = tree1->mark == tree2->mark;
+		break;
 	case isl_schedule_node_leaf:
 	case isl_schedule_node_sequence:
 	case isl_schedule_node_set:
@@ -710,6 +887,32 @@ __isl_give isl_schedule_tree *isl_schedu
 	return isl_schedule_tree_replace_child(res, 0, tree);
 }
 
+/* Create a new expansion schedule tree with the given contraction and
+ * expansion and with "tree" as single child.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_insert_expansion(
+	__isl_take isl_schedule_tree *tree,
+	__isl_take isl_union_pw_multi_aff *contraction,
+	__isl_take isl_union_map *expansion)
+{
+	isl_schedule_tree *res;
+
+	res = isl_schedule_tree_from_expansion(contraction, expansion);
+	return isl_schedule_tree_replace_child(res, 0, tree);
+}
+
+/* Create a new extension schedule tree with the given extension and
+ * with "tree" as single child.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_insert_extension(
+	__isl_take isl_schedule_tree *tree, __isl_take isl_union_map *extension)
+{
+	isl_schedule_tree *res;
+
+	res = isl_schedule_tree_from_extension(extension);
+	return isl_schedule_tree_replace_child(res, 0, tree);
+}
+
 /* Create a new filter schedule tree with the given filter and single child.
  *
  * If the root of "tree" is itself a filter node, then the two
@@ -762,6 +965,30 @@ error:
 	return NULL;
 }
 
+/* Create a new guard schedule tree with the given guard and
+ * with "tree" as single child.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_insert_guard(
+	__isl_take isl_schedule_tree *tree, __isl_take isl_set *guard)
+{
+	isl_schedule_tree *res;
+
+	res = isl_schedule_tree_from_guard(guard);
+	return isl_schedule_tree_replace_child(res, 0, tree);
+}
+
+/* Create a new mark schedule tree with the given mark identifier and
+ * single child.
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_insert_mark(
+	__isl_take isl_schedule_tree *tree, __isl_take isl_id *mark)
+{
+	isl_schedule_tree *res;
+
+	res = isl_schedule_tree_from_mark(mark);
+	return isl_schedule_tree_replace_child(res, 0, tree);
+}
+
 /* Return the number of members in the band tree root.
  */
 unsigned isl_schedule_tree_band_n_member(__isl_keep isl_schedule_tree *tree)
@@ -868,6 +1095,30 @@ __isl_give isl_space *isl_schedule_tree_
 	return isl_schedule_band_get_space(tree->band);
 }
 
+/* Intersect the domain of the band schedule of the band tree root
+ * with "domain".
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_band_intersect_domain(
+	__isl_take isl_schedule_tree *tree, __isl_take isl_union_set *domain)
+{
+	if (!tree || !domain)
+		goto error;
+
+	if (tree->type != isl_schedule_node_band)
+		isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+			"not a band node", goto error);
+
+	tree->band = isl_schedule_band_intersect_domain(tree->band, domain);
+	if (!tree->band)
+		return isl_schedule_tree_free(tree);
+
+	return tree;
+error:
+	isl_schedule_tree_free(tree);
+	isl_union_set_free(domain);
+	return NULL;
+}
+
 /* Return the schedule of the band tree root in isolation.
  */
 __isl_give isl_multi_union_pw_aff *isl_schedule_tree_band_get_partial_schedule(
@@ -883,6 +1134,29 @@ __isl_give isl_multi_union_pw_aff *isl_s
 	return isl_schedule_band_get_partial_schedule(tree->band);
 }
 
+/* Replace the schedule of the band tree root by "schedule".
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_band_set_partial_schedule(
+	__isl_take isl_schedule_tree *tree,
+	__isl_take isl_multi_union_pw_aff *schedule)
+{
+	tree = isl_schedule_tree_cow(tree);
+	if (!tree || !schedule)
+		goto error;
+
+	if (tree->type != isl_schedule_node_band)
+		isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+			"not a band node", return NULL);
+	tree->band = isl_schedule_band_set_partial_schedule(tree->band,
+								schedule);
+
+	return tree;
+error:
+	isl_schedule_tree_free(tree);
+	isl_multi_union_pw_aff_free(schedule);
+	return NULL;
+}
+
 /* Return the loop AST generation type for the band member
  * of the band tree root at position "pos".
  */
@@ -1063,6 +1337,103 @@ error:
 	return NULL;
 }
 
+/* Return the contraction of the expansion tree root.
+ */
+__isl_give isl_union_pw_multi_aff *isl_schedule_tree_expansion_get_contraction(
+	__isl_keep isl_schedule_tree *tree)
+{
+	if (!tree)
+		return NULL;
+
+	if (tree->type != isl_schedule_node_expansion)
+		isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+			"not an expansion node", return NULL);
+
+	return isl_union_pw_multi_aff_copy(tree->contraction);
+}
+
+/* Return the expansion of the expansion tree root.
+ */
+__isl_give isl_union_map *isl_schedule_tree_expansion_get_expansion(
+	__isl_keep isl_schedule_tree *tree)
+{
+	if (!tree)
+		return NULL;
+
+	if (tree->type != isl_schedule_node_expansion)
+		isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+			"not an expansion node", return NULL);
+
+	return isl_union_map_copy(tree->expansion);
+}
+
+/* Replace the contraction and the expansion of the expansion tree root "tree"
+ * by "contraction" and "expansion".
+ */
+__isl_give isl_schedule_tree *
+isl_schedule_tree_expansion_set_contraction_and_expansion(
+	__isl_take isl_schedule_tree *tree,
+	__isl_take isl_union_pw_multi_aff *contraction,
+	__isl_take isl_union_map *expansion)
+{
+	tree = isl_schedule_tree_cow(tree);
+	if (!tree || !contraction || !expansion)
+		goto error;
+
+	if (tree->type != isl_schedule_node_expansion)
+		isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+			"not an expansion node", return NULL);
+
+	isl_union_pw_multi_aff_free(tree->contraction);
+	tree->contraction = contraction;
+	isl_union_map_free(tree->expansion);
+	tree->expansion = expansion;
+
+	return tree;
+error:
+	isl_schedule_tree_free(tree);
+	isl_union_pw_multi_aff_free(contraction);
+	isl_union_map_free(expansion);
+	return NULL;
+}
+
+/* Return the extension of the extension tree root.
+ */
+__isl_give isl_union_map *isl_schedule_tree_extension_get_extension(
+	__isl_take isl_schedule_tree *tree)
+{
+	if (!tree)
+		return NULL;
+
+	if (tree->type != isl_schedule_node_extension)
+		isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+			"not an extension node", return NULL);
+
+	return isl_union_map_copy(tree->extension);
+}
+
+/* Replace the extension of extension tree root "tree" by "extension".
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_extension_set_extension(
+	__isl_take isl_schedule_tree *tree, __isl_take isl_union_map *extension)
+{
+	tree = isl_schedule_tree_cow(tree);
+	if (!tree || !extension)
+		goto error;
+
+	if (tree->type != isl_schedule_node_extension)
+		isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+			"not an extension node", return NULL);
+	isl_union_map_free(tree->extension);
+	tree->extension = extension;
+
+	return tree;
+error:
+	isl_schedule_tree_free(tree);
+	isl_union_map_free(extension);
+	return NULL;
+}
+
 /* Return the filter of the filter tree root.
  */
 __isl_give isl_union_set *isl_schedule_tree_filter_get_filter(
@@ -1101,6 +1472,36 @@ error:
 	return NULL;
 }
 
+/* Return the guard of the guard tree root.
+ */
+__isl_give isl_set *isl_schedule_tree_guard_get_guard(
+	__isl_take isl_schedule_tree *tree)
+{
+	if (!tree)
+		return NULL;
+
+	if (tree->type != isl_schedule_node_guard)
+		isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+			"not a guard node", return NULL);
+
+	return isl_set_copy(tree->guard);
+}
+
+/* Return the mark identifier of the mark tree root "tree".
+ */
+__isl_give isl_id *isl_schedule_tree_mark_get_id(
+	__isl_keep isl_schedule_tree *tree)
+{
+	if (!tree)
+		return NULL;
+
+	if (tree->type != isl_schedule_node_mark)
+		isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+			"not a mark node", return NULL);
+
+	return isl_id_copy(tree->mark);
+}
+
 /* Set dim to the range dimension of "map" and abort the search.
  */
 static int set_range_dim(__isl_take isl_map *map, void *user)
@@ -1168,6 +1569,9 @@ static __isl_give isl_union_map *append_
  *
  * We do not want to skip leaf or error nodes because there is
  * no point in looking any deeper from these nodes.
+ * We can only extract partial iteration domain information
+ * from an extension node, but extension nodes are not supported
+ * by the caller and it will error out on them.
  */
 static int domain_less(__isl_keep isl_schedule_tree *tree)
 {
@@ -1178,10 +1582,14 @@ static int domain_less(__isl_keep isl_sc
 	case isl_schedule_node_band:
 		return isl_schedule_tree_band_n_member(tree) == 0;
 	case isl_schedule_node_context:
+	case isl_schedule_node_guard:
+	case isl_schedule_node_mark:
 		return 1;
 	case isl_schedule_node_leaf:
 	case isl_schedule_node_error:
 	case isl_schedule_node_domain:
+	case isl_schedule_node_expansion:
+	case isl_schedule_node_extension:
 	case isl_schedule_node_filter:
 	case isl_schedule_node_set:
 	case isl_schedule_node_sequence:
@@ -1370,6 +1778,8 @@ static __isl_give isl_union_map *subtree
  * Otherwise, we extend the schedule map with the partial schedule
  * corresponding to the root of the tree and then continue with
  * the single child of this root.
+ * In the special case of an expansion, the schedule map is "extended"
+ * by applying the expansion to the domain of the schedule map.
  */
 static __isl_give isl_union_map *subtree_schedule_extend(
 	__isl_keep isl_schedule_tree *tree, __isl_take isl_union_map *outer)
@@ -1384,7 +1794,14 @@ static __isl_give isl_union_map *subtree
 	switch (tree->type) {
 	case isl_schedule_node_error:
 		return isl_union_map_free(outer);
+	case isl_schedule_node_extension:
+		isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+			"cannot construct subtree schedule of tree "
+			"with extension nodes",
+			return isl_union_map_free(outer));
 	case isl_schedule_node_context:
+	case isl_schedule_node_guard:
+	case isl_schedule_node_mark:
 		return subtree_schedule_extend_child(tree, outer);
 	case isl_schedule_node_band:
 		if (isl_schedule_tree_band_n_member(tree) == 0)
@@ -1400,6 +1817,11 @@ static __isl_give isl_union_map *subtree
 		outer = isl_union_map_flat_range_product(outer, umap);
 		umap = subtree_schedule_extend_child(tree, outer);
 		break;
+	case isl_schedule_node_expansion:
+		umap = isl_schedule_tree_expansion_get_expansion(tree);
+		outer = isl_union_map_apply_domain(outer, umap);
+		umap = subtree_schedule_extend_child(tree, outer);
+		break;
 	case isl_schedule_node_filter:
 		domain = isl_schedule_tree_filter_get_filter(tree);
 		umap = isl_union_map_from_domain(domain);
@@ -1466,6 +1888,7 @@ static __isl_give isl_union_set *initial
 {
 	isl_multi_union_pw_aff *mupa;
 	isl_union_set *domain;
+	isl_union_map *exp;
 
 	if (!tree)
 		return NULL;
@@ -1477,6 +1900,18 @@ static __isl_give isl_union_set *initial
 		isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal,
 			"context node should be handled by caller",
 			return NULL);
+	case isl_schedule_node_guard:
+		isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal,
+			"guard node should be handled by caller",
+			return NULL);
+	case isl_schedule_node_mark:
+		isl_die(isl_schedule_tree_get_ctx(tree), isl_error_internal,
+			"mark node should be handled by caller",
+			return NULL);
+	case isl_schedule_node_extension:
+		isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+			"cannot construct subtree schedule of tree "
+			"with extension nodes", return NULL);
 	case isl_schedule_node_band:
 		if (isl_schedule_tree_band_n_member(tree) == 0)
 			isl_die(isl_schedule_tree_get_ctx(tree),
@@ -1491,6 +1926,11 @@ static __isl_give isl_union_set *initial
 		domain = isl_schedule_tree_domain_get_domain(tree);
 		domain = isl_union_set_universe(domain);
 		break;
+	case isl_schedule_node_expansion:
+		exp = isl_schedule_tree_expansion_get_expansion(tree);
+		exp = isl_union_map_universe(exp);
+		domain = isl_union_map_domain(exp);
+		break;
 	case isl_schedule_node_filter:
 		domain = isl_schedule_tree_filter_get_filter(tree);
 		domain = isl_union_set_universe(domain);
@@ -1511,6 +1951,10 @@ static __isl_give isl_union_set *initial
  * information, i.e., a node that would not be skipped by
  * isl_schedule_tree_first_schedule_descendant and that is not a leaf.
  *
+ * If the tree contains any expansions, then the returned subtree
+ * schedule is formulated in terms of the expanded domains.
+ * The tree is not allowed to contain any extension nodes.
+ *
  * We start with an initial zero-dimensional subtree schedule based
  * on the domain information in the root node and then extend it
  * based on the schedule information in the root node and its descendants.
@@ -1580,6 +2024,48 @@ error:
 	return NULL;
 }
 
+/* Given two trees with sequence roots, replace the child at position
+ * "pos" of "tree" with the children of "child".
+ */
+__isl_give isl_schedule_tree *isl_schedule_tree_sequence_splice(
+	__isl_take isl_schedule_tree *tree, int pos,
+	__isl_take isl_schedule_tree *child)
+{
+	int n;
+	isl_schedule_tree_list *list1, *list2;
+
+	tree = isl_schedule_tree_cow(tree);
+	if (!tree || !child)
+		goto error;
+	if (isl_schedule_tree_get_type(tree) != isl_schedule_node_sequence)
+		isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+			"not a sequence node", goto error);
+	n = isl_schedule_tree_n_children(tree);
+	if (pos < 0 || pos >= n)
+		isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+			"position out of bounds", goto error);
+	if (isl_schedule_tree_get_type(child) != isl_schedule_node_sequence)
+		isl_die(isl_schedule_tree_get_ctx(tree), isl_error_invalid,
+			"not a sequence node", goto error);
+
+	list1 = isl_schedule_tree_list_copy(tree->children);
+	list1 = isl_schedule_tree_list_drop(list1, pos, n - pos);
+	list2 = isl_schedule_tree_list_copy(tree->children);
+	list2 = isl_schedule_tree_list_drop(list2, 0, pos + 1);
+	list1 = isl_schedule_tree_list_concat(list1,
+				isl_schedule_tree_list_copy(child->children));
+	list1 = isl_schedule_tree_list_concat(list1, list2);
+
+	isl_schedule_tree_free(tree);
+	isl_schedule_tree_free(child);
+	return isl_schedule_tree_from_children(isl_schedule_node_sequence,
+						list1);
+error:
+	isl_schedule_tree_free(tree);
+	isl_schedule_tree_free(child);
+	return NULL;
+}
+
 /* Tile the band root node of "tree" with tile sizes "sizes".
  *
  * We duplicate the band node, change the schedule of one of them
@@ -1732,12 +2218,30 @@ __isl_give isl_schedule_tree *isl_schedu
 		if (!tree->domain)
 			return isl_schedule_tree_free(tree);
 		break;
+	case isl_schedule_node_expansion:
+		tree->contraction =
+			isl_union_pw_multi_aff_reset_user(tree->contraction);
+		tree->expansion = isl_union_map_reset_user(tree->expansion);
+		if (!tree->contraction || !tree->expansion)
+			return isl_schedule_tree_free(tree);
+		break;
+	case isl_schedule_node_extension:
+		tree->extension = isl_union_map_reset_user(tree->extension);
+		if (!tree->extension)
+			return isl_schedule_tree_free(tree);
+		break;
 	case isl_schedule_node_filter:
 		tree->filter = isl_union_set_reset_user(tree->filter);
 		if (!tree->filter)
 			return isl_schedule_tree_free(tree);
 		break;
+	case isl_schedule_node_guard:
+		tree->guard = isl_set_reset_user(tree->guard);
+		if (!tree->guard)
+			return isl_schedule_tree_free(tree);
+		break;
 	case isl_schedule_node_leaf:
+	case isl_schedule_node_mark:
 	case isl_schedule_node_sequence:
 	case isl_schedule_node_set:
 		break;
@@ -1781,12 +2285,33 @@ __isl_give isl_schedule_tree *isl_schedu
 		if (!tree->domain)
 			return isl_schedule_tree_free(tree);
 		break;
+	case isl_schedule_node_expansion:
+		tree->contraction =
+			isl_union_pw_multi_aff_align_params(tree->contraction,
+							isl_space_copy(space));
+		tree->expansion = isl_union_map_align_params(tree->expansion,
+								space);
+		if (!tree->contraction || !tree->expansion)
+			return isl_schedule_tree_free(tree);
+		break;
+	case isl_schedule_node_extension:
+		tree->extension = isl_union_map_align_params(tree->extension,
+								space);
+		if (!tree->extension)
+			return isl_schedule_tree_free(tree);
+		break;
 	case isl_schedule_node_filter:
 		tree->filter = isl_union_set_align_params(tree->filter, space);
 		if (!tree->filter)
 			return isl_schedule_tree_free(tree);
 		break;
+	case isl_schedule_node_guard:
+		tree->guard = isl_set_align_params(tree->guard, space);
+		if (!tree->guard)
+			return isl_schedule_tree_free(tree);
+		break;
 	case isl_schedule_node_leaf:
+	case isl_schedule_node_mark:
 	case isl_schedule_node_sequence:
 	case isl_schedule_node_set:
 		isl_space_free(space);
@@ -1814,10 +2339,14 @@ static int involves_iteration_domain(__i
 		return -1;
 	case isl_schedule_node_band:
 	case isl_schedule_node_domain:
+	case isl_schedule_node_expansion:
+	case isl_schedule_node_extension:
 	case isl_schedule_node_filter:
 		return 1;
 	case isl_schedule_node_context:
 	case isl_schedule_node_leaf:
+	case isl_schedule_node_guard:
+	case isl_schedule_node_mark:
 	case isl_schedule_node_sequence:
 	case isl_schedule_node_set:
 		return 0;
@@ -1828,6 +2357,7 @@ static int involves_iteration_domain(__i
  * represented by "upma".
  * In other words, plug in "upma" in the iteration domains of
  * the root node of "tree".
+ * We currently do not handle expansion nodes.
  *
  * We first check if the root node involves any iteration domains.
  * If so, we handle the specific cases.
@@ -1864,6 +2394,15 @@ __isl_give isl_schedule_tree *isl_schedu
 									upma);
 		if (!tree->domain)
 			return isl_schedule_tree_free(tree);
+	} else if (tree->type == isl_schedule_node_expansion) {
+		isl_die(isl_schedule_tree_get_ctx(tree), isl_error_unsupported,
+			"cannot pullback expansion node", goto error);
+	} else if (tree->type == isl_schedule_node_extension) {
+		tree->extension =
+			isl_union_map_preimage_range_union_pw_multi_aff(
+			    tree->extension, upma);
+		if (!tree->extension)
+			return isl_schedule_tree_free(tree);
 	} else if (tree->type == isl_schedule_node_filter) {
 		tree->filter =
 			isl_union_set_preimage_union_pw_multi_aff(tree->filter,
@@ -2032,6 +2571,26 @@ __isl_give isl_printer *isl_printer_prin
 		p = isl_printer_print_union_set(p, tree->domain);
 		p = isl_printer_print_str(p, "\"");
 		break;
+	case isl_schedule_node_expansion:
+		p = isl_printer_print_str(p, "contraction");
+		p = isl_printer_yaml_next(p);
+		p = isl_printer_print_str(p, "\"");
+		p = isl_printer_print_union_pw_multi_aff(p, tree->contraction);
+		p = isl_printer_print_str(p, "\"");
+		p = isl_printer_yaml_next(p);
+		p = isl_printer_print_str(p, "expansion");
+		p = isl_printer_yaml_next(p);
+		p = isl_printer_print_str(p, "\"");
+		p = isl_printer_print_union_map(p, tree->expansion);
+		p = isl_printer_print_str(p, "\"");
+		break;
+	case isl_schedule_node_extension:
+		p = isl_printer_print_str(p, "extension");
+		p = isl_printer_yaml_next(p);
+		p = isl_printer_print_str(p, "\"");
+		p = isl_printer_print_union_map(p, tree->extension);
+		p = isl_printer_print_str(p, "\"");
+		break;
 	case isl_schedule_node_filter:
 		p = isl_printer_print_str(p, "filter");
 		p = isl_printer_yaml_next(p);
@@ -2039,6 +2598,20 @@ __isl_give isl_printer *isl_printer_prin
 		p = isl_printer_print_union_set(p, tree->filter);
 		p = isl_printer_print_str(p, "\"");
 		break;
+	case isl_schedule_node_guard:
+		p = isl_printer_print_str(p, "guard");
+		p = isl_printer_yaml_next(p);
+		p = isl_printer_print_str(p, "\"");
+		p = isl_printer_print_set(p, tree->guard);
+		p = isl_printer_print_str(p, "\"");
+		break;
+	case isl_schedule_node_mark:
+		p = isl_printer_print_str(p, "mark");
+		p = isl_printer_yaml_next(p);
+		p = isl_printer_print_str(p, "\"");
+		p = isl_printer_print_str(p, isl_id_get_name(tree->mark));
+		p = isl_printer_print_str(p, "\"");
+		break;
 	case isl_schedule_node_band:
 		p = print_tree_band(p, tree->band);
 		break;

Modified: polly/trunk/lib/External/isl/isl_schedule_tree.h
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/lib/External/isl/isl_schedule_tree.h?rev=233567&r1=233566&r2=233567&view=diff
==============================================================================
--- polly/trunk/lib/External/isl/isl_schedule_tree.h (original)
+++ polly/trunk/lib/External/isl/isl_schedule_tree.h Mon Mar 30 12:28:57 2015
@@ -26,9 +26,28 @@ ISL_DECLARE_LIST(schedule_tree)
  * possibly introducing additional parameters.
  * The "domain" field is valid when type is isl_schedule_node_domain
  * and introduces the statement instances scheduled by the tree.
+ *
+ * The "contraction" and "expansion" fields are valid when type
+ * is isl_schedule_node_expansion.
+ * "expansion" expands the reaching domain elements to one or more
+ * domain elements for the subtree.
+ * "contraction" maps these elements back to the corresponding
+ * reaching domain element.  It does not involve any domain constraints.
+ *
+ * The "extension" field is valid when the is isl_schedule_node_extension
+ * maps outer schedule dimenions (the flat product of the outer band nodes)
+ * to additional iteration domains.
+ *
  * The "filter" field is valid when type is isl_schedule_node_filter
  * and represents the statement instances selected by the node.
  *
+ * The "guard" field is valid when type is isl_schedule_node_guard
+ * and represents constraints on the flat product of the outer band nodes
+ * that need to be enforced by the outer nodes in the generated AST.
+ *
+ * The "mark" field is valid when type is isl_schedule_node_mark and
+ * identifies the mark.
+ *
  * The "children" field is valid for all types except
  * isl_schedule_node_leaf.  This field is NULL if there are
  * no children (except for the implicit leaves).
@@ -45,7 +64,14 @@ struct isl_schedule_tree {
 		isl_schedule_band *band;
 		isl_set *context;
 		isl_union_set *domain;
+		struct {
+			isl_union_pw_multi_aff *contraction;
+			isl_union_map *expansion;
+		};
+		isl_union_map *extension;
 		isl_union_set *filter;
+		isl_set *guard;
+		isl_id *mark;
 	};
 	isl_schedule_tree_list *children;
 };
@@ -71,21 +97,36 @@ __isl_give isl_schedule_tree *isl_schedu
 	__isl_take isl_set *context);
 __isl_give isl_schedule_tree *isl_schedule_tree_from_domain(
 	__isl_take isl_union_set *domain);
+__isl_give isl_schedule_tree *isl_schedule_tree_from_expansion(
+	__isl_take isl_union_pw_multi_aff *contraction,
+	__isl_take isl_union_map *expansion);
+__isl_give isl_schedule_tree *isl_schedule_tree_from_extension(
+	__isl_take isl_union_map *extension);
 __isl_give isl_schedule_tree *isl_schedule_tree_from_filter(
 	__isl_take isl_union_set *filter);
+__isl_give isl_schedule_tree *isl_schedule_tree_from_guard(
+	__isl_take isl_set *guard);
 __isl_give isl_schedule_tree *isl_schedule_tree_from_children(
 	enum isl_schedule_node_type type,
 	__isl_take isl_schedule_tree_list *list);
 __isl_give isl_schedule_tree *isl_schedule_tree_from_pair(
 	enum isl_schedule_node_type type, __isl_take isl_schedule_tree *tree1,
 	__isl_take isl_schedule_tree *tree2);
+__isl_give isl_schedule_tree *isl_schedule_tree_sequence_pair(
+	__isl_take isl_schedule_tree *tree1,
+	__isl_take isl_schedule_tree *tree2);
 
 int isl_schedule_tree_is_subtree_anchored(__isl_keep isl_schedule_tree *tree);
 
 __isl_give isl_space *isl_schedule_tree_band_get_space(
 	__isl_keep isl_schedule_tree *tree);
+__isl_give isl_schedule_tree *isl_schedule_tree_band_intersect_domain(
+	__isl_take isl_schedule_tree *tree, __isl_take isl_union_set *domain);
 __isl_give isl_multi_union_pw_aff *isl_schedule_tree_band_get_partial_schedule(
 	__isl_keep isl_schedule_tree *tree);
+__isl_give isl_schedule_tree *isl_schedule_tree_band_set_partial_schedule(
+	__isl_take isl_schedule_tree *tree,
+	__isl_take isl_multi_union_pw_aff *schedule);
 enum isl_ast_loop_type isl_schedule_tree_band_member_get_ast_loop_type(
 	__isl_keep isl_schedule_tree *tree, int pos);
 __isl_give isl_schedule_tree *isl_schedule_tree_band_member_set_ast_loop_type(
@@ -107,10 +148,28 @@ __isl_give isl_union_set *isl_schedule_t
 	__isl_keep isl_schedule_tree *tree);
 __isl_give isl_schedule_tree *isl_schedule_tree_domain_set_domain(
 	__isl_take isl_schedule_tree *tree, __isl_take isl_union_set *domain);
+__isl_give isl_union_pw_multi_aff *isl_schedule_tree_expansion_get_contraction(
+	__isl_keep isl_schedule_tree *tree);
+__isl_give isl_union_map *isl_schedule_tree_expansion_get_expansion(
+	__isl_keep isl_schedule_tree *tree);
+__isl_give isl_schedule_tree *
+isl_schedule_tree_expansion_set_contraction_and_expansion(
+	__isl_take isl_schedule_tree *tree,
+	__isl_take isl_union_pw_multi_aff *contraction,
+	__isl_take isl_union_map *expansion);
+__isl_give isl_union_map *isl_schedule_tree_extension_get_extension(
+	__isl_keep isl_schedule_tree *tree);
+__isl_give isl_schedule_tree *isl_schedule_tree_extension_set_extension(
+	__isl_take isl_schedule_tree *tree,
+	__isl_take isl_union_map *extension);
 __isl_give isl_union_set *isl_schedule_tree_filter_get_filter(
 	__isl_keep isl_schedule_tree *tree);
 __isl_give isl_schedule_tree *isl_schedule_tree_filter_set_filter(
 	__isl_take isl_schedule_tree *tree, __isl_take isl_union_set *filter);
+__isl_give isl_set *isl_schedule_tree_guard_get_guard(
+	__isl_keep isl_schedule_tree *tree);
+__isl_give isl_id *isl_schedule_tree_mark_get_id(
+	__isl_keep isl_schedule_tree *tree);
 
 __isl_give isl_schedule_tree *isl_schedule_tree_first_schedule_descendant(
 	__isl_take isl_schedule_tree *tree, __isl_keep isl_schedule_tree *leaf);
@@ -138,10 +197,21 @@ __isl_give isl_schedule_tree *isl_schedu
 	__isl_take isl_schedule_tree *tree, __isl_take isl_set *context);
 __isl_give isl_schedule_tree *isl_schedule_tree_insert_domain(
 	__isl_take isl_schedule_tree *tree, __isl_take isl_union_set *domain);
+__isl_give isl_schedule_tree *isl_schedule_tree_insert_expansion(
+	__isl_take isl_schedule_tree *tree,
+	__isl_take isl_union_pw_multi_aff *contraction,
+	__isl_take isl_union_map *expansion);
+__isl_give isl_schedule_tree *isl_schedule_tree_insert_extension(
+	__isl_take isl_schedule_tree *tree,
+	__isl_take isl_union_map *extension);
 __isl_give isl_schedule_tree *isl_schedule_tree_insert_filter(
 	__isl_take isl_schedule_tree *tree, __isl_take isl_union_set *filter);
 __isl_give isl_schedule_tree *isl_schedule_tree_children_insert_filter(
 	__isl_take isl_schedule_tree *tree, __isl_take isl_union_set *filter);
+__isl_give isl_schedule_tree *isl_schedule_tree_insert_guard(
+	__isl_take isl_schedule_tree *tree, __isl_take isl_set *guard);
+__isl_give isl_schedule_tree *isl_schedule_tree_insert_mark(
+	__isl_take isl_schedule_tree *tree, __isl_take isl_id *mark);
 
 __isl_give isl_schedule_tree *isl_schedule_tree_append_to_leaves(
 	__isl_take isl_schedule_tree *tree1,
@@ -167,6 +237,9 @@ __isl_give isl_schedule_tree *isl_schedu
 __isl_give isl_schedule_tree *isl_schedule_tree_replace_child(
 	__isl_take isl_schedule_tree *tree, int pos,
 	__isl_take isl_schedule_tree *new_child);
+__isl_give isl_schedule_tree *isl_schedule_tree_sequence_splice(
+	__isl_take isl_schedule_tree *tree, int pos,
+	__isl_take isl_schedule_tree *child);
 
 __isl_give isl_schedule_tree *isl_schedule_tree_reset_user(
 	__isl_take isl_schedule_tree *tree);

Modified: polly/trunk/lib/External/isl/isl_scheduler.c
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/lib/External/isl/isl_scheduler.c?rev=233567&r1=233566&r2=233567&view=diff
==============================================================================
--- polly/trunk/lib/External/isl/isl_scheduler.c (original)
+++ polly/trunk/lib/External/isl/isl_scheduler.c Mon Mar 30 12:28:57 2015
@@ -1,6 +1,7 @@
 /*
  * Copyright 2011      INRIA Saclay
  * Copyright 2012-2014 Ecole Normale Superieure
+ * Copyright 2015      Sven Verdoolaege
  *
  * Use of this software is governed by the MIT license
  *
@@ -2462,6 +2463,7 @@ static __isl_give isl_union_map *interse
 static int update_edge(struct isl_sched_graph *graph,
 	struct isl_sched_edge *edge)
 {
+	int empty;
 	isl_map *id;
 
 	id = specializer(edge->src, edge->dst);
@@ -2482,28 +2484,190 @@ static int update_edge(struct isl_sched_
 			goto error;
 	}
 
-	isl_map_free(id);
-	if (isl_map_plain_is_empty(edge->map))
+	empty = isl_map_plain_is_empty(edge->map);
+	if (empty < 0)
+		goto error;
+	if (empty)
 		graph_remove_edge(graph, edge);
 
+	isl_map_free(id);
 	return 0;
 error:
 	isl_map_free(id);
 	return -1;
 }
 
-/* Update the dependence relations of all edges based on the current schedule.
+/* Does the domain of "umap" intersect "uset"?
+ */
+static int domain_intersects(__isl_keep isl_union_map *umap,
+	__isl_keep isl_union_set *uset)
+{
+	int empty;
+
+	umap = isl_union_map_copy(umap);
+	umap = isl_union_map_intersect_domain(umap, isl_union_set_copy(uset));
+	empty = isl_union_map_is_empty(umap);
+	isl_union_map_free(umap);
+
+	return empty < 0 ? -1 : !empty;
+}
+
+/* Does the range of "umap" intersect "uset"?
+ */
+static int range_intersects(__isl_keep isl_union_map *umap,
+	__isl_keep isl_union_set *uset)
+{
+	int empty;
+
+	umap = isl_union_map_copy(umap);
+	umap = isl_union_map_intersect_range(umap, isl_union_set_copy(uset));
+	empty = isl_union_map_is_empty(umap);
+	isl_union_map_free(umap);
+
+	return empty < 0 ? -1 : !empty;
+}
+
+/* Are the condition dependences of "edge" local with respect to
+ * the current schedule?
+ *
+ * That is, are domain and range of the condition dependences mapped
+ * to the same point?
+ *
+ * In other words, is the condition false?
+ */
+static int is_condition_false(struct isl_sched_edge *edge)
+{
+	isl_union_map *umap;
+	isl_map *map, *sched, *test;
+	int empty, local;
+
+	empty = isl_union_map_is_empty(edge->tagged_condition);
+	if (empty < 0 || empty)
+		return empty;
+
+	umap = isl_union_map_copy(edge->tagged_condition);
+	umap = isl_union_map_zip(umap);
+	umap = isl_union_set_unwrap(isl_union_map_domain(umap));
+	map = isl_map_from_union_map(umap);
+
+	sched = node_extract_schedule(edge->src);
+	map = isl_map_apply_domain(map, sched);
+	sched = node_extract_schedule(edge->dst);
+	map = isl_map_apply_range(map, sched);
+
+	test = isl_map_identity(isl_map_get_space(map));
+	local = isl_map_is_subset(map, test);
+	isl_map_free(map);
+	isl_map_free(test);
+
+	return local;
+}
+
+/* For each conditional validity constraint that is adjacent
+ * to a condition with domain in condition_source or range in condition_sink,
+ * turn it into an unconditional validity constraint.
+ */
+static int unconditionalize_adjacent_validity(struct isl_sched_graph *graph,
+	__isl_take isl_union_set *condition_source,
+	__isl_take isl_union_set *condition_sink)
+{
+	int i;
+
+	condition_source = isl_union_set_coalesce(condition_source);
+	condition_sink = isl_union_set_coalesce(condition_sink);
+
+	for (i = 0; i < graph->n_edge; ++i) {
+		int adjacent;
+		isl_union_map *validity;
+
+		if (!graph->edge[i].conditional_validity)
+			continue;
+		if (graph->edge[i].validity)
+			continue;
+
+		validity = graph->edge[i].tagged_validity;
+		adjacent = domain_intersects(validity, condition_sink);
+		if (adjacent >= 0 && !adjacent)
+			adjacent = range_intersects(validity, condition_source);
+		if (adjacent < 0)
+			goto error;
+		if (!adjacent)
+			continue;
+
+		graph->edge[i].validity = 1;
+	}
+
+	isl_union_set_free(condition_source);
+	isl_union_set_free(condition_sink);
+	return 0;
+error:
+	isl_union_set_free(condition_source);
+	isl_union_set_free(condition_sink);
+	return -1;
+}
+
+/* Update the dependence relations of all edges based on the current schedule
+ * and enforce conditional validity constraints that are adjacent
+ * to satisfied condition constraints.
+ *
+ * First check if any of the condition constraints are satisfied
+ * (i.e., not local to the outer schedule) and keep track of
+ * their domain and range.
+ * Then update all dependence relations (which removes the non-local
+ * constraints).
+ * Finally, if any condition constraints turned out to be satisfied,
+ * then turn all adjacent conditional validity constraints into
+ * unconditional validity constraints.
  */
 static int update_edges(isl_ctx *ctx, struct isl_sched_graph *graph)
 {
 	int i;
+	int any = 0;
+	isl_union_set *source, *sink;
+
+	source = isl_union_set_empty(isl_space_params_alloc(ctx, 0));
+	sink = isl_union_set_empty(isl_space_params_alloc(ctx, 0));
+	for (i = 0; i < graph->n_edge; ++i) {
+		int local;
+		isl_union_set *uset;
+		isl_union_map *umap;
+
+		if (!graph->edge[i].condition)
+			continue;
+		if (graph->edge[i].local)
+			continue;
+		local = is_condition_false(&graph->edge[i]);
+		if (local < 0)
+			goto error;
+		if (local)
+			continue;
+
+		any = 1;
+
+		umap = isl_union_map_copy(graph->edge[i].tagged_condition);
+		uset = isl_union_map_domain(umap);
+		source = isl_union_set_union(source, uset);
+
+		umap = isl_union_map_copy(graph->edge[i].tagged_condition);
+		uset = isl_union_map_range(umap);
+		sink = isl_union_set_union(sink, uset);
+	}
 
 	for (i = graph->n_edge - 1; i >= 0; --i) {
 		if (update_edge(graph, &graph->edge[i]) < 0)
-			return -1;
+			goto error;
 	}
 
+	if (any)
+		return unconditionalize_adjacent_validity(graph, source, sink);
+
+	isl_union_set_free(source);
+	isl_union_set_free(sink);
 	return 0;
+error:
+	isl_union_set_free(source);
+	isl_union_set_free(sink);
+	return -1;
 }
 
 static void next_band(struct isl_sched_graph *graph)
@@ -3708,68 +3872,6 @@ static int is_violated(struct isl_sched_
 	return !empty;
 }
 
-/* Does the domain of "umap" intersect "uset"?
- */
-static int domain_intersects(__isl_keep isl_union_map *umap,
-	__isl_keep isl_union_set *uset)
-{
-	int empty;
-
-	umap = isl_union_map_copy(umap);
-	umap = isl_union_map_intersect_domain(umap, isl_union_set_copy(uset));
-	empty = isl_union_map_is_empty(umap);
-	isl_union_map_free(umap);
-
-	return empty < 0 ? -1 : !empty;
-}
-
-/* Does the range of "umap" intersect "uset"?
- */
-static int range_intersects(__isl_keep isl_union_map *umap,
-	__isl_keep isl_union_set *uset)
-{
-	int empty;
-
-	umap = isl_union_map_copy(umap);
-	umap = isl_union_map_intersect_range(umap, isl_union_set_copy(uset));
-	empty = isl_union_map_is_empty(umap);
-	isl_union_map_free(umap);
-
-	return empty < 0 ? -1 : !empty;
-}
-
-/* Are the condition dependences of "edge" local with respect to
- * the current schedule?
- *
- * That is, are domain and range of the condition dependences mapped
- * to the same point?
- *
- * In other words, is the condition false?
- */
-static int is_condition_false(struct isl_sched_edge *edge)
-{
-	isl_union_map *umap;
-	isl_map *map, *sched, *test;
-	int local;
-
-	umap = isl_union_map_copy(edge->tagged_condition);
-	umap = isl_union_map_zip(umap);
-	umap = isl_union_set_unwrap(isl_union_map_domain(umap));
-	map = isl_map_from_union_map(umap);
-
-	sched = node_extract_schedule(edge->src);
-	map = isl_map_apply_domain(map, sched);
-	sched = node_extract_schedule(edge->dst);
-	map = isl_map_apply_range(map, sched);
-
-	test = isl_map_identity(isl_map_get_space(map));
-	local = isl_map_is_subset(map, test);
-	isl_map_free(map);
-	isl_map_free(test);
-
-	return local;
-}
-
 /* Does "graph" have any satisfied condition edges that
  * are adjacent to the conditional validity constraint with
  * domain "conditional_source" and range "conditional_sink"?

Modified: polly/trunk/lib/External/isl/isl_test.c
URL: http://llvm.org/viewvc/llvm-project/polly/trunk/lib/External/isl/isl_test.c?rev=233567&r1=233566&r2=233567&view=diff
==============================================================================
--- polly/trunk/lib/External/isl/isl_test.c (original)
+++ polly/trunk/lib/External/isl/isl_test.c Mon Mar 30 12:28:57 2015
@@ -2926,6 +2926,84 @@ static int test_padded_schedule(isl_ctx
 	return 0;
 }
 
+/* Check that conditional validity constraints are also taken into
+ * account across bands.
+ * In particular, try to make sure that live ranges D[1,0]->C[2,1] and
+ * D[2,0]->C[3,0] are not local in the outer band of the generated schedule
+ * and then check that the adjacent order constraint C[2,1]->D[2,0]
+ * is enforced by the rest of the schedule.
+ */
+static int test_special_conditional_schedule_constraints(isl_ctx *ctx)
+{
+	const char *str;
+	isl_union_set *domain;
+	isl_union_map *validity, *proximity, *condition;
+	isl_union_map *sink, *source, *dep;
+	isl_schedule_constraints *sc;
+	isl_schedule *schedule;
+	isl_union_access_info *access;
+	isl_union_flow *flow;
+	int empty;
+
+	str = "[n] -> { C[k, i] : k <= -1 + n and i >= 0 and i <= -1 + k; "
+	    "A[k] : k >= 1 and k <= -1 + n; "
+	    "B[k, i] : k <= -1 + n and i >= 0 and i <= -1 + k; "
+	    "D[k, i] : k <= -1 + n and i >= 0 and i <= -1 + k }";
+	domain = isl_union_set_read_from_str(ctx, str);
+	sc = isl_schedule_constraints_on_domain(domain);
+	str = "[n] -> { D[k, i] -> C[1 + k, k - i] : "
+		"k <= -2 + n and i >= 1 and i <= -1 + k; "
+		"D[k, i] -> C[1 + k, i] : "
+		"k <= -2 + n and i >= 1 and i <= -1 + k; "
+		"D[k, 0] -> C[1 + k, k] : k >= 1 and k <= -2 + n; "
+		"D[k, 0] -> C[1 + k, 0] : k >= 1 and k <= -2 + n }";
+	validity = isl_union_map_read_from_str(ctx, str);
+	sc = isl_schedule_constraints_set_validity(sc, validity);
+	str = "[n] -> { C[k, i] -> D[k, i] : "
+		"0 <= i <= -1 + k and k <= -1 + n }";
+	proximity = isl_union_map_read_from_str(ctx, str);
+	sc = isl_schedule_constraints_set_proximity(sc, proximity);
+	str = "[n] -> { [D[k, i] -> a[]] -> [C[1 + k, k - i] -> b[]] : "
+		"i <= -1 + k and i >= 1 and k <= -2 + n; "
+		"[B[k, i] -> c[]] -> [B[k, 1 + i] -> c[]] : "
+		"k <= -1 + n and i >= 0 and i <= -2 + k }";
+	condition = isl_union_map_read_from_str(ctx, str);
+	str = "[n] -> { [B[k, i] -> e[]] -> [D[k, i] -> a[]] : "
+		"i >= 0 and i <= -1 + k and k <= -1 + n; "
+		"[C[k, i] -> b[]] -> [D[k', -1 + k - i] -> a[]] : "
+		"i >= 0 and i <= -1 + k and k <= -1 + n and "
+		"k' <= -1 + n and k' >= k - i and k' >= 1 + k; "
+		"[C[k, i] -> b[]] -> [D[k, -1 + k - i] -> a[]] : "
+		"i >= 0 and i <= -1 + k and k <= -1 + n; "
+		"[B[k, i] -> c[]] -> [A[k'] -> d[]] : "
+		"k <= -1 + n and i >= 0 and i <= -1 + k and "
+		"k' >= 1 and k' <= -1 + n and k' >= 1 + k }";
+	validity = isl_union_map_read_from_str(ctx, str);
+	sc = isl_schedule_constraints_set_conditional_validity(sc, condition,
+								validity);
+	schedule = isl_schedule_constraints_compute_schedule(sc);
+	str = "{ D[2,0] -> [] }";
+	sink = isl_union_map_read_from_str(ctx, str);
+	access = isl_union_access_info_from_sink(sink);
+	str = "{ C[2,1] -> [] }";
+	source = isl_union_map_read_from_str(ctx, str);
+	access = isl_union_access_info_set_must_source(access, source);
+	access = isl_union_access_info_set_schedule(access, schedule);
+	flow = isl_union_access_info_compute_flow(access);
+	dep = isl_union_flow_get_must_dependence(flow);
+	isl_union_flow_free(flow);
+	empty = isl_union_map_is_empty(dep);
+	isl_union_map_free(dep);
+
+	if (empty < 0)
+		return -1;
+	if (empty)
+		isl_die(ctx, isl_error_unknown,
+			"conditional validity not respected", return -1);
+
+	return 0;
+}
+
 /* Input for testing of schedule construction based on
  * conditional constraints.
  *
@@ -3028,6 +3106,9 @@ static int test_conditional_schedule_con
 	isl_schedule_node *node;
 	int n_member;
 
+	if (test_special_conditional_schedule_constraints(ctx) < 0)
+		return -1;
+
 	for (i = 0; i < ARRAY_SIZE(live_range_tests); ++i) {
 		domain = isl_union_set_read_from_str(ctx,
 				live_range_tests[i].domain);
@@ -5183,6 +5264,174 @@ static int test_compute_divs(isl_ctx *ct
 	return 0;
 }
 
+/* Check that the reaching domain elements and the prefix schedule
+ * at a leaf node are the same before and after grouping.
+ */
+static int test_schedule_tree_group_1(isl_ctx *ctx)
+{
+	int equal;
+	const char *str;
+	isl_id *id;
+	isl_union_set *uset;
+	isl_multi_union_pw_aff *mupa;
+	isl_union_pw_multi_aff *upma1, *upma2;
+	isl_union_set *domain1, *domain2;
+	isl_union_map *umap1, *umap2;
+	isl_schedule_node *node;
+
+	str = "{ S1[i,j] : 0 <= i,j < 10; S2[i,j] : 0 <= i,j < 10 }";
+	uset = isl_union_set_read_from_str(ctx, str);
+	node = isl_schedule_node_from_domain(uset);
+	node = isl_schedule_node_child(node, 0);
+	str = "[{ S1[i,j] -> [i]; S2[i,j] -> [9 - i] }]";
+	mupa = isl_multi_union_pw_aff_read_from_str(ctx, str);
+	node = isl_schedule_node_insert_partial_schedule(node, mupa);
+	node = isl_schedule_node_child(node, 0);
+	str = "[{ S1[i,j] -> [j]; S2[i,j] -> [j] }]";
+	mupa = isl_multi_union_pw_aff_read_from_str(ctx, str);
+	node = isl_schedule_node_insert_partial_schedule(node, mupa);
+	node = isl_schedule_node_child(node, 0);
+	umap1 = isl_schedule_node_get_prefix_schedule_union_map(node);
+	upma1 = isl_schedule_node_get_prefix_schedule_union_pw_multi_aff(node);
+	domain1 = isl_schedule_node_get_domain(node);
+	id = isl_id_alloc(ctx, "group", NULL);
+	node = isl_schedule_node_parent(node);
+	node = isl_schedule_node_group(node, id);
+	node = isl_schedule_node_child(node, 0);
+	umap2 = isl_schedule_node_get_prefix_schedule_union_map(node);
+	upma2 = isl_schedule_node_get_prefix_schedule_union_pw_multi_aff(node);
+	domain2 = isl_schedule_node_get_domain(node);
+	equal = isl_union_pw_multi_aff_plain_is_equal(upma1, upma2);
+	if (equal >= 0 && equal)
+		equal = isl_union_set_is_equal(domain1, domain2);
+	if (equal >= 0 && equal)
+		equal = isl_union_map_is_equal(umap1, umap2);
+	isl_union_map_free(umap1);
+	isl_union_map_free(umap2);
+	isl_union_set_free(domain1);
+	isl_union_set_free(domain2);
+	isl_union_pw_multi_aff_free(upma1);
+	isl_union_pw_multi_aff_free(upma2);
+	isl_schedule_node_free(node);
+
+	if (equal < 0)
+		return -1;
+	if (!equal)
+		isl_die(ctx, isl_error_unknown,
+			"expressions not equal", return -1);
+
+	return 0;
+}
+
+/* Check that we can have nested groupings and that the union map
+ * schedule representation is the same before and after the grouping.
+ * Note that after the grouping, the union map representation contains
+ * the domain constraints from the ranges of the expansion nodes,
+ * while they are missing from the union map representation of
+ * the tree without expansion nodes.
+ *
+ * Also check that the global expansion is as expected.
+ */
+static int test_schedule_tree_group_2(isl_ctx *ctx)
+{
+	int equal, equal_expansion;
+	const char *str;
+	isl_id *id;
+	isl_union_set *uset;
+	isl_union_map *umap1, *umap2;
+	isl_union_map *expansion1, *expansion2;
+	isl_union_set_list *filters;
+	isl_multi_union_pw_aff *mupa;
+	isl_schedule *schedule;
+	isl_schedule_node *node;
+
+	str = "{ S1[i,j] : 0 <= i,j < 10; S2[i,j] : 0 <= i,j < 10; "
+		"S3[i,j] : 0 <= i,j < 10 }";
+	uset = isl_union_set_read_from_str(ctx, str);
+	node = isl_schedule_node_from_domain(uset);
+	node = isl_schedule_node_child(node, 0);
+	str = "[{ S1[i,j] -> [i]; S2[i,j] -> [i]; S3[i,j] -> [i] }]";
+	mupa = isl_multi_union_pw_aff_read_from_str(ctx, str);
+	node = isl_schedule_node_insert_partial_schedule(node, mupa);
+	node = isl_schedule_node_child(node, 0);
+	str = "{ S1[i,j] }";
+	uset = isl_union_set_read_from_str(ctx, str);
+	filters = isl_union_set_list_from_union_set(uset);
+	str = "{ S2[i,j]; S3[i,j] }";
+	uset = isl_union_set_read_from_str(ctx, str);
+	filters = isl_union_set_list_add(filters, uset);
+	node = isl_schedule_node_insert_sequence(node, filters);
+	node = isl_schedule_node_child(node, 1);
+	node = isl_schedule_node_child(node, 0);
+	str = "{ S2[i,j] }";
+	uset = isl_union_set_read_from_str(ctx, str);
+	filters = isl_union_set_list_from_union_set(uset);
+	str = "{ S3[i,j] }";
+	uset = isl_union_set_read_from_str(ctx, str);
+	filters = isl_union_set_list_add(filters, uset);
+	node = isl_schedule_node_insert_sequence(node, filters);
+
+	schedule = isl_schedule_node_get_schedule(node);
+	umap1 = isl_schedule_get_map(schedule);
+	uset = isl_schedule_get_domain(schedule);
+	umap1 = isl_union_map_intersect_domain(umap1, uset);
+	isl_schedule_free(schedule);
+
+	node = isl_schedule_node_parent(node);
+	node = isl_schedule_node_parent(node);
+	id = isl_id_alloc(ctx, "group1", NULL);
+	node = isl_schedule_node_group(node, id);
+	node = isl_schedule_node_child(node, 1);
+	node = isl_schedule_node_child(node, 0);
+	id = isl_id_alloc(ctx, "group2", NULL);
+	node = isl_schedule_node_group(node, id);
+
+	schedule = isl_schedule_node_get_schedule(node);
+	umap2 = isl_schedule_get_map(schedule);
+	isl_schedule_free(schedule);
+
+	node = isl_schedule_node_root(node);
+	node = isl_schedule_node_child(node, 0);
+	expansion1 = isl_schedule_node_get_subtree_expansion(node);
+	isl_schedule_node_free(node);
+
+	str = "{ group1[i] -> S1[i,j] : 0 <= i,j < 10; "
+		"group1[i] -> S2[i,j] : 0 <= i,j < 10; "
+		"group1[i] -> S3[i,j] : 0 <= i,j < 10 }";
+
+	expansion2 = isl_union_map_read_from_str(ctx, str);
+
+	equal = isl_union_map_is_equal(umap1, umap2);
+	equal_expansion = isl_union_map_is_equal(expansion1, expansion2);
+
+	isl_union_map_free(umap1);
+	isl_union_map_free(umap2);
+	isl_union_map_free(expansion1);
+	isl_union_map_free(expansion2);
+
+	if (equal < 0 || equal_expansion < 0)
+		return -1;
+	if (!equal)
+		isl_die(ctx, isl_error_unknown,
+			"expressions not equal", return -1);
+	if (!equal_expansion)
+		isl_die(ctx, isl_error_unknown,
+			"unexpected expansion", return -1);
+
+	return 0;
+}
+
+/* Some tests for the isl_schedule_node_group function.
+ */
+static int test_schedule_tree_group(isl_ctx *ctx)
+{
+	if (test_schedule_tree_group_1(ctx) < 0)
+		return -1;
+	if (test_schedule_tree_group_2(ctx) < 0)
+		return -1;
+	return 0;
+}
+
 struct {
 	const char *set;
 	const char *dual;
@@ -5394,6 +5643,7 @@ struct {
 	{ "affine", &test_aff },
 	{ "injective", &test_injective },
 	{ "schedule", &test_schedule },
+	{ "schedule tree grouping", &test_schedule_tree_group },
 	{ "tile", &test_tile },
 	{ "union_pw", &test_union_pw },
 	{ "parse", &test_parse },





More information about the llvm-commits mailing list