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/typed_cpp.h> 17 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 /* Basic test of the templated interface. 288 * 289 * Intersecting the domain of an access relation 290 * with statement instances should be allowed, 291 * while intersecting the range with statement instances 292 * should result in a compile-time error. 293 */ 294 static void test_typed(isl::ctx ctx) 295 { 296 struct ST {}; 297 struct AR {}; 298 isl::typed::map<ST, AR> access(ctx, "{ S[i, j] -> A[i] }"); 299 isl::typed::set<ST> instances(ctx, "{ S[i, j] : 0 <= i, j < 10 }"); 300 301 #ifndef COMPILE_ERROR 302 access.intersect_domain(instances); 303 #else 304 access.intersect_range(instances); 305 #endif 306 } 307 308 /* Test the (unchecked) isl C++ interface 309 * 310 * This includes: 311 * - The isl C <-> C++ pointer interface 312 * - Object construction 313 * - Different parameter types 314 * - Different return types 315 * - Foreach functions 316 * - Exceptions 317 * - Spaces 318 * - Schedule trees 319 * - AST generation 320 * - AST expression generation 321 * - Templated interface 322 */ 323 int main() 324 { 325 isl_ctx *ctx = isl_ctx_alloc(); 326 327 isl_options_set_on_error(ctx, ISL_ON_ERROR_ABORT); 328 329 test_pointer(ctx); 330 test_constructors(ctx); 331 test_parameters(ctx); 332 test_return(ctx); 333 test_foreach(ctx); 334 test_every(ctx); 335 test_exception(ctx); 336 test_space(ctx); 337 test_schedule_tree(ctx); 338 test_ast_build(ctx); 339 test_ast_build_expr(ctx); 340 test_typed(ctx); 341 342 isl_ctx_free(ctx); 343 344 return EXIT_SUCCESS; 345 } 346