From c1d64d39a4ec2b0e34c3165a10a5f79308d65220 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 11 Sep 2026 10:45:46 -0700 Subject: [PATCH 01/28] go --- src/ir/constraint.cpp | 119 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 104 insertions(+), 15 deletions(-) diff --git a/src/ir/constraint.cpp b/src/ir/constraint.cpp index d99e978e0e0..9d73ca4b8c9 100644 --- a/src/ir/constraint.cpp +++ b/src/ir/constraint.cpp @@ -733,15 +733,87 @@ bool AndedConstraintSet::approximateOr(const AndedConstraintSet& other) { return changed; } -std::optional LocalConstraint::parse(Expression* curr) { +namespace { + +// Internal parsing, utilizing a map of tees. We can handle tees in an +// expression, so long as they do not interfere with each other: +// +// (i32.eq +// (local.tee $x ..) +// (i32.const 10) +// ) +// +// We can parse this into $x == 10, just as if we saw a local.get $x there. But +// we cannot handle this: +// +// (i32.eq +// (local.get $x) +// (local.tee $x ..) +// ) +// +// Parsing this into $x == $x would be wrong: the first $x is the old value. +// That is, fully handling tees requires a more SSA-like IR. To handle the +// common cases we want to, we parse the code in the natural order of execution, +// and maintain a list of local operations. A get before a tee indicates +// possible interference. +struct LocalOperations : public SmallVector { + // Check if an Expression returns a local's value: it is either a get or a + // tee. Returns the index and type if so, and notes it in our vector. + struct LocalOperation { + Index index; + Type type; + }; + std::optional parse(Expression* curr) { + if (auto* get = value->dynCast()) { + push_back(get); + return({get->index, get->type}); + } + if (auto* set = value->dynCast()) { + // Ignore unreachable code, so the callers don't need to handle it. + if (set->type != Type::unreachable) { + push_back(set); + return({set->index, set->type}); + } + } + // Unrecognized. + return {}; + } + + // Check for any possible interference between locals, which would tell the + // caller that whatever was parsed is not valid. + bool hasLocalInterference() const { + if (size() <= 1) { + return false; + } + + // Process the list in detail, as interference - a get before a set of the + // same local - is possible. We track the read locals, and if we see a + // later write, that shows a problem; + std::unordered_set read; + for (auto* curr : *this) { + if (auto* get = curr->dynCast()) { + read.insert(get->index); + } else if (auto* set = curr->dynCast()) { + if (read.contains(set->index)) { + return true; + } + } else { + WASM_UNREACHABLE("invalid local op"); + } + } + return false; + } +}; + +std::optional LocalConstraintParseInternal(Expression* curr, LocalOperations& localOperations) { using namespace Match; auto parseEqZArgument = [&](Expression* value) -> std::optional { - if (auto* get = value->dynCast()) { + if (auto localOp = localOperations.parse(value)) { // Canonicalize EqZ to Eq of 0. - auto value = Literal::makeZero(get->type); - return LocalConstraint{get->index, Constraint{Abstract::Eq, {value}}}; + auto value = Literal::makeZero(localOp->type); + return LocalConstraint{localOp->index, Constraint{Abstract::Eq, {value}}}; } // TODO: Recursively parse and reverse a constraint return {}; @@ -750,10 +822,12 @@ std::optional LocalConstraint::parse(Expression* curr) { if (auto* u = curr->dynCast()) { if (Abstract::getUnary(u->value->type, Abstract::EqZ) == u->op) { // EqZ of EqZ means a check that the value is *not* zero. - LocalGet* get; - if (matches(u->value, unary(Abstract::EqZ, Match::local(&get)))) { - auto value = Literal::makeZero(get->type); - return LocalConstraint{get->index, Constraint{Abstract::Ne, {value}}}; + Expression* nested; + if (matches(u->value, unary(Abstract::EqZ, &nested))) { + if (auto localOp = localOperations.parse(nested)) { + auto value = Literal::makeZero(localOp->type); + return LocalConstraint{localOp->index, Constraint{Abstract::Ne, {value}}}; + } } return parseEqZArgument(u->value); @@ -766,10 +840,10 @@ std::optional LocalConstraint::parse(Expression* curr) { return parseEqZArgument(refIsNull->value); } - // Parse a get or a constant. + // Parse a get, tee, or a constant. auto parseTerm = [&](Expression* expr) -> std::optional { - if (auto* get = expr->dynCast()) { - return Term{get->index}; + if (auto localOp = localOperations.parse(expr)) { + return Term{localOp->index}; } if (Properties::isSingleConstantExpression(expr)) { return Term{Properties::getLiteral(expr)}; @@ -781,11 +855,11 @@ std::optional LocalConstraint::parse(Expression* curr) { [&](Abstract::Op op, Expression* left, Expression* right) -> std::optional { - // The left must be a get. - if (auto* get = left->dynCast()) { + // The left must be a get or a tee. + if (auto localOp = localOperations.parse(left)) { // The right can be any term. if (auto value = parseTerm(right)) { - return LocalConstraint{get->index, Constraint{op, *value}}; + return LocalConstraint{localOp->index, Constraint{op, *value}}; } } return {}; @@ -817,12 +891,24 @@ std::optional LocalConstraint::parse(Expression* curr) { return {}; } +std::optional LocalConstraint::parse(Expression* curr) { + LocalOperations localOperations; + auto ret = LocalConstraintParseInternal(curr, localOperations); + if (localOperations.hasLocalInterference()) { + return {}; + } + return ret; +} + ParsedAndedConstraints ParsedAndedConstraints::parse(Expression* curr) { using namespace Match; // The final return value. ParsedAndedConstraints ret; + // We will track local operations over the entire tree we parse. + LocalOperations localOperations; + // Starting from |curr|, parse and recurse into sub-trees: when we see an AND, // we push both children as further work. SmallVector work; @@ -831,7 +917,7 @@ ParsedAndedConstraints ParsedAndedConstraints::parse(Expression* curr) { auto* curr = work.back(); work.pop_back(); - auto parsed = LocalConstraint::parse(curr); + auto parsed = LocalConstraintParseInternal(curr, localOperations); if (parsed) { ret.push_back(*parsed); continue; @@ -851,6 +937,9 @@ ParsedAndedConstraints ParsedAndedConstraints::parse(Expression* curr) { ret.hasUnknown = true; } + if (localOperations.hasLocalInterference()) { + return {}; + } return ret; } From 3b94b19305d5e9cb129d26407e82da7425e410ac Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 11 Sep 2026 10:45:52 -0700 Subject: [PATCH 02/28] format --- src/ir/constraint.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/ir/constraint.cpp b/src/ir/constraint.cpp index 9d73ca4b8c9..2d51dc5efdd 100644 --- a/src/ir/constraint.cpp +++ b/src/ir/constraint.cpp @@ -766,13 +766,13 @@ struct LocalOperations : public SmallVector { std::optional parse(Expression* curr) { if (auto* get = value->dynCast()) { push_back(get); - return({get->index, get->type}); + return ({get->index, get->type}); } if (auto* set = value->dynCast()) { // Ignore unreachable code, so the callers don't need to handle it. if (set->type != Type::unreachable) { push_back(set); - return({set->index, set->type}); + return ({set->index, set->type}); } } // Unrecognized. @@ -805,7 +805,9 @@ struct LocalOperations : public SmallVector { } }; -std::optional LocalConstraintParseInternal(Expression* curr, LocalOperations& localOperations) { +std::optional +LocalConstraintParseInternal(Expression* curr, + LocalOperations& localOperations) { using namespace Match; auto parseEqZArgument = @@ -826,7 +828,8 @@ std::optional LocalConstraintParseInternal(Expression* curr, Lo if (matches(u->value, unary(Abstract::EqZ, &nested))) { if (auto localOp = localOperations.parse(nested)) { auto value = Literal::makeZero(localOp->type); - return LocalConstraint{localOp->index, Constraint{Abstract::Ne, {value}}}; + return LocalConstraint{localOp->index, + Constraint{Abstract::Ne, {value}}}; } } From 5bea67563b232a48ea637733064a361d18850807 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 11 Sep 2026 10:48:16 -0700 Subject: [PATCH 03/28] work --- src/ir/constraint.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/ir/constraint.cpp b/src/ir/constraint.cpp index 2d51dc5efdd..636069d7fd9 100644 --- a/src/ir/constraint.cpp +++ b/src/ir/constraint.cpp @@ -764,15 +764,15 @@ struct LocalOperations : public SmallVector { Type type; }; std::optional parse(Expression* curr) { - if (auto* get = value->dynCast()) { + if (auto* get = curr->dynCast()) { push_back(get); - return ({get->index, get->type}); + return LocalOperation{get->index, get->type}; } - if (auto* set = value->dynCast()) { + if (auto* set = curr->dynCast()) { // Ignore unreachable code, so the callers don't need to handle it. if (set->type != Type::unreachable) { push_back(set); - return ({set->index, set->type}); + return LocalOperation{set->index, set->type}; } } // Unrecognized. @@ -825,7 +825,7 @@ LocalConstraintParseInternal(Expression* curr, if (Abstract::getUnary(u->value->type, Abstract::EqZ) == u->op) { // EqZ of EqZ means a check that the value is *not* zero. Expression* nested; - if (matches(u->value, unary(Abstract::EqZ, &nested))) { + if (matches(u->value, unary(Abstract::EqZ, any(&nested)))) { if (auto localOp = localOperations.parse(nested)) { auto value = Literal::makeZero(localOp->type); return LocalConstraint{localOp->index, @@ -894,6 +894,8 @@ LocalConstraintParseInternal(Expression* curr, return {}; } +} // anonymous namespace + std::optional LocalConstraint::parse(Expression* curr) { LocalOperations localOperations; auto ret = LocalConstraintParseInternal(curr, localOperations); From 379e5fe0ae9e3c92fbc8943b78a9a9710738aa26 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 11 Sep 2026 10:51:44 -0700 Subject: [PATCH 04/28] work --- test/lit/passes/constraint-analysis.wast | 58 ++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/test/lit/passes/constraint-analysis.wast b/test/lit/passes/constraint-analysis.wast index c180cec1d6d..723920356ce 100644 --- a/test/lit/passes/constraint-analysis.wast +++ b/test/lit/passes/constraint-analysis.wast @@ -5914,4 +5914,62 @@ ) ) ) + + ;; CHECK: (func $tee.condition (type $0) (param $param i32) + ;; CHECK-NEXT: (local $x i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.lt_s + ;; CHECK-NEXT: (local.tee $x + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; OPTIN: (func $tee.condition (type $0) (param $param i32) + ;; OPTIN-NEXT: (local $x i32) + ;; OPTIN-NEXT: (if + ;; OPTIN-NEXT: (i32.lt_s + ;; OPTIN-NEXT: (local.tee $x + ;; OPTIN-NEXT: (call $import) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (i32.const 42) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (then + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.eqz + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + (func $tee.condition (param $param i32) + (local $x i32) + ;; We can parse the tee in the condition below. The constraint is saying + ;; $x == 42. + (if + (i32.lt_s + (local.tee $x + (call $import) + ) + (i32.const 42) + ) + (then + ;; $x == 42, so this is false. + (drop + (i32.eqz + (local.get $x) + ) + ) + ) + ) + ) ) From 74750ba06f69d74db172c451c10464dca411b38b Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 11 Sep 2026 10:52:20 -0700 Subject: [PATCH 05/28] format --- test/lit/passes/constraint-analysis.wast | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/test/lit/passes/constraint-analysis.wast b/test/lit/passes/constraint-analysis.wast index 723920356ce..f10ba8f1947 100644 --- a/test/lit/passes/constraint-analysis.wast +++ b/test/lit/passes/constraint-analysis.wast @@ -5918,7 +5918,7 @@ ;; CHECK: (func $tee.condition (type $0) (param $param i32) ;; CHECK-NEXT: (local $x i32) ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.lt_s + ;; CHECK-NEXT: (i32.eq ;; CHECK-NEXT: (local.tee $x ;; CHECK-NEXT: (call $import) ;; CHECK-NEXT: ) @@ -5926,9 +5926,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: (then ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.eqz - ;; CHECK-NEXT: (local.get $x) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -5936,7 +5934,7 @@ ;; OPTIN: (func $tee.condition (type $0) (param $param i32) ;; OPTIN-NEXT: (local $x i32) ;; OPTIN-NEXT: (if - ;; OPTIN-NEXT: (i32.lt_s + ;; OPTIN-NEXT: (i32.eq ;; OPTIN-NEXT: (local.tee $x ;; OPTIN-NEXT: (call $import) ;; OPTIN-NEXT: ) @@ -5944,9 +5942,7 @@ ;; OPTIN-NEXT: ) ;; OPTIN-NEXT: (then ;; OPTIN-NEXT: (drop - ;; OPTIN-NEXT: (i32.eqz - ;; OPTIN-NEXT: (local.get $x) - ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (i32.const 0) ;; OPTIN-NEXT: ) ;; OPTIN-NEXT: ) ;; OPTIN-NEXT: ) @@ -5956,7 +5952,7 @@ ;; We can parse the tee in the condition below. The constraint is saying ;; $x == 42. (if - (i32.lt_s + (i32.eq (local.tee $x (call $import) ) From 4c647701dac9333b7ed137572fb9766d2394fa1a Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 11 Sep 2026 10:54:25 -0700 Subject: [PATCH 06/28] work --- test/lit/passes/constraint-analysis.wast | 90 +++++++++++++++++++++++- 1 file changed, 87 insertions(+), 3 deletions(-) diff --git a/test/lit/passes/constraint-analysis.wast b/test/lit/passes/constraint-analysis.wast index f10ba8f1947..35a708b056b 100644 --- a/test/lit/passes/constraint-analysis.wast +++ b/test/lit/passes/constraint-analysis.wast @@ -5915,7 +5915,7 @@ ) ) - ;; CHECK: (func $tee.condition (type $0) (param $param i32) + ;; CHECK: (func $tee-condition (type $0) (param $param i32) ;; CHECK-NEXT: (local $x i32) ;; CHECK-NEXT: (if ;; CHECK-NEXT: (i32.eq @@ -5931,7 +5931,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; OPTIN: (func $tee.condition (type $0) (param $param i32) + ;; OPTIN: (func $tee-condition (type $0) (param $param i32) ;; OPTIN-NEXT: (local $x i32) ;; OPTIN-NEXT: (if ;; OPTIN-NEXT: (i32.eq @@ -5947,7 +5947,7 @@ ;; OPTIN-NEXT: ) ;; OPTIN-NEXT: ) ;; OPTIN-NEXT: ) - (func $tee.condition (param $param i32) + (func $tee-condition (param $param i32) (local $x i32) ;; We can parse the tee in the condition below. The constraint is saying ;; $x == 42. @@ -5968,4 +5968,88 @@ ) ) ) + + ;; CHECK: (func $tee-condition-later-get (type $0) (param $param i32) + ;; CHECK-NEXT: (local $x i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.ne + ;; CHECK-NEXT: (local.tee $x + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.ne + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 1337) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; OPTIN: (func $tee-condition-later-get (type $0) (param $param i32) + ;; OPTIN-NEXT: (local $x i32) + ;; OPTIN-NEXT: (if + ;; OPTIN-NEXT: (i32.and + ;; OPTIN-NEXT: (i32.ne + ;; OPTIN-NEXT: (local.tee $x + ;; OPTIN-NEXT: (call $import) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (i32.const 42) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (i32.ne + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: (i32.const 1337) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (then + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.const 1) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.const 1) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + (func $tee-condition-later-get (param $param i32) + (local $x i32) + ;; The nested ANDs here include a tee and a later get, which is fine. + (if + (i32.and + (i32.ne + (local.tee $x + (call $import) + ) + (i32.const 42) + ) + (i32.ne + (local.get $x) + (i32.const 1337) + ) + ) + (then + ;; $x != 42 && x != 1337, so these are true. + (drop + (i32.ne + (local.get $x) + (i32.const 42) + ) + ) + (drop + (i32.ne + (local.get $x) + (i32.const 1337) + ) + ) + ) + ) + ) ) From 67a57c35ebc8013b783d232f5e1537d33ebb19d4 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 11 Sep 2026 10:56:17 -0700 Subject: [PATCH 07/28] work --- test/lit/passes/constraint-analysis.wast | 113 ++++++++++++++++++++--- 1 file changed, 101 insertions(+), 12 deletions(-) diff --git a/test/lit/passes/constraint-analysis.wast b/test/lit/passes/constraint-analysis.wast index 35a708b056b..dae60964c0b 100644 --- a/test/lit/passes/constraint-analysis.wast +++ b/test/lit/passes/constraint-analysis.wast @@ -5915,8 +5915,7 @@ ) ) - ;; CHECK: (func $tee-condition (type $0) (param $param i32) - ;; CHECK-NEXT: (local $x i32) + ;; CHECK: (func $tee-condition (type $0) (param $x i32) ;; CHECK-NEXT: (if ;; CHECK-NEXT: (i32.eq ;; CHECK-NEXT: (local.tee $x @@ -5931,8 +5930,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; OPTIN: (func $tee-condition (type $0) (param $param i32) - ;; OPTIN-NEXT: (local $x i32) + ;; OPTIN: (func $tee-condition (type $0) (param $x i32) ;; OPTIN-NEXT: (if ;; OPTIN-NEXT: (i32.eq ;; OPTIN-NEXT: (local.tee $x @@ -5947,8 +5945,7 @@ ;; OPTIN-NEXT: ) ;; OPTIN-NEXT: ) ;; OPTIN-NEXT: ) - (func $tee-condition (param $param i32) - (local $x i32) + (func $tee-condition (param $x i32) ;; We can parse the tee in the condition below. The constraint is saying ;; $x == 42. (if @@ -5969,8 +5966,7 @@ ) ) - ;; CHECK: (func $tee-condition-later-get (type $0) (param $param i32) - ;; CHECK-NEXT: (local $x i32) + ;; CHECK: (func $tee-condition-later-get (type $0) (param $x i32) ;; CHECK-NEXT: (if ;; CHECK-NEXT: (i32.and ;; CHECK-NEXT: (i32.ne @@ -5994,8 +5990,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; OPTIN: (func $tee-condition-later-get (type $0) (param $param i32) - ;; OPTIN-NEXT: (local $x i32) + ;; OPTIN: (func $tee-condition-later-get (type $0) (param $x i32) ;; OPTIN-NEXT: (if ;; OPTIN-NEXT: (i32.and ;; OPTIN-NEXT: (i32.ne @@ -6019,8 +6014,7 @@ ;; OPTIN-NEXT: ) ;; OPTIN-NEXT: ) ;; OPTIN-NEXT: ) - (func $tee-condition-later-get (param $param i32) - (local $x i32) + (func $tee-condition-later-get (param $x i32) ;; The nested ANDs here include a tee and a later get, which is fine. (if (i32.and @@ -6052,4 +6046,99 @@ ) ) ) + + ;; CHECK: (func $tee-condition-later-tee (type $0) (param $x i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.ne + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.ne + ;; CHECK-NEXT: (local.tee $x + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 1337) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.ne + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.ne + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 1337) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; OPTIN: (func $tee-condition-later-tee (type $0) (param $x i32) + ;; OPTIN-NEXT: (if + ;; OPTIN-NEXT: (i32.and + ;; OPTIN-NEXT: (i32.ne + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: (i32.const 42) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (i32.ne + ;; OPTIN-NEXT: (local.tee $x + ;; OPTIN-NEXT: (call $import) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (i32.const 1337) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (then + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.ne + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: (i32.const 42) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.ne + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: (i32.const 1337) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + (func $tee-condition-later-tee (param $x i32) + ;; Reverse the tee and get order. $x appears twice, with two different + ;; values, so we give up here. + ;; TODO: use SSA + (if + (i32.and + (i32.ne + (local.get $x) + (i32.const 42) + ) + (i32.ne + (local.tee $x + (call $import) + ) + (i32.const 1337) + ) + ) + (then + ;; We infer nothing here TODO: we could infer the last + (drop + (i32.ne + (local.get $x) + (i32.const 42) + ) + ) + (drop + (i32.ne + (local.get $x) + (i32.const 1337) + ) + ) + ) + ) + ) ) From 2d756d6c3010fa23f8b10d6a9c57fbe49660cc71 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 11 Sep 2026 10:57:31 -0700 Subject: [PATCH 08/28] work --- test/lit/passes/constraint-analysis.wast | 84 +++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/test/lit/passes/constraint-analysis.wast b/test/lit/passes/constraint-analysis.wast index dae60964c0b..a7df9890e0a 100644 --- a/test/lit/passes/constraint-analysis.wast +++ b/test/lit/passes/constraint-analysis.wast @@ -6030,7 +6030,7 @@ ) ) (then - ;; $x != 42 && x != 1337, so these are true. + ;; $x != 42 && $x != 1337, so these are true. (drop (i32.ne (local.get $x) @@ -6141,4 +6141,86 @@ ) ) ) + + ;; CHECK: (func $tee-condition-later-tee-different-local (type $2) (param $x i32) (param $y i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.ne + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.ne + ;; CHECK-NEXT: (local.tee $y + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 1337) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; OPTIN: (func $tee-condition-later-tee-different-local (type $2) (param $x i32) (param $y i32) + ;; OPTIN-NEXT: (if + ;; OPTIN-NEXT: (i32.and + ;; OPTIN-NEXT: (i32.ne + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: (i32.const 42) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (i32.ne + ;; OPTIN-NEXT: (local.tee $y + ;; OPTIN-NEXT: (call $import) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (i32.const 1337) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (then + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.const 1) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.const 1) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + (func $tee-condition-later-tee-different-local (param $x i32) (param $y i32) + ;; As above, a get and a tee that look like they interfere, but now the + ;; local indexes are different. This is fine. + (if + (i32.and + (i32.ne + (local.get $x) + (i32.const 42) + ) + (i32.ne + (local.tee $y + (call $import) + ) + (i32.const 1337) + ) + ) + (then + ;; $x != 42 && $y != 1337, so these are true. + (drop + (i32.ne + (local.get $x) + (i32.const 42) + ) + ) + (drop + (i32.ne + (local.get $y) + (i32.const 1337) + ) + ) + ) + ) + ) ) From 3c5363d09e754b651723eebeeae571eb03a24d74 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 11 Sep 2026 11:17:41 -0700 Subject: [PATCH 09/28] work --- test/lit/passes/constraint-analysis.wast | 49 ++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/test/lit/passes/constraint-analysis.wast b/test/lit/passes/constraint-analysis.wast index a7df9890e0a..8e7f50121e7 100644 --- a/test/lit/passes/constraint-analysis.wast +++ b/test/lit/passes/constraint-analysis.wast @@ -6223,4 +6223,53 @@ ) ) ) + + ;; CHECK: (func $tee-condition-eqz (type $0) (param $x i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.tee $x + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; OPTIN: (func $tee-condition-eqz (type $0) (param $x i32) + ;; OPTIN-NEXT: (if + ;; OPTIN-NEXT: (i32.eqz + ;; OPTIN-NEXT: (local.tee $x + ;; OPTIN-NEXT: (call $import) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (then + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.const 1) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + (func $tee-condition-eqz (param $x i32) + ;; Testing for parsing of tee in an eqz. + (if + (i32.eqz + (local.tee $x + (call $import) + ) + ) + (then + ;; This is true. + (drop + (i32.eqz + (local.get $x) + ) + ) + ) + ) + ) ) + +;; TODO: test that one direct call to LC::parse (optimize?) From 65f7470a227be464ddc381ef9cdad9a8c9dfb762 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 11 Sep 2026 11:20:46 -0700 Subject: [PATCH 10/28] work --- src/ir/constraint.cpp | 9 +++-- test/lit/passes/constraint-analysis.wast | 50 ++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/ir/constraint.cpp b/src/ir/constraint.cpp index 636069d7fd9..405105681fa 100644 --- a/src/ir/constraint.cpp +++ b/src/ir/constraint.cpp @@ -950,10 +950,11 @@ ParsedAndedConstraints ParsedAndedConstraints::parse(Expression* curr) { ParsedAndedConstraints ParsedAndedConstraints::parseCondition(Expression* curr) { - // A get by itself is a check for not being null. - if (auto* get = curr->dynCast()) { - auto value = Literal::makeZero(get->type); - return {LocalConstraint{get->index, Constraint{Abstract::Ne, {value}}}}; + // A get or tee by itself is a check for not being null. + LocalOperations localOperations; + if (auto localOp = localOperations.parse(curr)) { + auto value = Literal::makeZero(localOp->type); + return {LocalConstraint{localOp->index, Constraint{Abstract::Ne, {value}}}}; } // Otherwise, parse normally. diff --git a/test/lit/passes/constraint-analysis.wast b/test/lit/passes/constraint-analysis.wast index 8e7f50121e7..d8b19856729 100644 --- a/test/lit/passes/constraint-analysis.wast +++ b/test/lit/passes/constraint-analysis.wast @@ -6270,6 +6270,56 @@ ) ) ) + + ;; CHECK: (func $tee-condition-eqz-eqz (type $0) (param $x i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.tee $x + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; OPTIN: (func $tee-condition-eqz-eqz (type $0) (param $x i32) + ;; OPTIN-NEXT: (if + ;; OPTIN-NEXT: (local.tee $x + ;; OPTIN-NEXT: (call $import) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (then + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.const 0) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + (func $tee-condition-eqz-eqz (param $x i32) + ;; Testing for parsing of tee in an eqz^2. In the OPTIN case, the double + ;; eqz is optimized out, and we test a bare local.tee in a condition. + (if + (i32.eqz + (i32.eqz + (local.tee $x + (call $import) + ) + ) + ) + (then + ;; This is false. + (drop + (i32.eqz + (local.get $x) + ) + ) + ) + ) + ) ) ;; TODO: test that one direct call to LC::parse (optimize?) From d02964a21d2e0199a6f06e39fa0cc887daa0426e Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 11 Sep 2026 11:23:42 -0700 Subject: [PATCH 11/28] work --- test/lit/passes/constraint-analysis.wast | 56 ++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/test/lit/passes/constraint-analysis.wast b/test/lit/passes/constraint-analysis.wast index d8b19856729..7f37afd68d3 100644 --- a/test/lit/passes/constraint-analysis.wast +++ b/test/lit/passes/constraint-analysis.wast @@ -6320,6 +6320,62 @@ ) ) ) + + ;; CHECK: (func $tee-test (type $0) (param $x i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.tee $x + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; OPTIN: (func $tee-test (type $0) (param $x i32) + ;; OPTIN-NEXT: (if + ;; OPTIN-NEXT: (i32.eq + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: (i32.const 42) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (then + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.eqz + ;; OPTIN-NEXT: (local.tee $x + ;; OPTIN-NEXT: (call $import) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + (func $tee-test (param $x i32) + ;; A tee not in the condition, but in the test that we want to optimize. + (if + ;; $x == 42. + (i32.eq + (local.get $x) + (i32.const 42) + ) + (then + ;; The $x being tested is not the same as the one the constraint was on, + ;; so we do not optimize. + (drop + (i32.eqz + (local.tee $x + (call $import) + ) + ) + ) + ) + ) + ) ) ;; TODO: test that one direct call to LC::parse (optimize?) From c1eb015f70ce982e5e249998d9070b50966e1455 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 11 Sep 2026 12:41:42 -0700 Subject: [PATCH 12/28] format --- src/ir/constraint.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ir/constraint.cpp b/src/ir/constraint.cpp index 405105681fa..5a724ab3891 100644 --- a/src/ir/constraint.cpp +++ b/src/ir/constraint.cpp @@ -806,7 +806,7 @@ struct LocalOperations : public SmallVector { }; std::optional -LocalConstraintParseInternal(Expression* curr, +localConstraintParseInternal(Expression* curr, LocalOperations& localOperations) { using namespace Match; @@ -898,7 +898,7 @@ LocalConstraintParseInternal(Expression* curr, std::optional LocalConstraint::parse(Expression* curr) { LocalOperations localOperations; - auto ret = LocalConstraintParseInternal(curr, localOperations); + auto ret = localConstraintParseInternal(curr, localOperations); if (localOperations.hasLocalInterference()) { return {}; } @@ -922,7 +922,7 @@ ParsedAndedConstraints ParsedAndedConstraints::parse(Expression* curr) { auto* curr = work.back(); work.pop_back(); - auto parsed = LocalConstraintParseInternal(curr, localOperations); + auto parsed = localConstraintParseInternal(curr, localOperations); if (parsed) { ret.push_back(*parsed); continue; From 6ccfb794b86b4e975001d811b6afda82c0888a71 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 11 Sep 2026 15:55:56 -0700 Subject: [PATCH 13/28] work --- src/ir/constraint.cpp | 3 + test/lit/passes/constraint-analysis.wast | 80 +++++++++++++++++++++++- 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/src/ir/constraint.cpp b/src/ir/constraint.cpp index 5a724ab3891..80462ba3b79 100644 --- a/src/ir/constraint.cpp +++ b/src/ir/constraint.cpp @@ -772,6 +772,7 @@ struct LocalOperations : public SmallVector { // Ignore unreachable code, so the callers don't need to handle it. if (set->type != Type::unreachable) { push_back(set); + // XXX do we need to scan into the value... could be tees in there!1 return LocalOperation{set->index, set->type}; } } @@ -797,6 +798,8 @@ struct LocalOperations : public SmallVector { if (read.contains(set->index)) { return true; } + // Insert a read, because the tee does both a write and a read. + read.insert(set->index); } else { WASM_UNREACHABLE("invalid local op"); } diff --git a/test/lit/passes/constraint-analysis.wast b/test/lit/passes/constraint-analysis.wast index 7f37afd68d3..dbc7182f196 100644 --- a/test/lit/passes/constraint-analysis.wast +++ b/test/lit/passes/constraint-analysis.wast @@ -6376,6 +6376,84 @@ ) ) ) + + ;; CHECK: (func $tee-condition-another (type $0) (param $x i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.tee $x + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.tee $x + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; OPTIN: (func $tee-condition-another (type $0) (param $x i32) + ;; OPTIN-NEXT: (if + ;; OPTIN-NEXT: (block (result i32) + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (local.tee $x + ;; OPTIN-NEXT: (i32.const 0) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (local.tee $x + ;; OPTIN-NEXT: (i32.const 1) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (i32.const 0) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (then + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.const 0) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.const 1) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + (func $tee-condition-another (param $x i32) + ;; Two tees of the same local. + (if + (i32.ne + (local.tee $x + (i32.const 0) + ) + (local.tee $x + (i32.const 1) + ) + ) + (then + ;; We infer nothing here. TODO: we could infer the latter. + (drop + (i32.eq + (local.get $x) + (i32.const 0) + ) + ) + (drop + (i32.eq + (local.get $x) + (i32.const 1) + ) + ) + ) + ) + ) ) -;; TODO: test that one direct call to LC::parse (optimize?) From 77c38c1512458ed7642e4e9190b72767ffbb2cb3 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 11 Sep 2026 15:56:58 -0700 Subject: [PATCH 14/28] work --- test/lit/passes/constraint-analysis.wast | 44 ++++++------------------ 1 file changed, 11 insertions(+), 33 deletions(-) diff --git a/test/lit/passes/constraint-analysis.wast b/test/lit/passes/constraint-analysis.wast index dbc7182f196..48fe9622183 100644 --- a/test/lit/passes/constraint-analysis.wast +++ b/test/lit/passes/constraint-analysis.wast @@ -6379,24 +6379,16 @@ ;; CHECK: (func $tee-condition-another (type $0) (param $x i32) ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (block (result i32) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.tee $x - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.ne + ;; CHECK-NEXT: (local.tee $x + ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.tee $x - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.tee $x + ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (then ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -6404,24 +6396,16 @@ ;; CHECK-NEXT: ) ;; OPTIN: (func $tee-condition-another (type $0) (param $x i32) ;; OPTIN-NEXT: (if - ;; OPTIN-NEXT: (block (result i32) - ;; OPTIN-NEXT: (drop - ;; OPTIN-NEXT: (local.tee $x - ;; OPTIN-NEXT: (i32.const 0) - ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (i32.ne + ;; OPTIN-NEXT: (local.tee $x + ;; OPTIN-NEXT: (i32.const 0) ;; OPTIN-NEXT: ) - ;; OPTIN-NEXT: (drop - ;; OPTIN-NEXT: (local.tee $x - ;; OPTIN-NEXT: (i32.const 1) - ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (local.tee $x + ;; OPTIN-NEXT: (i32.const 1) ;; OPTIN-NEXT: ) - ;; OPTIN-NEXT: (i32.const 0) ;; OPTIN-NEXT: ) ;; OPTIN-NEXT: (then ;; OPTIN-NEXT: (drop - ;; OPTIN-NEXT: (i32.const 0) - ;; OPTIN-NEXT: ) - ;; OPTIN-NEXT: (drop ;; OPTIN-NEXT: (i32.const 1) ;; OPTIN-NEXT: ) ;; OPTIN-NEXT: ) @@ -6439,13 +6423,7 @@ ) ) (then - ;; We infer nothing here. TODO: we could infer the latter. - (drop - (i32.eq - (local.get $x) - (i32.const 0) - ) - ) + ;; We know that $x == 1 here (the last tee). (drop (i32.eq (local.get $x) From 905c7c9d75d6418d4c9359e5221c61214c84f055 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 11 Sep 2026 16:05:45 -0700 Subject: [PATCH 15/28] work --- test/lit/passes/constraint-analysis.wast | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/lit/passes/constraint-analysis.wast b/test/lit/passes/constraint-analysis.wast index 48fe9622183..9ded859960e 100644 --- a/test/lit/passes/constraint-analysis.wast +++ b/test/lit/passes/constraint-analysis.wast @@ -6423,7 +6423,8 @@ ) ) (then - ;; We know that $x == 1 here (the last tee). + ;; We know that $x == 1 here (the last tee; first should not confuse + ;; us). (drop (i32.eq (local.get $x) From f60ebcb46ab09bc30b861dbed67210a01b1f13ff Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 11 Sep 2026 16:10:44 -0700 Subject: [PATCH 16/28] work --- test/lit/passes/constraint-analysis.wast | 82 ++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/test/lit/passes/constraint-analysis.wast b/test/lit/passes/constraint-analysis.wast index 9ded859960e..a46198895ef 100644 --- a/test/lit/passes/constraint-analysis.wast +++ b/test/lit/passes/constraint-analysis.wast @@ -6047,6 +6047,61 @@ ) ) + ;; CHECK: (func $tee-condition-later-get-2 (type $0) (param $x i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.tee $x + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; OPTIN: (func $tee-condition-later-get-2 (type $0) (param $x i32) + ;; OPTIN-NEXT: (if + ;; OPTIN-NEXT: (block (result i32) + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (local.tee $x + ;; OPTIN-NEXT: (i32.const 42) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (i32.const 1) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (then + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.const 1) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + (func $tee-condition-later-get-2 (param $x i32) + ;; A later get without two ANDs - in the same comparison. We can infer the + ;; comparison is 1. + (if + (i32.eq + (local.tee $x + (i32.const 42) + ) + (local.get $x) + ) + (then + ;; $x == 42, so this is true. + (drop + (i32.eq + (local.get $x) + (i32.const 42) + ) + ) + ) + ) + ) ;; CHECK: (func $tee-condition-later-tee (type $0) (param $x i32) ;; CHECK-NEXT: (if ;; CHECK-NEXT: (i32.and @@ -6436,3 +6491,30 @@ ) ) +(;; + + (func $tee-condition-nested (param $x i32) + ;; A second set is nested inside. + (if + (i32.eq + (local.tee $x + (block (result i32) + (local.set $ + (call $import) + ) + ) + (i32.const 42) + ) + (then + ;; $x == 42, so this is false. + (drop + (i32.eqz + (local.get $x) + ) + ) + ) + ) + ) + +also nested get, and etc. +;;) From 5a01d76768ee3e28418eac5495ba652551588bb3 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 11 Sep 2026 16:30:41 -0700 Subject: [PATCH 17/28] work --- src/ir/constraint.cpp | 16 ++- test/lit/passes/constraint-analysis.wast | 125 ++++++++++++++++++++--- 2 files changed, 123 insertions(+), 18 deletions(-) diff --git a/src/ir/constraint.cpp b/src/ir/constraint.cpp index 80462ba3b79..7846fa5dd1a 100644 --- a/src/ir/constraint.cpp +++ b/src/ir/constraint.cpp @@ -17,6 +17,7 @@ #include #include "ir/constraint.h" +#include "ir/find_all.h" #include "ir/properties.h" #include "wasm.h" @@ -770,11 +771,18 @@ struct LocalOperations : public SmallVector { } if (auto* set = curr->dynCast()) { // Ignore unreachable code, so the callers don't need to handle it. - if (set->type != Type::unreachable) { - push_back(set); - // XXX do we need to scan into the value... could be tees in there!1 - return LocalOperation{set->index, set->type}; + if (set->type == Type::unreachable) { + return {}; } + + // We know the value of this expression - the local the tee writes to - + // but further sets may be nested in the value, affecting other locals. + for (auto* nested : FindAll(set->value).list) { + push_back(nested); + } + + push_back(set); + return LocalOperation{set->index, set->type}; } // Unrecognized. return {}; diff --git a/test/lit/passes/constraint-analysis.wast b/test/lit/passes/constraint-analysis.wast index a46198895ef..4da18ae2c97 100644 --- a/test/lit/passes/constraint-analysis.wast +++ b/test/lit/passes/constraint-analysis.wast @@ -6489,32 +6489,129 @@ ) ) ) -) - -(;; - (func $tee-condition-nested (param $x i32) - ;; A second set is nested inside. + ;; CHECK: (func $tee-condition-nested (type $2) (param $x i32) (param $y i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (local.tee $y + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (local.set $x + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 30) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: (i32.const 30) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; OPTIN: (func $tee-condition-nested (type $2) (param $x i32) (param $y i32) + ;; OPTIN-NEXT: (if + ;; OPTIN-NEXT: (i32.and + ;; OPTIN-NEXT: (i32.eq + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: (i32.const 10) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (i32.eq + ;; OPTIN-NEXT: (local.tee $y + ;; OPTIN-NEXT: (block (result i32) + ;; OPTIN-NEXT: (local.set $x + ;; OPTIN-NEXT: (i32.const 20) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (call $import) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (i32.const 30) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (then + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.const 0) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.const 1) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.eq + ;; OPTIN-NEXT: (local.get $y) + ;; OPTIN-NEXT: (i32.const 30) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + (func $tee-condition-nested (param $x i32) (param $y i32) + ;; A second set is nested inside the tee. $x initially seems like it must be + ;; equal to 10 in the If body, but it is set to 20 after. (if - (i32.eq - (local.tee $x - (block (result i32) - (local.set $ - (call $import) + (i32.and + (i32.eq + (local.get $x) + (i32.const 10) + ) + (i32.eq + (local.tee $y + (block (result i32) + (local.set $x + (i32.const 20) + ) + (call $import) + ) ) + (i32.const 30) ) - (i32.const 42) ) (then - ;; $x == 42, so this is false. + ;; This is false: $x was 10 (if the first ANDed expression was true), + ;; but $x was set to 20 after. (drop - (i32.eqz + (i32.eq + (local.get $x) + (i32.const 10) + ) + ) + ;; This is true. + (drop + (i32.eq (local.get $x) + (i32.const 20) + ) + ) + ;; We don't infer this, even though we could: the get and later set of + ;; $x make us give up. TODO + (drop + (i32.eq + (local.get $y) + (i32.const 30) ) ) ) ) ) +) + +(;; +TODO: repeat testcase, but nested set is of another local, so it is cool -also nested get, and etc. +TODO: nested *get*, which is fine, not pushed, no problem, even if set after it ;;) From 24d906fab194b6ccd1dd58b839c71d1364569259 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 11 Sep 2026 16:42:17 -0700 Subject: [PATCH 18/28] work --- test/lit/passes/constraint-analysis.wast | 135 ++++++++++++++++++++--- 1 file changed, 121 insertions(+), 14 deletions(-) diff --git a/test/lit/passes/constraint-analysis.wast b/test/lit/passes/constraint-analysis.wast index 4da18ae2c97..215400a769f 100644 --- a/test/lit/passes/constraint-analysis.wast +++ b/test/lit/passes/constraint-analysis.wast @@ -13,8 +13,8 @@ ;; OPTIN: (type $array (array (mut i32))) (type $array (array (mut i32))) - ;; CHECK: (import "a" "b" (func $import (type $3) (result i32))) - ;; OPTIN: (import "a" "b" (func $import (type $3) (result i32))) + ;; CHECK: (import "a" "b" (func $import (type $4) (result i32))) + ;; OPTIN: (import "a" "b" (func $import (type $4) (result i32))) (import "a" "b" (func $import (result i32))) ;; CHECK: (func $simple (type $1) @@ -2480,7 +2480,7 @@ ) ) - ;; CHECK: (func $local-changes (type $4) (param $x i32) (param $y i32) (param $z i32) + ;; CHECK: (func $local-changes (type $3) (param $x i32) (param $y i32) (param $z i32) ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (local.get $y) ;; CHECK-NEXT: ) @@ -2536,7 +2536,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; OPTIN: (func $local-changes (type $4) (param $x i32) (param $y i32) (param $z i32) + ;; OPTIN: (func $local-changes (type $3) (param $x i32) (param $y i32) (param $z i32) ;; OPTIN-NEXT: (local.set $x ;; OPTIN-NEXT: (local.get $y) ;; OPTIN-NEXT: ) @@ -2666,7 +2666,7 @@ ) ) - ;; CHECK: (func $local-changes-2 (type $4) (param $x i32) (param $y i32) (param $z i32) + ;; CHECK: (func $local-changes-2 (type $3) (param $x i32) (param $y i32) (param $z i32) ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (local.get $y) ;; CHECK-NEXT: ) @@ -2722,7 +2722,7 @@ ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; OPTIN: (func $local-changes-2 (type $4) (param $x i32) (param $y i32) (param $z i32) + ;; OPTIN: (func $local-changes-2 (type $3) (param $x i32) (param $y i32) (param $z i32) ;; OPTIN-NEXT: (local.set $x ;; OPTIN-NEXT: (local.get $y) ;; OPTIN-NEXT: ) @@ -3056,7 +3056,7 @@ ) ) - ;; CHECK: (func $local-changes-ne (type $4) (param $x i32) (param $y i32) (param $z i32) + ;; CHECK: (func $local-changes-ne (type $3) (param $x i32) (param $y i32) (param $z i32) ;; CHECK-NEXT: (if ;; CHECK-NEXT: (i32.ne ;; CHECK-NEXT: (local.get $x) @@ -3108,7 +3108,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; OPTIN: (func $local-changes-ne (type $4) (param $x i32) (param $y i32) (param $z i32) + ;; OPTIN: (func $local-changes-ne (type $3) (param $x i32) (param $y i32) (param $z i32) ;; OPTIN-NEXT: (if ;; OPTIN-NEXT: (i32.ne ;; OPTIN-NEXT: (local.get $x) @@ -4461,7 +4461,7 @@ ) ) - ;; CHECK: (func $flipped-contradiction (type $3) (result i32) + ;; CHECK: (func $flipped-contradiction (type $4) (result i32) ;; CHECK-NEXT: (local $x i32) ;; CHECK-NEXT: (loop $loop ;; CHECK-NEXT: (br_if $loop @@ -4473,7 +4473,7 @@ ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; OPTIN: (func $flipped-contradiction (type $3) (result i32) + ;; OPTIN: (func $flipped-contradiction (type $4) (result i32) ;; OPTIN-NEXT: (local $x i32) ;; OPTIN-NEXT: (loop $loop ;; OPTIN-NEXT: (br_if $loop @@ -4507,7 +4507,7 @@ ) ) - ;; CHECK: (func $flipped-contradiction-no (type $3) (result i32) + ;; CHECK: (func $flipped-contradiction-no (type $4) (result i32) ;; CHECK-NEXT: (local $x i32) ;; CHECK-NEXT: (loop $loop (result i32) ;; CHECK-NEXT: (br_if $loop @@ -4521,7 +4521,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; OPTIN: (func $flipped-contradiction-no (type $3) (result i32) + ;; OPTIN: (func $flipped-contradiction-no (type $4) (result i32) ;; OPTIN-NEXT: (local $x i32) ;; OPTIN-NEXT: (loop $loop (result i32) ;; OPTIN-NEXT: (br_if $loop @@ -6608,10 +6608,117 @@ ) ) ) + + ;; CHECK: (func $tee-condition-nested-2 (type $3) (param $x i32) (param $y i32) (param $z i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (local.tee $y + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (local.set $z + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 30) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; OPTIN: (func $tee-condition-nested-2 (type $3) (param $x i32) (param $y i32) (param $z i32) + ;; OPTIN-NEXT: (if + ;; OPTIN-NEXT: (i32.and + ;; OPTIN-NEXT: (i32.eq + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: (i32.const 10) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (i32.eq + ;; OPTIN-NEXT: (local.tee $y + ;; OPTIN-NEXT: (block (result i32) + ;; OPTIN-NEXT: (local.set $z + ;; OPTIN-NEXT: (i32.const 20) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (call $import) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (i32.const 30) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (then + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.const 1) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.const 1) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.const 1) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + (func $tee-condition-nested-2 (param $x i32) (param $y i32) (param $z i32) + ;; As above, but the second set is of another local. Without any conflict, + ;; we can infer more. + (if + (i32.and + (i32.eq + (local.get $x) + (i32.const 10) + ) + (i32.eq + (local.tee $y + (block (result i32) + (local.set $z ;; this changed + (i32.const 20) + ) + (call $import) + ) + ) + (i32.const 30) + ) + ) + (then + ;; These are all true. + (drop + (i32.eq + (local.get $x) + (i32.const 10) + ) + ) + (drop + (i32.eq + (local.get $y) + (i32.const 30) + ) + ) + (drop + (i32.eq + (local.get $z) + (i32.const 20) + ) + ) + ) + ) + ) ) (;; -TODO: repeat testcase, but nested set is of another local, so it is cool - TODO: nested *get*, which is fine, not pushed, no problem, even if set after it ;;) From cd2191a20ee9c859958df312861e8de22bed99c1 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 11 Sep 2026 16:43:32 -0700 Subject: [PATCH 19/28] work --- test/lit/passes/constraint-analysis.wast | 100 ++++++++++++++++++++++- 1 file changed, 97 insertions(+), 3 deletions(-) diff --git a/test/lit/passes/constraint-analysis.wast b/test/lit/passes/constraint-analysis.wast index 215400a769f..2e45615d47d 100644 --- a/test/lit/passes/constraint-analysis.wast +++ b/test/lit/passes/constraint-analysis.wast @@ -6717,8 +6717,102 @@ ) ) ) + + ;; CHECK: (func $tee-condition-nested-get (type $2) (param $x i32) (param $y i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (local.tee $y + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 30) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; OPTIN: (func $tee-condition-nested-get (type $2) (param $x i32) (param $y i32) + ;; OPTIN-NEXT: (if + ;; OPTIN-NEXT: (i32.and + ;; OPTIN-NEXT: (i32.eq + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: (i32.const 10) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (i32.eq + ;; OPTIN-NEXT: (local.tee $y + ;; OPTIN-NEXT: (block (result i32) + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (call $import) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (i32.const 30) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (then + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.const 1) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.const 1) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + (func $tee-condition-nested-get (param $x i32) (param $y i32) + ;; As $tee-condition-nested, but rather than a nested *set*, a get. A get + ;; is not a problem for us. + (if + (i32.and + (i32.eq + (local.get $x) + (i32.const 10) + ) + (i32.eq + (local.tee $y + (block (result i32) + (drop + (local.get $x) ;; this changed. + ) + (call $import) + ) + ) + (i32.const 30) + ) + ) + (then + ;; These are all true. + (drop + (i32.eq + (local.get $x) + (i32.const 10) + ) + ) + (drop + (i32.eq + (local.get $y) + (i32.const 30) + ) + ) + ) + ) + ) ) -(;; -TODO: nested *get*, which is fine, not pushed, no problem, even if set after it -;;) From 2eca65417af183babb0ba280dbd94d4c1ff85c14 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 16 Sep 2026 09:04:43 -0700 Subject: [PATCH 20/28] fix --- src/ir/constraint.cpp | 10 +++- test/lit/passes/constraint-analysis.wast | 61 ++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/src/ir/constraint.cpp b/src/ir/constraint.cpp index 7846fa5dd1a..109a87a58a4 100644 --- a/src/ir/constraint.cpp +++ b/src/ir/constraint.cpp @@ -784,7 +784,10 @@ struct LocalOperations : public SmallVector { push_back(set); return LocalOperation{set->index, set->type}; } - // Unrecognized. + // Unrecognized. As above, we must scan for nested tees. + for (auto* nested : FindAll(curr).list) { + push_back(nested); + } return {}; } @@ -949,7 +952,10 @@ ParsedAndedConstraints ParsedAndedConstraints::parse(Expression* curr) { } // TODO: support OR - // We failed to parse this. + // We failed to parse this as constraints. We do still need to check for + // local operations that might interfere with the things we did parse, + // otherwise. + localOperations.parse(curr); ret.hasUnknown = true; } diff --git a/test/lit/passes/constraint-analysis.wast b/test/lit/passes/constraint-analysis.wast index 2e45615d47d..067845236dd 100644 --- a/test/lit/passes/constraint-analysis.wast +++ b/test/lit/passes/constraint-analysis.wast @@ -6814,5 +6814,66 @@ ) ) ) + + ;; CHECK: (func $tee-trample-simple (type $0) (param $x i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.tee $x + ;; CHECK-NEXT: (call $import) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; OPTIN: (func $tee-trample-simple (type $0) (param $x i32) + ;; OPTIN-NEXT: (if + ;; OPTIN-NEXT: (i32.and + ;; OPTIN-NEXT: (i32.eqz + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (local.tee $x + ;; OPTIN-NEXT: (call $import) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (then + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.eqz + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + (func $tee-trample-simple (param $x i32) + (if + (i32.and + ;; $x == 0 + (i32.eqz + (local.get $x) + ) + ;; But then trample $x with an unknown value. + (local.tee $x + (call $import) + ) + ) + (then + ;; $x may not be 0 here, as it was trampled, so we infer nothing. + (drop + (i32.eqz + (local.get $x) + ) + ) + ) + ) + ) ) From 2ebbcf04e487900df9aaa4a4d617195fbd1520a4 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 16 Sep 2026 11:13:37 -0700 Subject: [PATCH 21/28] update test benefits --- .../inlining-optimizing_optimize-level=3.wast | 59 +- test/passes/fannkuch3_manyopts_dwarf.bin.txt | 1279 ++++++++--------- 2 files changed, 636 insertions(+), 702 deletions(-) diff --git a/test/lit/passes/inlining-optimizing_optimize-level=3.wast b/test/lit/passes/inlining-optimizing_optimize-level=3.wast index f6df7305620..bf931609666 100644 --- a/test/lit/passes/inlining-optimizing_optimize-level=3.wast +++ b/test/lit/passes/inlining-optimizing_optimize-level=3.wast @@ -9443,47 +9443,36 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (if (result i32) - ;; CHECK-NEXT: (i32.lt_s - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: (i32.const 10) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (loop $while-in132 (result i32) - ;; CHECK-NEXT: (if - ;; CHECK-NEXT: (i32.load - ;; CHECK-NEXT: (i32.add - ;; CHECK-NEXT: (local.get $4) - ;; CHECK-NEXT: (i32.shl - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: (i32.const 2) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (then - ;; CHECK-NEXT: (local.set $17 - ;; CHECK-NEXT: (i32.const -1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (br $label$break$L343) + ;; CHECK-NEXT: (loop $while-in132 (result i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.load + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (local.get $4) + ;; CHECK-NEXT: (i32.shl + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (br_if $while-in132 - ;; CHECK-NEXT: (i32.lt_s - ;; CHECK-NEXT: (local.tee $0 - ;; CHECK-NEXT: (i32.add - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 10) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (local.set $17 + ;; CHECK-NEXT: (i32.const -1) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (br $label$break$L343) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (else - ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (br_if $while-in132 + ;; CHECK-NEXT: (i32.lt_s + ;; CHECK-NEXT: (local.tee $0 + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (else diff --git a/test/passes/fannkuch3_manyopts_dwarf.bin.txt b/test/passes/fannkuch3_manyopts_dwarf.bin.txt index 0f159c3feff..b6fd3128e21 100644 --- a/test/passes/fannkuch3_manyopts_dwarf.bin.txt +++ b/test/passes/fannkuch3_manyopts_dwarf.bin.txt @@ -2303,7 +2303,7 @@ Contains section .debug_info (851 bytes) Contains section .debug_loc (1073 bytes) Contains section .debug_ranges (88 bytes) Contains section .debug_abbrev (333 bytes) -Contains section .debug_line (2322 bytes) +Contains section .debug_line (2300 bytes) Contains section .debug_str (434 bytes) .debug_abbrev contents: @@ -2470,7 +2470,7 @@ Abbrev table for offset: 0x00000000 DW_AT_low_pc [DW_FORM_addr] (0x0000000000000000) DW_AT_ranges [DW_FORM_sec_offset] (0x00000040 [0x00000006, 0x00000377) - [0x00000379, 0x000005a8)) + [0x00000379, 0x00000588)) 0x00000026: DW_TAG_pointer_type [2] DW_AT_type [DW_FORM_ref4] (cu + 0x002b => {0x0000002b} "worker_args") @@ -2818,7 +2818,7 @@ Abbrev table for offset: 0x00000000 0x0000023b: DW_TAG_subprogram [23] * DW_AT_low_pc [DW_FORM_addr] (0x0000000000000379) - DW_AT_high_pc [DW_FORM_data4] (0x0000022f) + DW_AT_high_pc [DW_FORM_data4] (0x0000020f) DW_AT_frame_base [DW_FORM_exprloc] (DW_OP_WASM_location 0x0 +2, DW_OP_stack_value) DW_AT_GNU_all_call_sites [DW_FORM_flag_present] (true) DW_AT_name [DW_FORM_strp] ( .debug_str[0x0000018c] = "main") @@ -2867,7 +2867,7 @@ Abbrev table for offset: 0x00000000 0x00000296: DW_TAG_variable [26] DW_AT_location [DW_FORM_sec_offset] (0x000002a2: - [0xffffffff, 0x0000056d): + [0xffffffff, 0x0000054d): [0x00000001, 0x00000001): DW_OP_lit0, DW_OP_stack_value [0x00000000, 0x00000018): DW_OP_WASM_location 0x0 +6, DW_OP_stack_value) DW_AT_abstract_origin [DW_FORM_ref4] (cu + 0x01ce => {0x000001ce} "args") @@ -2878,9 +2878,9 @@ Abbrev table for offset: 0x00000000 [0x00000001, 0x00000001): DW_OP_consts +0, DW_OP_stack_value [0x00000000, 0x00000005): DW_OP_WASM_location 0x0 +0, DW_OP_stack_value [0x00000001, 0x00000001): DW_OP_consts +0, DW_OP_stack_value - [0x0000003f, 0x00000044): DW_OP_WASM_location 0x0 +0, DW_OP_stack_value - [0x0000005d, 0x00000061): DW_OP_consts +0, DW_OP_stack_value - [0x00000088, 0x0000008d): DW_OP_WASM_location 0x0 +0, DW_OP_stack_value + [0x00000036, 0x0000003b): DW_OP_WASM_location 0x0 +0, DW_OP_stack_value + [0x00000049, 0x0000004d): DW_OP_consts +0, DW_OP_stack_value + [0x00000074, 0x00000079): DW_OP_WASM_location 0x0 +0, DW_OP_stack_value [0x00000001, 0x00000001): DW_OP_consts +0, DW_OP_stack_value [0x00000001, 0x00000001): DW_OP_consts +0, DW_OP_stack_value [0x00000001, 0x00000001): DW_OP_consts +0, DW_OP_stack_value) @@ -2903,31 +2903,31 @@ Abbrev table for offset: 0x00000000 0x000002bf: DW_TAG_variable [26] DW_AT_location [DW_FORM_sec_offset] (0x00000390: - [0xffffffff, 0x0000052d): + [0xffffffff, 0x00000519): [0x00000000, 0x00000007): DW_OP_WASM_location 0x0 +6, DW_OP_stack_value [0x00000001, 0x00000001): DW_OP_WASM_location 0x0 +6, DW_OP_stack_value) DW_AT_abstract_origin [DW_FORM_ref4] (cu + 0x0205 => {0x00000205} "r") 0x000002c8: DW_TAG_variable [26] DW_AT_location [DW_FORM_sec_offset] (0x000003e8: - [0xffffffff, 0x00000556): + [0xffffffff, 0x00000536): [0x00000001, 0x00000001): DW_OP_consts +0, DW_OP_stack_value [0x00000027, 0x0000002f): DW_OP_WASM_location 0x0 +0, DW_OP_stack_value) DW_AT_abstract_origin [DW_FORM_ref4] (cu + 0x0210 => {0x00000210} "maxflips") 0x000002d1: DW_TAG_variable [26] DW_AT_location [DW_FORM_sec_offset] (0x00000413: - [0xffffffff, 0x00000566): + [0xffffffff, 0x00000546): [0x00000000, 0x0000001f): DW_OP_WASM_location 0x0 +1, DW_OP_stack_value) DW_AT_abstract_origin [DW_FORM_ref4] (cu + 0x021b => {0x0000021b} "flips") 0x000002da: DW_TAG_label [28] DW_AT_abstract_origin [DW_FORM_ref4] (cu + 0x0226 => {0x00000226} "cleanup") - DW_AT_low_pc [DW_FORM_addr] (0x000000000000054a) + DW_AT_low_pc [DW_FORM_addr] (0x000000000000052a) 0x000002e3: DW_TAG_lexical_block [14] * DW_AT_ranges [DW_FORM_sec_offset] (0x00000028 - [0x000004c3, 0x00000508) + [0x000004af, 0x000004f4) [0x00000000, 0x00000001)) 0x000002e8: DW_TAG_variable [26] @@ -2957,31 +2957,31 @@ Abbrev table for offset: 0x00000000 DW_AT_low_pc [DW_FORM_addr] (0x000000000000040e) 0x0000030c: DW_TAG_GNU_call_site [15] - DW_AT_low_pc [DW_FORM_addr] (0x0000000000000474) + DW_AT_low_pc [DW_FORM_addr] (0x0000000000000460) 0x00000311: DW_TAG_GNU_call_site [15] - DW_AT_low_pc [DW_FORM_addr] (0x0000000000000486) + DW_AT_low_pc [DW_FORM_addr] (0x0000000000000472) 0x00000316: DW_TAG_GNU_call_site [15] - DW_AT_low_pc [DW_FORM_addr] (0x0000000000000544) + DW_AT_low_pc [DW_FORM_addr] (0x0000000000000000) 0x0000031b: DW_TAG_GNU_call_site [16] DW_AT_abstract_origin [DW_FORM_ref4] (cu + 0x019a => {0x0000019a} "free") - DW_AT_low_pc [DW_FORM_addr] (0x000000000000054e) + DW_AT_low_pc [DW_FORM_addr] (0x000000000000052e) 0x00000324: DW_TAG_GNU_call_site [16] DW_AT_abstract_origin [DW_FORM_ref4] (cu + 0x019a => {0x0000019a} "free") - DW_AT_low_pc [DW_FORM_addr] (0x0000000000000552) + DW_AT_low_pc [DW_FORM_addr] (0x0000000000000532) 0x0000032d: DW_TAG_GNU_call_site [15] - DW_AT_low_pc [DW_FORM_addr] (0x0000000000000564) + DW_AT_low_pc [DW_FORM_addr] (0x0000000000000544) 0x00000332: DW_TAG_GNU_call_site [16] DW_AT_abstract_origin [DW_FORM_ref4] (cu + 0x019a => {0x0000019a} "free") - DW_AT_low_pc [DW_FORM_addr] (0x0000000000000571) + DW_AT_low_pc [DW_FORM_addr] (0x0000000000000551) 0x0000033b: DW_TAG_GNU_call_site [15] - DW_AT_low_pc [DW_FORM_addr] (0x000000000000059c) + DW_AT_low_pc [DW_FORM_addr] (0x000000000000057c) 0x00000340: NULL @@ -3073,7 +3073,7 @@ Abbrev table for offset: 0x00000000 [0x00000001, 0x00000001): DW_OP_consts +30, DW_OP_stack_value 0x000002a2: - [0xffffffff, 0x0000056d): + [0xffffffff, 0x0000054d): [0x00000001, 0x00000001): DW_OP_lit0, DW_OP_stack_value [0x00000000, 0x00000018): DW_OP_WASM_location 0x0 +6, DW_OP_stack_value @@ -3082,9 +3082,9 @@ Abbrev table for offset: 0x00000000 [0x00000001, 0x00000001): DW_OP_consts +0, DW_OP_stack_value [0x00000000, 0x00000005): DW_OP_WASM_location 0x0 +0, DW_OP_stack_value [0x00000001, 0x00000001): DW_OP_consts +0, DW_OP_stack_value - [0x0000003f, 0x00000044): DW_OP_WASM_location 0x0 +0, DW_OP_stack_value - [0x0000005d, 0x00000061): DW_OP_consts +0, DW_OP_stack_value - [0x00000088, 0x0000008d): DW_OP_WASM_location 0x0 +0, DW_OP_stack_value + [0x00000036, 0x0000003b): DW_OP_WASM_location 0x0 +0, DW_OP_stack_value + [0x00000049, 0x0000004d): DW_OP_consts +0, DW_OP_stack_value + [0x00000074, 0x00000079): DW_OP_WASM_location 0x0 +0, DW_OP_stack_value [0x00000001, 0x00000001): DW_OP_consts +0, DW_OP_stack_value [0x00000001, 0x00000001): DW_OP_consts +0, DW_OP_stack_value [0x00000001, 0x00000001): DW_OP_consts +0, DW_OP_stack_value @@ -3098,7 +3098,7 @@ Abbrev table for offset: 0x00000000 [0x00000001, 0x00000001): DW_OP_WASM_location 0x0 +5, DW_OP_stack_value 0x00000390: - [0xffffffff, 0x0000052d): + [0xffffffff, 0x00000519): [0x00000000, 0x00000007): DW_OP_WASM_location 0x0 +6, DW_OP_stack_value [0x00000001, 0x00000001): DW_OP_WASM_location 0x0 +6, DW_OP_stack_value @@ -3108,18 +3108,18 @@ Abbrev table for offset: 0x00000000 [0x00000001, 0x00000001): DW_OP_WASM_location 0x0 +8, DW_OP_stack_value 0x000003e8: - [0xffffffff, 0x00000556): + [0xffffffff, 0x00000536): [0x00000001, 0x00000001): DW_OP_consts +0, DW_OP_stack_value [0x00000027, 0x0000002f): DW_OP_WASM_location 0x0 +0, DW_OP_stack_value 0x00000413: - [0xffffffff, 0x00000566): + [0xffffffff, 0x00000546): [0x00000000, 0x0000001f): DW_OP_WASM_location 0x0 +1, DW_OP_stack_value .debug_line contents: debug_line[0x00000000] Line table prologue: - total_length: 0x0000090e + total_length: 0x000008f8 version: 4 prologue_length: 0x000000dd min_inst_length: 1 @@ -4079,352 +4079,339 @@ file_names[ 4]: 0x000006d2: 00 DW_LNE_set_address (0x0000000000000416) -0x000006d9: 03 DW_LNS_advance_line (105) -0x000006db: 01 DW_LNS_copy - 0x0000000000000416 105 18 1 0 0 is_stmt +0x000006d9: 03 DW_LNS_advance_line (106) +0x000006db: 05 DW_LNS_set_column (7) +0x000006dd: 01 DW_LNS_copy + 0x0000000000000416 106 7 1 0 0 is_stmt -0x000006dc: 00 DW_LNE_set_address (0x000000000000041f) -0x000006e3: 03 DW_LNS_advance_line (106) -0x000006e5: 05 DW_LNS_set_column (7) -0x000006e7: 01 DW_LNS_copy - 0x000000000000041f 106 7 1 0 0 is_stmt +0x000006de: 00 DW_LNE_set_address (0x000000000000041e) +0x000006e5: 05 DW_LNS_set_column (16) +0x000006e7: 06 DW_LNS_negate_stmt +0x000006e8: 01 DW_LNS_copy + 0x000000000000041e 106 16 1 0 0 -0x000006e8: 00 DW_LNE_set_address (0x0000000000000427) -0x000006ef: 05 DW_LNS_set_column (16) -0x000006f1: 06 DW_LNS_negate_stmt -0x000006f2: 01 DW_LNS_copy - 0x0000000000000427 106 16 1 0 0 +0x000006e9: 00 DW_LNE_set_address (0x0000000000000423) +0x000006f0: 03 DW_LNS_advance_line (105) +0x000006f2: 05 DW_LNS_set_column (24) +0x000006f4: 06 DW_LNS_negate_stmt +0x000006f5: 01 DW_LNS_copy + 0x0000000000000423 105 24 1 0 0 is_stmt -0x000006f3: 00 DW_LNE_set_address (0x000000000000042c) -0x000006fa: 03 DW_LNS_advance_line (105) -0x000006fc: 05 DW_LNS_set_column (24) -0x000006fe: 06 DW_LNS_negate_stmt -0x000006ff: 01 DW_LNS_copy - 0x000000000000042c 105 24 1 0 0 is_stmt +0x000006f6: 00 DW_LNE_set_address (0x0000000000000428) +0x000006fd: 05 DW_LNS_set_column (18) +0x000006ff: 06 DW_LNS_negate_stmt +0x00000700: 01 DW_LNS_copy + 0x0000000000000428 105 18 1 0 0 -0x00000700: 00 DW_LNE_set_address (0x0000000000000431) -0x00000707: 05 DW_LNS_set_column (18) -0x00000709: 06 DW_LNS_negate_stmt -0x0000070a: 01 DW_LNS_copy - 0x0000000000000431 105 18 1 0 0 +0x00000701: 00 DW_LNE_set_address (0x0000000000000443) +0x00000708: 03 DW_LNS_advance_line (112) +0x0000070a: 05 DW_LNS_set_column (13) +0x0000070c: 06 DW_LNS_negate_stmt +0x0000070d: 01 DW_LNS_copy + 0x0000000000000443 112 13 1 0 0 is_stmt -0x0000070b: 00 DW_LNE_set_address (0x0000000000000457) -0x00000712: 03 DW_LNS_advance_line (112) -0x00000714: 05 DW_LNS_set_column (13) -0x00000716: 06 DW_LNS_negate_stmt -0x00000717: 01 DW_LNS_copy - 0x0000000000000457 112 13 1 0 0 is_stmt +0x0000070e: 00 DW_LNE_set_address (0x0000000000000445) +0x00000715: 05 DW_LNS_set_column (26) +0x00000717: 06 DW_LNS_negate_stmt +0x00000718: 01 DW_LNS_copy + 0x0000000000000445 112 26 1 0 0 -0x00000718: 00 DW_LNE_set_address (0x0000000000000459) -0x0000071f: 05 DW_LNS_set_column (26) -0x00000721: 06 DW_LNS_negate_stmt +0x00000719: 00 DW_LNE_set_address (0x0000000000000452) +0x00000720: 05 DW_LNS_set_column (35) 0x00000722: 01 DW_LNS_copy - 0x0000000000000459 112 26 1 0 0 + 0x0000000000000452 112 35 1 0 0 -0x00000723: 00 DW_LNE_set_address (0x0000000000000466) -0x0000072a: 05 DW_LNS_set_column (35) +0x00000723: 00 DW_LNE_set_address (0x0000000000000453) +0x0000072a: 05 DW_LNS_set_column (13) 0x0000072c: 01 DW_LNS_copy - 0x0000000000000466 112 35 1 0 0 + 0x0000000000000453 112 13 1 0 0 -0x0000072d: 00 DW_LNE_set_address (0x0000000000000467) -0x00000734: 05 DW_LNS_set_column (13) -0x00000736: 01 DW_LNS_copy - 0x0000000000000467 112 13 1 0 0 +0x0000072d: 00 DW_LNE_set_address (0x0000000000000461) +0x00000734: 03 DW_LNS_advance_line (111) +0x00000736: 05 DW_LNS_set_column (30) +0x00000738: 06 DW_LNS_negate_stmt +0x00000739: 01 DW_LNS_copy + 0x0000000000000461 111 30 1 0 0 is_stmt -0x00000737: 00 DW_LNE_set_address (0x0000000000000475) -0x0000073e: 03 DW_LNS_advance_line (111) -0x00000740: 05 DW_LNS_set_column (30) -0x00000742: 06 DW_LNS_negate_stmt -0x00000743: 01 DW_LNS_copy - 0x0000000000000475 111 30 1 0 0 is_stmt +0x0000073a: 00 DW_LNE_set_address (0x0000000000000466) +0x00000741: 05 DW_LNS_set_column (24) +0x00000743: 06 DW_LNS_negate_stmt +0x00000744: 01 DW_LNS_copy + 0x0000000000000466 111 24 1 0 0 -0x00000744: 00 DW_LNE_set_address (0x000000000000047a) -0x0000074b: 05 DW_LNS_set_column (24) -0x0000074d: 06 DW_LNS_negate_stmt +0x00000745: 00 DW_LNE_set_address (0x000000000000046b) +0x0000074c: 05 DW_LNS_set_column (10) 0x0000074e: 01 DW_LNS_copy - 0x000000000000047a 111 24 1 0 0 + 0x000000000000046b 111 10 1 0 0 -0x0000074f: 00 DW_LNE_set_address (0x000000000000047f) -0x00000756: 05 DW_LNS_set_column (10) -0x00000758: 01 DW_LNS_copy - 0x000000000000047f 111 10 1 0 0 +0x0000074f: 00 DW_LNE_set_address (0x0000000000000470) +0x00000756: 03 DW_LNS_advance_line (113) +0x00000758: 06 DW_LNS_negate_stmt +0x00000759: 01 DW_LNS_copy + 0x0000000000000470 113 10 1 0 0 is_stmt -0x00000759: 00 DW_LNE_set_address (0x0000000000000484) -0x00000760: 03 DW_LNS_advance_line (113) -0x00000762: 06 DW_LNS_negate_stmt -0x00000763: 01 DW_LNS_copy - 0x0000000000000484 113 10 1 0 0 is_stmt +0x0000075a: 00 DW_LNE_set_address (0x0000000000000473) +0x00000761: 03 DW_LNS_advance_line (118) +0x00000763: 05 DW_LNS_set_column (16) +0x00000765: 01 DW_LNS_copy + 0x0000000000000473 118 16 1 0 0 is_stmt -0x00000764: 00 DW_LNE_set_address (0x0000000000000487) -0x0000076b: 03 DW_LNS_advance_line (118) -0x0000076d: 05 DW_LNS_set_column (16) -0x0000076f: 01 DW_LNS_copy - 0x0000000000000487 118 16 1 0 0 is_stmt +0x00000766: 00 DW_LNE_set_address (0x000000000000047c) +0x0000076d: 03 DW_LNS_advance_line (119) +0x0000076f: 05 DW_LNS_set_column (10) +0x00000771: 01 DW_LNS_copy + 0x000000000000047c 119 10 1 0 0 is_stmt -0x00000770: 00 DW_LNE_set_address (0x0000000000000490) -0x00000777: 03 DW_LNS_advance_line (119) -0x00000779: 05 DW_LNS_set_column (10) -0x0000077b: 01 DW_LNS_copy - 0x0000000000000490 119 10 1 0 0 is_stmt +0x00000772: 00 DW_LNE_set_address (0x000000000000047e) +0x00000779: 05 DW_LNS_set_column (18) +0x0000077b: 06 DW_LNS_negate_stmt +0x0000077c: 01 DW_LNS_copy + 0x000000000000047e 119 18 1 0 0 -0x0000077c: 00 DW_LNE_set_address (0x0000000000000492) -0x00000783: 05 DW_LNS_set_column (18) -0x00000785: 06 DW_LNS_negate_stmt +0x0000077d: 00 DW_LNE_set_address (0x0000000000000487) +0x00000784: 05 DW_LNS_set_column (10) 0x00000786: 01 DW_LNS_copy - 0x0000000000000492 119 18 1 0 0 + 0x0000000000000487 119 10 1 0 0 -0x00000787: 00 DW_LNE_set_address (0x000000000000049b) -0x0000078e: 05 DW_LNS_set_column (10) +0x00000787: 00 DW_LNE_set_address (0x0000000000000489) +0x0000078e: 05 DW_LNS_set_column (23) 0x00000790: 01 DW_LNS_copy - 0x000000000000049b 119 10 1 0 0 + 0x0000000000000489 119 23 1 0 0 -0x00000791: 00 DW_LNE_set_address (0x000000000000049d) -0x00000798: 05 DW_LNS_set_column (23) -0x0000079a: 01 DW_LNS_copy - 0x000000000000049d 119 23 1 0 0 +0x00000791: 00 DW_LNE_set_address (0x000000000000048e) +0x00000798: 03 DW_LNS_advance_line (118) +0x0000079a: 05 DW_LNS_set_column (16) +0x0000079c: 06 DW_LNS_negate_stmt +0x0000079d: 01 DW_LNS_copy + 0x000000000000048e 118 16 1 0 0 is_stmt -0x0000079b: 00 DW_LNE_set_address (0x00000000000004a2) -0x000007a2: 03 DW_LNS_advance_line (118) -0x000007a4: 05 DW_LNS_set_column (16) -0x000007a6: 06 DW_LNS_negate_stmt +0x0000079e: 00 DW_LNE_set_address (0x000000000000049b) +0x000007a5: 03 DW_LNS_advance_line (122) 0x000007a7: 01 DW_LNS_copy - 0x00000000000004a2 118 16 1 0 0 is_stmt + 0x000000000000049b 122 16 1 0 0 is_stmt 0x000007a8: 00 DW_LNE_set_address (0x00000000000004af) -0x000007af: 03 DW_LNS_advance_line (122) -0x000007b1: 01 DW_LNS_copy - 0x00000000000004af 122 16 1 0 0 is_stmt +0x000007af: 03 DW_LNS_advance_line (125) +0x000007b1: 05 DW_LNS_set_column (22) +0x000007b3: 01 DW_LNS_copy + 0x00000000000004af 125 22 1 0 0 is_stmt -0x000007b2: 00 DW_LNE_set_address (0x00000000000004c3) -0x000007b9: 03 DW_LNS_advance_line (125) -0x000007bb: 05 DW_LNS_set_column (22) -0x000007bd: 01 DW_LNS_copy - 0x00000000000004c3 125 22 1 0 0 is_stmt +0x000007b4: 00 DW_LNE_set_address (0x00000000000004b6) +0x000007bb: 03 DW_LNS_advance_line (126) +0x000007bd: 05 DW_LNS_set_column (27) +0x000007bf: 01 DW_LNS_copy + 0x00000000000004b6 126 27 1 0 0 is_stmt -0x000007be: 00 DW_LNE_set_address (0x00000000000004ca) -0x000007c5: 03 DW_LNS_advance_line (126) -0x000007c7: 05 DW_LNS_set_column (27) -0x000007c9: 01 DW_LNS_copy - 0x00000000000004ca 126 27 1 0 0 is_stmt +0x000007c0: 00 DW_LNE_set_address (0x00000000000004bf) +0x000007c7: 03 DW_LNS_advance_line (127) +0x000007c9: 05 DW_LNS_set_column (16) +0x000007cb: 01 DW_LNS_copy + 0x00000000000004bf 127 16 1 0 0 is_stmt -0x000007ca: 00 DW_LNE_set_address (0x00000000000004d3) -0x000007d1: 03 DW_LNS_advance_line (127) -0x000007d3: 05 DW_LNS_set_column (16) -0x000007d5: 01 DW_LNS_copy - 0x00000000000004d3 127 16 1 0 0 is_stmt +0x000007cc: 00 DW_LNE_set_address (0x00000000000004c7) +0x000007d3: 05 DW_LNS_set_column (27) +0x000007d5: 06 DW_LNS_negate_stmt +0x000007d6: 01 DW_LNS_copy + 0x00000000000004c7 127 27 1 0 0 -0x000007d6: 00 DW_LNE_set_address (0x00000000000004db) -0x000007dd: 05 DW_LNS_set_column (27) -0x000007df: 06 DW_LNS_negate_stmt +0x000007d7: 00 DW_LNE_set_address (0x00000000000004c9) +0x000007de: 05 DW_LNS_set_column (35) 0x000007e0: 01 DW_LNS_copy - 0x00000000000004db 127 27 1 0 0 + 0x00000000000004c9 127 35 1 0 0 -0x000007e1: 00 DW_LNE_set_address (0x00000000000004dd) -0x000007e8: 05 DW_LNS_set_column (35) +0x000007e1: 00 DW_LNE_set_address (0x00000000000004d2) +0x000007e8: 05 DW_LNS_set_column (27) 0x000007ea: 01 DW_LNS_copy - 0x00000000000004dd 127 35 1 0 0 + 0x00000000000004d2 127 27 1 0 0 -0x000007eb: 00 DW_LNE_set_address (0x00000000000004e6) -0x000007f2: 05 DW_LNS_set_column (27) +0x000007eb: 00 DW_LNE_set_address (0x00000000000004d7) +0x000007f2: 05 DW_LNS_set_column (25) 0x000007f4: 01 DW_LNS_copy - 0x00000000000004e6 127 27 1 0 0 + 0x00000000000004d7 127 25 1 0 0 -0x000007f5: 00 DW_LNE_set_address (0x00000000000004eb) -0x000007fc: 05 DW_LNS_set_column (25) -0x000007fe: 01 DW_LNS_copy - 0x00000000000004eb 127 25 1 0 0 +0x000007f5: 00 DW_LNE_set_address (0x00000000000004da) +0x000007fc: 03 DW_LNS_advance_line (126) +0x000007fe: 05 DW_LNS_set_column (27) +0x00000800: 06 DW_LNS_negate_stmt +0x00000801: 01 DW_LNS_copy + 0x00000000000004da 126 27 1 0 0 is_stmt -0x000007ff: 00 DW_LNE_set_address (0x00000000000004ee) -0x00000806: 03 DW_LNS_advance_line (126) -0x00000808: 05 DW_LNS_set_column (27) -0x0000080a: 06 DW_LNS_negate_stmt -0x0000080b: 01 DW_LNS_copy - 0x00000000000004ee 126 27 1 0 0 is_stmt +0x00000802: 00 DW_LNE_set_address (0x00000000000004df) +0x00000809: 05 DW_LNS_set_column (13) +0x0000080b: 06 DW_LNS_negate_stmt +0x0000080c: 01 DW_LNS_copy + 0x00000000000004df 126 13 1 0 0 -0x0000080c: 00 DW_LNE_set_address (0x00000000000004f3) -0x00000813: 05 DW_LNS_set_column (13) -0x00000815: 06 DW_LNS_negate_stmt -0x00000816: 01 DW_LNS_copy - 0x00000000000004f3 126 13 1 0 0 +0x0000080d: 00 DW_LNE_set_address (0x00000000000004e7) +0x00000814: 03 DW_LNS_advance_line (128) +0x00000816: 06 DW_LNS_negate_stmt +0x00000817: 01 DW_LNS_copy + 0x00000000000004e7 128 13 1 0 0 is_stmt -0x00000817: 00 DW_LNE_set_address (0x00000000000004fb) -0x0000081e: 03 DW_LNS_advance_line (128) -0x00000820: 06 DW_LNS_negate_stmt -0x00000821: 01 DW_LNS_copy - 0x00000000000004fb 128 13 1 0 0 is_stmt +0x00000818: 00 DW_LNE_set_address (0x00000000000004ef) +0x0000081f: 05 DW_LNS_set_column (22) +0x00000821: 06 DW_LNS_negate_stmt +0x00000822: 01 DW_LNS_copy + 0x00000000000004ef 128 22 1 0 0 -0x00000822: 00 DW_LNE_set_address (0x0000000000000503) -0x00000829: 05 DW_LNS_set_column (22) -0x0000082b: 06 DW_LNS_negate_stmt -0x0000082c: 01 DW_LNS_copy - 0x0000000000000503 128 22 1 0 0 +0x00000823: 00 DW_LNE_set_address (0x00000000000004f4) +0x0000082a: 03 DW_LNS_advance_line (130) +0x0000082c: 05 DW_LNS_set_column (16) +0x0000082e: 06 DW_LNS_negate_stmt +0x0000082f: 01 DW_LNS_copy + 0x00000000000004f4 130 16 1 0 0 is_stmt -0x0000082d: 00 DW_LNE_set_address (0x0000000000000508) -0x00000834: 03 DW_LNS_advance_line (130) -0x00000836: 05 DW_LNS_set_column (16) -0x00000838: 06 DW_LNS_negate_stmt -0x00000839: 01 DW_LNS_copy - 0x0000000000000508 130 16 1 0 0 is_stmt +0x00000830: 00 DW_LNE_set_address (0x00000000000004fc) +0x00000837: 05 DW_LNS_set_column (14) +0x00000839: 06 DW_LNS_negate_stmt +0x0000083a: 01 DW_LNS_copy + 0x00000000000004fc 130 14 1 0 0 -0x0000083a: 00 DW_LNE_set_address (0x0000000000000510) -0x00000841: 05 DW_LNS_set_column (14) -0x00000843: 06 DW_LNS_negate_stmt +0x0000083b: 00 DW_LNE_set_address (0x000000000000050b) +0x00000842: 05 DW_LNS_set_column (25) 0x00000844: 01 DW_LNS_copy - 0x0000000000000510 130 14 1 0 0 + 0x000000000000050b 130 25 1 0 0 -0x00000845: 00 DW_LNE_set_address (0x000000000000051f) -0x0000084c: 05 DW_LNS_set_column (25) -0x0000084e: 01 DW_LNS_copy - 0x000000000000051f 130 25 1 0 0 +0x00000845: 00 DW_LNE_set_address (0x0000000000000512) +0x0000084c: 03 DW_LNS_advance_line (133) +0x0000084e: 05 DW_LNS_set_column (11) +0x00000850: 06 DW_LNS_negate_stmt +0x00000851: 01 DW_LNS_copy + 0x0000000000000512 133 11 1 0 0 is_stmt -0x0000084f: 00 DW_LNE_set_address (0x0000000000000526) -0x00000856: 03 DW_LNS_advance_line (133) -0x00000858: 05 DW_LNS_set_column (11) -0x0000085a: 06 DW_LNS_negate_stmt -0x0000085b: 01 DW_LNS_copy - 0x0000000000000526 133 11 1 0 0 is_stmt +0x00000852: 00 DW_LNE_set_address (0x0000000000000517) +0x00000859: 03 DW_LNS_advance_line (122) +0x0000085b: 05 DW_LNS_set_column (16) +0x0000085d: 01 DW_LNS_copy + 0x0000000000000517 122 16 1 0 0 is_stmt -0x0000085c: 00 DW_LNE_set_address (0x000000000000052b) -0x00000863: 03 DW_LNS_advance_line (122) -0x00000865: 05 DW_LNS_set_column (16) -0x00000867: 01 DW_LNS_copy - 0x000000000000052b 122 16 1 0 0 is_stmt +0x0000085e: 00 DW_LNE_set_address (0x000000000000051c) +0x00000865: 05 DW_LNS_set_column (14) +0x00000867: 06 DW_LNS_negate_stmt +0x00000868: 01 DW_LNS_copy + 0x000000000000051c 122 14 1 0 0 -0x00000868: 00 DW_LNE_set_address (0x0000000000000530) -0x0000086f: 05 DW_LNS_set_column (14) -0x00000871: 06 DW_LNS_negate_stmt -0x00000872: 01 DW_LNS_copy - 0x0000000000000530 122 14 1 0 0 +0x00000869: 00 DW_LNE_set_address (0x0000000000000522) +0x00000870: 03 DW_LNS_advance_line (110) +0x00000872: 05 DW_LNS_set_column (11) +0x00000874: 06 DW_LNS_negate_stmt +0x00000875: 01 DW_LNS_copy + 0x0000000000000522 110 11 1 0 0 is_stmt -0x00000873: 00 DW_LNE_set_address (0x0000000000000536) -0x0000087a: 03 DW_LNS_advance_line (110) -0x0000087c: 05 DW_LNS_set_column (11) -0x0000087e: 06 DW_LNS_negate_stmt -0x0000087f: 01 DW_LNS_copy - 0x0000000000000536 110 11 1 0 0 is_stmt +0x00000876: 00 DW_LNE_set_address (0x000000000000052a) +0x0000087d: 03 DW_LNS_advance_line (138) +0x0000087f: 05 DW_LNS_set_column (4) +0x00000881: 01 DW_LNS_copy + 0x000000000000052a 138 4 1 0 0 is_stmt -0x00000880: 00 DW_LNE_set_address (0x0000000000000542) -0x00000887: 03 DW_LNS_advance_line (113) -0x00000889: 05 DW_LNS_set_column (10) +0x00000882: 00 DW_LNE_set_address (0x000000000000052e) +0x00000889: 03 DW_LNS_advance_line (139) 0x0000088b: 01 DW_LNS_copy - 0x0000000000000542 113 10 1 0 0 is_stmt + 0x000000000000052e 139 4 1 0 0 is_stmt -0x0000088c: 00 DW_LNE_set_address (0x000000000000054a) -0x00000893: 03 DW_LNS_advance_line (138) -0x00000895: 05 DW_LNS_set_column (4) +0x0000088c: 00 DW_LNE_set_address (0x000000000000053e) +0x00000893: 03 DW_LNS_advance_line (142) +0x00000895: 05 DW_LNS_set_column (20) 0x00000897: 01 DW_LNS_copy - 0x000000000000054a 138 4 1 0 0 is_stmt + 0x000000000000053e 142 20 1 0 0 is_stmt -0x00000898: 00 DW_LNE_set_address (0x000000000000054e) -0x0000089f: 03 DW_LNS_advance_line (139) +0x00000898: 00 DW_LNE_set_address (0x0000000000000546) +0x0000089f: 03 DW_LNS_advance_line (146) 0x000008a1: 01 DW_LNS_copy - 0x000000000000054e 139 4 1 0 0 is_stmt + 0x0000000000000546 146 20 1 0 0 is_stmt -0x000008a2: 00 DW_LNE_set_address (0x000000000000055e) -0x000008a9: 03 DW_LNS_advance_line (142) -0x000008ab: 05 DW_LNS_set_column (20) +0x000008a2: 00 DW_LNE_set_address (0x000000000000054d) +0x000008a9: 03 DW_LNS_advance_line (147) +0x000008ab: 05 DW_LNS_set_column (7) 0x000008ad: 01 DW_LNS_copy - 0x000000000000055e 142 20 1 0 0 is_stmt + 0x000000000000054d 147 7 1 0 0 is_stmt -0x000008ae: 00 DW_LNE_set_address (0x0000000000000566) -0x000008b5: 03 DW_LNS_advance_line (146) -0x000008b7: 01 DW_LNS_copy - 0x0000000000000566 146 20 1 0 0 is_stmt +0x000008ae: 00 DW_LNE_set_address (0x0000000000000551) +0x000008b5: 03 DW_LNS_advance_line (143) +0x000008b7: 05 DW_LNS_set_column (11) +0x000008b9: 01 DW_LNS_copy + 0x0000000000000551 143 11 1 0 0 is_stmt -0x000008b8: 00 DW_LNE_set_address (0x000000000000056d) -0x000008bf: 03 DW_LNS_advance_line (147) -0x000008c1: 05 DW_LNS_set_column (7) -0x000008c3: 01 DW_LNS_copy - 0x000000000000056d 147 7 1 0 0 is_stmt +0x000008ba: 00 DW_LNE_set_address (0x0000000000000555) +0x000008c1: 05 DW_LNS_set_column (20) +0x000008c3: 06 DW_LNS_negate_stmt +0x000008c4: 01 DW_LNS_copy + 0x0000000000000555 143 20 1 0 0 -0x000008c4: 00 DW_LNE_set_address (0x0000000000000571) -0x000008cb: 03 DW_LNS_advance_line (143) -0x000008cd: 05 DW_LNS_set_column (11) -0x000008cf: 01 DW_LNS_copy - 0x0000000000000571 143 11 1 0 0 is_stmt +0x000008c5: 00 DW_LNE_set_address (0x000000000000055a) +0x000008cc: 05 DW_LNS_set_column (11) +0x000008ce: 01 DW_LNS_copy + 0x000000000000055a 143 11 1 0 0 -0x000008d0: 00 DW_LNE_set_address (0x0000000000000575) -0x000008d7: 05 DW_LNS_set_column (20) -0x000008d9: 06 DW_LNS_negate_stmt -0x000008da: 01 DW_LNS_copy - 0x0000000000000575 143 20 1 0 0 +0x000008cf: 00 DW_LNE_set_address (0x0000000000000561) +0x000008d6: 03 DW_LNS_advance_line (141) +0x000008d8: 05 DW_LNS_set_column (4) +0x000008da: 06 DW_LNS_negate_stmt +0x000008db: 01 DW_LNS_copy + 0x0000000000000561 141 4 1 0 0 is_stmt -0x000008db: 00 DW_LNE_set_address (0x000000000000057a) -0x000008e2: 05 DW_LNS_set_column (11) -0x000008e4: 01 DW_LNS_copy - 0x000000000000057a 143 11 1 0 0 +0x000008dc: 00 DW_LNE_set_address (0x0000000000000567) +0x000008e3: 03 DW_LNS_advance_line (159) +0x000008e5: 01 DW_LNS_copy + 0x0000000000000567 159 4 1 0 0 is_stmt -0x000008e5: 00 DW_LNE_set_address (0x0000000000000581) -0x000008ec: 03 DW_LNS_advance_line (141) -0x000008ee: 05 DW_LNS_set_column (4) -0x000008f0: 06 DW_LNS_negate_stmt +0x000008e6: 00 DW_LNE_set_address (0x000000000000057e) +0x000008ed: 03 DW_LNS_advance_line (161) +0x000008ef: 05 DW_LNS_set_column (1) 0x000008f1: 01 DW_LNS_copy - 0x0000000000000581 141 4 1 0 0 is_stmt - - -0x000008f2: 00 DW_LNE_set_address (0x0000000000000587) -0x000008f9: 03 DW_LNS_advance_line (159) -0x000008fb: 01 DW_LNS_copy - 0x0000000000000587 159 4 1 0 0 is_stmt - + 0x000000000000057e 161 1 1 0 0 is_stmt -0x000008fc: 00 DW_LNE_set_address (0x000000000000059e) -0x00000903: 03 DW_LNS_advance_line (161) -0x00000905: 05 DW_LNS_set_column (1) -0x00000907: 01 DW_LNS_copy - 0x000000000000059e 161 1 1 0 0 is_stmt - -0x00000908: 00 DW_LNE_set_address (0x00000000000005a8) -0x0000090f: 00 DW_LNE_end_sequence - 0x00000000000005a8 161 1 1 0 0 is_stmt end_sequence +0x000008f2: 00 DW_LNE_set_address (0x0000000000000588) +0x000008f9: 00 DW_LNE_end_sequence + 0x0000000000000588 161 1 1 0 0 is_stmt end_sequence .debug_str contents: @@ -4470,11 +4457,11 @@ file_names[ 4]: 00000000 000002f2 00000330 00000000 0000035a 00000363 00000000 -00000028 000004c3 00000508 +00000028 000004af 000004f4 00000028 00000000 00000001 00000028 00000040 00000006 00000377 -00000040 00000379 000005a8 +00000040 00000379 00000588 00000040 (module (type $0 (func (param i32) (result i32))) @@ -5684,7 +5671,7 @@ file_names[ 4]: ) ) ) - ;; code offset: 0x391 - 0x59e + ;; code offset: 0x391 - 0x57e (block $block1 ;; code offset: 0x393 - 0x3b8 (block $block @@ -5849,553 +5836,511 @@ file_names[ 4]: (local.get $2) ) ) - ;; code offset: 0x410 - 0x54a - (block $block4 - ;; code offset: 0x412 - 0x53e - (block $block3 - ;; code offset: 0x414 - 0x44f - (block $block2 - ;; code offset: 0x41b - 0x444 - (if - ;; code offset: 0x41a - 0x41b - (i32.gt_s + ;; code offset: 0x410 - 0x52a + (block $block3 + ;; code offset: 0x412 - 0x43b + (block $block2 + ;; code offset: 0x414 - 0x430 + (loop $label1 + ;; code offset: 0x420 - 0x423 + (i32.store + ;; code offset: 0x41d - 0x41e + (i32.add ;; code offset: 0x416 - 0x418 - (local.get $3) - ;; code offset: 0x418 - 0x41a - (i32.const 0) + (local.get $1) + ;; code offset: 0x41c - 0x41d + (i32.shl + ;; code offset: 0x418 - 0x41a + (local.get $0) + ;; code offset: 0x41a - 0x41c + (i32.const 2) + ) ) - (then - ;; code offset: 0x41d - 0x439 - (loop $label1 - ;; code offset: 0x429 - 0x42c - (i32.store - ;; code offset: 0x426 - 0x427 - (i32.add - ;; code offset: 0x41f - 0x421 - (local.get $1) - ;; code offset: 0x425 - 0x426 - (i32.shl - ;; code offset: 0x421 - 0x423 - (local.get $0) - ;; code offset: 0x423 - 0x425 - (i32.const 2) - ) - ) - ;; code offset: 0x427 - 0x429 + ;; code offset: 0x41e - 0x420 + (local.get $0) + ) + ;; code offset: 0x42d - 0x42f + (br_if $label1 + ;; code offset: 0x42c - 0x42d + (i32.ne + ;; code offset: 0x428 - 0x42a + (local.tee $0 + ;; code offset: 0x427 - 0x428 + (i32.add + ;; code offset: 0x423 - 0x425 (local.get $0) + ;; code offset: 0x425 - 0x427 + (i32.const 1) ) - ;; code offset: 0x436 - 0x438 - (br_if $label1 - ;; code offset: 0x435 - 0x436 - (i32.ne - ;; code offset: 0x431 - 0x433 - (local.tee $0 - ;; code offset: 0x430 - 0x431 - (i32.add - ;; code offset: 0x42c - 0x42e - (local.get $0) - ;; code offset: 0x42e - 0x430 - (i32.const 1) - ) - ) - ;; code offset: 0x433 - 0x435 - (local.get $3) - ) - ) - ) - ;; code offset: 0x43b - 0x43d - (local.set $6 - ;; code offset: 0x439 - 0x43b - (i32.const 30) - ) - ;; code offset: 0x43f - 0x441 - (local.set $2 - ;; code offset: 0x43d - 0x43f - (local.get $3) ) - ;; code offset: 0x441 - 0x443 - (br $block2) + ;; code offset: 0x42a - 0x42c + (local.get $3) ) ) - ;; code offset: 0x446 - 0x448 - (local.set $6 - ;; code offset: 0x444 - 0x446 - (i32.const 30) - ) - ;; code offset: 0x44a - 0x44c - (local.set $2 - ;; code offset: 0x448 - 0x44a - (local.get $3) - ) - ;; code offset: 0x44c - 0x44e - (br $block3) ) - ;; code offset: 0x44f - 0x53b - (loop $label6 - ;; code offset: 0x453 - 0x455 - (local.set $0 - ;; code offset: 0x451 - 0x453 - (i32.const 0) - ) - ;; code offset: 0x455 - 0x482 - (loop $label2 - ;; code offset: 0x467 - 0x46a - (i32.store offset=16 - ;; code offset: 0x457 - 0x459 - (local.get $8) - ;; code offset: 0x466 - 0x467 - (i32.add - ;; code offset: 0x461 - 0x464 - (i32.load - ;; code offset: 0x460 - 0x461 - (i32.add - ;; code offset: 0x459 - 0x45b - (local.get $1) - ;; code offset: 0x45f - 0x460 - (i32.shl - ;; code offset: 0x45b - 0x45d - (local.get $0) - ;; code offset: 0x45d - 0x45f - (i32.const 2) - ) - ) - ) - ;; code offset: 0x464 - 0x466 - (i32.const 1) - ) - ) - ;; code offset: 0x474 - 0x475 - (drop - ;; code offset: 0x472 - 0x474 - (call $iprintf - ;; code offset: 0x46a - 0x46d - (i32.const 1047) - ;; code offset: 0x471 - 0x472 + ;; code offset: 0x432 - 0x434 + (local.set $6 + ;; code offset: 0x430 - 0x432 + (i32.const 30) + ) + ;; code offset: 0x436 - 0x438 + (local.set $2 + ;; code offset: 0x434 - 0x436 + (local.get $3) + ) + ;; code offset: 0x438 - 0x43a + (br $block2) + ) + ;; code offset: 0x43b - 0x527 + (loop $label6 + ;; code offset: 0x43f - 0x441 + (local.set $0 + ;; code offset: 0x43d - 0x43f + (i32.const 0) + ) + ;; code offset: 0x441 - 0x46e + (loop $label2 + ;; code offset: 0x453 - 0x456 + (i32.store offset=16 + ;; code offset: 0x443 - 0x445 + (local.get $8) + ;; code offset: 0x452 - 0x453 + (i32.add + ;; code offset: 0x44d - 0x450 + (i32.load + ;; code offset: 0x44c - 0x44d (i32.add - ;; code offset: 0x46d - 0x46f - (local.get $8) - ;; code offset: 0x46f - 0x471 - (i32.const 16) - ) - ) - ) - ;; code offset: 0x47f - 0x481 - (br_if $label2 - ;; code offset: 0x47e - 0x47f - (i32.ne - ;; code offset: 0x47a - 0x47c - (local.tee $0 - ;; code offset: 0x479 - 0x47a - (i32.add - ;; code offset: 0x475 - 0x477 + ;; code offset: 0x445 - 0x447 + (local.get $1) + ;; code offset: 0x44b - 0x44c + (i32.shl + ;; code offset: 0x447 - 0x449 (local.get $0) - ;; code offset: 0x477 - 0x479 - (i32.const 1) + ;; code offset: 0x449 - 0x44b + (i32.const 2) ) ) - ;; code offset: 0x47c - 0x47e - (local.get $3) ) + ;; code offset: 0x450 - 0x452 + (i32.const 1) ) ) - ;; code offset: 0x486 - 0x487 + ;; code offset: 0x460 - 0x461 (drop - ;; code offset: 0x484 - 0x486 - (call $putchar - ;; code offset: 0x482 - 0x484 - (i32.const 10) + ;; code offset: 0x45e - 0x460 + (call $iprintf + ;; code offset: 0x456 - 0x459 + (i32.const 1047) + ;; code offset: 0x45d - 0x45e + (i32.add + ;; code offset: 0x459 - 0x45b + (local.get $8) + ;; code offset: 0x45b - 0x45d + (i32.const 16) + ) ) ) - ;; code offset: 0x48c - 0x4af - (if - ;; code offset: 0x48b - 0x48c - (i32.gt_s - ;; code offset: 0x487 - 0x489 - (local.get $2) - ;; code offset: 0x489 - 0x48b - (i32.const 1) + ;; code offset: 0x46b - 0x46d + (br_if $label2 + ;; code offset: 0x46a - 0x46b + (i32.ne + ;; code offset: 0x466 - 0x468 + (local.tee $0 + ;; code offset: 0x465 - 0x466 + (i32.add + ;; code offset: 0x461 - 0x463 + (local.get $0) + ;; code offset: 0x463 - 0x465 + (i32.const 1) + ) + ) + ;; code offset: 0x468 - 0x46a + (local.get $3) ) - (then - ;; code offset: 0x48e - 0x4ae - (loop $label3 - ;; code offset: 0x49f - 0x4a2 - (i32.store - ;; code offset: 0x49c - 0x49d - (i32.add - ;; code offset: 0x490 - 0x492 - (local.get $5) - ;; code offset: 0x49b - 0x49c - (i32.shl - ;; code offset: 0x497 - 0x499 - (local.tee $0 - ;; code offset: 0x496 - 0x497 - (i32.sub - ;; code offset: 0x492 - 0x494 - (local.get $2) - ;; code offset: 0x494 - 0x496 - (i32.const 1) - ) + ) + ) + ;; code offset: 0x472 - 0x473 + (drop + ;; code offset: 0x470 - 0x472 + (call $putchar + ;; code offset: 0x46e - 0x470 + (i32.const 10) + ) + ) + ;; code offset: 0x478 - 0x49b + (if + ;; code offset: 0x477 - 0x478 + (i32.gt_s + ;; code offset: 0x473 - 0x475 + (local.get $2) + ;; code offset: 0x475 - 0x477 + (i32.const 1) + ) + (then + ;; code offset: 0x47a - 0x49a + (loop $label3 + ;; code offset: 0x48b - 0x48e + (i32.store + ;; code offset: 0x488 - 0x489 + (i32.add + ;; code offset: 0x47c - 0x47e + (local.get $5) + ;; code offset: 0x487 - 0x488 + (i32.shl + ;; code offset: 0x483 - 0x485 + (local.tee $0 + ;; code offset: 0x482 - 0x483 + (i32.sub + ;; code offset: 0x47e - 0x480 + (local.get $2) + ;; code offset: 0x480 - 0x482 + (i32.const 1) ) - ;; code offset: 0x499 - 0x49b - (i32.const 2) ) + ;; code offset: 0x485 - 0x487 + (i32.const 2) ) - ;; code offset: 0x49d - 0x49f - (local.get $2) ) - ;; code offset: 0x4ab - 0x4ad - (br_if $label3 - (block (result i32) - (local.set $scratch - ;; code offset: 0x4a6 - 0x4a7 - (i32.gt_s - ;; code offset: 0x4a2 - 0x4a4 - (local.get $2) - ;; code offset: 0x4a4 - 0x4a6 - (i32.const 2) - ) - ) - ;; code offset: 0x4a9 - 0x4ab - (local.set $2 - ;; code offset: 0x4a7 - 0x4a9 - (local.get $0) + ;; code offset: 0x489 - 0x48b + (local.get $2) + ) + ;; code offset: 0x497 - 0x499 + (br_if $label3 + (block (result i32) + (local.set $scratch + ;; code offset: 0x492 - 0x493 + (i32.gt_s + ;; code offset: 0x48e - 0x490 + (local.get $2) + ;; code offset: 0x490 - 0x492 + (i32.const 2) ) - (local.get $scratch) ) + ;; code offset: 0x495 - 0x497 + (local.set $2 + ;; code offset: 0x493 - 0x495 + (local.get $0) + ) + (local.get $scratch) ) ) ) ) + ) + ;; code offset: 0x4a0 - 0x4a2 + (br_if $block3 + ;; code offset: 0x49f - 0x4a0 + (i32.eq + ;; code offset: 0x49b - 0x49d + (local.get $2) + ;; code offset: 0x49d - 0x49f + (local.get $3) + ) + ) + ;; code offset: 0x4a7 - 0x4a9 + (local.set $6 + ;; code offset: 0x4a6 - 0x4a7 + (i32.sub + ;; code offset: 0x4a2 - 0x4a4 + (local.get $6) + ;; code offset: 0x4a4 - 0x4a6 + (i32.const 1) + ) + ) + ;; code offset: 0x4a9 - 0x522 + (loop $label5 + ;; code offset: 0x4ad - 0x4af + (local.set $0 + ;; code offset: 0x4ab - 0x4ad + (i32.const 0) + ) ;; code offset: 0x4b4 - 0x4b6 - (br_if $block4 - ;; code offset: 0x4b3 - 0x4b4 - (i32.eq + (local.set $7 + ;; code offset: 0x4b1 - 0x4b4 + (i32.load ;; code offset: 0x4af - 0x4b1 - (local.get $2) - ;; code offset: 0x4b1 - 0x4b3 - (local.get $3) + (local.get $1) ) ) - ;; code offset: 0x4bb - 0x4bd - (local.set $6 + ;; code offset: 0x4bb - 0x4e7 + (if ;; code offset: 0x4ba - 0x4bb - (i32.sub + (i32.gt_s ;; code offset: 0x4b6 - 0x4b8 - (local.get $6) + (local.get $2) ;; code offset: 0x4b8 - 0x4ba - (i32.const 1) - ) - ) - ;; code offset: 0x4bd - 0x536 - (loop $label5 - ;; code offset: 0x4c1 - 0x4c3 - (local.set $0 - ;; code offset: 0x4bf - 0x4c1 (i32.const 0) ) - ;; code offset: 0x4c8 - 0x4ca - (local.set $7 - ;; code offset: 0x4c5 - 0x4c8 - (i32.load - ;; code offset: 0x4c3 - 0x4c5 - (local.get $1) - ) - ) - ;; code offset: 0x4cf - 0x4fb - (if - ;; code offset: 0x4ce - 0x4cf - (i32.gt_s - ;; code offset: 0x4ca - 0x4cc - (local.get $2) - ;; code offset: 0x4cc - 0x4ce - (i32.const 0) - ) - (then - ;; code offset: 0x4d1 - 0x4f6 - (loop $label4 - ;; code offset: 0x4eb - 0x4ee - (i32.store - ;; code offset: 0x4da - 0x4db + (then + ;; code offset: 0x4bd - 0x4e2 + (loop $label4 + ;; code offset: 0x4d7 - 0x4da + (i32.store + ;; code offset: 0x4c6 - 0x4c7 + (i32.add + ;; code offset: 0x4bf - 0x4c1 + (local.get $1) + ;; code offset: 0x4c5 - 0x4c6 + (i32.shl + ;; code offset: 0x4c1 - 0x4c3 + (local.get $0) + ;; code offset: 0x4c3 - 0x4c5 + (i32.const 2) + ) + ) + ;; code offset: 0x4d4 - 0x4d7 + (i32.load + ;; code offset: 0x4d3 - 0x4d4 (i32.add - ;; code offset: 0x4d3 - 0x4d5 + ;; code offset: 0x4c7 - 0x4c9 (local.get $1) - ;; code offset: 0x4d9 - 0x4da + ;; code offset: 0x4d2 - 0x4d3 (i32.shl - ;; code offset: 0x4d5 - 0x4d7 - (local.get $0) - ;; code offset: 0x4d7 - 0x4d9 - (i32.const 2) - ) - ) - ;; code offset: 0x4e8 - 0x4eb - (i32.load - ;; code offset: 0x4e7 - 0x4e8 - (i32.add - ;; code offset: 0x4db - 0x4dd - (local.get $1) - ;; code offset: 0x4e6 - 0x4e7 - (i32.shl - ;; code offset: 0x4e2 - 0x4e4 - (local.tee $0 - ;; code offset: 0x4e1 - 0x4e2 - (i32.add - ;; code offset: 0x4dd - 0x4df - (local.get $0) - ;; code offset: 0x4df - 0x4e1 - (i32.const 1) - ) + ;; code offset: 0x4ce - 0x4d0 + (local.tee $0 + ;; code offset: 0x4cd - 0x4ce + (i32.add + ;; code offset: 0x4c9 - 0x4cb + (local.get $0) + ;; code offset: 0x4cb - 0x4cd + (i32.const 1) ) - ;; code offset: 0x4e4 - 0x4e6 - (i32.const 2) ) + ;; code offset: 0x4d0 - 0x4d2 + (i32.const 2) ) ) ) - ;; code offset: 0x4f3 - 0x4f5 - (br_if $label4 - ;; code offset: 0x4f2 - 0x4f3 - (i32.ne - ;; code offset: 0x4ee - 0x4f0 - (local.get $0) - ;; code offset: 0x4f0 - 0x4f2 - (local.get $2) - ) - ) ) - ;; code offset: 0x4f8 - 0x4fa - (local.set $0 - ;; code offset: 0x4f6 - 0x4f8 - (local.get $2) + ;; code offset: 0x4df - 0x4e1 + (br_if $label4 + ;; code offset: 0x4de - 0x4df + (i32.ne + ;; code offset: 0x4da - 0x4dc + (local.get $0) + ;; code offset: 0x4dc - 0x4de + (local.get $2) + ) ) ) + ;; code offset: 0x4e4 - 0x4e6 + (local.set $0 + ;; code offset: 0x4e2 - 0x4e4 + (local.get $2) + ) ) - ;; code offset: 0x505 - 0x508 - (i32.store - ;; code offset: 0x502 - 0x503 + ) + ;; code offset: 0x4f1 - 0x4f4 + (i32.store + ;; code offset: 0x4ee - 0x4ef + (i32.add + ;; code offset: 0x4e7 - 0x4e9 + (local.get $1) + ;; code offset: 0x4ed - 0x4ee + (i32.shl + ;; code offset: 0x4e9 - 0x4eb + (local.get $0) + ;; code offset: 0x4eb - 0x4ed + (i32.const 2) + ) + ) + ;; code offset: 0x4ef - 0x4f1 + (local.get $7) + ) + ;; code offset: 0x508 - 0x50b + (i32.store + ;; code offset: 0x4fc - 0x4fe + (local.tee $0 + ;; code offset: 0x4fb - 0x4fc (i32.add - ;; code offset: 0x4fb - 0x4fd - (local.get $1) - ;; code offset: 0x501 - 0x502 + ;; code offset: 0x4f4 - 0x4f6 + (local.get $5) + ;; code offset: 0x4fa - 0x4fb (i32.shl - ;; code offset: 0x4fd - 0x4ff - (local.get $0) - ;; code offset: 0x4ff - 0x501 + ;; code offset: 0x4f6 - 0x4f8 + (local.get $2) + ;; code offset: 0x4f8 - 0x4fa (i32.const 2) ) ) - ;; code offset: 0x503 - 0x505 - (local.get $7) ) - ;; code offset: 0x51c - 0x51f - (i32.store - ;; code offset: 0x510 - 0x512 + ;; code offset: 0x507 - 0x508 + (i32.sub + ;; code offset: 0x503 - 0x505 (local.tee $0 - ;; code offset: 0x50f - 0x510 - (i32.add - ;; code offset: 0x508 - 0x50a - (local.get $5) - ;; code offset: 0x50e - 0x50f - (i32.shl - ;; code offset: 0x50a - 0x50c - (local.get $2) - ;; code offset: 0x50c - 0x50e - (i32.const 2) - ) - ) - ) - ;; code offset: 0x51b - 0x51c - (i32.sub - ;; code offset: 0x517 - 0x519 - (local.tee $0 - ;; code offset: 0x514 - 0x517 - (i32.load - ;; code offset: 0x512 - 0x514 - (local.get $0) - ) + ;; code offset: 0x500 - 0x503 + (i32.load + ;; code offset: 0x4fe - 0x500 + (local.get $0) ) - ;; code offset: 0x519 - 0x51b - (i32.const 1) ) + ;; code offset: 0x505 - 0x507 + (i32.const 1) ) - ;; code offset: 0x524 - 0x535 - (if - ;; code offset: 0x523 - 0x524 - (i32.le_s - ;; code offset: 0x51f - 0x521 - (local.get $0) - ;; code offset: 0x521 - 0x523 - (i32.const 1) - ) - (then - ;; code offset: 0x530 - 0x532 - (br_if $label5 - ;; code offset: 0x52f - 0x530 - (i32.ne - ;; code offset: 0x52b - 0x52d - (local.tee $2 - ;; code offset: 0x52a - 0x52b - (i32.add - ;; code offset: 0x526 - 0x528 - (local.get $2) - ;; code offset: 0x528 - 0x52a - (i32.const 1) - ) + ) + ;; code offset: 0x510 - 0x521 + (if + ;; code offset: 0x50f - 0x510 + (i32.le_s + ;; code offset: 0x50b - 0x50d + (local.get $0) + ;; code offset: 0x50d - 0x50f + (i32.const 1) + ) + (then + ;; code offset: 0x51c - 0x51e + (br_if $label5 + ;; code offset: 0x51b - 0x51c + (i32.ne + ;; code offset: 0x517 - 0x519 + (local.tee $2 + ;; code offset: 0x516 - 0x517 + (i32.add + ;; code offset: 0x512 - 0x514 + (local.get $2) + ;; code offset: 0x514 - 0x516 + (i32.const 1) ) - ;; code offset: 0x52d - 0x52f - (local.get $3) ) + ;; code offset: 0x519 - 0x51b + (local.get $3) ) - ;; code offset: 0x532 - 0x534 - (br $block4) ) + ;; code offset: 0x51e - 0x520 + (br $block3) ) ) - ;; code offset: 0x538 - 0x53a - (br_if $label6 - ;; code offset: 0x536 - 0x538 - (local.get $6) - ) ) - ;; code offset: 0x53b - 0x53d - (br $block4) - ) - ;; code offset: 0x53e - 0x548 - (loop - ;; code offset: 0x544 - 0x545 - (drop - ;; code offset: 0x542 - 0x544 - (call $putchar - ;; code offset: 0x540 - 0x542 - (i32.const 10) - ) + ;; code offset: 0x524 - 0x526 + (br_if $label6 + ;; code offset: 0x522 - 0x524 + (local.get $6) ) - ;; code offset: 0x545 - 0x547 - (br $block4) ) - ;; code offset: 0x548 - 0x549 - (unreachable) + ;; code offset: 0x527 - 0x529 + (br $block3) ) - ;; code offset: 0x54c - 0x54e + ;; code offset: 0x52c - 0x52e (call $free - ;; code offset: 0x54a - 0x54c + ;; code offset: 0x52a - 0x52c (local.get $1) ) - ;; code offset: 0x550 - 0x552 + ;; code offset: 0x530 - 0x532 (call $free - ;; code offset: 0x54e - 0x550 + ;; code offset: 0x52e - 0x530 (local.get $5) ) - ;; code offset: 0x554 - 0x556 + ;; code offset: 0x534 - 0x536 (local.set $5 - ;; code offset: 0x552 - 0x554 + ;; code offset: 0x532 - 0x534 (i32.const 0) ) - ;; code offset: 0x558 - 0x55a + ;; code offset: 0x538 - 0x53a (local.set $0 - ;; code offset: 0x556 - 0x558 + ;; code offset: 0x536 - 0x538 (i32.const 0) ) - ;; code offset: 0x55c - 0x587 + ;; code offset: 0x53c - 0x567 (if - ;; code offset: 0x55a - 0x55c + ;; code offset: 0x53a - 0x53c (local.get $4) (then - ;; code offset: 0x55e - 0x586 + ;; code offset: 0x53e - 0x566 (loop $label7 - ;; code offset: 0x564 - 0x566 + ;; code offset: 0x544 - 0x546 (local.set $1 - ;; code offset: 0x562 - 0x564 + ;; code offset: 0x542 - 0x544 (call $"fannkuch_worker(void*)" - ;; code offset: 0x560 - 0x562 + ;; code offset: 0x540 - 0x542 (local.get $4) ) ) - ;; code offset: 0x56b - 0x56d + ;; code offset: 0x54b - 0x54d (local.set $2 - ;; code offset: 0x568 - 0x56b + ;; code offset: 0x548 - 0x54b (i32.load offset=8 - ;; code offset: 0x566 - 0x568 + ;; code offset: 0x546 - 0x548 (local.get $4) ) ) - ;; code offset: 0x56f - 0x571 + ;; code offset: 0x54f - 0x551 (call $free - ;; code offset: 0x56d - 0x56f + ;; code offset: 0x54d - 0x54f (local.get $4) ) - ;; code offset: 0x57b - 0x57d + ;; code offset: 0x55b - 0x55d (local.set $0 - ;; code offset: 0x57a - 0x57b + ;; code offset: 0x55a - 0x55b (select - ;; code offset: 0x571 - 0x573 + ;; code offset: 0x551 - 0x553 (local.get $1) - ;; code offset: 0x573 - 0x575 + ;; code offset: 0x553 - 0x555 (local.get $0) - ;; code offset: 0x579 - 0x57a + ;; code offset: 0x559 - 0x55a (i32.lt_s - ;; code offset: 0x575 - 0x577 + ;; code offset: 0x555 - 0x557 (local.get $0) - ;; code offset: 0x577 - 0x579 + ;; code offset: 0x557 - 0x559 (local.get $1) ) ) ) - ;; code offset: 0x57f - 0x581 + ;; code offset: 0x55f - 0x561 (local.set $4 - ;; code offset: 0x57d - 0x57f + ;; code offset: 0x55d - 0x55f (local.get $2) ) - ;; code offset: 0x583 - 0x585 + ;; code offset: 0x563 - 0x565 (br_if $label7 - ;; code offset: 0x581 - 0x583 + ;; code offset: 0x561 - 0x563 (local.get $2) ) ) ) ) - ;; code offset: 0x58b - 0x58e + ;; code offset: 0x56b - 0x56e (i32.store offset=4 - ;; code offset: 0x587 - 0x589 + ;; code offset: 0x567 - 0x569 (local.get $8) - ;; code offset: 0x589 - 0x58b + ;; code offset: 0x569 - 0x56b (local.get $0) ) - ;; code offset: 0x592 - 0x595 + ;; code offset: 0x572 - 0x575 (i32.store - ;; code offset: 0x58e - 0x590 + ;; code offset: 0x56e - 0x570 (local.get $8) - ;; code offset: 0x590 - 0x592 + ;; code offset: 0x570 - 0x572 (local.get $3) ) - ;; code offset: 0x59c - 0x59d + ;; code offset: 0x57c - 0x57d (drop - ;; code offset: 0x59a - 0x59c + ;; code offset: 0x57a - 0x57c (call $iprintf - ;; code offset: 0x595 - 0x598 + ;; code offset: 0x575 - 0x578 (i32.const 1024) - ;; code offset: 0x598 - 0x59a + ;; code offset: 0x578 - 0x57a (local.get $8) ) ) ) - ;; code offset: 0x5a3 - 0x5a5 + ;; code offset: 0x583 - 0x585 (global.set $global$0 - ;; code offset: 0x5a2 - 0x5a3 + ;; code offset: 0x582 - 0x583 (i32.add - ;; code offset: 0x59e - 0x5a0 + ;; code offset: 0x57e - 0x580 (local.get $8) - ;; code offset: 0x5a0 - 0x5a2 + ;; code offset: 0x580 - 0x582 (i32.const 32) ) ) - ;; code offset: 0x5a5 - 0x5a7 + ;; code offset: 0x585 - 0x587 (local.get $5) ) ;; custom section ".debug_info", size 851 ;; custom section ".debug_loc", size 1073 ;; custom section ".debug_ranges", size 88 ;; custom section ".debug_abbrev", size 333 - ;; custom section ".debug_line", size 2322 + ;; custom section ".debug_line", size 2300 ;; custom section ".debug_str", size 434 ;; custom section "producers", size 135 ) From a4f81c14988296e5c69ae3ec8bac03b0055c5907 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 16 Sep 2026 12:41:42 -0700 Subject: [PATCH 22/28] faster --- src/ir/constraint.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ir/constraint.cpp b/src/ir/constraint.cpp index 109a87a58a4..46ad9c0d397 100644 --- a/src/ir/constraint.cpp +++ b/src/ir/constraint.cpp @@ -757,7 +757,7 @@ namespace { // common cases we want to, we parse the code in the natural order of execution, // and maintain a list of local operations. A get before a tee indicates // possible interference. -struct LocalOperations : public SmallVector { +struct LocalOperations : public SmallVector { // Check if an Expression returns a local's value: it is either a get or a // tee. Returns the index and type if so, and notes it in our vector. struct LocalOperation { From df36e237e31f3b9ea4c87330dd8742b22357aba9 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 16 Sep 2026 12:44:10 -0700 Subject: [PATCH 23/28] fix --- src/ir/constraint.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/ir/constraint.cpp b/src/ir/constraint.cpp index 46ad9c0d397..5a715bea775 100644 --- a/src/ir/constraint.cpp +++ b/src/ir/constraint.cpp @@ -765,29 +765,31 @@ struct LocalOperations : public SmallVector { Type type; }; std::optional parse(Expression* curr) { + auto handleNestedSets = [&](Expression* from) { + for (auto* nested : FindAll(from).list) { + push_back(nested); + } + }; + if (auto* get = curr->dynCast()) { push_back(get); return LocalOperation{get->index, get->type}; } if (auto* set = curr->dynCast()) { + // We know the value of this expression - the local the tee writes to - + // but further sets may be nested in the value, affecting other locals. + handleNestedSets(set->value); + // Ignore unreachable code, so the callers don't need to handle it. if (set->type == Type::unreachable) { return {}; } - // We know the value of this expression - the local the tee writes to - - // but further sets may be nested in the value, affecting other locals. - for (auto* nested : FindAll(set->value).list) { - push_back(nested); - } - push_back(set); return LocalOperation{set->index, set->type}; } - // Unrecognized. As above, we must scan for nested tees. - for (auto* nested : FindAll(curr).list) { - push_back(nested); - } + // Unrecognized. As above, we must scan for nested sets. + handleNestedSets(curr); return {}; } From b64117c1e2f2b18e36252571916db5e784cc4412 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 16 Sep 2026 12:50:09 -0700 Subject: [PATCH 24/28] tpo --- src/ir/constraint.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ir/constraint.cpp b/src/ir/constraint.cpp index 5a715bea775..28899680c0f 100644 --- a/src/ir/constraint.cpp +++ b/src/ir/constraint.cpp @@ -802,7 +802,7 @@ struct LocalOperations : public SmallVector { // Process the list in detail, as interference - a get before a set of the // same local - is possible. We track the read locals, and if we see a - // later write, that shows a problem; + // later write, that shows a problem. std::unordered_set read; for (auto* curr : *this) { if (auto* get = curr->dynCast()) { From 8647dcbbb187952c01184dfa44e53f0e445c8cf6 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 16 Sep 2026 14:21:29 -0700 Subject: [PATCH 25/28] assert tee --- src/ir/constraint.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ir/constraint.cpp b/src/ir/constraint.cpp index 28899680c0f..81531f134d2 100644 --- a/src/ir/constraint.cpp +++ b/src/ir/constraint.cpp @@ -776,6 +776,9 @@ struct LocalOperations : public SmallVector { return LocalOperation{get->index, get->type}; } if (auto* set = curr->dynCast()) { + // We are parsing expressions in a tree, not none-typed items in a block. + assert(set->isTee()); + // We know the value of this expression - the local the tee writes to - // but further sets may be nested in the value, affecting other locals. handleNestedSets(set->value); From 2e0f0eca9352c7db210691a29ed8d9ade2b3212e Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 16 Sep 2026 14:23:02 -0700 Subject: [PATCH 26/28] check for tee --- src/ir/constraint.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ir/constraint.cpp b/src/ir/constraint.cpp index 81531f134d2..6d243b3c454 100644 --- a/src/ir/constraint.cpp +++ b/src/ir/constraint.cpp @@ -815,7 +815,9 @@ struct LocalOperations : public SmallVector { return true; } // Insert a read, because the tee does both a write and a read. - read.insert(set->index); + if (set->isTee()) { + read.insert(set->index); + } } else { WASM_UNREACHABLE("invalid local op"); } From ab4564faef3a78acd013d98ae8b79a8328289c1c Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 16 Sep 2026 14:23:44 -0700 Subject: [PATCH 27/28] don't subclass SmallVector --- src/ir/constraint.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/ir/constraint.cpp b/src/ir/constraint.cpp index 6d243b3c454..aa7200389ab 100644 --- a/src/ir/constraint.cpp +++ b/src/ir/constraint.cpp @@ -757,7 +757,9 @@ namespace { // common cases we want to, we parse the code in the natural order of execution, // and maintain a list of local operations. A get before a tee indicates // possible interference. -struct LocalOperations : public SmallVector { +struct LocalOperations { + SmallVector vec; + // Check if an Expression returns a local's value: it is either a get or a // tee. Returns the index and type if so, and notes it in our vector. struct LocalOperation { @@ -767,12 +769,12 @@ struct LocalOperations : public SmallVector { std::optional parse(Expression* curr) { auto handleNestedSets = [&](Expression* from) { for (auto* nested : FindAll(from).list) { - push_back(nested); + vec.push_back(nested); } }; if (auto* get = curr->dynCast()) { - push_back(get); + vec.push_back(get); return LocalOperation{get->index, get->type}; } if (auto* set = curr->dynCast()) { @@ -788,7 +790,7 @@ struct LocalOperations : public SmallVector { return {}; } - push_back(set); + vec.push_back(set); return LocalOperation{set->index, set->type}; } // Unrecognized. As above, we must scan for nested sets. @@ -799,7 +801,7 @@ struct LocalOperations : public SmallVector { // Check for any possible interference between locals, which would tell the // caller that whatever was parsed is not valid. bool hasLocalInterference() const { - if (size() <= 1) { + if (vec.size() <= 1) { return false; } @@ -807,7 +809,7 @@ struct LocalOperations : public SmallVector { // same local - is possible. We track the read locals, and if we see a // later write, that shows a problem. std::unordered_set read; - for (auto* curr : *this) { + for (auto* curr : vec) { if (auto* get = curr->dynCast()) { read.insert(get->index); } else if (auto* set = curr->dynCast()) { From e7c9222d4d914b620d34b557099d8cecf706e302 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 16 Sep 2026 14:46:10 -0700 Subject: [PATCH 28/28] avoid storing the type --- src/ir/constraint.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/ir/constraint.cpp b/src/ir/constraint.cpp index aa7200389ab..0c9cc14fefe 100644 --- a/src/ir/constraint.cpp +++ b/src/ir/constraint.cpp @@ -764,7 +764,6 @@ struct LocalOperations { // tee. Returns the index and type if so, and notes it in our vector. struct LocalOperation { Index index; - Type type; }; std::optional parse(Expression* curr) { auto handleNestedSets = [&](Expression* from) { @@ -775,7 +774,7 @@ struct LocalOperations { if (auto* get = curr->dynCast()) { vec.push_back(get); - return LocalOperation{get->index, get->type}; + return LocalOperation{get->index}; } if (auto* set = curr->dynCast()) { // We are parsing expressions in a tree, not none-typed items in a block. @@ -791,7 +790,7 @@ struct LocalOperations { } vec.push_back(set); - return LocalOperation{set->index, set->type}; + return LocalOperation{set->index}; } // Unrecognized. As above, we must scan for nested sets. handleNestedSets(curr); @@ -837,8 +836,8 @@ localConstraintParseInternal(Expression* curr, [&](Expression* value) -> std::optional { if (auto localOp = localOperations.parse(value)) { // Canonicalize EqZ to Eq of 0. - auto value = Literal::makeZero(localOp->type); - return LocalConstraint{localOp->index, Constraint{Abstract::Eq, {value}}}; + auto zero = Literal::makeZero(value->type); + return LocalConstraint{localOp->index, Constraint{Abstract::Eq, {zero}}}; } // TODO: Recursively parse and reverse a constraint return {}; @@ -850,7 +849,7 @@ localConstraintParseInternal(Expression* curr, Expression* nested; if (matches(u->value, unary(Abstract::EqZ, any(&nested)))) { if (auto localOp = localOperations.parse(nested)) { - auto value = Literal::makeZero(localOp->type); + auto value = Literal::makeZero(nested->type); return LocalConstraint{localOp->index, Constraint{Abstract::Ne, {value}}}; } @@ -979,7 +978,7 @@ ParsedAndedConstraints::parseCondition(Expression* curr) { // A get or tee by itself is a check for not being null. LocalOperations localOperations; if (auto localOp = localOperations.parse(curr)) { - auto value = Literal::makeZero(localOp->type); + auto value = Literal::makeZero(curr->type); return {LocalConstraint{localOp->index, Constraint{Abstract::Ne, {value}}}}; }