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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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