14template<
class Id, Id
id, nat_t w>
21 if constexpr (std::is_same_v<Id, wrap>) {
24 if (
nuw && res <
u)
return {};
32 if constexpr (std::is_same_v<UT, bool>)
37 if (b >= w)
return {};
39 if constexpr (std::is_same_v<UT, bool>)
43 if (
nuw && res <
u)
return {};
47 []<
bool flag =
false>() {
static_assert(flag,
"missing sub tag"); }();
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) {}
59 else if constexpr (
id ==
div::udiv)
return u / v;
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>) {
65 auto pm = !(
u >> UT(w - 1)) && (v >> UT(w - 1));
66 auto mp = (
u >> UT(w - 1)) && !(v >> UT(w - 1));
73 }
else if constexpr (std::is_same_v<Id, extrema>) {
74 if constexpr (
false) {}
80 []<
bool flag =
false>() {
static_assert(flag,
"missing tag"); }();
86template<
class Id, Id
id>
87const Def*
fold(World& world,
const Def* type,
const Def*&
a,
const Def*& b,
const Def*
mode = {}) {
88 if (
a->isa<
Bot>() || b->isa<
Bot>())
return world.bot(type);
94 bool nsw =
false,
nuw =
false;
95 if constexpr (std::is_same_v<Id, wrap>) {
104 case i: res = fold<Id, id, i>(*la, *lb, nsw, nuw); break;
109 res = fold<Id, id, 64>(*la, *lb,
false,
false);
110 if (res && !std::is_same_v<Id, icmp>) *res %=
size;
113 return res ? world.lit(type, *res) : world.bot(type);
121template<
class Id, nat_t w>
122Res
fold(
u64 a, [[maybe_unused]]
bool nsw, [[maybe_unused]]
bool nuw) {
126 if constexpr (std::is_same_v<Id, abs>)
129 []<
bool flag =
false>() {
static_assert(flag,
"missing tag"); }();
133const Def*
fold(World& world,
const Def* type,
const Def*&
a) {
134 if (
a->isa<
Bot>())
return world.bot(type);
139 bool nsw =
false,
nuw =
false;
143 case i: res = fold<Id, i>(*la, nsw, nuw); break;
147 res = fold<Id, 64>(*la,
false,
false);
148 if (res && !std::is_same_v<Id, icmp>) *res %=
size;
151 return res ? world.lit(type, *res) : world.bot(type);
168const Def* reassociate(Id
id, World& world, [[maybe_unused]]
const App* ab,
const Def*
a,
const Def* b) {
173 auto la =
a->isa<
Lit>();
174 auto [x, y] = xy ? xy->template args<2>() : std::array<const Def*, 2>{
nullptr,
nullptr};
175 auto [z,
w] = zw ? zw->template args<2>() : std::array<const Def*, 2>{
nullptr,
nullptr};
180 auto make_op = [&world,
id](
const Def*
a,
const Def* b) {
return world.call(
id,
Mode::none,
Defs{
a, b}); };
182 if (la && lz)
return make_op(make_op(
a, z), w);
183 if (lx && lz)
return make_op(make_op(x, z), make_op(y, w));
184 if (lz)
return make_op(z, make_op(
a, w));
185 if (lx)
return make_op(x, make_op(y, b));
190const Def* merge_cmps(std::array<std::array<u64, 2>, 2> tab,
const Def*
a,
const Def* b) {
191 static_assert(
sizeof(
sub_t) == 1,
"if this ever changes, please adjust the logic below");
192 static constexpr size_t num_bits = std::bit_width(
Annex::num<Id>() - 1_u64);
194 auto& world =
a->world();
198 if (a_cmp && b_cmp && a_cmp->arg() == b_cmp->arg()) {
201 sub_t a_sub = a_cmp.sub();
202 sub_t b_sub = b_cmp.sub();
203 for (
size_t i = 0; i != num_bits; ++i, res >>= 1, a_sub >>= 1, b_sub >>= 1)
204 res |= tab[a_sub & 1][b_sub & 1] << 7_u8;
205 res >>= (7_u8 -
u8(num_bits));
207 if constexpr (std::is_same_v<Id, math::cmp>)
208 return world.call(
math::cmp(res), a_cmp->decurry()->arg(), a_cmp->arg());
220 auto& world = type->world();
221 auto [
a, b] = arg->
projs<2>();
229 case nat::add:
return world.lit_nat(*la + *lb);
230 case nat::sub:
return *la < *lb ? world.lit_nat_0() : world.lit_nat(*la - *lb);
231 case nat::mul:
return world.lit_nat(*la * *lb);
243 if (*la == 1 &&
id ==
nat::mul)
return b;
246 if (lb && *lb == 0 &&
id ==
nat::sub)
return a;
251 case nat::sub:
return world.lit_nat(0);
256 return world.raw_app(type, callee, {
a, b});
261 auto& world = type->world();
263 if (
id ==
ncmp::t)
return world.lit_tt();
264 if (
id ==
ncmp::f)
return world.lit_ff();
266 auto [
a, b] = arg->
projs<2>();
270 if (
id & (
icmp::e & 0xff))
return world.lit_tt();
271 if (
id ==
ncmp::ne)
return world.lit_ff();
278 case ncmp:: e:
return world.lit_bool(*la == *lb);
279 case ncmp::ne:
return world.lit_bool(*la != *lb);
280 case ncmp::l :
return world.lit_bool(*la < *lb);
281 case ncmp::le:
return world.lit_bool(*la <= *lb);
282 case ncmp::g :
return world.lit_bool(*la > *lb);
283 case ncmp::ge:
return world.lit_bool(*la >= *lb);
284 default: fe::unreachable();
290 return world.raw_app(type, callee, {
a, b});
295 auto& world = type->world();
296 auto callee = c->as<
App>();
297 auto [
a, b] = arg->
projs<2>();
299 if (
auto result = fold<icmp, id>(world, type,
a, b))
return result;
300 if (
id ==
icmp::f)
return world.lit_ff();
301 if (
id ==
icmp::t)
return world.lit_tt();
303 if (
id & (
icmp::e & 0xff))
return world.lit_tt();
304 if (
id ==
icmp::ne)
return world.lit_ff();
307 return world.raw_app(type, callee, {
a, b});
312 auto& world = type->world();
313 auto callee = c->as<
App>();
314 auto [
a, b] = arg->
projs<2>();
315 if (
auto result = fold<extrema, id>(world, type,
a, b))
return result;
316 return world.raw_app(type, callee, {
a, b});
320 auto& world = type->world();
322 auto [_, actual_type] = type->projs<2>();
323 auto make_res = [&,
mem =
mem](
const Def* res) {
return world.tuple({
mem, res}); };
325 if (
auto result = fold<abs>(world, actual_type,
a))
return make_res(result);
331 auto& world = type->world();
332 auto callee = c->as<
App>();
333 auto s = callee->decurry()->arg();
340 case bit1::f:
return world.lit_idx(*ls, 0);
341 case bit1::t:
return world.lit_idx(*ls, *ls - 1_u64);
347 if (
auto la =
Lit::isa(
a))
return world.lit_idx_mod(*ls, ~*la);
355 auto& world = type->world();
356 auto callee = c->as<
App>();
357 auto [
a, b] = arg->
projs<2>();
358 auto s = callee->decurry()->arg();
365 if (
auto res = merge_cmps<icmp>(tab,
a, b))
return res;
366 if (
auto res = merge_cmps<math::cmp>(tab,
a, b))
return res;
373 case bit2:: f:
return world.lit(type, 0);
374 case bit2:: t:
if (ls)
return world.lit(type, *ls-1_u64);
break;
375 case bit2:: fst:
return a;
376 case bit2:: snd:
return b;
384 if (la && lb && ls) {
386 case bit2::and_:
return world.lit_idx (*ls, *la & *lb);
387 case bit2:: or_:
return world.lit_idx (*ls, *la | *lb);
388 case bit2::xor_:
return world.lit_idx (*ls, *la ^ *lb);
389 case bit2::nand:
return world.lit_idx_mod(*ls, ~(*la & *lb));
390 case bit2:: nor:
return world.lit_idx_mod(*ls, ~(*la | *lb));
391 case bit2::nxor:
return world.lit_idx_mod(*ls, ~(*la ^ *lb));
392 case bit2:: iff:
return world.lit_idx_mod(*ls, ~ *la | *lb);
393 case bit2::niff:
return world.lit_idx (*ls, *la & ~*lb);
394 default: fe::unreachable();
399 auto unary = [&](
bool x,
bool y,
const Def*
a) ->
const Def* {
400 if (!x && !y)
return world.lit(type, 0);
401 if ( x && y)
return ls ? world.lit(type, *ls-1_u64) :
nullptr;
402 if (!x && y)
return a;
409 if (
auto res = unary(tab[0][0], tab[1][1],
a))
return res;
414 if (
auto res = unary(tab[0][0], tab[0][1], b))
return res;
415 }
else if (ls && *la == *ls - 1_u64) {
416 if (
auto res = unary(tab[1][0], tab[1][1], b))
return res;
422 if (
auto res = unary(tab[0][0], tab[1][0],
a))
return res;
423 }
else if (ls && *lb == *ls - 1_u64) {
424 if (
auto res = unary(tab[0][1], tab[1][1],
a))
return res;
428 if (
auto res = reassociate<bit2>(
id, world, callee,
a, b))
return res;
430 return world.raw_app(type, callee, {
a, b});
434 auto& world = type->world();
435 auto callee = c->as<
App>();
438 if (*i < *
s)
return world.lit_idx(*
s, *i);
439 if (
auto m =
Lit::isa(callee->arg()))
return *m ? world.bot(type) : world.lit_idx_mod(*
s, *i);
447 auto& world = arg->
world();
454 auto& world = type->world();
455 auto callee = c->as<
App>();
456 auto [
a, b] = arg->
projs<2>();
460 if (
auto result = fold<shr, id>(world, type,
a, b))
return result;
462 if (
auto la =
Lit::isa(
a); la && *la == 0) {
470 if (ls && *lb > *ls)
return world.bot(type);
480 return world.raw_app(type, callee, {
a, b});
485 auto& world = type->world();
486 auto callee = c->as<
App>();
487 auto [
a, b] = arg->
projs<2>();
488 auto mode = callee->arg();
492 if (
auto result = fold<wrap, id>(world, type,
a, b))
return result;
503 }
else if (*la == 1) {
518 default: fe::unreachable();
524 return world.call(
wrap::add,
mode,
Defs{a, world.lit_idx_mod(*ls, ~*lb + 1_u64)});
525 else if (
id ==
wrap::shl && ls && *lb > *ls)
526 return world.bot(type);
532 case wrap::sub:
return world.lit(type, 0);
539 if (
auto res = reassociate<wrap>(
id, world, callee,
a, b))
return res;
541 return world.raw_app(type, callee, {
a, b});
546 auto& world = full_type->
world();
548 auto [
a, b] = ab->projs<2>();
549 auto [_, type] = full_type->
projs<2>();
550 auto make_res = [&,
mem =
mem](
const Def* res) {
return world.tuple({
mem, res}); };
552 if (
auto result = fold<div, id>(world, type,
a, b))
return make_res(result);
555 if (*la == 0)
return make_res(
a);
559 if (*lb == 0)
return make_res(world.bot(type));
565 case div::srem:
return make_res(world.lit(type, 0));
566 case div::urem:
return make_res(world.lit(type, 0));
573 case div::sdiv:
return make_res(world.lit(type, 1));
574 case div::udiv:
return make_res(world.lit(type, 1));
575 case div::srem:
return make_res(world.lit(type, 0));
576 case div::urem:
return make_res(world.lit(type, 0));
585 auto& world = dst_t->
world();
586 auto s_t = x->
type()->as<
App>();
587 auto d_t = dst_t->as<
App>();
593 if (s_t == d_t)
return x;
594 if (x->isa<
Bot>())
return world.bot(d_t);
596 if (ls && ld && *ld < *ls)
return world.call(
conv::u, d, x);
601 if (*ld == 0)
return world.lit(d_t, *
l);
602 return world.lit(d_t, *
l % *ld);
611 else if (S == sw && D == dw) return world.lit(d_t, w2s<D>(mim::bitcast<w2s<S>>(*l)));
612 M( 1, 8)
M( 1, 16)
M( 1, 32)
M( 1, 64)
613 M( 8, 16)
M( 8, 32)
M( 8, 64)
616 else assert(
false &&
"TODO: conversion between different Idx sizes");
624 auto& world = dst_t->
world();
625 auto src_t = src->
type();
627 if (src->isa<
Bot>())
return world.bot(dst_t);
628 if (src_t == dst_t)
return src;
631 return other->arg()->
type() == dst_t ? other->arg() : world.call<
bitcast>(dst_t, other->arg());
634 if (dst_t->isa<
Nat>())
return world.lit(dst_t, *
l);
635 if (
Idx::isa(dst_t))
return world.lit(dst_t, *
l);
647 auto& world = type->world();
649 return world.lit_nat(8);
650 }
else if (type->isa<
Pi>()) {
651 return world.lit_nat(8);
656 case 16:
return world.lit_nat(2);
657 case 32:
return world.lit_nat(4);
658 case 64:
return world.lit_nat(8);
659 default: fe::unreachable();
661 }
else if (type->isa<
Sigma>() || type->isa<
Meet>()) {
664 for (
auto t : type->ops()) {
667 if (!
a || !
s)
return {};
670 offset =
pad(offset, *
a) + *
s;
674 u64 size = std::max(1_u64, offset);
680 }
else if (
auto arr = type->isa_imm<
Arr>()) {
685 }
else if (
auto join = type->isa<
Join>()) {
694 auto& world = type->world();
698 if (arg->
is_closed())
return world.lit_tt();
A (possibly paramterized) Array.
static auto isa(const Def *def)
World & world() const noexcept
auto projs(F f) const
Splits this Def via Def::projections into an Array (if A == std::dynamic_extent) or std::array (other...
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
static bool greater(const Def *a, const Def *b)
bool is_closed() const
Has no free_vars()?
static constexpr nat_t size2bitwidth(nat_t n)
static const Def * isa(const Def *def)
Checks if def is a Idx s and returns s or nullptr otherwise.
static std::optional< T > isa(const Def *def)
static T as(const Def *def)
A dependent function type.
const Lit * lit_idx_unsafe(u64 val)
#define MIM_core_NORMALIZER_IMPL
const Def * normalize_nat(const Def *type, const Def *callee, const Def *arg)
const Def * normalize_idx_unsafe(const Def *, const Def *, const Def *arg)
const Sigma * convert(const TBound< up > *b)
const Def * normalize_div(const Def *full_type, const Def *, const Def *arg)
const Def * normalize_pe(const Def *type, const Def *, const Def *arg)
const Def * normalize_extrema(const Def *type, const Def *c, const Def *arg)
const Def * normalize_icmp(const Def *type, const Def *c, const Def *arg)
const Def * normalize_bit1(const Def *type, const Def *c, const Def *a)
const Def * normalize_conv(const Def *dst_t, const Def *, const Def *x)
const Def * normalize_bit2(const Def *type, const Def *c, const Def *arg)
const Def * normalize_wrap(const Def *type, const Def *c, const Def *arg)
const Def * normalize_trait(const Def *, const Def *, const Def *type)
const Def * op(trait o, const Def *type)
const Def * normalize_abs(const Def *type, const Def *, const Def *arg)
const Def * normalize_idx(const Def *type, const Def *c, const Def *arg)
constexpr std::array< std::array< u64, 2 >, 2 > make_truth_table(bit2 id)
const Def * normalize_bitcast(const Def *dst_t, const Def *, const Def *src)
const Def * normalize_ncmp(const Def *type, const Def *callee, const Def *arg)
@ nuw
No Unsigned Wrap around.
@ nsw
No Signed Wrap around.
const Def * normalize_shr(const Def *type, const Def *c, const Def *arg)
std::optional< nat_t > isa_f(const Def *def)
D bitcast(const S &src)
A bitcast from src of type S to D.
TBound< true > Join
AKA union.
u64 pad(u64 offset, u64 align)
constexpr bool is_commutative(Id)
typename detail::w2s_< w >::type w2s
constexpr bool is_associative(Id id)
typename detail::w2u_< w >::type w2u
TBound< false > Meet
AKA intersection.
static consteval size_t num()
static consteval flags_t base()
#define MIM_1_8_16_32_64(m)