summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xscripts/process_optimize_instructions.py16
-rw-r--r--src/passes/OptimizeInstructions.cpp186
-rw-r--r--src/passes/OptimizeInstructions.wast152
-rw-r--r--src/passes/OptimizeInstructions.wast.processed152
4 files changed, 456 insertions, 50 deletions
diff --git a/scripts/process_optimize_instructions.py b/scripts/process_optimize_instructions.py
new file mode 100755
index 000000000..a9337b13b
--- /dev/null
+++ b/scripts/process_optimize_instructions.py
@@ -0,0 +1,16 @@
+#!/usr/bin/python
+
+import os
+
+root = os.path.dirname(os.path.dirname(__file__))
+
+infile = os.path.join(root, 'src', 'passes', 'OptimizeInstructions.wast')
+outfile = os.path.join(root, 'src', 'passes',
+ 'OptimizeInstructions.wast.processed')
+
+out = open(outfile, 'w')
+
+for line in open(infile):
+ out.write('"' + line.strip().replace('"', '\\"') + '\\n"\n')
+
+out.close()
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp
index efa9409a7..9fa059327 100644
--- a/src/passes/OptimizeInstructions.cpp
+++ b/src/passes/OptimizeInstructions.cpp
@@ -22,66 +22,152 @@
#include <wasm.h>
#include <pass.h>
+#include <wasm-s-parser.h>
namespace wasm {
-struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions, Visitor<OptimizeInstructions>>> {
- bool isFunctionParallel() override { return true; }
+Name I32_EXPR = "i32.expr",
+ I64_EXPR = "i64.expr",
+ F32_EXPR = "f32.expr",
+ F64_EXPR = "f64.expr",
+ ANY_EXPR = "any.expr";
- Pass* create() override { return new OptimizeInstructions; }
+// A pattern
+struct Pattern {
+ Expression* input;
+ Expression* output;
+
+ Pattern(Expression* input, Expression* output) : input(input), output(output) {}
+};
+
+// Database of patterns
+struct PatternDatabase {
+ Module wasm;
+
+ char* input;
+
+ std::map<Expression::Id, std::vector<Pattern>> patternMap; // root expression id => list of all patterns for it TODO optimize more
+
+ PatternDatabase() {
+ // TODO: do this on first use, with a lock, to avoid startup pause
+ // generate module
+ input = strdup(
+ #include "OptimizeInstructions.wast.processed"
+ );
+ SExpressionParser parser(input);
+ Element& root = *parser.root;
+ SExpressionWasmBuilder builder(wasm, *root[0]);
+ // parse module form
+ auto* func = wasm.getFunction("patterns");
+ auto* body = func->body->cast<Block>();
+ for (auto* item : body->list) {
+ auto* pair = item->cast<Block>();
+ patternMap[pair->list[0]->_id].emplace_back(pair->list[0], pair->list[1]);
+ }
+ }
+
+ ~PatternDatabase() {
+ free(input);
+ };
+};
+
+static PatternDatabase database;
+
+// Check for matches and apply them
+struct Match {
+ Module& wasm;
+ Pattern& pattern;
+
+ Match(Module& wasm, Pattern& pattern) : wasm(wasm), pattern(pattern) {}
+
+ std::vector<Expression*> wildcards; // id in i32.any(id) etc. => the expression it represents in this match
- void visitIf(If* curr) {
- // flip branches to get rid of an i32.eqz
- if (curr->ifFalse) {
- auto condition = curr->condition->dynCast<Unary>();
- if (condition && condition->op == EqZInt32 && condition->value->type == i32) {
- curr->condition = condition->value;
- std::swap(curr->ifTrue, curr->ifFalse);
+ // Comparing/checking
+
+ // Check if we can match to this pattern, updating ourselves with the info if so
+ bool check(Expression* seen) {
+ // compare seen to the pattern input, doing a special operation for our "wildcards"
+ assert(wildcards.size() == 0);
+ return ExpressionAnalyzer::flexibleEqual(pattern.input, seen, *this);
+ }
+
+ bool compare(Expression* subInput, Expression* subSeen) {
+ CallImport* call = subInput->dynCast<CallImport>();
+ if (!call || call->operands.size() != 1 || call->operands[0]->type != i32 || !call->operands[0]->is<Const>()) return false;
+ Index index = call->operands[0]->cast<Const>()->value.geti32();
+ // handle our special functions
+ auto checkMatch = [&](WasmType type) {
+ if (type != none && subSeen->type != type) return false;
+ while (index >= wildcards.size()) {
+ wildcards.push_back(nullptr);
}
+ if (!wildcards[index]) {
+ // new wildcard
+ wildcards[index] = subSeen; // NB: no need to copy
+ return true;
+ } else {
+ // We are seeing this index for a second or later time, check it matches
+ return ExpressionAnalyzer::equal(subSeen, wildcards[index]);
+ };
+ };
+ if (call->target == I32_EXPR) {
+ if (checkMatch(i32)) return true;
+ } else if (call->target == I64_EXPR) {
+ if (checkMatch(i64)) return true;
+ } else if (call->target == F32_EXPR) {
+ if (checkMatch(f32)) return true;
+ } else if (call->target == F64_EXPR) {
+ if (checkMatch(f64)) return true;
+ } else if (call->target == ANY_EXPR) {
+ if (checkMatch(none)) return true;
}
+ return false;
+ }
+
+ // Applying/copying
+
+ // Apply the match, generate an output expression from the matched input, performing substitutions as necessary
+ Expression* apply() {
+ return ExpressionManipulator::flexibleCopy(pattern.output, wasm, *this);
}
- void visitUnary(Unary* curr) {
- if (curr->op == EqZInt32) {
- // fold comparisons that flow into an EqZ
- auto* child = curr->value->dynCast<Binary>();
- if (child && (child->type == i32 || child->type == i64)) {
- switch (child->op) {
- case EqInt32: child->op = NeInt32; break;
- case NeInt32: child->op = EqInt32; break;
- case LtSInt32: child->op = GeSInt32; break;
- case LtUInt32: child->op = GeUInt32; break;
- case LeSInt32: child->op = GtSInt32; break;
- case LeUInt32: child->op = GtUInt32; break;
- case GtSInt32: child->op = LeSInt32; break;
- case GtUInt32: child->op = LeUInt32; break;
- case GeSInt32: child->op = LtSInt32; break;
- case GeUInt32: child->op = LtUInt32; break;
- case EqInt64: child->op = NeInt64; break;
- case NeInt64: child->op = EqInt64; break;
- case LtSInt64: child->op = GeSInt64; break;
- case LtUInt64: child->op = GeUInt64; break;
- case LeSInt64: child->op = GtSInt64; break;
- case LeUInt64: child->op = GtUInt64; break;
- case GtSInt64: child->op = LeSInt64; break;
- case GtUInt64: child->op = LeUInt64; break;
- case GeSInt64: child->op = LtSInt64; break;
- case GeUInt64: child->op = LtUInt64; break;
- case EqFloat32: child->op = NeFloat32; break;
- case NeFloat32: child->op = EqFloat32; break;
- case LtFloat32: child->op = GeFloat32; break;
- case LeFloat32: child->op = GtFloat32; break;
- case GtFloat32: child->op = LeFloat32; break;
- case GeFloat32: child->op = LtFloat32; break;
- case EqFloat64: child->op = NeFloat64; break;
- case NeFloat64: child->op = EqFloat64; break;
- case LtFloat64: child->op = GeFloat64; break;
- case LeFloat64: child->op = GtFloat64; break;
- case GtFloat64: child->op = LeFloat64; break;
- case GeFloat64: child->op = LtFloat64; break;
- default: return;
+
+ // When copying a wildcard, perform the substitution.
+ // TODO: we can reuse nodes, not copying a wildcard when it appears just once, and we can reuse other individual nodes when they are discarded anyhow.
+ Expression* copy(Expression* curr) {
+ CallImport* call = curr->dynCast<CallImport>();
+ if (!call || call->operands.size() != 1 || call->operands[0]->type != i32 || !call->operands[0]->is<Const>()) return nullptr;
+ Index index = call->operands[0]->cast<Const>()->value.geti32();
+ // handle our special functions
+ if (call->target == I32_EXPR || call->target == I64_EXPR || call->target == F32_EXPR || call->target == F64_EXPR || call->target == ANY_EXPR) {
+ return ExpressionManipulator::copy(wildcards.at(index), wasm);
+ }
+ return nullptr;
+ }
+};
+
+// Main pass class
+struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions, UnifiedExpressionVisitor<OptimizeInstructions>>> {
+ bool isFunctionParallel() override { return true; }
+
+ Pass* create() override { return new OptimizeInstructions; }
+
+ void visitExpression(Expression* curr) {
+ // we may be able to apply multiple patterns, one may open opportunities that look deeper NB: patterns must not have cycles
+ while (1) {
+ auto iter = database.patternMap.find(curr->_id);
+ if (iter == database.patternMap.end()) return;
+ auto& patterns = iter->second;
+ bool more = false;
+ for (auto& pattern : patterns) {
+ Match match(*getModule(), pattern);
+ if (match.check(curr)) {
+ curr = match.apply();
+ replaceCurrent(curr);
+ more = true;
+ break; // exit pattern for loop, return to main while loop
}
- replaceCurrent(child);
}
+ if (!more) break;
}
}
};
diff --git a/src/passes/OptimizeInstructions.wast b/src/passes/OptimizeInstructions.wast
new file mode 100644
index 000000000..83459ad14
--- /dev/null
+++ b/src/passes/OptimizeInstructions.wast
@@ -0,0 +1,152 @@
+
+;; This file contains patterns for OptimizeInstructions. Basically, we use a DSL for the patterns,
+;; and the DSL is just wasm itself, plus some functions with special meanings
+;;
+;; This file is converted into OptimizeInstructions.wast.processed by
+;; scripts/process_optimize_instructions.py
+;; which makes it importable by C++. Then we just #include it there, avoiding the need to ship
+;; a data file on the side.
+
+(module
+ ;; "expr" represents an arbitrary expression. The input is an id, so the same expression
+ ;; can appear more than once. The type (i32 in i32.expr, etc.) is the return type, as this
+ ;; needs to have the right type for where it is placed.
+ (import $i32.expr "dsl" "i32.expr" (param i32) (result i32))
+ (import $i64.expr "dsl" "i64.expr" (param i32) (result i64))
+ (import $f32.expr "dsl" "f32.expr" (param i32) (result f32))
+ (import $f64.expr "dsl" "f64.expr" (param i32) (result f64))
+ (import $any.expr "dsl" "any.expr" (param i32) (result i32)) ;; ignorable return type
+
+ ;; main function. each block here is a pattern pair of input => output
+ (func $patterns
+ (block
+ ;; flip if-else arms to get rid of an eqz
+ (if
+ (i32.eqz
+ (call_import $i32.expr (i32.const 0))
+ )
+ (call_import $any.expr (i32.const 1))
+ (call_import $any.expr (i32.const 2))
+ )
+ (if
+ (call_import $i32.expr (i32.const 0))
+ (call_import $any.expr (i32.const 2))
+ (call_import $any.expr (i32.const 1))
+ )
+ )
+ ;; De Morgans Laws
+ (block
+ (i32.eqz (i32.eq (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1))))
+ (i32.ne (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1)))
+ )
+ (block
+ (i32.eqz (i32.ne (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1))))
+ (i32.eq (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1)))
+ )
+ (block
+ (i32.eqz (i32.lt_s (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1))))
+ (i32.ge_s (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1)))
+ )
+ (block
+ (i32.eqz (i32.lt_u (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1))))
+ (i32.ge_u (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1)))
+ )
+ (block
+ (i32.eqz (i32.le_s (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1))))
+ (i32.gt_s (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1)))
+ )
+ (block
+ (i32.eqz (i32.le_u (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1))))
+ (i32.gt_u (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1)))
+ )
+ (block
+ (i32.eqz (i32.gt_s (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1))))
+ (i32.le_s (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1)))
+ )
+ (block
+ (i32.eqz (i32.gt_u (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1))))
+ (i32.le_u (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1)))
+ )
+ (block
+ (i32.eqz (i32.ge_s (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1))))
+ (i32.lt_s (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1)))
+ )
+ (block
+ (i32.eqz (i32.ge_u (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1))))
+ (i32.lt_u (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1)))
+ )
+ (block
+ (i32.eqz (i64.eq (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1))))
+ (i64.ne (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1)))
+ )
+ (block
+ (i32.eqz (i64.ne (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1))))
+ (i64.eq (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1)))
+ )
+ (block
+ (i32.eqz (i64.lt_s (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1))))
+ (i64.ge_s (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1)))
+ )
+ (block
+ (i32.eqz (i64.lt_u (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1))))
+ (i64.ge_u (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1)))
+ )
+ (block
+ (i32.eqz (i64.le_s (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1))))
+ (i64.gt_s (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1)))
+ )
+ (block
+ (i32.eqz (i64.le_u (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1))))
+ (i64.gt_u (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1)))
+ )
+ (block
+ (i32.eqz (i64.gt_s (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1))))
+ (i64.le_s (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1)))
+ )
+ (block
+ (i32.eqz (i64.gt_u (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1))))
+ (i64.le_u (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1)))
+ )
+ (block
+ (i32.eqz (i64.ge_s (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1))))
+ (i64.lt_s (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1)))
+ )
+ (block
+ (i32.eqz (i64.ge_u (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1))))
+ (i64.lt_u (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1)))
+ )
+ (block
+ (i32.eqz (f32.eq (call_import $f32.expr (i32.const 0)) (call_import $f32.expr (i32.const 1))))
+ (f32.ne (call_import $f32.expr (i32.const 0)) (call_import $f32.expr (i32.const 1)))
+ )
+ (block
+ (i32.eqz (f32.ne (call_import $f32.expr (i32.const 0)) (call_import $f32.expr (i32.const 1))))
+ (f32.eq (call_import $f32.expr (i32.const 0)) (call_import $f32.expr (i32.const 1)))
+ )
+ (block
+ (i32.eqz (f32.lt (call_import $f32.expr (i32.const 0)) (call_import $f32.expr (i32.const 1))))
+ (f32.ge (call_import $f32.expr (i32.const 0)) (call_import $f32.expr (i32.const 1)))
+ )
+ (block
+ (i32.eqz (f32.le (call_import $f32.expr (i32.const 0)) (call_import $f32.expr (i32.const 1))))
+ (f32.gt (call_import $f32.expr (i32.const 0)) (call_import $f32.expr (i32.const 1)))
+ )
+ (block
+ (i32.eqz (f64.eq (call_import $f64.expr (i32.const 0)) (call_import $f64.expr (i32.const 1))))
+ (f64.ne (call_import $f64.expr (i32.const 0)) (call_import $f64.expr (i32.const 1)))
+ )
+ (block
+ (i32.eqz (f64.ne (call_import $f64.expr (i32.const 0)) (call_import $f64.expr (i32.const 1))))
+ (f64.eq (call_import $f64.expr (i32.const 0)) (call_import $f64.expr (i32.const 1)))
+ )
+ (block
+ (i32.eqz (f64.lt (call_import $f64.expr (i32.const 0)) (call_import $f64.expr (i32.const 1))))
+ (f64.ge (call_import $f64.expr (i32.const 0)) (call_import $f64.expr (i32.const 1)))
+ )
+ (block
+ (i32.eqz (f64.le (call_import $f64.expr (i32.const 0)) (call_import $f64.expr (i32.const 1))))
+ (f64.gt (call_import $f64.expr (i32.const 0)) (call_import $f64.expr (i32.const 1)))
+ )
+ )
+)
+
diff --git a/src/passes/OptimizeInstructions.wast.processed b/src/passes/OptimizeInstructions.wast.processed
new file mode 100644
index 000000000..6bd251c95
--- /dev/null
+++ b/src/passes/OptimizeInstructions.wast.processed
@@ -0,0 +1,152 @@
+"\n"
+";; This file contains patterns for OptimizeInstructions. Basically, we use a DSL for the patterns,\n"
+";; and the DSL is just wasm itself, plus some functions with special meanings\n"
+";;\n"
+";; This file is converted into OptimizeInstructions.wast.processed by\n"
+";; scripts/process_optimize_instructions.py\n"
+";; which makes it importable by C++. Then we just #include it there, avoiding the need to ship\n"
+";; a data file on the side.\n"
+"\n"
+"(module\n"
+";; \"expr\" represents an arbitrary expression. The input is an id, so the same expression\n"
+";; can appear more than once. The type (i32 in i32.expr, etc.) is the return type, as this\n"
+";; needs to have the right type for where it is placed.\n"
+"(import $i32.expr \"dsl\" \"i32.expr\" (param i32) (result i32))\n"
+"(import $i64.expr \"dsl\" \"i64.expr\" (param i32) (result i64))\n"
+"(import $f32.expr \"dsl\" \"f32.expr\" (param i32) (result f32))\n"
+"(import $f64.expr \"dsl\" \"f64.expr\" (param i32) (result f64))\n"
+"(import $any.expr \"dsl\" \"any.expr\" (param i32) (result i32)) ;; ignorable return type\n"
+"\n"
+";; main function. each block here is a pattern pair of input => output\n"
+"(func $patterns\n"
+"(block\n"
+";; flip if-else arms to get rid of an eqz\n"
+"(if\n"
+"(i32.eqz\n"
+"(call_import $i32.expr (i32.const 0))\n"
+")\n"
+"(call_import $any.expr (i32.const 1))\n"
+"(call_import $any.expr (i32.const 2))\n"
+")\n"
+"(if\n"
+"(call_import $i32.expr (i32.const 0))\n"
+"(call_import $any.expr (i32.const 2))\n"
+"(call_import $any.expr (i32.const 1))\n"
+")\n"
+")\n"
+";; De Morgans Laws\n"
+"(block\n"
+"(i32.eqz (i32.eq (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1))))\n"
+"(i32.ne (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1)))\n"
+")\n"
+"(block\n"
+"(i32.eqz (i32.ne (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1))))\n"
+"(i32.eq (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1)))\n"
+")\n"
+"(block\n"
+"(i32.eqz (i32.lt_s (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1))))\n"
+"(i32.ge_s (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1)))\n"
+")\n"
+"(block\n"
+"(i32.eqz (i32.lt_u (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1))))\n"
+"(i32.ge_u (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1)))\n"
+")\n"
+"(block\n"
+"(i32.eqz (i32.le_s (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1))))\n"
+"(i32.gt_s (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1)))\n"
+")\n"
+"(block\n"
+"(i32.eqz (i32.le_u (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1))))\n"
+"(i32.gt_u (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1)))\n"
+")\n"
+"(block\n"
+"(i32.eqz (i32.gt_s (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1))))\n"
+"(i32.le_s (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1)))\n"
+")\n"
+"(block\n"
+"(i32.eqz (i32.gt_u (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1))))\n"
+"(i32.le_u (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1)))\n"
+")\n"
+"(block\n"
+"(i32.eqz (i32.ge_s (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1))))\n"
+"(i32.lt_s (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1)))\n"
+")\n"
+"(block\n"
+"(i32.eqz (i32.ge_u (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1))))\n"
+"(i32.lt_u (call_import $i32.expr (i32.const 0)) (call_import $i32.expr (i32.const 1)))\n"
+")\n"
+"(block\n"
+"(i32.eqz (i64.eq (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1))))\n"
+"(i64.ne (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1)))\n"
+")\n"
+"(block\n"
+"(i32.eqz (i64.ne (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1))))\n"
+"(i64.eq (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1)))\n"
+")\n"
+"(block\n"
+"(i32.eqz (i64.lt_s (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1))))\n"
+"(i64.ge_s (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1)))\n"
+")\n"
+"(block\n"
+"(i32.eqz (i64.lt_u (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1))))\n"
+"(i64.ge_u (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1)))\n"
+")\n"
+"(block\n"
+"(i32.eqz (i64.le_s (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1))))\n"
+"(i64.gt_s (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1)))\n"
+")\n"
+"(block\n"
+"(i32.eqz (i64.le_u (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1))))\n"
+"(i64.gt_u (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1)))\n"
+")\n"
+"(block\n"
+"(i32.eqz (i64.gt_s (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1))))\n"
+"(i64.le_s (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1)))\n"
+")\n"
+"(block\n"
+"(i32.eqz (i64.gt_u (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1))))\n"
+"(i64.le_u (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1)))\n"
+")\n"
+"(block\n"
+"(i32.eqz (i64.ge_s (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1))))\n"
+"(i64.lt_s (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1)))\n"
+")\n"
+"(block\n"
+"(i32.eqz (i64.ge_u (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1))))\n"
+"(i64.lt_u (call_import $i64.expr (i32.const 0)) (call_import $i64.expr (i32.const 1)))\n"
+")\n"
+"(block\n"
+"(i32.eqz (f32.eq (call_import $f32.expr (i32.const 0)) (call_import $f32.expr (i32.const 1))))\n"
+"(f32.ne (call_import $f32.expr (i32.const 0)) (call_import $f32.expr (i32.const 1)))\n"
+")\n"
+"(block\n"
+"(i32.eqz (f32.ne (call_import $f32.expr (i32.const 0)) (call_import $f32.expr (i32.const 1))))\n"
+"(f32.eq (call_import $f32.expr (i32.const 0)) (call_import $f32.expr (i32.const 1)))\n"
+")\n"
+"(block\n"
+"(i32.eqz (f32.lt (call_import $f32.expr (i32.const 0)) (call_import $f32.expr (i32.const 1))))\n"
+"(f32.ge (call_import $f32.expr (i32.const 0)) (call_import $f32.expr (i32.const 1)))\n"
+")\n"
+"(block\n"
+"(i32.eqz (f32.le (call_import $f32.expr (i32.const 0)) (call_import $f32.expr (i32.const 1))))\n"
+"(f32.gt (call_import $f32.expr (i32.const 0)) (call_import $f32.expr (i32.const 1)))\n"
+")\n"
+"(block\n"
+"(i32.eqz (f64.eq (call_import $f64.expr (i32.const 0)) (call_import $f64.expr (i32.const 1))))\n"
+"(f64.ne (call_import $f64.expr (i32.const 0)) (call_import $f64.expr (i32.const 1)))\n"
+")\n"
+"(block\n"
+"(i32.eqz (f64.ne (call_import $f64.expr (i32.const 0)) (call_import $f64.expr (i32.const 1))))\n"
+"(f64.eq (call_import $f64.expr (i32.const 0)) (call_import $f64.expr (i32.const 1)))\n"
+")\n"
+"(block\n"
+"(i32.eqz (f64.lt (call_import $f64.expr (i32.const 0)) (call_import $f64.expr (i32.const 1))))\n"
+"(f64.ge (call_import $f64.expr (i32.const 0)) (call_import $f64.expr (i32.const 1)))\n"
+")\n"
+"(block\n"
+"(i32.eqz (f64.le (call_import $f64.expr (i32.const 0)) (call_import $f64.expr (i32.const 1))))\n"
+"(f64.gt (call_import $f64.expr (i32.const 0)) (call_import $f64.expr (i32.const 1)))\n"
+")\n"
+")\n"
+")\n"
+"\n"