1 /* Copyright 2016-2017 Tobias Grosser
2  *
3  * Use of this software is governed by the MIT license
4  *
5  * Written by Tobias Grosser, Weststrasse 47, CH-8003, Zurich
6  */
7 
8 #ifndef IS_TRUE
9 #define IS_TRUE(b)	(b)
10 #endif
11 #ifndef SIZE_VAL
12 #define SIZE_VAL(s)	(s)
13 #endif
14 
15 /* Test the pointer interface for interaction between isl C and C++ types.
16  *
17  * This tests:
18  * - construction from an isl C object
19  * - check that constructed objects are non-null
20  * - get a non-owned C pointer from an isl C++ object usable in __isl_keep
21  *   methods
22  * - use copy to get an owned C pointer from an isl C++ object which is usable
23  *   in __isl_take methods. Verify that the original C++ object retains a valid
24  *   pointer.
25  * - use release to get an owned C pointer from an isl C++ object which is
26  *   usable in __isl_take methods. Verify that the original C++ object gave up
27  *   its pointer and now is null.
28  */
test_pointer(isl::ctx ctx)29 void test_pointer(isl::ctx ctx)
30 {
31 	isl_set *c_empty = isl_set_read_from_str(ctx.get(), "{ : false }");
32 	isl::set empty = isl::manage(c_empty);
33 	assert(IS_TRUE(empty.is_empty()));
34 	assert(isl_set_is_empty(empty.get()));
35 
36 	assert(!empty.is_null());
37 	isl_set_free(empty.copy());
38 	assert(!empty.is_null());
39 	isl_set_free(empty.release());
40 	assert(empty.is_null());
41 }
42 
43 /* Test that isl objects can be constructed.
44  *
45  * This tests:
46  *  - construction of a null object
47  *  - construction from a string
48  *  - construction from an integer
49  *  - static constructor without a parameter
50  *  - conversion construction (implicit)
51  *  - conversion construction (explicit)
52  *  - construction of empty union set
53  *
54  *  The tests to construct from integers and strings cover functionality that
55  *  is also tested in the parameter type tests, but here we verify that
56  *  multiple overloaded constructors are available and that overload resolution
57  *  works as expected.
58  *
59  *  Construction from an isl C pointer is tested in test_pointer.
60  */
test_constructors(isl::ctx ctx)61 void test_constructors(isl::ctx ctx)
62 {
63 	isl::val null;
64 	assert(null.is_null());
65 
66 	isl::val zero_from_str = isl::val(ctx, "0");
67 	assert(IS_TRUE(zero_from_str.is_zero()));
68 
69 	isl::val zero_int_con = isl::val(ctx, 0);
70 	assert(IS_TRUE(zero_int_con.is_zero()));
71 
72 	isl::val zero_static_con = isl::val::zero(ctx);
73 	assert(IS_TRUE(zero_static_con.is_zero()));
74 
75 	isl::basic_set bs(ctx, "{ [1] }");
76 	isl::set result(ctx, "{ [1] }");
77 	isl::set s = bs;
78 	assert(IS_TRUE(s.is_equal(result)));
79 	isl::set s2(bs);
80 	assert(IS_TRUE(s.unite(s2).is_equal(result)));
81 
82 	isl::union_set us(ctx, "{ A[1]; B[2, 3] }");
83 	isl::union_set empty = isl::union_set::empty(ctx);
84 	assert(IS_TRUE(us.is_equal(us.unite(empty))));
85 }
86 
87 /* Test integer function parameters.
88  *
89  * Verify that extreme values and zero work.
90  */
test_parameters_int(isl::ctx ctx)91 void test_parameters_int(isl::ctx ctx)
92 {
93 	isl::val long_max_str(ctx, std::to_string(LONG_MAX));
94 	isl::val long_max_int(ctx, LONG_MAX);
95 	assert(IS_TRUE(long_max_str.eq(long_max_int)));
96 
97 	isl::val long_min_str(ctx, std::to_string(LONG_MIN));
98 	isl::val long_min_int(ctx, LONG_MIN);
99 	assert(IS_TRUE(long_min_str.eq(long_min_int)));
100 
101 	isl::val long_zero_str = isl::val(ctx, std::to_string(0));
102 	isl::val long_zero_int = isl::val(ctx, 0);
103 	assert(IS_TRUE(long_zero_str.eq(long_zero_int)));
104 }
105 
106 /* Test isl objects parameters.
107  *
108  * Verify that isl objects can be passed as lvalue and rvalue parameters.
109  * Also verify that isl object parameters are automatically type converted if
110  * there is an inheritance relation. Finally, test function calls without
111  * any additional parameters, apart from the isl object on which
112  * the method is called.
113  */
test_parameters_obj(isl::ctx ctx)114 void test_parameters_obj(isl::ctx ctx)
115 {
116 	isl::set a(ctx, "{ [0] }");
117 	isl::set b(ctx, "{ [1] }");
118 	isl::set c(ctx, "{ [2] }");
119 	isl::set expected(ctx, "{ [i] : 0 <= i <= 2 }");
120 
121 	isl::set tmp = a.unite(b);
122 	isl::set res_lvalue_param = tmp.unite(c);
123 	assert(IS_TRUE(res_lvalue_param.is_equal(expected)));
124 
125 	isl::set res_rvalue_param = a.unite(b).unite(c);
126 	assert(IS_TRUE(res_rvalue_param.is_equal(expected)));
127 
128 	isl::basic_set a2(ctx, "{ [0] }");
129 	assert(IS_TRUE(a.is_equal(a2)));
130 
131 	isl::val two(ctx, 2);
132 	isl::val half(ctx, "1/2");
133 	isl::val res_only_this_param = two.inv();
134 	assert(IS_TRUE(res_only_this_param.eq(half)));
135 }
136 
137 /* Test different kinds of parameters to be passed to functions.
138  *
139  * This includes integer and isl C++ object parameters.
140  */
test_parameters(isl::ctx ctx)141 void test_parameters(isl::ctx ctx)
142 {
143 	test_parameters_int(ctx);
144 	test_parameters_obj(ctx);
145 }
146 
147 /* Test that isl objects are returned correctly.
148  *
149  * This only tests that after combining two objects, the result is successfully
150  * returned.
151  */
test_return_obj(isl::ctx ctx)152 void test_return_obj(isl::ctx ctx)
153 {
154 	isl::val one(ctx, "1");
155 	isl::val two(ctx, "2");
156 	isl::val three(ctx, "3");
157 
158 	isl::val res = one.add(two);
159 
160 	assert(IS_TRUE(res.eq(three)));
161 }
162 
163 /* Test that integer values are returned correctly.
164  */
test_return_int(isl::ctx ctx)165 void test_return_int(isl::ctx ctx)
166 {
167 	isl::val one(ctx, "1");
168 	isl::val neg_one(ctx, "-1");
169 	isl::val zero(ctx, "0");
170 
171 	assert(one.sgn() > 0);
172 	assert(neg_one.sgn() < 0);
173 	assert(zero.sgn() == 0);
174 }
175 
176 /* Test that strings are returned correctly.
177  * Do so by calling overloaded isl::ast_build::from_expr methods.
178  */
test_return_string(isl::ctx ctx)179 void test_return_string(isl::ctx ctx)
180 {
181 	isl::set context(ctx, "[n] -> { : }");
182 	isl::ast_build build = isl::ast_build::from_context(context);
183 	isl::pw_aff pw_aff(ctx, "[n] -> { [n] }");
184 	isl::set set(ctx, "[n] -> { : n >= 0 }");
185 
186 	isl::ast_expr expr = build.expr_from(pw_aff);
187 	const char *expected_string = "n";
188 	assert(expected_string == expr.to_C_str());
189 
190 	expr = build.expr_from(set);
191 	expected_string = "n >= 0";
192 	assert(expected_string == expr.to_C_str());
193 }
194 
195 /* Test the functionality of "every" functions
196  * that does not depend on the type of C++ bindings.
197  */
test_every_generic(isl::ctx ctx)198 static void test_every_generic(isl::ctx ctx)
199 {
200 	isl::union_set us(ctx, "{ A[i]; B[j] }");
201 
202 	auto is_empty = [] (isl::set s) {
203 		return s.is_empty();
204 	};
205 	assert(!IS_TRUE(us.every_set(is_empty)));
206 
207 	auto is_non_empty = [] (isl::set s) {
208 		return !s.is_empty();
209 	};
210 	assert(IS_TRUE(us.every_set(is_non_empty)));
211 
212 	auto in_A = [] (isl::set s) {
213 		return s.is_subset(isl::set(s.ctx(), "{ A[x] }"));
214 	};
215 	assert(!IS_TRUE(us.every_set(in_A)));
216 
217 	auto not_in_A = [] (isl::set s) {
218 		return !s.is_subset(isl::set(s.ctx(), "{ A[x] }"));
219 	};
220 	assert(!IS_TRUE(us.every_set(not_in_A)));
221 }
222 
223 /* Check basic construction of spaces.
224  */
test_space(isl::ctx ctx)225 static void test_space(isl::ctx ctx)
226 {
227 	isl::space unit = isl::space::unit(ctx);
228 	isl::space set_space = unit.add_named_tuple("A", 3);
229 	isl::space map_space = set_space.add_named_tuple("B", 2);
230 
231 	isl::set set = isl::set::universe(set_space);
232 	isl::map map = isl::map::universe(map_space);
233 	assert(IS_TRUE(set.is_equal(isl::set(ctx, "{ A[*,*,*] }"))));
234 	assert(IS_TRUE(map.is_equal(isl::map(ctx, "{ A[*,*,*] -> B[*,*] }"))));
235 }
236 
237 /* Construct a simple schedule tree with an outer sequence node and
238  * a single-dimensional band node in each branch, with one of them
239  * marked coincident.
240  */
construct_schedule_tree(isl::ctx ctx)241 static isl::schedule construct_schedule_tree(isl::ctx ctx)
242 {
243 	isl::union_set A(ctx, "{ A[i] : 0 <= i < 10 }");
244 	isl::union_set B(ctx, "{ B[i] : 0 <= i < 20 }");
245 
246 	auto node = isl::schedule_node::from_domain(A.unite(B));
247 	node = node.child(0);
248 
249 	isl::union_set_list filters(ctx, 0);
250 	filters = filters.add(A).add(B);
251 	node = node.insert_sequence(filters);
252 
253 	isl::multi_union_pw_aff f_A(ctx, "[ { A[i] -> [i] } ]");
254 	node = node.child(0);
255 	node = node.child(0);
256 	node = node.insert_partial_schedule(f_A);
257 	auto band = node.as<isl::schedule_node_band>();
258 	band = band.member_set_coincident(0, true);
259 	node = band.ancestor(2);
260 
261 	isl::multi_union_pw_aff f_B(ctx, "[ { B[i] -> [i] } ]");
262 	node = node.child(1);
263 	node = node.child(0);
264 	node = node.insert_partial_schedule(f_B);
265 	node = node.ancestor(2);
266 
267 	return node.schedule();
268 }
269 
270 /* Test basic schedule tree functionality that is independent
271  * of the type of bindings.
272  *
273  * In particular, create a simple schedule tree and
274  * - check that the root node is a domain node
275  * - check that an object of a subclass can be used as one of the superclass
276  * - test map_descendant_bottom_up in the successful case
277  */
test_schedule_tree_generic(isl::ctx ctx)278 static isl::schedule_node test_schedule_tree_generic(isl::ctx ctx)
279 {
280 	auto schedule = construct_schedule_tree(ctx);
281 	auto root = schedule.root();
282 
283 	assert(IS_TRUE(root.isa<isl::schedule_node_domain>()));
284 	root = root.as<isl::schedule_node_domain>().child(0).parent();
285 
286 	int count = 0;
287 	auto inc_count = [&count](isl::schedule_node node) {
288 		count++;
289 		return node;
290 	};
291 	root = root.map_descendant_bottom_up(inc_count);
292 	assert(count == 8);
293 
294 	return root;
295 }
296 
297 /* Test marking band members for unrolling.
298  * "schedule" is the schedule created by construct_schedule_tree.
299  * It schedules two statements, with 10 and 20 instances, respectively.
300  * Unrolling all band members therefore results in 30 at-domain calls
301  * by the AST generator.
302  */
test_ast_build_unroll(isl::schedule schedule)303 static void test_ast_build_unroll(isl::schedule schedule)
304 {
305 	auto root = schedule.root();
306 	auto mark_unroll = [](isl::schedule_node node) {
307 		if (IS_TRUE(node.isa<isl::schedule_node_band>())) {
308 			auto band = node.as<isl::schedule_node_band>();
309 			node = band.member_set_ast_loop_unroll(0);
310 		}
311 		return node;
312 	};
313 	root = root.map_descendant_bottom_up(mark_unroll);
314 	schedule = root.schedule();
315 
316 	int count_ast = 0;
317 	auto inc_count_ast =
318 	    [&count_ast](isl::ast_node node, isl::ast_build build) {
319 		count_ast++;
320 		return node;
321 	};
322 	auto build = isl::ast_build(schedule.ctx());
323 	build = build.set_at_each_domain(inc_count_ast);
324 	auto ast = build.node_from(schedule);
325 	assert(count_ast == 30);
326 }
327 
328 /* Test basic AST generation from a schedule tree that is independent
329  * of the type of bindings.
330  *
331  * In particular, create a simple schedule tree and
332  * - generate an AST from the schedule tree
333  * - test at_each_domain in the successful case
334  * - test unrolling
335  */
test_ast_build_generic(isl::ctx ctx)336 static isl::schedule test_ast_build_generic(isl::ctx ctx)
337 {
338 	auto schedule = construct_schedule_tree(ctx);
339 
340 	int count_ast = 0;
341 	auto inc_count_ast =
342 	    [&count_ast](isl::ast_node node, isl::ast_build build) {
343 		count_ast++;
344 		return node;
345 	};
346 	auto build = isl::ast_build(ctx);
347 	auto build_copy = build.set_at_each_domain(inc_count_ast);
348 	auto ast = build.node_from(schedule);
349 	assert(count_ast == 0);
350 	count_ast = 0;
351 	ast = build_copy.node_from(schedule);
352 	assert(count_ast == 2);
353 	build = build_copy;
354 	count_ast = 0;
355 	ast = build.node_from(schedule);
356 	assert(count_ast == 2);
357 
358 	test_ast_build_unroll(schedule);
359 
360 	return schedule;
361 }
362 
363 /* Test basic AST expression generation from an affine expression.
364  */
test_ast_build_expr(isl::ctx ctx)365 static void test_ast_build_expr(isl::ctx ctx)
366 {
367 	isl::pw_aff pa(ctx, "[n] -> { [n + 1] }");
368 	isl::ast_build build = isl::ast_build::from_context(pa.domain());
369 
370 	auto expr = build.expr_from(pa);
371 	auto op = expr.as<isl::ast_expr_op>();
372 	assert(IS_TRUE(op.isa<isl::ast_expr_op_add>()));
373 	assert(SIZE_VAL(op.n_arg()) == 2);
374 }
375