| 1 | /* |
| 2 | * Copyright 2010 INRIA Saclay |
| 3 | * Copyright 2013 Ecole Normale Superieure |
| 4 | * |
| 5 | * Use of this software is governed by the MIT license |
| 6 | * |
| 7 | * Written by Sven Verdoolaege, INRIA Saclay - Ile-de-France, |
| 8 | * Parc Club Orsay Universite, ZAC des vignes, 4 rue Jacques Monod, |
| 9 | * 91893 Orsay, France |
| 10 | * and Ecole Normale Superieure, 45 rue d'Ulm, 75230 Paris, France |
| 11 | */ |
| 12 | |
| 13 | #include <isl/hash.h> |
| 14 | #include <isl_union_macro.h> |
| 15 | |
| 16 | /* A union of expressions defined over different domain spaces. |
| 17 | * "space" describes the parameters. |
| 18 | * The entries of "table" are keyed on the domain space of the entry |
| 19 | * (ignoring parameters). |
| 20 | */ |
| 21 | struct UNION { |
| 22 | int ref; |
| 23 | #ifdef HAS_TYPE |
| 24 | enum isl_fold type; |
| 25 | #endif |
| 26 | isl_space *space; |
| 27 | |
| 28 | struct isl_hash_table table; |
| 29 | }; |
| 30 | |
| 31 | /* Return the number of base expressions in "u". |
| 32 | */ |
| 33 | isl_size FN(FN(UNION,n),BASE)(__isl_keep UNION *u) |
| 34 | { |
| 35 | return u ? u->table.n : isl_size_error; |
| 36 | } |
| 37 | |
| 38 | S(UNION,foreach_data) |
| 39 | { |
| 40 | isl_stat (*fn)(__isl_take PART *part, void *user); |
| 41 | void *user; |
| 42 | }; |
| 43 | |
| 44 | static isl_stat FN(UNION,call_on_copy)(void **entry, void *user) |
| 45 | { |
| 46 | PART *part = *entry; |
| 47 | S(UNION,foreach_data) *data = (S(UNION,foreach_data) *)user; |
| 48 | |
| 49 | part = FN(PART,copy)(pw: part); |
| 50 | if (!part) |
| 51 | return isl_stat_error; |
| 52 | return data->fn(part, data->user); |
| 53 | } |
| 54 | |
| 55 | isl_stat FN(FN(UNION,foreach),BASE)(__isl_keep UNION *u, |
| 56 | isl_stat (*fn)(__isl_take PART *part, void *user), void *user) |
| 57 | { |
| 58 | S(UNION,foreach_data) data = { fn, user }; |
| 59 | |
| 60 | if (!u) |
| 61 | return isl_stat_error; |
| 62 | |
| 63 | return isl_hash_table_foreach(ctx: u->space->ctx, table: &u->table, |
| 64 | fn: &FN(UNION,call_on_copy), user: &data); |
| 65 | } |
| 66 | |
| 67 | /* Is the domain space of "entry" equal to the domain of "space", |
| 68 | * ignoring parameters? |
| 69 | */ |
| 70 | static isl_bool FN(UNION,has_same_domain_space_tuples)(const void *entry, |
| 71 | const void *val) |
| 72 | { |
| 73 | PART *part = (PART *)entry; |
| 74 | isl_space *space = (isl_space *) val; |
| 75 | |
| 76 | if (isl_space_is_set(space)) |
| 77 | return isl_space_is_set(space: part->dim); |
| 78 | |
| 79 | return isl_space_tuple_is_equal(space1: part->dim, type1: isl_dim_in, |
| 80 | space2: space, type2: isl_dim_in); |
| 81 | } |
| 82 | |
| 83 | /* Return the entry, if any, in "u" that lives in "space". |
| 84 | * If "reserve" is set, then an entry is created if it does not exist yet. |
| 85 | * Return NULL on error and isl_hash_table_entry_none if no entry was found. |
| 86 | * Note that when "reserve" is set, the function will never return |
| 87 | * isl_hash_table_entry_none. |
| 88 | * |
| 89 | * First look for the entry (if any) with the same domain space. |
| 90 | * If it exists, then check if the range space also matches. |
| 91 | */ |
| 92 | static struct isl_hash_table_entry *FN(UNION,find_part_entry)( |
| 93 | __isl_keep UNION *u, __isl_keep isl_space *space, int reserve) |
| 94 | { |
| 95 | isl_ctx *ctx; |
| 96 | uint32_t hash; |
| 97 | struct isl_hash_table_entry *entry; |
| 98 | isl_bool equal; |
| 99 | PART *part; |
| 100 | |
| 101 | if (!u || !space) |
| 102 | return NULL; |
| 103 | |
| 104 | ctx = FN(UNION,get_ctx)(upwqp: u); |
| 105 | hash = isl_space_get_tuple_domain_hash(space); |
| 106 | entry = isl_hash_table_find(ctx, table: &u->table, key_hash: hash, |
| 107 | eq: &FN(UNION,has_same_domain_space_tuples), val: space, reserve); |
| 108 | if (!entry || entry == isl_hash_table_entry_none) |
| 109 | return entry; |
| 110 | if (reserve && !entry->data) |
| 111 | return entry; |
| 112 | part = entry->data; |
| 113 | equal = isl_space_tuple_is_equal(space1: part->dim, type1: isl_dim_out, |
| 114 | space2: space, type2: isl_dim_out); |
| 115 | if (equal < 0) |
| 116 | return NULL; |
| 117 | if (equal) |
| 118 | return entry; |
| 119 | if (!reserve) |
| 120 | return isl_hash_table_entry_none; |
| 121 | isl_die(FN(UNION,get_ctx)(u), isl_error_invalid, |
| 122 | "union expression can only contain a single " |
| 123 | "expression over a given domain" , return NULL); |
| 124 | } |
| 125 | |
| 126 | /* Remove "part_entry" from the hash table of "u". |
| 127 | */ |
| 128 | static __isl_give UNION *FN(UNION,remove_part_entry)(__isl_take UNION *u, |
| 129 | struct isl_hash_table_entry *part_entry) |
| 130 | { |
| 131 | isl_ctx *ctx; |
| 132 | |
| 133 | if (!u || !part_entry) |
| 134 | return FN(UNION,free)(upwqp: u); |
| 135 | |
| 136 | FN(PART,free)(pw: part_entry->data); |
| 137 | ctx = FN(UNION,get_ctx)(upwqp: u); |
| 138 | isl_hash_table_remove(ctx, table: &u->table, entry: part_entry); |
| 139 | |
| 140 | return u; |
| 141 | } |
| 142 | |
| 143 | /* Check that the domain of "part" is disjoint from the domain of the entries |
| 144 | * in "u" that are defined on the same domain space, but have a different |
| 145 | * target space. |
| 146 | * Since a UNION with a single entry per domain space is not allowed |
| 147 | * to contain two entries with the same domain space, there cannot be |
| 148 | * any such other entry. |
| 149 | */ |
| 150 | static isl_stat FN(UNION,check_disjoint_domain_other)(__isl_keep UNION *u, |
| 151 | __isl_keep PART *part) |
| 152 | { |
| 153 | return isl_stat_ok; |
| 154 | } |
| 155 | |
| 156 | /* Check that the domain of "part1" is disjoint from the domain of "part2". |
| 157 | * This check is performed before "part2" is added to a UNION to ensure |
| 158 | * that the UNION expression remains a function. |
| 159 | * Since a UNION with a single entry per domain space is not allowed |
| 160 | * to contain two entries with the same domain space, fail unconditionally. |
| 161 | */ |
| 162 | static isl_stat FN(UNION,check_disjoint_domain)(__isl_keep PART *part1, |
| 163 | __isl_keep PART *part2) |
| 164 | { |
| 165 | isl_die(FN(PART,get_ctx)(part1), isl_error_invalid, |
| 166 | "additional part should live on separate space" , |
| 167 | return isl_stat_error); |
| 168 | } |
| 169 | |
| 170 | /* Call "fn" on each part entry of "u". |
| 171 | */ |
| 172 | static isl_stat FN(UNION,foreach_inplace)(__isl_keep UNION *u, |
| 173 | isl_stat (*fn)(void **part, void *user), void *user) |
| 174 | { |
| 175 | isl_ctx *ctx; |
| 176 | |
| 177 | if (!u) |
| 178 | return isl_stat_error; |
| 179 | ctx = FN(UNION,get_ctx)(upwqp: u); |
| 180 | return isl_hash_table_foreach(ctx, table: &u->table, fn, user); |
| 181 | } |
| 182 | |
| 183 | static isl_bool FN(PART,has_domain_space_tuples)(__isl_keep PART *part, |
| 184 | __isl_keep isl_space *space); |
| 185 | |
| 186 | /* Do the tuples of "space" correspond to those of the domain of "part"? |
| 187 | * That is, is the domain space of "part" equal to "space", ignoring parameters? |
| 188 | */ |
| 189 | static isl_bool FN(UNION,has_domain_space_tuples)(const void *entry, |
| 190 | const void *val) |
| 191 | { |
| 192 | PART *part = (PART *)entry; |
| 193 | isl_space *space = (isl_space *) val; |
| 194 | |
| 195 | return FN(PART,has_domain_space_tuples)(part, space); |
| 196 | } |
| 197 | |
| 198 | /* Call "fn" on each part of "u" that has domain space "space". |
| 199 | * |
| 200 | * Since each entry is defined over a different domain space, |
| 201 | * simply look for an entry with the given domain space and |
| 202 | * call "fn" on the corresponding part if there is one. |
| 203 | */ |
| 204 | isl_stat FN(UNION,foreach_on_domain)(__isl_keep UNION *u, |
| 205 | __isl_keep isl_space *space, |
| 206 | isl_stat (*fn)(__isl_take PART *part, void *user), void *user) |
| 207 | { |
| 208 | uint32_t hash; |
| 209 | struct isl_hash_table_entry *entry; |
| 210 | |
| 211 | if (!u || !space) |
| 212 | return isl_stat_error; |
| 213 | hash = isl_space_get_tuple_hash(space); |
| 214 | entry = isl_hash_table_find(FN(UNION,get_ctx)(upwqp: u), table: &u->table, |
| 215 | key_hash: hash, eq: &FN(UNION,has_domain_space_tuples), |
| 216 | val: space, reserve: 0); |
| 217 | if (!entry) |
| 218 | return isl_stat_error; |
| 219 | if (entry == isl_hash_table_entry_none) |
| 220 | return isl_stat_ok; |
| 221 | return fn(FN(PART,copy)(pw: entry->data), user); |
| 222 | } |
| 223 | |
| 224 | static isl_stat FN(UNION,free_u_entry)(void **entry, void *user) |
| 225 | { |
| 226 | PART *part = *entry; |
| 227 | FN(PART,free)(pw: part); |
| 228 | return isl_stat_ok; |
| 229 | } |
| 230 | |
| 231 | #include <isl_union_templ.c> |
| 232 | |