diff --git a/src/ir/CMakeLists.txt b/src/ir/CMakeLists.txt index 5521529cd91..4bedfc886ec 100644 --- a/src/ir/CMakeLists.txt +++ b/src/ir/CMakeLists.txt @@ -1,5 +1,6 @@ FILE(GLOB ir_HEADERS *.h) set(ir_SOURCES + abstract.cpp ExpressionAnalyzer.cpp ExpressionManipulator.cpp constraint.cpp diff --git a/src/ir/abstract.cpp b/src/ir/abstract.cpp new file mode 100644 index 00000000000..54180ad785f --- /dev/null +++ b/src/ir/abstract.cpp @@ -0,0 +1,64 @@ +/* + * Copyright 2026 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ir/abstract.h" +#include "wasm.h" + +namespace wasm::Abstract { + +std::ostream& operator<<(std::ostream& o, Op op) { + +#define HANDLE(x) \ + case x: \ + return o << #x; + + switch (op) { + HANDLE(Abs) + HANDLE(Neg) + HANDLE(Popcnt) + HANDLE(Add) + HANDLE(Sub) + HANDLE(Mul) + HANDLE(DivU) + HANDLE(DivS) + HANDLE(RemU) + HANDLE(RemS) + HANDLE(Shl) + HANDLE(ShrU) + HANDLE(ShrS) + HANDLE(RotL) + HANDLE(RotR) + HANDLE(And) + HANDLE(Or) + HANDLE(Xor) + HANDLE(CopySign) + HANDLE(EqZ) + HANDLE(Eq) + HANDLE(Ne) + HANDLE(LtS) + HANDLE(LtU) + HANDLE(LeS) + HANDLE(LeU) + HANDLE(GtS) + HANDLE(GtU) + HANDLE(GeS) + HANDLE(GeU) + default: + WASM_UNREACHABLE("bad abstract op"); + } +} + +} // namespace wasm::Abstract diff --git a/src/ir/abstract.h b/src/ir/abstract.h index 44124381876..33a297efb5f 100644 --- a/src/ir/abstract.h +++ b/src/ir/abstract.h @@ -349,6 +349,8 @@ inline bool isRelationalAntisymmetric(Op op) { } } +std::ostream& operator<<(std::ostream& o, Op op); + } // namespace wasm::Abstract #endif // wasm_ir_abstract_h diff --git a/src/ir/constraint.cpp b/src/ir/constraint.cpp index fc273301712..8a192bbd6de 100644 --- a/src/ir/constraint.cpp +++ b/src/ir/constraint.cpp @@ -234,7 +234,16 @@ std::optional LocalConstraint::parse(Expression* curr) { if (auto* binary = curr->dynCast()) { // The operation must be one we recognize. - for (auto op : {Abstract::Eq, Abstract::Ne}) { + for (auto op : {Abstract::Eq, + Abstract::Ne, + Abstract::LtS, + Abstract::LtU, + Abstract::LeS, + Abstract::LeU, + Abstract::GtS, + Abstract::GtU, + Abstract::GeS, + Abstract::GeU}) { if (Abstract::getBinary(binary->type, op) == binary->op) { return parseBinaryArguments(op, binary->left, binary->right); } @@ -421,7 +430,7 @@ void BasicBlockConstraintMap::eraseStaleRefs(Index index) { } std::ostream& operator<<(std::ostream& o, const Constraint& c) { - o << "Constraint{" << int(c.op) << ", "; + o << "Constraint{" << c.op << ", "; if (auto* cc = std::get_if(&c.term)) { o << *cc; } else if (auto* i = std::get_if(&c.term)) { diff --git a/test/lit/passes/constraint-analysis.wast b/test/lit/passes/constraint-analysis.wast index c5c7eb93245..5b51766a9a8 100644 --- a/test/lit/passes/constraint-analysis.wast +++ b/test/lit/passes/constraint-analysis.wast @@ -9,6 +9,10 @@ ;; RUN: wasm-opt %s --optimize-instructions --constraint-analysis -all -S -o - | filecheck %s --check-prefix=OPTIN (module + ;; CHECK: (type $array (array (mut i32))) + ;; OPTIN: (type $array (array (mut i32))) + (type $array (array (mut i32))) + ;; CHECK: (func $simple (type $1) ;; CHECK-NEXT: (local $x i32) ;; CHECK-NEXT: (local.set $x @@ -1510,7 +1514,7 @@ ) ) - ;; CHECK: (func $br_on_null (type $4) (param $param anyref) + ;; CHECK: (func $br_on_null (type $5) (param $param anyref) ;; CHECK-NEXT: (block $block ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (ref.is_null @@ -1531,7 +1535,7 @@ ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; OPTIN: (func $br_on_null (type $4) (param $param anyref) + ;; OPTIN: (func $br_on_null (type $5) (param $param anyref) ;; OPTIN-NEXT: (block $block ;; OPTIN-NEXT: (drop ;; OPTIN-NEXT: (ref.is_null @@ -1581,7 +1585,7 @@ ) ) - ;; CHECK: (func $br_on_non_null (type $4) (param $param anyref) + ;; CHECK: (func $br_on_non_null (type $5) (param $param anyref) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block $block (result (ref any)) ;; CHECK-NEXT: (drop @@ -1602,7 +1606,7 @@ ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; OPTIN: (func $br_on_non_null (type $4) (param $param anyref) + ;; OPTIN: (func $br_on_non_null (type $5) (param $param anyref) ;; OPTIN-NEXT: (drop ;; OPTIN-NEXT: (block $block (result (ref any)) ;; OPTIN-NEXT: (drop @@ -1652,137 +1656,740 @@ ) ) - ;; 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: ) - ;; 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.eq - ;; CHECK-NEXT: (local.get $x) - ;; CHECK-NEXT: (local.get $z) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.eq - ;; CHECK-NEXT: (local.get $z) - ;; CHECK-NEXT: (local.get $x) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $x - ;; CHECK-NEXT: (local.get $z) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.eq - ;; CHECK-NEXT: (local.get $x) - ;; CHECK-NEXT: (local.get $y) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.eq - ;; CHECK-NEXT: (local.get $y) + ;; CHECK: (func $inequalities-lt_u (type $0) (param $x i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.lt_u ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 10) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; 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.eq - ;; CHECK-NEXT: (local.get $y) - ;; CHECK-NEXT: (local.get $z) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.eq - ;; CHECK-NEXT: (local.get $z) - ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: (else + ;; 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 $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: ) - ;; OPTIN-NEXT: (drop - ;; OPTIN-NEXT: (i32.const 1) - ;; OPTIN-NEXT: ) - ;; OPTIN-NEXT: (drop - ;; OPTIN-NEXT: (i32.const 1) - ;; OPTIN-NEXT: ) + ;; OPTIN: (func $inequalities-lt_u (type $0) (param $x i32) ;; OPTIN-NEXT: (drop - ;; OPTIN-NEXT: (i32.eq + ;; OPTIN-NEXT: (i32.lt_u ;; OPTIN-NEXT: (local.get $x) - ;; OPTIN-NEXT: (local.get $z) + ;; OPTIN-NEXT: (i32.const 10) ;; OPTIN-NEXT: ) ;; OPTIN-NEXT: ) ;; OPTIN-NEXT: (drop - ;; OPTIN-NEXT: (i32.eq + ;; OPTIN-NEXT: (i32.ge_u ;; OPTIN-NEXT: (local.get $x) - ;; OPTIN-NEXT: (local.get $z) + ;; OPTIN-NEXT: (i32.const 10) ;; OPTIN-NEXT: ) ;; OPTIN-NEXT: ) - ;; OPTIN-NEXT: (local.set $x - ;; OPTIN-NEXT: (local.get $z) - ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + (func $inequalities-lt_u (param $x i32) + ;; The simple case where an inequality is seen again, in exact form. + (if + (i32.lt_u + (local.get $x) + (i32.const 10) + ) + (then + ;; We can infer 1, 0 here. + (drop + (i32.lt_u + (local.get $x) + (i32.const 10) + ) + ) + (drop + (i32.ge_u + (local.get $x) + (i32.const 10) + ) + ) + ) + (else + ;; We can infer 0, 1 here. + (drop + (i32.lt_u + (local.get $x) + (i32.const 10) + ) + ) + (drop + (i32.ge_u + (local.get $x) + (i32.const 10) + ) + ) + ) + ) + ) + + ;; CHECK: (func $inequalities-lt_s (type $0) (param $x i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.lt_s + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; 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 $inequalities-lt_s (type $0) (param $x i32) ;; OPTIN-NEXT: (drop - ;; OPTIN-NEXT: (i32.eq + ;; OPTIN-NEXT: (i32.lt_s ;; OPTIN-NEXT: (local.get $x) - ;; OPTIN-NEXT: (local.get $y) + ;; OPTIN-NEXT: (i32.const 10) ;; OPTIN-NEXT: ) ;; OPTIN-NEXT: ) ;; OPTIN-NEXT: (drop - ;; OPTIN-NEXT: (i32.eq + ;; OPTIN-NEXT: (i32.ge_s ;; OPTIN-NEXT: (local.get $x) - ;; OPTIN-NEXT: (local.get $y) + ;; OPTIN-NEXT: (i32.const 10) ;; OPTIN-NEXT: ) ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + (func $inequalities-lt_s (param $x i32) + (if + (i32.lt_s + (local.get $x) + (i32.const 10) + ) + (then + (drop + (i32.lt_s + (local.get $x) + (i32.const 10) + ) + ) + (drop + (i32.ge_s + (local.get $x) + (i32.const 10) + ) + ) + ) + (else + (drop + (i32.lt_s + (local.get $x) + (i32.const 10) + ) + ) + (drop + (i32.ge_s + (local.get $x) + (i32.const 10) + ) + ) + ) + ) + ) + + ;; CHECK: (func $inequalities-le_u (type $0) (param $x i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.le_u + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; 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 $inequalities-le_u (type $0) (param $x i32) ;; 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.eq - ;; OPTIN-NEXT: (local.get $y) - ;; OPTIN-NEXT: (local.get $z) + ;; OPTIN-NEXT: (i32.le_u + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: (i32.const 10) ;; OPTIN-NEXT: ) ;; OPTIN-NEXT: ) ;; OPTIN-NEXT: (drop - ;; OPTIN-NEXT: (i32.eq - ;; OPTIN-NEXT: (local.get $y) - ;; OPTIN-NEXT: (local.get $z) + ;; OPTIN-NEXT: (i32.gt_u + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: (i32.const 10) ;; OPTIN-NEXT: ) ;; OPTIN-NEXT: ) ;; OPTIN-NEXT: ) - (func $local-changes (param $x i32) (param $y i32) (param $z i32) - (local.set $x - (local.get $y) - ) - ;; x == y but not z. - (drop - (i32.eq + (func $inequalities-le_u (param $x i32) + (if + (i32.le_u (local.get $x) - (local.get $y) + (i32.const 10) ) - ) - (drop - (i32.eq - (local.get $y) - (local.get $x) + (then + (drop + (i32.le_u + (local.get $x) + (i32.const 10) + ) + ) + (drop + (i32.gt_u + (local.get $x) + (i32.const 10) + ) + ) + ) + (else + (drop + (i32.le_u + (local.get $x) + (i32.const 10) + ) + ) + (drop + (i32.gt_u + (local.get $x) + (i32.const 10) + ) + ) ) ) - (drop - (i32.eq + ) + + ;; CHECK: (func $inequalities-le_s (type $0) (param $x i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.le_s + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; 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 $inequalities-le_s (type $0) (param $x i32) + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.le_s + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: (i32.const 10) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.gt_s + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: (i32.const 10) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + (func $inequalities-le_s (param $x i32) + (if + (i32.le_s + (local.get $x) + (i32.const 10) + ) + (then + (drop + (i32.le_s + (local.get $x) + (i32.const 10) + ) + ) + (drop + (i32.gt_s + (local.get $x) + (i32.const 10) + ) + ) + ) + (else + (drop + (i32.le_s + (local.get $x) + (i32.const 10) + ) + ) + (drop + (i32.gt_s + (local.get $x) + (i32.const 10) + ) + ) + ) + ) + ) + + ;; CHECK: (func $inequalities-gt_u (type $0) (param $x i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.gt_u + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; 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 $inequalities-gt_u (type $0) (param $x i32) + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.gt_u + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: (i32.const 10) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.le_u + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: (i32.const 10) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + (func $inequalities-gt_u (param $x i32) + (if + (i32.gt_u + (local.get $x) + (i32.const 10) + ) + (then + (drop + (i32.gt_u + (local.get $x) + (i32.const 10) + ) + ) + (drop + (i32.le_u + (local.get $x) + (i32.const 10) + ) + ) + ) + (else + (drop + (i32.gt_u + (local.get $x) + (i32.const 10) + ) + ) + (drop + (i32.le_u + (local.get $x) + (i32.const 10) + ) + ) + ) + ) + ) + + ;; CHECK: (func $inequalities-gt_s (type $0) (param $x i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.gt_s + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; 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 $inequalities-gt_s (type $0) (param $x i32) + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.gt_s + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: (i32.const 10) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.le_s + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: (i32.const 10) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + (func $inequalities-gt_s (param $x i32) + (if + (i32.gt_s + (local.get $x) + (i32.const 10) + ) + (then + (drop + (i32.gt_s + (local.get $x) + (i32.const 10) + ) + ) + (drop + (i32.le_s + (local.get $x) + (i32.const 10) + ) + ) + ) + (else + (drop + (i32.gt_s + (local.get $x) + (i32.const 10) + ) + ) + (drop + (i32.le_s + (local.get $x) + (i32.const 10) + ) + ) + ) + ) + ) + + ;; CHECK: (func $inequalities-ge_u (type $0) (param $x i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.ge_u + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; 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 $inequalities-ge_u (type $0) (param $x i32) + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.ge_u + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: (i32.const 10) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.lt_u + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: (i32.const 10) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + (func $inequalities-ge_u (param $x i32) + (if + (i32.ge_u + (local.get $x) + (i32.const 10) + ) + (then + (drop + (i32.ge_u + (local.get $x) + (i32.const 10) + ) + ) + (drop + (i32.lt_u + (local.get $x) + (i32.const 10) + ) + ) + ) + (else + (drop + (i32.ge_u + (local.get $x) + (i32.const 10) + ) + ) + (drop + (i32.lt_u + (local.get $x) + (i32.const 10) + ) + ) + ) + ) + ) + + ;; CHECK: (func $inequalities-ge_s (type $0) (param $x i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.ge_s + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; 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 $inequalities-ge_s (type $0) (param $x i32) + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.ge_s + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: (i32.const 10) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.lt_s + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: (i32.const 10) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + (func $inequalities-ge_s (param $x i32) + (if + (i32.ge_s + (local.get $x) + (i32.const 10) + ) + (then + (drop + (i32.ge_s + (local.get $x) + (i32.const 10) + ) + ) + (drop + (i32.lt_s + (local.get $x) + (i32.const 10) + ) + ) + ) + (else + (drop + (i32.ge_s + (local.get $x) + (i32.const 10) + ) + ) + (drop + (i32.lt_s + (local.get $x) + (i32.const 10) + ) + ) + ) + ) + ) + + ;; 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: ) + ;; 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.eq + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (local.get $z) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (local.get $z) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $x + ;; CHECK-NEXT: (local.get $z) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; 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.eq + ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: (local.get $z) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (local.get $z) + ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; 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: ) + ;; 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.eq + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: (local.get $z) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.eq + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: (local.get $z) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (local.set $x + ;; OPTIN-NEXT: (local.get $z) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.eq + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: (local.get $y) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.eq + ;; OPTIN-NEXT: (local.get $x) + ;; OPTIN-NEXT: (local.get $y) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; 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.eq + ;; OPTIN-NEXT: (local.get $y) + ;; OPTIN-NEXT: (local.get $z) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (drop + ;; OPTIN-NEXT: (i32.eq + ;; OPTIN-NEXT: (local.get $y) + ;; OPTIN-NEXT: (local.get $z) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + (func $local-changes (param $x i32) (param $y i32) (param $z i32) + (local.set $x + (local.get $y) + ) + ;; x == y but not z. + (drop + (i32.eq + (local.get $x) + (local.get $y) + ) + ) + (drop + (i32.eq + (local.get $y) + (local.get $x) + ) + ) + (drop + (i32.eq (local.get $x) (local.get $z) ) @@ -2025,7 +2632,7 @@ ) ) - ;; CHECK: (func $local-changes-if (type $5) (param $x i32) (param $y i32) (param $z i32) (param $w i32) + ;; CHECK: (func $local-changes-if (type $6) (param $x i32) (param $y i32) (param $z i32) (param $w i32) ;; CHECK-NEXT: (if ;; CHECK-NEXT: (i32.eq ;; CHECK-NEXT: (local.get $x) @@ -2082,7 +2689,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; OPTIN: (func $local-changes-if (type $5) (param $x i32) (param $y i32) (param $z i32) (param $w i32) + ;; OPTIN: (func $local-changes-if (type $6) (param $x i32) (param $y i32) (param $z i32) (param $w i32) ;; OPTIN-NEXT: (if ;; OPTIN-NEXT: (i32.eq ;; OPTIN-NEXT: (local.get $x) @@ -2717,4 +3324,152 @@ ) ) ) + + ;; CHECK: (func $simple-array-sum (type $7) (param $param (ref $array)) (result i32) + ;; CHECK-NEXT: (local $index i32) + ;; CHECK-NEXT: (local $sum i32) + ;; CHECK-NEXT: (local $len i32) + ;; CHECK-NEXT: (local.set $len + ;; CHECK-NEXT: (array.len + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (loop $loop + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.ge_u + ;; CHECK-NEXT: (local.get $index) + ;; CHECK-NEXT: (local.get $len) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (return + ;; CHECK-NEXT: (local.get $sum) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $sum + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (local.get $sum) + ;; CHECK-NEXT: (array.get $array + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: (local.get $index) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $index + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (local.get $index) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $loop) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; OPTIN: (func $simple-array-sum (type $7) (param $param (ref $array)) (result i32) + ;; OPTIN-NEXT: (local $index i32) + ;; OPTIN-NEXT: (local $sum i32) + ;; OPTIN-NEXT: (local $len i32) + ;; OPTIN-NEXT: (local.set $len + ;; OPTIN-NEXT: (array.len + ;; OPTIN-NEXT: (local.get $param) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (loop $loop + ;; OPTIN-NEXT: (if + ;; OPTIN-NEXT: (i32.ge_u + ;; OPTIN-NEXT: (local.get $index) + ;; OPTIN-NEXT: (local.get $len) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (then + ;; OPTIN-NEXT: (return + ;; OPTIN-NEXT: (local.get $sum) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (if + ;; OPTIN-NEXT: (i32.const 0) + ;; OPTIN-NEXT: (then + ;; OPTIN-NEXT: (unreachable) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (local.set $sum + ;; OPTIN-NEXT: (i32.add + ;; OPTIN-NEXT: (array.get $array + ;; OPTIN-NEXT: (local.get $param) + ;; OPTIN-NEXT: (local.get $index) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (local.get $sum) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (local.set $index + ;; OPTIN-NEXT: (i32.add + ;; OPTIN-NEXT: (local.get $index) + ;; OPTIN-NEXT: (i32.const 1) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: (br $loop) + ;; OPTIN-NEXT: ) + ;; OPTIN-NEXT: ) + (func $simple-array-sum (param $param (ref $array)) (result i32) + ;; A simple but realistic software bounds check that we can remove. + (local $index i32) + (local $sum i32) + (local $len i32) + ;; Stash the length in a local. + (local.set $len + (array.len + (local.get $param) + ) + ) + ;; Loop over the array. + (loop $loop + ;; If we reached the end of the array, return the sum. + (if + (i32.ge_u + (local.get $index) + (local.get $len) + ) + (then + (return + (local.get $sum) + ) + ) + ) + ;; Software bounds check, right before the actual access. After we + ;; optimize, the condition here becomes zero, so it will be removed later. + (if + (i32.ge_u + (local.get $index) + (local.get $len) + ) + (then + (unreachable) + ) + ) + ;; Read the array and add to the sum. + (local.set $sum + (i32.add + (local.get $sum) + (array.get $array + (local.get $param) + (local.get $index) + ) + ) + ) + ;; Increment. + (local.set $index + (i32.add + (local.get $index) + (i32.const 1) + ) + ) + ;; Keep looping. + (br $loop) + ) + ) )