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 #include <vector>
9 #include <string>
10 #include <limits.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 
15 #include <isl/options.h>
16 #include <isl/cpp.h>
17 
die_impl(const char * file,int line,const char * message)18 static void die_impl(const char *file, int line, const char *message)
19 {
20 	fprintf(stderr, "Assertion failed in %s:%d %s\n", file, line, message);
21 	exit(EXIT_FAILURE);
22 }
23 
assert_impl(bool condition,const char * file,int line,const char * message)24 static void assert_impl(bool condition, const char *file, int line,
25 	const char *message)
26 {
27 	if (condition)
28 		return;
29 
30 	return die_impl(file, line, message);
31 }
32 
33 #define die(msg) die_impl(__FILE__, __LINE__, msg)
34 #undef assert
35 #define assert(exp) assert_impl(exp, __FILE__, __LINE__, #exp)
36 
37 #include "isl_test_cpp-generic.cc"
38 
39 /* Test that isl_bool values are returned correctly.
40  *
41  * In particular, check the conversion to bool in case of true and false, and
42  * exception throwing in case of error.
43  */
test_return_bool(isl::ctx ctx)44 static void test_return_bool(isl::ctx ctx)
45 {
46 	isl::set empty(ctx, "{ : false }");
47 	isl::set univ(ctx, "{ : }");
48 	isl::set null;
49 
50 	bool b_true = empty.is_empty();
51 	bool b_false = univ.is_empty();
52 	bool caught = false;
53 	try {
54 		null.is_empty();
55 		die("no exception raised");
56 	} catch (const isl::exception_invalid &e) {
57 		caught = true;
58 	}
59 
60 	assert(b_true);
61 	assert(!b_false);
62 	assert(caught);
63 }
64 
65 /* Test that return values are handled correctly.
66  *
67  * Test that isl C++ objects, integers, boolean values, and strings are
68  * returned correctly.
69  */
test_return(isl::ctx ctx)70 static void test_return(isl::ctx ctx)
71 {
72 	test_return_obj(ctx);
73 	test_return_int(ctx);
74 	test_return_bool(ctx);
75 	test_return_string(ctx);
76 }
77 
78 /* Test that foreach functions are modeled correctly.
79  *
80  * Verify that lambdas are correctly called as callback of a 'foreach'
81  * function and that variables captured by the lambda work correctly. Also
82  * check that the foreach function handles exceptions thrown from
83  * the lambda and that it propagates the exception.
84  */
test_foreach(isl::ctx ctx)85 static void test_foreach(isl::ctx ctx)
86 {
87 	isl::set s(ctx, "{ [0]; [1]; [2] }");
88 
89 	std::vector<isl::basic_set> basic_sets;
90 
91 	auto add_to_vector = [&] (isl::basic_set bs) {
92 		basic_sets.push_back(bs);
93 	};
94 
95 	s.foreach_basic_set(add_to_vector);
96 
97 	assert(basic_sets.size() == 3);
98 	assert(isl::set(basic_sets[0]).is_subset(s));
99 	assert(isl::set(basic_sets[1]).is_subset(s));
100 	assert(isl::set(basic_sets[2]).is_subset(s));
101 	assert(!basic_sets[0].is_equal(basic_sets[1]));
102 
103 	auto fail = [&] (isl::basic_set bs) {
104 		throw "fail";
105 	};
106 
107 	bool caught = false;
108 	try {
109 		s.foreach_basic_set(fail);
110 		die("no exception raised");
111 	} catch (char const *s) {
112 		caught = true;
113 	}
114 	assert(caught);
115 }
116 
117 /* Test the functionality of "every" functions.
118  *
119  * In particular, test the generic functionality and
120  * test that exceptions are properly propagated.
121  */
test_every(isl::ctx ctx)122 static void test_every(isl::ctx ctx)
123 {
124 	isl::union_set us(ctx, "{ A[i]; B[j] }");
125 
126 	test_every_generic(ctx);
127 
128 	auto fail = [] (isl::set s) -> bool {
129 		throw "fail";
130 	};
131 	bool caught = false;
132 	try {
133 		us.every_set(fail);
134 		die("no exception raised");
135 	} catch (char const *s) {
136 		caught = true;
137 	}
138 	assert(caught);
139 }
140 
141 /* Test that an exception is generated for an isl error and
142  * that the error message is captured by the exception.
143  * Also check that the exception can be copied and that copying
144  * does not throw any exceptions.
145  */
test_exception(isl::ctx ctx)146 static void test_exception(isl::ctx ctx)
147 {
148 	isl::multi_union_pw_aff mupa(ctx, "[]");
149 	isl::exception copy;
150 
151 	static_assert(std::is_nothrow_copy_constructible<isl::exception>::value,
152 		"exceptions must be nothrow-copy-constructible");
153 	static_assert(std::is_nothrow_assignable<isl::exception,
154 						isl::exception>::value,
155 		"exceptions must be nothrow-assignable");
156 
157 	try {
158 		auto umap = isl::union_map::from(mupa);
159 	} catch (const isl::exception_unsupported &error) {
160 		die("caught wrong exception");
161 	} catch (const isl::exception &error) {
162 		assert(strstr(error.what(), "without explicit domain"));
163 		copy = error;
164 	}
165 	assert(strstr(copy.what(), "without explicit domain"));
166 }
167 
168 /* Test basic schedule tree functionality.
169  *
170  * In particular, create a simple schedule tree and
171  * - perform some generic tests
172  * - test map_descendant_bottom_up in the failing case
173  * - test foreach_descendant_top_down
174  * - test every_descendant
175  */
test_schedule_tree(isl::ctx ctx)176 static void test_schedule_tree(isl::ctx ctx)
177 {
178 	auto root = test_schedule_tree_generic(ctx);
179 
180 	auto fail_map = [](isl::schedule_node node) {
181 		throw "fail";
182 		return node;
183 	};
184 	auto caught = false;
185 	try {
186 		root.map_descendant_bottom_up(fail_map);
187 		die("no exception raised");
188 	} catch (char const *s) {
189 		caught = true;
190 	}
191 	assert(caught);
192 
193 	int count = 0;
194 	auto inc_count = [&count](isl::schedule_node node) {
195 		count++;
196 		return true;
197 	};
198 	root.foreach_descendant_top_down(inc_count);
199 	assert(count == 8);
200 
201 	count = 0;
202 	auto inc_count_once = [&count](isl::schedule_node node) {
203 		count++;
204 		return false;
205 	};
206 	root.foreach_descendant_top_down(inc_count_once);
207 	assert(count == 1);
208 
209 	auto is_not_domain = [](isl::schedule_node node) {
210 		return !node.isa<isl::schedule_node_domain>();
211 	};
212 	assert(root.child(0).every_descendant(is_not_domain));
213 	assert(!root.every_descendant(is_not_domain));
214 
215 	auto fail = [](isl::schedule_node node) {
216 		throw "fail";
217 		return true;
218 	};
219 	caught = false;
220 	try {
221 		root.every_descendant(fail);
222 		die("no exception raised");
223 	} catch (char const *s) {
224 		caught = true;
225 	}
226 	assert(caught);
227 
228 	auto domain = root.as<isl::schedule_node_domain>().domain();
229 	auto filters = isl::union_set(ctx, "{}");
230 	auto collect_filters = [&filters](isl::schedule_node node) {
231 		if (node.isa<isl::schedule_node_filter>()) {
232 			auto filter = node.as<isl::schedule_node_filter>();
233 			filters = filters.unite(filter.filter());
234 		}
235 		return true;
236 	};
237 	root.every_descendant(collect_filters);
238 	assert(domain.is_equal(filters));
239 }
240 
241 /* Test basic AST generation from a schedule tree.
242  *
243  * In particular, create a simple schedule tree and
244  * - perform some generic tests
245  * - test at_each_domain in the failing case
246  */
test_ast_build(isl::ctx ctx)247 static void test_ast_build(isl::ctx ctx)
248 {
249 	auto schedule = test_ast_build_generic(ctx);
250 
251 	bool do_fail = true;
252 	int count_ast_fail = 0;
253 	auto fail_inc_count_ast =
254 	    [&count_ast_fail, &do_fail](isl::ast_node node,
255 					isl::ast_build build) {
256 		count_ast_fail++;
257 		if (do_fail)
258 			throw "fail";
259 		return node;
260 	};
261 	auto build = isl::ast_build(ctx);
262 	build = build.set_at_each_domain(fail_inc_count_ast);
263 	auto caught = false;
264 	try {
265 		auto ast = build.node_from(schedule);
266 	} catch (char const *s) {
267 		caught = true;
268 	}
269 	assert(caught);
270 	assert(count_ast_fail > 0);
271 	auto build_copy = build;
272 	int count_ast = 0;
273 	auto inc_count_ast =
274 	    [&count_ast](isl::ast_node node, isl::ast_build build) {
275 		count_ast++;
276 		return node;
277 	};
278 	build_copy = build_copy.set_at_each_domain(inc_count_ast);
279 	auto ast = build_copy.node_from(schedule);
280 	assert(count_ast == 2);
281 	count_ast_fail = 0;
282 	do_fail = false;
283 	ast = build.node_from(schedule);
284 	assert(count_ast_fail == 2);
285 }
286 
287 /* Test the (unchecked) isl C++ interface
288  *
289  * This includes:
290  *  - The isl C <-> C++ pointer interface
291  *  - Object construction
292  *  - Different parameter types
293  *  - Different return types
294  *  - Foreach functions
295  *  - Exceptions
296  *  - Spaces
297  *  - Schedule trees
298  *  - AST generation
299  *  - AST expression generation
300  */
main()301 int main()
302 {
303 	isl_ctx *ctx = isl_ctx_alloc();
304 
305 	isl_options_set_on_error(ctx, ISL_ON_ERROR_ABORT);
306 
307 	test_pointer(ctx);
308 	test_constructors(ctx);
309 	test_parameters(ctx);
310 	test_return(ctx);
311 	test_foreach(ctx);
312 	test_every(ctx);
313 	test_exception(ctx);
314 	test_space(ctx);
315 	test_schedule_tree(ctx);
316 	test_ast_build(ctx);
317 	test_ast_build_expr(ctx);
318 
319 	isl_ctx_free(ctx);
320 
321 	return EXIT_SUCCESS;
322 }
323