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