Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
37e3f98
comparisons
kripken Jul 21, 2026
aa3dc24
fmt
kripken Jul 21, 2026
6c7e2b0
work
kripken Jul 21, 2026
8854733
work
kripken Jul 21, 2026
f9ae5f1
work
kripken Jul 21, 2026
f708a61
work
kripken Jul 21, 2026
d00bed6
work
kripken Jul 21, 2026
e58adf7
work
kripken Jul 21, 2026
d9f1f87
test
kripken Jul 22, 2026
1617154
work
kripken Jul 22, 2026
0da0740
work
kripken Jul 22, 2026
e793be3
work
kripken Jul 22, 2026
e508ca6
work
kripken Jul 22, 2026
48ee24c
work
kripken Jul 22, 2026
c4b9d36
Merge remote-tracking branch 'origin/main' into compar.b
kripken Jul 23, 2026
b31d7f9
work
kripken Jul 23, 2026
f0dd6c8
work
kripken Jul 23, 2026
cf8f32e
work
kripken Jul 23, 2026
f6b58b4
work
kripken Jul 23, 2026
60e9620
work
kripken Jul 23, 2026
a4fc9e9
work
kripken Jul 23, 2026
7d337dc
work
kripken Jul 23, 2026
fb07869
work
kripken Jul 23, 2026
023cb40
work
kripken Jul 23, 2026
6268c82
work
kripken Jul 23, 2026
f15411c
work
kripken Jul 23, 2026
fd61010
work
kripken Jul 23, 2026
9a7e163
work
kripken Jul 23, 2026
9458a8a
work
kripken Jul 23, 2026
0628af6
work
kripken Jul 23, 2026
7e267ba
work
kripken Jul 23, 2026
71736fd
work
kripken Jul 23, 2026
859faa7
work
kripken Jul 23, 2026
e72fa61
work
kripken Jul 23, 2026
ebcfa80
work
kripken Jul 23, 2026
8c76ee7
work
kripken Jul 23, 2026
65f7508
work
kripken Jul 23, 2026
f28a35a
work
kripken Jul 23, 2026
2b6e73b
work
kripken Jul 23, 2026
753490d
work
kripken Jul 23, 2026
fa1144a
work
kripken Jul 23, 2026
dc858e8
work
kripken Jul 23, 2026
ed9d41f
work
kripken Jul 23, 2026
00b33c5
work
kripken Jul 23, 2026
e470ae1
work
kripken Jul 23, 2026
65f85dd
work
kripken Jul 23, 2026
2ac1514
work
kripken Jul 23, 2026
e713c6b
work
kripken Jul 23, 2026
f478cd0
work
kripken Jul 23, 2026
70085e5
work
kripken Jul 23, 2026
d153b16
work
kripken Jul 23, 2026
e16c4c6
work
kripken Jul 23, 2026
5ad4c40
work
kripken Jul 23, 2026
abcfee8
work
kripken Jul 23, 2026
a3d9a54
work
kripken Jul 23, 2026
4b16180
work
kripken Jul 23, 2026
14e5098
work
kripken Jul 23, 2026
3c59516
work
kripken Jul 23, 2026
ea598cb
work
kripken Jul 23, 2026
f31ae72
work
kripken Jul 23, 2026
6a3d5bc
work
kripken Jul 23, 2026
437174c
work
kripken Jul 23, 2026
ba4f0b5
work
kripken Jul 23, 2026
8a4f04a
work
kripken Jul 23, 2026
1b9efc0
work
kripken Jul 23, 2026
0789050
work
kripken Jul 23, 2026
5f0ad62
work
kripken Jul 23, 2026
242dd6e
work
kripken Jul 23, 2026
cda9027
work
kripken Jul 23, 2026
b141979
work
kripken Jul 23, 2026
32fa2b3
UNDO
kripken Jul 23, 2026
51cf381
go
kripken Jul 23, 2026
60d3fdf
go
kripken Jul 24, 2026
8362896
form
kripken Jul 24, 2026
d350b97
work
kripken Jul 24, 2026
b56f466
work
kripken Jul 24, 2026
c536d88
work
kripken Jul 24, 2026
0f48d73
work
kripken Jul 24, 2026
d8747d7
work
kripken Jul 24, 2026
09ebda2
work
kripken Jul 24, 2026
5d4a89f
work
kripken Jul 24, 2026
7cd6884
work
kripken Jul 24, 2026
9b7cac8
work
kripken Jul 24, 2026
8678398
work
kripken Jul 24, 2026
b11e984
work
kripken Jul 24, 2026
0069b7a
work
kripken Jul 24, 2026
2ce896f
work
kripken Jul 24, 2026
1acec6e
Merge remote-tracking branch 'origin/main' into compar.b.replace
kripken Jul 24, 2026
d6599aa
Update test/gtest/constraint.cpp
kripken Jul 24, 2026
128e9f4
Update test/gtest/constraint.cpp
kripken Jul 24, 2026
e11289e
work
kripken Jul 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
88 changes: 82 additions & 6 deletions src/ir/constraint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,83 @@ void AndedConstraintSet::approximateAnd(const Constraint& c) {
// useful to implement that).
}

namespace {

// Do an OR of a pair of constraints where the terms are known to be equal. If
// we can't find a good way to express their ORing, return nullopt.
std::optional<Constraint> approximateOrTermEqualPair(const Abstract::Op aOp,
const Abstract::Op bOp,
const Term& term) {
using namespace Abstract;

// x == C || x > C === x >= C
if (aOp == Eq && bOp == GtS) {
return Constraint{GeS, term};
}

// TODO: all the rest

return {};
}

// Do an OR of a pair of constraints. If we can't find a good way to express
// their ORing, return nullopt.
std::optional<Constraint> approximateOrPair(const Constraint& a,
const Constraint& b,
bool recursing = false) {
if (a.term == b.term) {
if (auto result = approximateOrTermEqualPair(a.op, b.op, a.term)) {
return result;
}
}

// If a proves b, e.g. x = 5 proves x >= 0 is true, then the OR is b.
if (provesPair(a, b) == True) {
return b;
}

// TODO: more smarts

if (!recursing) {
// The flipped form may be recognized.
return approximateOrPair(b, a, true);
}

return {};
}

// Do an OR in full detail, looking at every constraint in each of the given
// sets.
AndedConstraintSet detailedApproximateOr(const AndedConstraintSet& a,
const AndedConstraintSet& b) {
// We can process this in full detail by looking at all the combinations of
// individual constraints, because of the distributive property:
//
// (A & B) | (C & D) == ((A & B) | C) & ((A & B) | D)
// == (A | C) & (B | C) & (A | D) & (B | D)
//
// This is quadratic, but constraint sets are limited to a very small size,
// making this reasonable.
//
// Also, note that we don't need to worry about new contradictions here: ORing
// things never leads to a contradiction, and we can assume the inputs are
// not contradictions.
assert(!a.provesEverything() && !b.provesEverything());

auto result = AndedConstraintSet::makeProvesNothing();
for (auto& ac : a) {
for (auto& bc : b) {
if (auto combined = approximateOrPair(ac, bc)) {
// We found something useful by ORing them, keep it.
result.approximateAnd(*combined);
}
}
}
return result;
}

} // anonymous namespace

bool AndedConstraintSet::approximateOr(const AndedConstraintSet& other) {
// If one proves everything, the only thing that matters is the other.
if (other.provesEverything()) {
Expand All @@ -226,12 +303,11 @@ bool AndedConstraintSet::approximateOr(const AndedConstraintSet& other) {
return true;
}

// TODO smarts: handle <= > and so forth

// Otherwise, we don't know how to nicely OR these things, and expand to the
// trivial set of no constraints.
clear();
return true;
// For more complex cases, do a detailed analysis.
auto result = detailedApproximateOr(*this, other);
auto changed = (result != *this);
*this = result;
return changed;
}

std::optional<LocalConstraint> LocalConstraint::parse(Expression* curr) {
Expand Down
8 changes: 8 additions & 0 deletions src/ir/constraint.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,14 @@ struct AndedConstraintSet : inplace_vector<Constraint, MaxConstraints> {
// until something changes.
bool isContradiction = true;

AndedConstraintSet() = default;
AndedConstraintSet(std::initializer_list<Constraint> constraints) {
isContradiction = false;
for (auto& c : constraints) {
approximateAnd(c);
}
}

// Proving everything (even contradictions) is equivalent to being a
// contradiction. (This and provesNothing can be seen as the top/bottom of a
// poset, if one wants to think of things that way.)
Expand Down
102 changes: 100 additions & 2 deletions test/gtest/constraint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -240,5 +240,103 @@ TEST(ConstraintTest, TestDeredundancy) {
EXPECT_EQ(t[0], eq0);
}

// TODO: test an approximateOr of { x = 10 } and { x >= 0 }, once we support
// inequalities
static void checkOr(const AndedConstraintSet& a,
const AndedConstraintSet& b,
const AndedConstraintSet& result) {
auto ored = a;
ored.approximateOr(b);
EXPECT_EQ(ored, result);

ored = b;
ored.approximateOr(a);
EXPECT_EQ(ored, result);
}

TEST(ConstraintTest, TestOrInequality) {
// x == 5 || x >= 0 => x >= 0
AndedConstraintSet eq5{Constraint{Eq, {Literal(int32_t(5))}}};
AndedConstraintSet ge0{Constraint{GeU, {Literal(int32_t(0))}}};
checkOr(eq5, ge0, ge0);

// x == 5 || x > 5 => x >= 5
AndedConstraintSet gts5{Constraint{GtS, {Literal(int32_t(5))}}};
AndedConstraintSet ges5{Constraint{GeS, {Literal(int32_t(5))}}};
checkOr(eq5, gts5, ges5);

// x == 5 || x >= 5 => x >= 5
checkOr(eq5, ges5, ges5);
}

TEST(ConstraintTest, TestOrLoop) {
// Check common loop patterns:
// { x == A } || { x > A && x <= B } ==> { x >= A && x <= B }

// { x == 5 } || { x > 5 && x <= 42 } ==> { x >= 5 && x <= 42 }
AndedConstraintSet left{Constraint{Eq, {Literal(int32_t(5))}}};
AndedConstraintSet right(
{{GtS, {Literal(int32_t(5))}}, {LeS, {Literal(int32_t(42))}}});
AndedConstraintSet result(
{{GeS, {Literal(int32_t(5))}}, {LeS, {Literal(int32_t(42))}}});
checkOr(left, right, result);

// Changes to constants:

// Change 5 on the left to 7:
// { x == 7 } || { x > 5 && x <= 42 } ==> { x > 5 && x <= 42}
AndedConstraintSet left7{Constraint{Eq, {Literal(int32_t(7))}}};
checkOr(left7, right, right);

// Change 5 on the left to 99:
// { x == 99 } || { x > 5 && x <= 42 } ==> { x > 5 }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can have a TODO for adding x <= 99 to the result.

// TODO: we could emit a range (5, 99]
AndedConstraintSet left99{Constraint{Eq, {Literal(int32_t(99))}}};
AndedConstraintSet rightOnly5{Constraint{GtS, {Literal(int32_t(5))}}};
checkOr(left99, right, rightOnly5);

// Change 5 on the left to 4:
// { x == 4 } || { x > 5 && x <= 42 } ==> { x <= 42 }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Similarly, we can have a TODO for adding x >= 4.

// TODO: we could emit a range [4, 42]
AndedConstraintSet left4{Constraint{Eq, {Literal(int32_t(4))}}};
AndedConstraintSet rightOnly42({{LeS, {Literal(int32_t(42))}}});
checkOr(left4, right, rightOnly42);

// Change 5 on the right to 6:
// { x == 5 } || { x > 6 && x <= 42 } ==> { x <= 42 }
AndedConstraintSet right6(
{{GtS, {Literal(int32_t(6))}}, {LeS, {Literal(int32_t(42))}}});
checkOr(left, right6, rightOnly42);

// Changes to operations:

// Change the Eq on the left to Ne. We fail to find anything for the OR.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

TODO the result could be x != 5.

// { x != 5 } || { x > 5 && x <= 42 } ==> {}
// TODO: we could emit x != 5
AndedConstraintSet leftNe{Constraint{Ne, {Literal(int32_t(5))}}};
auto empty = AndedConstraintSet::makeProvesNothing();
checkOr(leftNe, right, empty);

// Change the GtS on the right to GtU:
// { x == 5 } || { x >U 5 && x <= 42 } ==> { x <= 42 }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I assume this will be improved in the next PR?

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.

Eventually, yes, though maybe not the next PR. I'm trying to prove out the path to getting some specific Kotlin code working. I'll fill out related cases later.

AndedConstraintSet rightGtU(
{{GtU, {Literal(int32_t(5))}}, {LeS, {Literal(int32_t(42))}}});
checkOr(left, rightGtU, rightOnly42);

// Change the LeS on the right to LeU:
// { x == 5 } || { x > 5 && x <=U 42 } ==> { x >= 5 && x <=U 42 }
AndedConstraintSet rightLeU(
{{GtS, {Literal(int32_t(5))}}, {LeU, {Literal(int32_t(42))}}});
AndedConstraintSet rightGesLeU(
{{GeS, {Literal(int32_t(5))}}, {LeU, {Literal(int32_t(42))}}});
checkOr(left, rightLeU, rightGesLeU);

// Add an operation on the right, x != 21:
// { x == 5 } || { x > 5 && x <= 42 && x != 21 } ==>
// { x >= 5 && x <= 42 && x != 21 }
AndedConstraintSet rightAdded({{GtS, {Literal(int32_t(5))}},
{LeS, {Literal(int32_t(42))}},
{Ne, {Literal(int32_t(21))}}});
AndedConstraintSet resultAdded({{GeS, {Literal(int32_t(5))}},
{LeS, {Literal(int32_t(42))}},
{Ne, {Literal(int32_t(21))}}});
checkOr(left, rightAdded, resultAdded);
}
Loading
Loading