From ab1c83b8e713a32d6dfc5e1526c844a8fccca6a6 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 15 Jul 2026 13:11:30 -0700 Subject: [PATCH 1/5] fix --- src/ir/constraint.cpp | 13 +++++++++++-- test/gtest/constraint.cpp | 21 +++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/ir/constraint.cpp b/src/ir/constraint.cpp index 3dd7fd0aa9a..346541e4bb6 100644 --- a/src/ir/constraint.cpp +++ b/src/ir/constraint.cpp @@ -137,8 +137,6 @@ void AndedConstraintSet::approximateAnd(const Constraint& c) { auto result = proves(c); if (result == True) { // We already prove c to be true, so it adds nothing. - // TODO: we could also see if c proves us true, and replace things we - // already have with c when possible return; } else if (result == False) { // We are now a contradiction. @@ -146,6 +144,17 @@ void AndedConstraintSet::approximateAnd(const Constraint& c) { return; } + // If c proves something already present to be true, it can just replace it. + for (auto& existing : *this) { + auto result = provesPair(c, existing); + if (result == True) { + existing = c; + return; + } + // There cannot be a contradiction here, because we checked for that above. + assert(result != False); + } + if (size() < MaxConstraints) { push_back(c); return; diff --git a/test/gtest/constraint.cpp b/test/gtest/constraint.cpp index 025ccf75749..ea3fc10167c 100644 --- a/test/gtest/constraint.cpp +++ b/test/gtest/constraint.cpp @@ -219,5 +219,26 @@ TEST(ConstraintTest, TestDeduplication) { EXPECT_EQ(s.size(), 1); } +TEST(ConstraintTest, TestDeredundancy) { + Constraint eq0{Eq, {Literal(int32_t(0))}}; + Constraint ne1{Ne, {Literal(int32_t(1))}}; + + // If x == 0, then x != 1 is redundant, and does not need to be added, is it + // is implied by x == 0. + AndedConstraintSet s; + s.set(eq0); + s.approximateAnd(ne1); + EXPECT_EQ(s.size(), 1); + EXPECT_EQ(s[0], eq0); + + // Reverse order, same result, even though we added x == 0 last: we remove + // x != 1. + AndedConstraintSet t; + t.set(ne1); + t.approximateAnd(eq0); + EXPECT_EQ(t.size(), 1); + EXPECT_EQ(t[0], eq0); +} + // TODO: test an approximateOr of { x = 10 } and { x >= 0 }, once we support // inequalities From 642229c1858598252273f1cd303373be95d90ba9 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 16 Jul 2026 08:43:35 -0700 Subject: [PATCH 2/5] fix sorting --- src/ir/constraint.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ir/constraint.cpp b/src/ir/constraint.cpp index e1ac6e6af5f..7c138bc1371 100644 --- a/src/ir/constraint.cpp +++ b/src/ir/constraint.cpp @@ -149,8 +149,13 @@ void AndedConstraintSet::approximateAnd(const Constraint& c) { auto result = provesPair(c, existing); if (result == True) { existing = c; + + // Sort to ensure we are in the right place. + std::sort(begin(), end()); + return; } + // There cannot be a contradiction here, because we checked for that above. assert(result != False); } From b24f3fd8c0fcbd87bbeeafefedee1ebd8f4d159b Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 16 Jul 2026 10:05:57 -0700 Subject: [PATCH 3/5] fast --- src/ir/constraint.cpp | 36 +++++++++++++++++++------------ src/ir/constraint.h | 4 ++-- src/passes/ConstraintAnalysis.cpp | 4 +--- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/ir/constraint.cpp b/src/ir/constraint.cpp index 9e384a6c40f..03190b44ea2 100644 --- a/src/ir/constraint.cpp +++ b/src/ir/constraint.cpp @@ -159,25 +159,25 @@ void AndedConstraintSet::approximateAnd(const Constraint& c) { // useful to implement that). } -void AndedConstraintSet::approximateOr(const AndedConstraintSet& other) { +bool AndedConstraintSet::approximateOr(const AndedConstraintSet& other) { // If one proves everything, the only thing that matters is the other. + if (other.provesEverything()) { + return false; + } if (provesEverything()) { *this = other; - return; - } - if (other.provesEverything()) { - return; + return true; } // If this is already implied by current constraints, then it is redundant. // E.g. if we are { x = 10 } and other is { x >= 0 } then all we need is // { x >= 0 } as the result of the OR. + if (other.proves(*this) == True) { + return false; + } if (proves(other) == True) { *this = other; - return; - } - if (other.proves(*this) == True) { - return; + return true; } // TODO smarts: handle <= > and so forth @@ -185,6 +185,7 @@ void AndedConstraintSet::approximateOr(const AndedConstraintSet& other) { // Otherwise, we don't know how to nicely OR these things, and expand to the // trivial set of no constraints. clear(); + return true; } std::optional LocalConstraint::parse(Expression* curr) { @@ -303,21 +304,22 @@ void BasicBlockConstraintMap::setProvesNothing(Index index) { map.erase(index); } -void BasicBlockConstraintMap::approximateOr( +bool BasicBlockConstraintMap::approximateOr( const BasicBlockConstraintMap& other) { // If one is unreachable, it adds nothing to the other. if (other.unreachable) { - return; + return false; } if (unreachable) { *this = other; - return; + return true; } // We only need to loop on our locals, as any local that is missing in us is // one that would end up proving nothing (and get removed). + bool changed = false; for (auto& [local, constraints] : map) { - constraints.approximateOr(other.get(local)); + changed |= constraints.approximateOr(other.get(local)); } // Anything that became trivial after the OR must be removed. @@ -325,8 +327,14 @@ void BasicBlockConstraintMap::approximateOr( const auto& [local, constraints] = item; // We do not store contradictions. assert(!constraints.provesEverything()); - return constraints.provesNothing(); + if (constraints.provesNothing()) { + changed = true; + return true; + } + return false; }); + + return changed; } void BasicBlockConstraintMap::approximateAndInternal(Index index, diff --git a/src/ir/constraint.h b/src/ir/constraint.h index e3be8305268..038d115deee 100644 --- a/src/ir/constraint.h +++ b/src/ir/constraint.h @@ -177,7 +177,7 @@ struct AndedConstraintSet : inplace_vector { // { x >= 0 } // // If we become too imprecise, we lose the ability to imply anything useful. - void approximateOr(const AndedConstraintSet& other); + bool approximateOr(const AndedConstraintSet& other); // Set a constraint, replacing all previous state. void set(const Constraint& c) { @@ -266,7 +266,7 @@ struct BasicBlockConstraintMap { // Perform an OR as above. When a local only appears in one map, we treat it // as if it contains a contradiction there, that is, as if the code is // unreachable. - void approximateOr(const BasicBlockConstraintMap& other); + bool approximateOr(const BasicBlockConstraintMap& other); // Perform an AND as above, on a particular index. void approximateAnd(Index index, const Constraint& c) { diff --git a/src/passes/ConstraintAnalysis.cpp b/src/passes/ConstraintAnalysis.cpp index 356e6e035b1..d16a326dc29 100644 --- a/src/passes/ConstraintAnalysis.cpp +++ b/src/passes/ConstraintAnalysis.cpp @@ -171,9 +171,7 @@ struct ConstraintAnalysis } // If anything changed at the start of the target block, flow onwards. - auto old = outStartConstraints; - outStartConstraints.approximateOr(sentConstraints); - if (outStartConstraints != old) { + if (outStartConstraints.approximateOr(sentConstraints)) { work.push(out); } } From 976dbf82cee4bd341210afeb731d9028373c302d Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 16 Jul 2026 10:06:56 -0700 Subject: [PATCH 4/5] fast --- src/ir/constraint.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ir/constraint.h b/src/ir/constraint.h index 038d115deee..4412c53a21b 100644 --- a/src/ir/constraint.h +++ b/src/ir/constraint.h @@ -177,6 +177,8 @@ struct AndedConstraintSet : inplace_vector { // { x >= 0 } // // If we become too imprecise, we lose the ability to imply anything useful. + // + // Returns whether we changed anything. bool approximateOr(const AndedConstraintSet& other); // Set a constraint, replacing all previous state. @@ -266,6 +268,8 @@ struct BasicBlockConstraintMap { // Perform an OR as above. When a local only appears in one map, we treat it // as if it contains a contradiction there, that is, as if the code is // unreachable. + // + // Returns whether we changed anything. bool approximateOr(const BasicBlockConstraintMap& other); // Perform an AND as above, on a particular index. From f6eb4df314a7b34ed2116bcd8e69ea79a17e6fc5 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 16 Jul 2026 12:03:02 -0700 Subject: [PATCH 5/5] more --- src/passes/ConstraintAnalysis.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/passes/ConstraintAnalysis.cpp b/src/passes/ConstraintAnalysis.cpp index d16a326dc29..0c7e940577c 100644 --- a/src/passes/ConstraintAnalysis.cpp +++ b/src/passes/ConstraintAnalysis.cpp @@ -165,14 +165,19 @@ struct ConstraintAnalysis // Find the constraints sent to this specific successor, if there is a // branch, and use them. - auto sentConstraints = constraints; if (auto branch = getBranchConstraints(block, out)) { + auto sentConstraints = constraints; sentConstraints.approximateAnd(branch->local, branch->constraint); - } - - // If anything changed at the start of the target block, flow onwards. - if (outStartConstraints.approximateOr(sentConstraints)) { - work.push(out); + // If anything changed at the start of the target block, flow onwards. + if (outStartConstraints.approximateOr(sentConstraints)) { + work.push(out); + } + } else { + // There are no specific branch constraints, so send the unmodified + // |constraints|, avoiding a copy. + if (outStartConstraints.approximateOr(constraints)) { + work.push(out); + } } } }