Thorin 1.9.0
The Higher ORder INtermediate representation
Loading...
Searching...
No Matches
normalizers.cpp
Go to the documentation of this file.
1#include <thorin/normalize.h>
2
5
7
8namespace thorin::plug::core {
9
10namespace {
11
12// clang-format off
13// See https://stackoverflow.com/a/64354296 for static_assert trick below.
14template<class Id, Id id, nat_t w>
15Res fold(u64 a, u64 b, [[maybe_unused]] bool nsw, [[maybe_unused]] bool nuw) {
16 using ST = w2s<w>;
17 using UT = w2u<w>;
18 auto s = thorin::bitcast<ST>(a), t = thorin::bitcast<ST>(b);
19 auto u = thorin::bitcast<UT>(a), v = thorin::bitcast<UT>(b);
20
21 if constexpr (std::is_same_v<Id, wrap>) {
22 if constexpr (id == wrap::add) {
23 auto res = u + v;
24 if (nuw && res < u) return {};
25 // TODO nsw
26 return res;
27 } else if constexpr (id == wrap::sub) {
28 auto res = u - v;
29 // TODO nsw
30 return res;
31 } else if constexpr (id == wrap::mul) {
32 if constexpr (std::is_same_v<UT, bool>)
33 return UT(u & v);
34 else
35 return UT(u * v);
36 } else if constexpr (id == wrap::shl) {
37 if (b >= w) return {};
38 decltype(u) res;
39 if constexpr (std::is_same_v<UT, bool>)
40 res = bool(u64(u) << u64(v));
41 else
42 res = u << v;
43 if (nuw && res < u) return {};
44 if (nsw && get_sign(u) != get_sign(res)) return {};
45 return res;
46 } else {
47 []<bool flag = false>() { static_assert(flag, "missing sub tag"); }();
48 }
49 } else if constexpr (std::is_same_v<Id, shr>) {
50 if (b >= w) return {};
51 if constexpr (false) {}
52 else if constexpr (id == shr::a) return s >> t;
53 else if constexpr (id == shr::l) return u >> v;
54 else []<bool flag = false>() { static_assert(flag, "missing sub tag"); }();
55 } else if constexpr (std::is_same_v<Id, div>) {
56 if (b == 0) return {};
57 if constexpr (false) {}
58 else if constexpr (id == div::sdiv) return s / t;
59 else if constexpr (id == div::udiv) return u / v;
60 else if constexpr (id == div::srem) return s % t;
61 else if constexpr (id == div::urem) return u % v;
62 else []<bool flag = false>() { static_assert(flag, "missing sub tag"); }();
63 } else if constexpr (std::is_same_v<Id, icmp>) {
64 bool res = false;
65 auto pm = !(u >> UT(w - 1)) && (v >> UT(w - 1));
66 auto mp = (u >> UT(w - 1)) && !(v >> UT(w - 1));
67 res |= ((id & icmp::Xygle) != icmp::f) && pm;
68 res |= ((id & icmp::xYgle) != icmp::f) && mp;
69 res |= ((id & icmp::xyGle) != icmp::f) && u > v && !mp;
70 res |= ((id & icmp::xygLe) != icmp::f) && u < v && !pm;
71 res |= ((id & icmp::xyglE) != icmp::f) && u == v;
72 return res;
73 } else if constexpr (std::is_same_v<Id, extrema>) {
74 if constexpr (false) {}
75 else if(id == extrema::sm) return std::min(u, v);
76 else if(id == extrema::Sm) return std::min(s, t);
77 else if(id == extrema::sM) return std::max(u, v);
78 else if(id == extrema::SM) return std::max(s, t);
79 } else {
80 []<bool flag = false>() { static_assert(flag, "missing tag"); }();
81 }
82}
83// clang-format on
84
85// Note that @p a and @p b are passed by reference as fold also commutes if possible.
86template<class Id, Id id> Ref fold(World& world, Ref type, const Def*& a, const Def*& b, Ref mode = {}) {
87 if (a->isa<Bot>() || b->isa<Bot>()) return world.bot(type);
88
89 if (auto la = Lit::isa(a)) {
90 if (auto lb = Lit::isa(b)) {
91 auto size = Lit::as(Idx::size(a->type()));
92 auto width = Idx::size2bitwidth(size);
93 bool nsw = false, nuw = false;
94 if constexpr (std::is_same_v<Id, wrap>) {
95 auto m = mode ? Lit::as(mode) : 0_n;
96 nsw = m & Mode::nsw;
97 nuw = m & Mode::nuw;
98 }
99
100 Res res;
101 switch (width) {
102#define CODE(i) \
103 case i: res = fold<Id, id, i>(*la, *lb, nsw, nuw); break;
105#undef CODE
106 default:
107 // TODO this is super rough but at least better than just bailing out
108 res = fold<Id, id, 64>(*la, *lb, false, false);
109 if (res && !std::is_same_v<Id, icmp>) *res %= size;
110 }
111
112 return res ? world.lit(type, *res) : world.bot(type);
113 }
114 }
115
117 return nullptr;
118}
119
120template<class Id, nat_t w> Res fold(u64 a, [[maybe_unused]] bool nsw, [[maybe_unused]] bool nuw) {
121 using ST = w2s<w>;
122 auto s = thorin::bitcast<ST>(a);
123
124 if constexpr (std::is_same_v<Id, abs>) {
125 return std::abs(s);
126 } else {
127 []<bool flag = false>() { static_assert(flag, "missing tag"); }
128 ();
129 }
130}
131
132template<class Id> Ref fold(World& world, Ref type, const Def*& a) {
133 if (a->isa<Bot>()) return world.bot(type);
134
135 if (auto la = Lit::isa(a)) {
136 auto size = Lit::as(Idx::size(a->type()));
137 auto width = Idx::size2bitwidth(size);
138 bool nsw = false, nuw = false;
139 Res res;
140 switch (width) {
141#define CODE(i) \
142 case i: res = fold<Id, i>(*la, nsw, nuw); break;
144#undef CODE
145 default:
146 res = fold<Id, 64>(*la, false, false);
147 if (res && !std::is_same_v<Id, icmp>) *res %= size;
148 }
149
150 return res ? world.lit(type, *res) : world.bot(type);
151 }
152 return nullptr;
153}
154
155/// Reassociates @p a and @p b according to following rules.
156/// We use the following naming convention while literals are prefixed with an `l`:
157/// ```
158/// a op b
159/// (x op y) op (z op w)
160///
161/// (1) la op (lz op w) -> (la op lz) op w
162/// (2) (lx op y) op (lz op w) -> (lx op lz) op (y op w)
163/// (3) a op (lz op w) -> lz op (a op w)
164/// (4) (lx op y) op b -> lx op (y op b)
165/// ```
166template<class Id> Ref reassociate(Id id, World& world, [[maybe_unused]] const App* ab, Ref a, Ref b) {
167 if (!is_associative(id)) return nullptr;
168
169 auto xy = match<Id>(id, a);
170 auto zw = match<Id>(id, b);
171 auto la = a->isa<Lit>();
172 auto [x, y] = xy ? xy->template args<2>() : std::array<const Def*, 2>{nullptr, nullptr};
173 auto [z, w] = zw ? zw->template args<2>() : std::array<const Def*, 2>{nullptr, nullptr};
174 auto lx = Lit::isa(x);
175 auto lz = Lit::isa(z);
176
177 // if we reassociate, we have to forget about nsw/nuw
178 auto make_op = [&world, id](Ref a, Ref b) { return world.call(id, Mode::none, Defs{a, b}); };
179
180 if (la && lz) return make_op(make_op(a, z), w); // (1)
181 if (lx && lz) return make_op(make_op(x, z), make_op(y, w)); // (2)
182 if (lz) return make_op(z, make_op(a, w)); // (3)
183 if (lx) return make_op(x, make_op(y, b)); // (4)
184 return nullptr;
185}
186
187template<class Id> Ref merge_cmps(std::array<std::array<u64, 2>, 2> tab, Ref a, Ref b) {
188 static_assert(sizeof(sub_t) == 1, "if this ever changes, please adjust the logic below");
189 static constexpr size_t num_bits = std::bit_width(Annex::Num<Id> - 1_u64);
190
191 auto& world = a->world();
192 auto a_cmp = match<Id>(a);
193 auto b_cmp = match<Id>(b);
194
195 if (a_cmp && b_cmp && a_cmp->arg() == b_cmp->arg()) {
196 // push sub bits of a_cmp and b_cmp through truth table
197 sub_t res = 0;
198 sub_t a_sub = a_cmp.sub();
199 sub_t b_sub = b_cmp.sub();
200 for (size_t i = 0; i != num_bits; ++i, res >>= 1, a_sub >>= 1, b_sub >>= 1)
201 res |= tab[a_sub & 1][b_sub & 1] << 7_u8;
202 res >>= (7_u8 - u8(num_bits));
203
204 if constexpr (std::is_same_v<Id, math::cmp>)
205 return world.call(math::cmp(res), /*mode*/ a_cmp->decurry()->arg(), a_cmp->arg());
206 else
207 return world.call(icmp(Annex::Base<icmp> | res), a_cmp->arg());
208 }
209
210 return nullptr;
211}
212
213} // namespace
214
215template<nat id> Ref normalize_nat(Ref type, Ref callee, Ref arg) {
216 auto& world = type->world();
217 auto [a, b] = arg->projs<2>();
218 if (is_commutative(id)) commute(a, b);
219 auto la = Lit::isa(a);
220 auto lb = Lit::isa(b);
221
222 if (la) {
223 if (lb) {
224 switch (id) {
225 case nat::add: return world.lit_nat(*la + *lb);
226 case nat::sub: return *la < *lb ? world.lit_nat_0() : world.lit_nat(*la - *lb);
227 case nat::mul: return world.lit_nat(*la * *lb);
228 }
229 }
230
231 if (*la == 0) {
232 switch (id) {
233 case nat::add: return b;
234 case nat::sub: return a; // 0 - b = 0
235 case nat::mul: return a; // 0 * b = 0
236 }
237 }
238
239 if (*la == 1 && id == nat::mul) return b; // 1 * b = b
240 }
241
242 if (lb && *lb == 0 && id == nat::sub) return a; // a - 0 = a
243
244 return world.raw_app(type, callee, {a, b});
245}
246
247template<ncmp id> Ref normalize_ncmp(Ref type, Ref callee, Ref arg) {
248 auto& world = type->world();
249
250 if (id == ncmp::t) return world.lit_tt();
251 if (id == ncmp::f) return world.lit_ff();
252
253 auto [a, b] = arg->projs<2>();
254 if (is_commutative(id)) commute(a, b);
255
256 if (auto la = Lit::isa(a)) {
257 if (auto lb = Lit::isa(b)) {
258 // clang-format off
259 switch (id) {
260 case ncmp:: e: return world.lit_nat(*la == *lb);
261 case ncmp::ne: return world.lit_nat(*la != *lb);
262 case ncmp::l : return world.lit_nat(*la < *lb);
263 case ncmp::le: return world.lit_nat(*la <= *lb);
264 case ncmp::g : return world.lit_nat(*la > *lb);
265 case ncmp::ge: return world.lit_nat(*la >= *lb);
266 default: fe::unreachable();
267 }
268 // clang-format on
269 }
270 }
271
272 return world.raw_app(type, callee, {a, b});
273}
274
275template<icmp id> Ref normalize_icmp(Ref type, Ref c, Ref arg) {
276 auto& world = type->world();
277 auto callee = c->as<App>();
278 auto [a, b] = arg->projs<2>();
279
280 if (auto result = fold<icmp, id>(world, type, a, b)) return result;
281 if (id == icmp::f) return world.lit_ff();
282 if (id == icmp::t) return world.lit_tt();
283 if (a == b) {
284 if (id & (icmp::e & 0xff)) return world.lit_tt();
285 if (id == icmp::ne) return world.lit_ff();
286 }
287
288 return world.raw_app(type, callee, {a, b});
289}
290
291template<extrema id> Ref normalize_extrema(Ref type, Ref c, Ref arg) {
292 auto& world = type->world();
293 auto callee = c->as<App>();
294 auto [a, b] = arg->projs<2>();
295 if (auto result = fold<extrema, id>(world, type, a, b)) return result;
296 return world.raw_app(type, callee, {a, b});
297}
298
299Ref normalize_abs(Ref type, Ref c, Ref arg) {
300 auto& world = type->world();
301 auto callee = c->as<App>();
302 auto [mem, a] = arg->projs<2>();
303 auto [_, actual_type] = type->projs<2>();
304 auto make_res = [&, mem = mem](Ref res) { return world.tuple({mem, res}); };
305
306 if (auto result = fold<abs>(world, actual_type, a)) return make_res(result);
307 return world.raw_app(type, callee, arg);
308}
309
310template<bit1 id> Ref normalize_bit1(Ref type, Ref c, Ref a) {
311 auto& world = type->world();
312 auto callee = c->as<App>();
313 auto s = callee->decurry()->arg();
314 // TODO cope with wrap around
315
316 if constexpr (id == bit1::id) return a;
317
318 if (auto ls = Lit::isa(s)) {
319 switch (id) {
320 case bit1::f: return world.lit_idx(*ls, 0);
321 case bit1::t: return world.lit_idx(*ls, *ls - 1_u64);
322 case bit1::id: fe::unreachable();
323 default: break;
324 }
325
326 assert(id == bit1::neg);
327 if (auto la = Lit::isa(a)) return world.lit_idx_mod(*ls, ~*la);
328 }
329
330 return world.raw_app(type, callee, a);
331}
332
333template<bit2 id> Ref normalize_bit2(Ref type, Ref c, Ref arg) {
334 auto& world = type->world();
335 auto callee = c->as<App>();
336 auto [a, b] = arg->projs<2>();
337 auto s = callee->decurry()->arg();
338 auto ls = Lit::isa(s);
339 // TODO cope with wrap around
340
341 if (is_commutative(id)) commute(a, b);
342
343 auto tab = make_truth_table(id);
344 if (auto res = merge_cmps<icmp>(tab, a, b)) return res;
345 if (auto res = merge_cmps<math::cmp>(tab, a, b)) return res;
346
347 auto la = Lit::isa(a);
348 auto lb = Lit::isa(b);
349
350 // clang-format off
351 switch (id) {
352 case bit2:: f: return world.lit(type, 0);
353 case bit2:: t: if (ls) return world.lit(type, *ls-1_u64); break;
354 case bit2:: fst: return a;
355 case bit2:: snd: return b;
356 case bit2:: nfst: return world.call(bit1::neg, s, a);
357 case bit2:: nsnd: return world.call(bit1::neg, s, b);
358 case bit2:: ciff: return world.call(bit2:: iff, s, Defs{b, a});
359 case bit2::nciff: return world.call(bit2::niff, s, Defs{b, a});
360 default: break;
361 }
362
363 if (la && lb && ls) {
364 switch (id) {
365 case bit2::and_: return world.lit_idx (*ls, *la & *lb);
366 case bit2:: or_: return world.lit_idx (*ls, *la | *lb);
367 case bit2::xor_: return world.lit_idx (*ls, *la ^ *lb);
368 case bit2::nand: return world.lit_idx_mod(*ls, ~(*la & *lb));
369 case bit2:: nor: return world.lit_idx_mod(*ls, ~(*la | *lb));
370 case bit2::nxor: return world.lit_idx_mod(*ls, ~(*la ^ *lb));
371 case bit2:: iff: return world.lit_idx_mod(*ls, ~ *la | *lb);
372 case bit2::niff: return world.lit_idx (*ls, *la & ~*lb);
373 default: fe::unreachable();
374 }
375 }
376
377 // TODO rewrite using bit2
378 auto unary = [&](bool x, bool y, Ref a) -> Ref {
379 if (!x && !y) return world.lit(type, 0);
380 if ( x && y) return ls ? world.lit(type, *ls-1_u64) : nullptr;
381 if (!x && y) return a;
382 if ( x && !y && id != bit2::xor_) return world.call(bit1::neg, s, a);
383 return nullptr;
384 };
385 // clang-format on
386
387 if (is_commutative(id) && a == b) {
388 if (auto res = unary(tab[0][0], tab[1][1], a)) return res;
389 }
390
391 if (la) {
392 if (*la == 0) {
393 if (auto res = unary(tab[0][0], tab[0][1], b)) return res;
394 } else if (ls && *la == *ls - 1_u64) {
395 if (auto res = unary(tab[1][0], tab[1][1], b)) return res;
396 }
397 }
398
399 if (lb) {
400 if (*lb == 0) {
401 if (auto res = unary(tab[0][0], tab[1][0], a)) return res;
402 } else if (ls && *lb == *ls - 1_u64) {
403 if (auto res = unary(tab[0][1], tab[1][1], a)) return res;
404 }
405 }
406
407 if (auto res = reassociate<bit2>(id, world, callee, a, b)) return res;
408
409 return world.raw_app(type, callee, {a, b});
410}
411
412Ref normalize_idx(Ref type, Ref c, Ref arg) {
413 auto& world = type->world();
414 auto callee = c->as<App>();
415 if (auto i = Lit::isa(arg)) {
416 if (auto s = Lit::isa(Idx::size(type))) {
417 if (*i < *s) return world.lit_idx(*s, *i);
418 if (auto m = Lit::isa(callee->arg())) return *m ? world.bot(type) : world.lit_idx_mod(*s, *i);
419 }
420 }
421
422 return world.raw_app(type, c, arg);
423}
424
425template<shr id> Ref normalize_shr(Ref type, Ref c, Ref arg) {
426 auto& world = type->world();
427 auto callee = c->as<App>();
428 auto [a, b] = arg->projs<2>();
429 auto s = Idx::size(arg->type());
430 auto ls = Lit::isa(s);
431
432 if (auto result = fold<shr, id>(world, type, a, b)) return result;
433
434 if (auto la = Lit::isa(a); la && *la == 0) {
435 switch (id) {
436 case shr::a: return a;
437 case shr::l: return a;
438 }
439 }
440
441 if (auto lb = Lit::isa(b)) {
442 if (ls && *lb > *ls) return world.bot(type);
443
444 if (*lb == 0) {
445 switch (id) {
446 case shr::a: return a;
447 case shr::l: return a;
448 }
449 }
450 }
451
452 return world.raw_app(type, callee, {a, b});
453}
454
455template<wrap id> Ref normalize_wrap(Ref type, Ref c, Ref arg) {
456 auto& world = type->world();
457 auto callee = c->as<App>();
458 auto [a, b] = arg->projs<2>();
459 auto mode = callee->arg();
460 auto s = Idx::size(a->type());
461 auto ls = Lit::isa(s);
462
463 if (auto result = fold<wrap, id>(world, type, a, b)) return result;
464
465 // clang-format off
466 if (auto la = Lit::isa(a)) {
467 if (*la == 0) {
468 switch (id) {
469 case wrap::add: return b; // 0 + b -> b
470 case wrap::sub: break;
471 case wrap::mul: return a; // 0 * b -> 0
472 case wrap::shl: return a; // 0 << b -> 0
473 }
474 } else if (*la == 1) {
475 switch (id) {
476 case wrap::add: break;
477 case wrap::sub: break;
478 case wrap::mul: return b; // 1 * b -> b
479 case wrap::shl: break;
480 }
481 }
482 }
483
484 if (auto lb = Lit::isa(b)) {
485 if (*lb == 0) {
486 switch (id) {
487 case wrap::sub: return a; // a - 0 -> a
488 case wrap::shl: return a; // a >> 0 -> a
489 default: fe::unreachable();
490 // add, mul are commutative, the literal has been normalized to the left
491 }
492 }
493
494 if (auto lm = Lit::isa(mode); lm && *lm == 0 && id == wrap::sub)
495 return world.call(wrap::add, mode, Defs{a, world.lit_idx_mod(*ls, ~*lb + 1_u64)}); // a - lb -> a + (~lb + 1)
496 else if (id == wrap::shl && ls && *lb > *ls)
497 return world.bot(type);
498 }
499
500 if (a == b) {
501 switch (id) {
502 case wrap::add: return world.call(wrap::mul, mode, Defs{world.lit(type, 2), a}); // a + a -> 2 * a
503 case wrap::sub: return world.lit(type, 0); // a - a -> 0
504 case wrap::mul: break;
505 case wrap::shl: break;
506 }
507 }
508 // clang-format on
509
510 if (auto res = reassociate<wrap>(id, world, callee, a, b)) return res;
511
512 return world.raw_app(type, callee, {a, b});
513}
514
515template<div id> Ref normalize_div(Ref full_type, Ref c, Ref arg) {
516 auto& world = full_type->world();
517 auto callee = c->as<App>();
518 auto [mem, ab] = arg->projs<2>();
519 auto [a, b] = ab->projs<2>();
520 auto [_, type] = full_type->projs<2>(); // peel off actual type
521 auto make_res = [&, mem = mem](Ref res) { return world.tuple({mem, res}); };
522
523 if (auto result = fold<div, id>(world, type, a, b)) return make_res(result);
524
525 if (auto la = Lit::isa(a)) {
526 if (*la == 0) return make_res(a); // 0 / b -> 0 and 0 % b -> 0
527 }
528
529 if (auto lb = Lit::isa(b)) {
530 if (*lb == 0) return make_res(world.bot(type)); // a / 0 -> ⊥ and a % 0 -> ⊥
531
532 if (*lb == 1) {
533 switch (id) {
534 case div::sdiv: return make_res(a); // a / 1 -> a
535 case div::udiv: return make_res(a); // a / 1 -> a
536 case div::srem: return make_res(world.lit(type, 0)); // a % 1 -> 0
537 case div::urem: return make_res(world.lit(type, 0)); // a % 1 -> 0
538 }
539 }
540 }
541
542 if (a == b) {
543 switch (id) {
544 case div::sdiv: return make_res(world.lit(type, 1)); // a / a -> 1
545 case div::udiv: return make_res(world.lit(type, 1)); // a / a -> 1
546 case div::srem: return make_res(world.lit(type, 0)); // a % a -> 0
547 case div::urem: return make_res(world.lit(type, 0)); // a % a -> 0
548 }
549 }
550
551 return world.raw_app(full_type, callee, arg);
552}
553
554template<conv id> Ref normalize_conv(Ref dst_t, Ref c, Ref x) {
555 auto& world = dst_t->world();
556 auto callee = c->as<App>();
557 auto s_t = x->type()->as<App>();
558 auto d_t = dst_t->as<App>();
559 auto s = s_t->arg();
560 auto d = d_t->arg();
561 auto ls = Lit::isa(s);
562 auto ld = Lit::isa(d);
563
564 if (s_t == d_t) return x;
565 if (x->isa<Bot>()) return world.bot(d_t);
566 if constexpr (id == conv::s) {
567 if (ls && ld && *ld < *ls) return world.call(conv::u, d, x); // just truncate - we don't care for signedness
568 }
569
570 if (auto l = Lit::isa(x); l && ls && ld) {
571 if constexpr (id == conv::u) {
572 if (*ld == 0) return world.lit(d_t, *l); // I64
573 return world.lit(d_t, *l % *ld);
574 }
575
576 auto sw = Idx::size2bitwidth(*ls);
577 auto dw = Idx::size2bitwidth(*ld);
578
579 // clang-format off
580 if (false) {}
581#define M(S, D) \
582 else if (S == sw && D == dw) return world.lit(d_t, w2s<D>(thorin::bitcast<w2s<S>>(*l)));
583 M( 1, 8) M( 1, 16) M( 1, 32) M( 1, 64)
584 M( 8, 16) M( 8, 32) M( 8, 64)
585 M(16, 32) M(16, 64)
586 M(32, 64)
587 else assert(false && "TODO: conversion between different Idx sizes");
588 // clang-format on
589 }
590
591 return world.raw_app(dst_t, callee, x);
592}
593
594Ref normalize_bitcast(Ref dst_t, Ref callee, Ref src) {
595 auto& world = dst_t->world();
596 auto src_t = src->type();
597
598 if (src->isa<Bot>()) return world.bot(dst_t);
599 if (src_t == dst_t) return src;
600
601 if (auto other = match<bitcast>(src))
602 return other->arg()->type() == dst_t ? other->arg() : world.call<bitcast>(dst_t, other->arg());
603
604 if (auto l = Lit::isa(src)) {
605 if (dst_t->isa<Nat>()) return world.lit(dst_t, *l);
606 if (Idx::size(dst_t)) return world.lit(dst_t, *l);
607 }
608
609 return world.raw_app(dst_t, callee, src);
610}
611
612// TODO this currently hard-codes x86_64 ABI
613// TODO in contrast to C, we might want to give singleton types like '.Idx 1' or '[]' a size of 0 and simply nuke each
614// and every occurance of these types in a later phase
615// TODO Pi and others
616template<trait id> Ref normalize_trait(Ref nat, Ref callee, Ref type) {
617 auto& world = type->world();
618 if (auto ptr = match<mem::Ptr>(type)) {
619 return world.lit_nat(8);
620 } else if (type->isa<Pi>()) {
621 return world.lit_nat(8); // Gets lowered to function ptr
622 } else if (auto size = Idx::size(type)) {
623 if (auto w = Idx::size2bitwidth(size)) return world.lit_nat(std::max(1_n, std::bit_ceil(*w) / 8_n));
624 } else if (auto w = math::isa_f(type)) {
625 switch (*w) {
626 case 16: return world.lit_nat(2);
627 case 32: return world.lit_nat(4);
628 case 64: return world.lit_nat(8);
629 default: fe::unreachable();
630 }
631 } else if (type->isa<Sigma>() || type->isa<Meet>()) {
632 u64 offset = 0;
633 u64 align = 1;
634 for (auto t : type->ops()) {
635 auto a = Lit::isa(core::op(trait::align, t));
636 auto s = Lit::isa(core::op(trait::size, t));
637 if (!a || !s) goto out;
638
639 align = std::max(align, *a);
640 offset = pad(offset, *a) + *s;
641 }
642
643 offset = pad(offset, align);
644 u64 size = std::max(1_u64, offset);
645
646 switch (id) {
647 case trait::align: return world.lit_nat(align);
648 case trait::size: return world.lit_nat(size);
649 }
650 } else if (auto arr = type->isa_imm<Arr>()) {
651 auto align = op(trait::align, arr->body());
652 if constexpr (id == trait::align) return align;
653 auto b = op(trait::size, arr->body());
654 if (b->isa<Lit>()) return world.call(nat::mul, Defs{arr->shape(), b});
655 } else if (auto join = type->isa<Join>()) {
656 if (auto sigma = convert(join)) return core::op(id, sigma);
657 }
658
659out:
660 return world.raw_app(nat, callee, type);
661}
662
663Ref normalize_zip(Ref type, Ref c, Ref arg) {
664 auto& w = type->world();
665 auto callee = c->as<App>();
666 auto is_os = callee->arg();
667 auto [n_i, Is, n_o, Os, f] = is_os->projs<5>();
668 auto [r, s] = callee->decurry()->args<2>();
669 auto lr = Lit::isa(r);
670 auto ls = Lit::isa(s);
671
672 // TODO commute
673 // TODO reassociate
674 // TODO more than one Os
675 // TODO select which Is/Os to zip
676
677 if (lr && ls && *lr == 1 && *ls == 1) return w.app(f, arg);
678
679 if (auto l_in = Lit::isa(n_i)) {
680 auto args = arg->projs(*l_in);
681
682 if (lr && std::ranges::all_of(args, [](Ref arg) { return arg->isa<Tuple, Pack>(); })) {
683 auto shapes = s->projs(*lr);
684 auto s_n = Lit::isa(shapes.front());
685
686 if (s_n) {
687 auto elems = DefVec(*s_n, [&, f = f](size_t s_i) {
688 auto inner_args = DefVec(args.size(), [&](size_t i) { return args[i]->proj(*s_n, s_i); });
689 if (*lr == 1) {
690 return w.app(f, inner_args);
691 } else {
692 auto app_zip = w.app(w.annex<zip>(), {w.lit_nat(*lr - 1), w.tuple(shapes.view().subspan(1))});
693 return w.app(w.app(app_zip, is_os), inner_args);
694 }
695 });
696 return w.tuple(elems);
697 }
698 }
699 }
700
701 return w.raw_app(type, callee, arg);
702}
703
704template<pe id> Ref normalize_pe(Ref type, Ref callee, Ref arg) {
705 auto& world = type->world();
706
707 if constexpr (id == pe::known) {
708 if (match(pe::hlt, arg)) return world.lit_ff();
709 if (arg->dep_const()) return world.lit_tt();
710 }
711
712 return world.raw_app(type, callee, arg);
713}
714
716
717} // namespace thorin::plug::core
const App * decurry() const
Returns App::callee again as App.
Definition lam.h:207
const Def * arg() const
Definition lam.h:215
A (possibly paramterized) Array.
Definition tuple.h:50
auto ops() const
Definition def.h:268
auto projs(F f) const
Splits this Def via Def::projections into an Array (if A == -1_n) or std::array (otherwise).
Definition def.h:369
const T * isa_imm() const
Definition def.h:443
const Def * type() const
Yields the raw type of this Def, i.e. maybe nullptr.
Definition def.h:248
bool dep_const() const
Definition def.h:338
World & world() const
Definition def.cpp:421
static Ref size(Ref def)
Checks if def is a .Idx s and returns s or nullptr otherwise.
Definition def.cpp:569
static constexpr nat_t size2bitwidth(nat_t n)
Definition def.h:758
static std::optional< T > isa(Ref def)
Definition def.h:726
static T as(Ref def)
Definition def.h:731
A (possibly paramterized) Tuple.
Definition tuple.h:87
A dependent function type.
Definition lam.h:11
Helper class to retrieve Infer::arg if present.
Definition def.h:87
A dependent tuple type.
Definition tuple.h:9
This is a thin wrapper for std::span<T, N> with the following additional features:
Definition span.h:28
Specific Bound depending on Up.
Definition lattice.h:31
Extremum. Either Top (Up) or Bottom.
Definition lattice.h:130
Data constructor for a Sigma.
Definition tuple.h:39
const Lit * lit_tt()
Definition world.h:406
const Lit * lit_nat(nat_t a)
Definition world.h:367
const Lit * lit_ff()
Definition world.h:405
#define THORIN_core_NORMALIZER_IMPL
Definition autogen.h:380
#define M(S, D)
#define CODE(node, name)
Definition def.h:40
Definition span.h:103
The core Plugin
Definition core.h:8
Ref normalize_ncmp(Ref type, Ref callee, Ref arg)
Ref normalize_icmp(Ref type, Ref c, Ref arg)
Ref normalize_bit1(Ref type, Ref c, Ref a)
Ref normalize_extrema(Ref type, Ref c, Ref arg)
constexpr std::array< std::array< u64, 2 >, 2 > make_truth_table(bit2 id)
Use like this: a op b = tab[a][b]
Definition core.h:53
Ref normalize_bit2(Ref type, Ref c, Ref arg)
Ref normalize_zip(Ref type, Ref c, Ref arg)
@ nuw
No Unsigned Wrap around.
@ nsw
No Signed Wrap around.
Ref normalize_nat(Ref type, Ref callee, Ref arg)
Ref op(trait o, Ref type)
Definition core.h:35
Ref normalize_div(Ref full_type, Ref c, Ref arg)
Ref normalize_idx(Ref type, Ref c, Ref arg)
Ref normalize_trait(Ref nat, Ref callee, Ref type)
Ref normalize_conv(Ref dst_t, Ref c, Ref x)
Ref normalize_abs(Ref type, Ref c, Ref arg)
Ref normalize_wrap(Ref type, Ref c, Ref arg)
Ref normalize_shr(Ref type, Ref c, Ref arg)
Ref normalize_pe(Ref type, Ref callee, Ref arg)
Ref normalize_bitcast(Ref dst_t, Ref callee, Ref src)
const Sigma * convert(const TBound< up > *b)
Definition core.cpp:19
std::optional< nat_t > isa_f(Ref def)
Definition math.h:78
The mem Plugin
Definition mem.h:11
u8 sub_t
Definition types.h:49
uint64_t u64
Definition types.h:35
auto match(Ref def)
Definition axiom.h:105
constexpr bool is_associative(Id id)
Definition axiom.h:135
View< const Def * > Defs
Definition def.h:62
void commute(const Def *&a, const Def *&b)
Swap Lit to left - or smaller Def::gid, if no lit present.
Definition normalize.h:25
u64 pad(u64 offset, u64 align)
Definition util.h:49
constexpr bool is_commutative(Id)
Definition axiom.h:132
bool get_sign(T val)
Definition util.h:40
uint8_t u8
Definition types.h:35
TExt< false > Bot
Definition lattice.h:151
Vector< const Def * > DefVec
Definition def.h:63
static constexpr flags_t Base
Definition plugin.h:131
#define THORIN_1_8_16_32_64(m)
Definition types.h:25