Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
117 commits
Select commit Hold shift + click to select a range
adec816
fuzzz
kripken Jul 10, 2026
14e19d9
fuzzz
kripken Jul 10, 2026
46003bd
Merge remote-tracking branch 'origin/main' into fuzz.constraint
kripken Jul 10, 2026
7ea0ffd
undo
kripken Jul 10, 2026
ddd4edb
Merge remote-tracking branch 'origin/main' into fuzz.constraint
kripken Jul 14, 2026
e0276db
update
kripken Jul 14, 2026
c5deeeb
moar
kripken Jul 15, 2026
c3434b5
Merge remote-tracking branch 'origin/main' into fuzz.constraint
kripken Jul 30, 2026
ed44408
Merge remote-tracking branch 'origin/main' into fuzz.constraint
kripken Jul 31, 2026
e83c054
work
kripken Jul 31, 2026
192d924
work
kripken Jul 31, 2026
3096b52
less
kripken Aug 7, 2026
2ef5df7
Merge remote-tracking branch 'origin/main' into fuzz.constraint
kripken Aug 12, 2026
df548b9
fix
kripken Aug 12, 2026
ff3b15a
Merge remote-tracking branch 'origin/main' into fuzz.constraint
kripken Aug 14, 2026
cfc644f
finish
kripken Aug 14, 2026
9cdfcbe
work
kripken Aug 14, 2026
e880306
Merge remote-tracking branch 'origin/main' into fuzz.constraint
kripken Aug 17, 2026
433559a
move
kripken Aug 17, 2026
e083272
update
kripken Aug 17, 2026
1af77da
PR number
kripken Aug 17, 2026
abd5148
issue
kripken Aug 17, 2026
8b1e33f
test
kripken Aug 17, 2026
c66b1bd
go
kripken Aug 18, 2026
b74ca8e
go
kripken Aug 18, 2026
01d20c4
work
kripken Aug 18, 2026
71a0a25
work
kripken Aug 18, 2026
553a481
work
kripken Aug 18, 2026
df402c9
work
kripken Aug 18, 2026
19fc708
changes so far
kripken Aug 18, 2026
b645fab
format
kripken Aug 18, 2026
b3184ed
rename
kripken Aug 19, 2026
c317fb8
format
kripken Aug 19, 2026
8763274
builds
kripken Aug 19, 2026
0e49329
work
kripken Aug 19, 2026
4bfa032
work
kripken Aug 19, 2026
af0bc97
work
kripken Aug 19, 2026
32cc04b
work
kripken Aug 19, 2026
3b59e5d
work
kripken Aug 19, 2026
69a97de
testt
kripken Aug 19, 2026
424c1ab
work
kripken Aug 19, 2026
c05fba8
changes
kripken Aug 19, 2026
b5ea854
work
kripken Aug 19, 2026
7048dd2
work
kripken Aug 19, 2026
9e42b9d
work
kripken Aug 19, 2026
3677bd8
Merge remote-tracking branch 'origin/main' into fuzz.constraint
kripken Aug 19, 2026
06f2c48
Merge remote-tracking branch 'myself/fuzz.constraint' into fuzz.const…
kripken Aug 19, 2026
d80eae5
work
kripken Aug 19, 2026
2960da7
work
kripken Aug 19, 2026
e7db8d7
:Merge branch 'i65_span.use' into c.add.overf
kripken Aug 19, 2026
1443d26
work
kripken Aug 19, 2026
d6c1a3c
work
kripken Aug 19, 2026
c494247
work
kripken Aug 19, 2026
dd60469
work
kripken Aug 19, 2026
195d786
work
kripken Aug 19, 2026
0a0c003
work
kripken Aug 19, 2026
afb3935
work
kripken Aug 19, 2026
2530b8b
work
kripken Aug 19, 2026
ed93ddf
work
kripken Aug 19, 2026
00a6dcc
work
kripken Aug 19, 2026
6f45275
work
kripken Aug 19, 2026
2188b37
feedback
kripken Aug 19, 2026
073fbaf
format
kripken Aug 19, 2026
ddf2108
Merge remote-tracking branch 'myself/i65_span' into i65_span.use
kripken Aug 20, 2026
39b22d6
Merge remote-tracking branch 'origin/main' into i65_span.use
kripken Aug 20, 2026
e14eaf0
todos
kripken Aug 20, 2026
d3a71ea
Merge remote-tracking branch 'myself/i65_span.use' into c.add.overf
kripken Aug 20, 2026
d332f0f
fix
kripken Aug 20, 2026
5faa478
merg
kripken Aug 20, 2026
818b9c5
fix
kripken Aug 20, 2026
e8bec8c
fix
kripken Aug 20, 2026
2f06af4
braces
kripken Aug 20, 2026
d73cf00
Merge remote-tracking branch 'myself/i65_span.use' into c.add.overf
kripken Aug 20, 2026
42121a2
Merge remote-tracking branch 'origin/main' into fuzz.constraint
kripken Aug 20, 2026
1ef4c61
Merge remote-tracking branch 'origin/main' into fuzz.constraint
kripken Aug 20, 2026
9509fce
fix
kripken Aug 20, 2026
8347259
Merge remote-tracking branch 'myself/fuzz.constraint' into c.add.overf
kripken Aug 20, 2026
7659e7c
simpl
kripken Aug 20, 2026
1ebaeab
Merge remote-tracking branch 'myself/i65_span.use' into c.add.overf
kripken Aug 21, 2026
a9ae680
:wq
kripken Aug 21, 2026
0f051c9
undo
kripken Aug 21, 2026
578362c
undo
kripken Aug 21, 2026
c164b4e
test
kripken Aug 21, 2026
5b3ef90
work
kripken Aug 21, 2026
33c5548
work
kripken Aug 21, 2026
5a4997c
work
kripken Aug 21, 2026
ab8463b
work
kripken Aug 21, 2026
5a2c93c
work
kripken Aug 21, 2026
af1ff98
work
kripken Aug 21, 2026
73ef037
work
kripken Aug 21, 2026
9afad8e
work
kripken Aug 21, 2026
1f3495a
work
kripken Aug 21, 2026
936d172
work
kripken Aug 21, 2026
86ef62a
work
kripken Aug 21, 2026
4c0bd9c
work
kripken Aug 21, 2026
2173823
work
kripken Aug 21, 2026
f1ba9f7
fix
kripken Aug 21, 2026
e552d7c
work
kripken Aug 21, 2026
b840e68
work
kripken Aug 21, 2026
cb8980c
work
kripken Aug 21, 2026
4291d84
work
kripken Aug 21, 2026
d9e14e4
work
kripken Aug 21, 2026
1ac0fdb
work
kripken Aug 21, 2026
ee00fdc
work
kripken Aug 21, 2026
b49b5c5
work
kripken Aug 21, 2026
d1789d1
work
kripken Aug 21, 2026
5db2da7
Merge remote-tracking branch 'origin/main' into c.add.overf
kripken Aug 21, 2026
f14f674
work
kripken Aug 21, 2026
48cddc7
fix
kripken Aug 21, 2026
fe80b41
Merge branch 'span.nostore.cont' into span.proven
kripken Aug 21, 2026
922d856
Merge remote-tracking branch 'origin/main' into span.proven
kripken Aug 21, 2026
befc119
Merge remote-tracking branch 'myself/span.proven' into c.add.overf
kripken Aug 21, 2026
344ac09
oops, do not error on non-numbers in getSpan
kripken Aug 21, 2026
09ebe8f
Merge remote-tracking branch 'myself/span.proven' into c.add.overf
kripken Aug 21, 2026
6987abc
Merge remote-tracking branch 'origin/main' into c.add.overf
kripken Aug 24, 2026
70799e3
fix
kripken Aug 24, 2026
650e21f
fix
kripken Aug 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions src/ir/constraint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -808,11 +808,19 @@ void BasicBlockConstraintMap::set(Index index, Expression* value) {
case Eq:
*N = N->add(Literal::makeFromInt32(1, N->type));
break;
// x >= N, x++ => x > N
// x >= N, x++ => x > N if no overflow
case GeS:
if (old.proves({LtS, Literal::makeSignedMax(N->type)}) != True) {
iter = new_.erase(iter);
continue;
}
c.op = GtS;
break;
case GeU:
if (old.proves({LtU, Literal::makeUnsignedMax(N->type)}) != True) {
iter = new_.erase(iter);
continue;
}
c.op = GtU;
break;
// x < N, x++ => x <= N
Expand Down Expand Up @@ -1022,7 +1030,7 @@ std::ostream& operator<<(std::ostream& o, const Constraint& c) {
if (auto* cc = std::get_if<Literal>(&c.term)) {
o << *cc;
} else if (auto* i = std::get_if<Index>(&c.term)) {
o << "Index(" << *i << ')';
o << "$" << *i;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a drive-by. This makes debug output nicer.

}
o << '}';
return o;
Expand Down
55 changes: 45 additions & 10 deletions test/gtest/constraint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -595,15 +595,31 @@ TEST(ConstraintTest, TestIncrement) {
map.set(0, &add);
check(map.get(0), {Eq, {Literal(int32_t(1))}});

// $0 >= 5, $0++ => $0 > 5 (signed)
// $0 >= 5, $0++ => nothing, since the ++ might overflow into negative
map.set(0, {GeS, {Literal(int32_t(5))}});
map.set(0, &add);
check(map.get(0), {GtS, {Literal(int32_t(5))}});
EXPECT_TRUE(map.get(0).empty());

// Ditto, unsigned
// $0 >= 5, $0 < 100, $0++ => $0 > 5, $0 <= 100 (signed)
Constraint lts100{LtS, {Literal(int32_t(100))}};
Constraint les100{LeS, {Literal(int32_t(100))}};
map.set(0, {{GeS, {Literal(int32_t(5))}}, lts100});
map.set(0, &add);
EXPECT_EQ(map.get(0),
(AndedConstraintSet{{GtS, {Literal(int32_t(5))}}, les100}));

// Ditto, unsigned: without an upper bound we can overflow.
map.set(0, {GeU, {Literal(int32_t(5))}});
map.set(0, &add);
check(map.get(0), {GtU, {Literal(int32_t(5))}});
EXPECT_TRUE(map.get(0).empty());

// With an upper bound, we can optimize like before.
Constraint ltu100{LtU, {Literal(int32_t(100))}};
Constraint leu100{LeU, {Literal(int32_t(100))}};
map.set(0, {{GeU, {Literal(int32_t(5))}}, ltu100});
map.set(0, &add);
EXPECT_EQ(map.get(0),
(AndedConstraintSet{{GtU, {Literal(int32_t(5))}}, leu100}));

// $0 < 5, $0++ => $0 <= 5 (signed)
map.set(0, {LtS, {Literal(int32_t(5))}});
Expand Down Expand Up @@ -656,18 +672,37 @@ TEST(ConstraintTest, TestIncrement) {
(AndedConstraintSet{{GtS, {Literal(int32_t(10))}},
{LeS, {Literal(int32_t(20))}}}));

// $0 >= 10 && $0 <= max_signed, $0++ => $0 > 10 (overflowing constraint
// removed)
// $0 >= 10 && $0 <= max_signed, $0++ => nothing, as we may overflow.
map.set(0, {GeS, {Literal(int32_t(10))}});
map.approximateAnd(0, {LeS, {Literal::makeSignedMax(Type::i32)}});
map.set(0, &add);
EXPECT_EQ(map.get(0), (AndedConstraintSet{{GtS, {Literal(int32_t(10))}}}));
EXPECT_EQ(map.get(0).size(), 0);

// $0 >= 10 && $0 == $2, $0++ => $0 > 10 (non-constant term removed)
map.set(0, {GeS, {Literal(int32_t(10))}});
// Ditto, unsigned.
map.set(0, {GeU, {Literal(int32_t(10))}});
map.approximateAnd(0, {LeU, {Literal::makeUnsignedMax(Type::i32)}});
map.set(0, &add);
EXPECT_EQ(map.get(0).size(), 0);

// Ditto, 64-bit signed.
map.set(0, {GeS, {Literal(int64_t(10))}});
map.approximateAnd(0, {LeS, {Literal::makeSignedMax(Type::i64)}});
map.set(0, &add);
EXPECT_EQ(map.get(0).size(), 0);

// Ditto, 64-bit unsigned.
map.set(0, {GeU, {Literal(int64_t(10))}});
map.approximateAnd(0, {LeU, {Literal::makeUnsignedMax(Type::i64)}});
map.set(0, &add);
EXPECT_EQ(map.get(0).size(), 0);

// $0 >= 5 && $0 < 100 && $0 == $2, $0++ => we increment and remove the non-
// constant term, leaving $0 > 5 && $0 <= 100.
map.set(0, {{GeS, {Literal(int32_t(5))}}, lts100});
map.approximateAnd(0, {Eq, {Index(2)}});
map.set(0, &add);
EXPECT_EQ(map.get(0), (AndedConstraintSet{{GtS, {Literal(int32_t(10))}}}));
EXPECT_EQ(map.get(0),
(AndedConstraintSet{{GtS, {Literal(int32_t(5))}}, les100}));
}

TEST(ConstraintTest, TestEqConstraints) {
Expand Down
215 changes: 215 additions & 0 deletions test/lit/passes/constraint-analysis-loops.wast
Original file line number Diff line number Diff line change
Expand Up @@ -1573,4 +1573,219 @@
)
)
)

;; CHECK: (func $add-overflow (type $1) (param $x i32)
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.ge_s
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.gt_s
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $add-overflow (param $x i32)
(if
(i32.ge_s
(local.get $x)
(i32.const 0)
)
(then
;; Normally, x >= 0 and x++ lead to x > 0. However, we overflow if
;; x == MAX_INT, so we cannot optimize the dropped value below us.
(local.set $x
(i32.add
(local.get $x)
(i32.const 1)
)
)
(drop
(i32.gt_s
(local.get $x)
(i32.const 0)
)
)
)
)
)

;; CHECK: (func $add-overflow-yes (type $1) (param $x i32)
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.ge_s
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.le_s
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1000)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $add-overflow-yes (param $x i32)
;; As above, but we add an inner if that does x < 1000. Now x cannot be
;; MAX_INT, so we do optimize to 1.
(if
(i32.ge_s
(local.get $x)
(i32.const 0)
)
(then
(if
(i32.le_s
(local.get $x)
(i32.const 1000)
)
(then
(local.set $x
(i32.add
(local.get $x)
(i32.const 1)
)
)
(drop
(i32.gt_s
(local.get $x)
(i32.const 0)
)
)
)
)
)
)
)

;; CHECK: (func $add-overflow-unsigned (type $1) (param $x i32)
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.ge_u
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.gt_u
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $add-overflow-unsigned (param $x i32)
;; As above, but unsigned. We change the 0 to 1, as x >= 0 is always true
;; for unsigned anyhow.
(if
(i32.ge_u
(local.get $x)
(i32.const 1)
)
(then
(local.set $x
(i32.add
(local.get $x)
(i32.const 1)
)
)
;; We do not optimize, due to the risk of overflow.
(drop
(i32.gt_u
(local.get $x)
(i32.const 1)
)
)
)
)
)

;; CHECK: (func $add-overflow-unsigned-yes (type $1) (param $x i32)
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.ge_u
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.le_u
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1000)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $add-overflow-unsigned-yes (param $x i32)
;; As above, but unsigned.
(if
(i32.ge_u
(local.get $x)
(i32.const 1)
)
(then
(if
(i32.le_u
(local.get $x)
(i32.const 1000)
)
(then
(local.set $x
(i32.add
(local.get $x)
(i32.const 1)
)
)
;; We do optimize to 1.
(drop
(i32.gt_u
(local.get $x)
(i32.const 1)
)
)
)
)
)
)
)
)
Loading