diff options
author | Thomas Lively <7121787+tlively@users.noreply.github.com> | 2018-11-19 13:31:47 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-11-19 13:31:47 -0800 |
commit | 7011e47ff4ef1a48d15d399f4dfaa761de4779af (patch) | |
tree | a6da8ea3b3416cc5210c5b9edcee64615405f48e | |
parent | fa0dfb6a3da3d05e01051a72d9348b2f3024222b (diff) | |
download | binaryen-7011e47ff4ef1a48d15d399f4dfaa761de4779af.tar.gz binaryen-7011e47ff4ef1a48d15d399f4dfaa761de4779af.tar.bz2 binaryen-7011e47ff4ef1a48d15d399f4dfaa761de4779af.zip |
Generate sexp instruction parser (#1754)
Also fix broken tests surfaced by the new parser.
-rw-r--r-- | .flake8 | 7 | ||||
-rw-r--r-- | .travis.yml | 6 | ||||
-rwxr-xr-x | scripts/gen-s-parser.py | 408 | ||||
-rw-r--r-- | src/gen-s-parser.inc | 688 | ||||
-rw-r--r-- | src/wasm-s-parser.h | 6 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 302 | ||||
-rw-r--r-- | test/ctor-eval/basics-flatten.wast | 6 | ||||
-rw-r--r-- | test/ctor-eval/basics.wast | 6 | ||||
-rw-r--r-- | test/passes/duplicate-function-elimination_optimize-level=1.wast | 2 | ||||
-rw-r--r-- | test/passes/duplicate-function-elimination_optimize-level=2.wast | 2 | ||||
-rw-r--r-- | test/wasm2js/br_table_temp.wast | 2 |
11 files changed, 1134 insertions, 301 deletions
@@ -1,3 +1,8 @@ [flake8] -ignore = E111,E114,E501,E121 +ignore = + E111, # indentation not a multiple of 4 (we use 2)) + E114, # comment indentation not a multiple of 4 + E501, # line too long + E121, # continuation line under-indented for hanging indent + E241 # space after comma (ignored for list in gen-s-parser.py) exclude = ./test/emscripten,./test/spec,./test/wasm-install diff --git a/.travis.yml b/.travis.yml index 0ab529690..e36c091e6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,9 @@ jobs: - &test-ubuntu stage: test compiler: clang - python: 2.7 + python: + - 2.7 + - 3.6 addons: apt: sources: ['ubuntu-toolchain-r-test'] @@ -33,6 +35,8 @@ jobs: script: - set -o errexit - flake8 + # ensure generated parser is up to date + - ./scripts/gen-s-parser.py | diff src/gen-s-parser.inc - - BUILD_SUBDIR=${BUILD_SUBDIR:-.} - mkdir -p ${BUILD_SUBDIR} && cd ${BUILD_SUBDIR} - cmake ${TRAVIS_BUILD_DIR} -DCMAKE_C_FLAGS="$COMPILER_FLAGS" -DCMAKE_CXX_FLAGS="$COMPILER_FLAGS" -DCMAKE_INSTALL_PREFIX=install diff --git a/scripts/gen-s-parser.py b/scripts/gen-s-parser.py new file mode 100755 index 000000000..00989ab43 --- /dev/null +++ b/scripts/gen-s-parser.py @@ -0,0 +1,408 @@ +#!/usr/bin/env python3 +# +# Copyright 2018 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. + +instructions = [ + ("unreachable", "makeUnreachable()"), + ("nop", "makeNop()"), + ("block", "makeBlock(s)"), + ("loop", "makeLoop(s)"), + ("if", "makeIf(s)"), + ("then", "makeThenOrElse(s)"), + ("else", "makeThenOrElse(s)"), + ("br", "makeBreak(s)"), + ("br_if", "makeBreak(s)"), + ("br_table", "makeBreakTable(s)"), + ("return", "makeReturn(s)"), + ("call", "makeCall(s)"), + ("call_indirect", "makeCallIndirect(s)"), + ("drop", "makeDrop(s)"), + ("select", "makeSelect(s)"), + ("get_local", "makeGetLocal(s)"), + ("set_local", "makeSetLocal(s)"), + ("tee_local", "makeTeeLocal(s)"), + ("get_global", "makeGetGlobal(s)"), + ("set_global", "makeSetGlobal(s)"), + ("i32.load", "makeLoad(s, i32, /*isAtomic=*/false)"), + ("i64.load", "makeLoad(s, i64, /*isAtomic=*/false)"), + ("f32.load", "makeLoad(s, f32, /*isAtomic=*/false)"), + ("f64.load", "makeLoad(s, f64, /*isAtomic=*/false)"), + ("i32.load8_s", "makeLoad(s, i32, /*isAtomic=*/false)"), + ("i32.load8_u", "makeLoad(s, i32, /*isAtomic=*/false)"), + ("i32.load16_s", "makeLoad(s, i32, /*isAtomic=*/false)"), + ("i32.load16_u", "makeLoad(s, i32, /*isAtomic=*/false)"), + ("i64.load8_s", "makeLoad(s, i64, /*isAtomic=*/false)"), + ("i64.load8_u", "makeLoad(s, i64, /*isAtomic=*/false)"), + ("i64.load16_s", "makeLoad(s, i64, /*isAtomic=*/false)"), + ("i64.load16_u", "makeLoad(s, i64, /*isAtomic=*/false)"), + ("i64.load32_s", "makeLoad(s, i64, /*isAtomic=*/false)"), + ("i64.load32_u", "makeLoad(s, i64, /*isAtomic=*/false)"), + ("i32.store", "makeStore(s, i32, /*isAtomic=*/false)"), + ("i64.store", "makeStore(s, i64, /*isAtomic=*/false)"), + ("f32.store", "makeStore(s, f32, /*isAtomic=*/false)"), + ("f64.store", "makeStore(s, f64, /*isAtomic=*/false)"), + ("i32.store8", "makeStore(s, i32, /*isAtomic=*/false)"), + ("i32.store16", "makeStore(s, i32, /*isAtomic=*/false)"), + ("i64.store8", "makeStore(s, i64, /*isAtomic=*/false)"), + ("i64.store16", "makeStore(s, i64, /*isAtomic=*/false)"), + ("i64.store32", "makeStore(s, i64, /*isAtomic=*/false)"), + ("current_memory", "makeHost(s, HostOp::CurrentMemory)"), + ("grow_memory", "makeHost(s, HostOp::GrowMemory)"), + ("i32.const", "makeConst(s, i32)"), + ("i64.const", "makeConst(s, i64)"), + ("f32.const", "makeConst(s, f32)"), + ("f64.const", "makeConst(s, f64)"), + ("i32.eqz", "makeUnary(s, UnaryOp::EqZInt32)"), + ("i32.eq", "makeBinary(s, BinaryOp::EqInt32)"), + ("i32.ne", "makeBinary(s, BinaryOp::NeInt32)"), + ("i32.lt_s", "makeBinary(s, BinaryOp::LtSInt32)"), + ("i32.lt_u", "makeBinary(s, BinaryOp::LtUInt32)"), + ("i32.gt_s", "makeBinary(s, BinaryOp::GtSInt32)"), + ("i32.gt_u", "makeBinary(s, BinaryOp::GtUInt32)"), + ("i32.le_s", "makeBinary(s, BinaryOp::LeSInt32)"), + ("i32.le_u", "makeBinary(s, BinaryOp::LeUInt32)"), + ("i32.ge_s", "makeBinary(s, BinaryOp::GeSInt32)"), + ("i32.ge_u", "makeBinary(s, BinaryOp::GeUInt32)"), + ("i64.eqz", "makeUnary(s, UnaryOp::EqZInt64)"), + ("i64.eq", "makeBinary(s, BinaryOp::EqInt64)"), + ("i64.ne", "makeBinary(s, BinaryOp::NeInt64)"), + ("i64.lt_s", "makeBinary(s, BinaryOp::LtSInt64)"), + ("i64.lt_u", "makeBinary(s, BinaryOp::LtUInt64)"), + ("i64.gt_s", "makeBinary(s, BinaryOp::GtSInt64)"), + ("i64.gt_u", "makeBinary(s, BinaryOp::GtUInt64)"), + ("i64.le_s", "makeBinary(s, BinaryOp::LeSInt64)"), + ("i64.le_u", "makeBinary(s, BinaryOp::LeUInt64)"), + ("i64.ge_s", "makeBinary(s, BinaryOp::GeSInt64)"), + ("i64.ge_u", "makeBinary(s, BinaryOp::GeUInt64)"), + ("f32.eq", "makeBinary(s, BinaryOp::EqFloat32)"), + ("f32.ne", "makeBinary(s, BinaryOp::NeFloat32)"), + ("f32.lt", "makeBinary(s, BinaryOp::LtFloat32)"), + ("f32.gt", "makeBinary(s, BinaryOp::GtFloat32)"), + ("f32.le", "makeBinary(s, BinaryOp::LeFloat32)"), + ("f32.ge", "makeBinary(s, BinaryOp::GeFloat32)"), + ("f64.eq", "makeBinary(s, BinaryOp::EqFloat64)"), + ("f64.ne", "makeBinary(s, BinaryOp::NeFloat64)"), + ("f64.lt", "makeBinary(s, BinaryOp::LtFloat64)"), + ("f64.gt", "makeBinary(s, BinaryOp::GtFloat64)"), + ("f64.le", "makeBinary(s, BinaryOp::LeFloat64)"), + ("f64.ge", "makeBinary(s, BinaryOp::GeFloat64)"), + ("i32.clz", "makeUnary(s, UnaryOp::ClzInt32)"), + ("i32.ctz", "makeUnary(s, UnaryOp::CtzInt32)"), + ("i32.popcnt", "makeUnary(s, UnaryOp::PopcntInt32)"), + ("i32.add", "makeBinary(s, BinaryOp::AddInt32)"), + ("i32.sub", "makeBinary(s, BinaryOp::SubInt32)"), + ("i32.mul", "makeBinary(s, BinaryOp::MulInt32)"), + ("i32.div_s", "makeBinary(s, BinaryOp::DivSInt32)"), + ("i32.div_u", "makeBinary(s, BinaryOp::DivUInt32)"), + ("i32.rem_s", "makeBinary(s, BinaryOp::RemSInt32)"), + ("i32.rem_u", "makeBinary(s, BinaryOp::RemUInt32)"), + ("i32.and", "makeBinary(s, BinaryOp::AndInt32)"), + ("i32.or", "makeBinary(s, BinaryOp::OrInt32)"), + ("i32.xor", "makeBinary(s, BinaryOp::XorInt32)"), + ("i32.shl", "makeBinary(s, BinaryOp::ShlInt32)"), + ("i32.shr_s", "makeBinary(s, BinaryOp::ShrSInt32)"), + ("i32.shr_u", "makeBinary(s, BinaryOp::ShrUInt32)"), + ("i32.rotl", "makeBinary(s, BinaryOp::RotLInt32)"), + ("i32.rotr", "makeBinary(s, BinaryOp::RotRInt32)"), + ("i64.clz", "makeUnary(s, UnaryOp::ClzInt64)"), + ("i64.ctz", "makeUnary(s, UnaryOp::CtzInt64)"), + ("i64.popcnt", "makeUnary(s, UnaryOp::PopcntInt64)"), + ("i64.add", "makeBinary(s, BinaryOp::AddInt64)"), + ("i64.sub", "makeBinary(s, BinaryOp::SubInt64)"), + ("i64.mul", "makeBinary(s, BinaryOp::MulInt64)"), + ("i64.div_s", "makeBinary(s, BinaryOp::DivSInt64)"), + ("i64.div_u", "makeBinary(s, BinaryOp::DivUInt64)"), + ("i64.rem_s", "makeBinary(s, BinaryOp::RemSInt64)"), + ("i64.rem_u", "makeBinary(s, BinaryOp::RemUInt64)"), + ("i64.and", "makeBinary(s, BinaryOp::AndInt64)"), + ("i64.or", "makeBinary(s, BinaryOp::OrInt64)"), + ("i64.xor", "makeBinary(s, BinaryOp::XorInt64)"), + ("i64.shl", "makeBinary(s, BinaryOp::ShlInt64)"), + ("i64.shr_s", "makeBinary(s, BinaryOp::ShrSInt64)"), + ("i64.shr_u", "makeBinary(s, BinaryOp::ShrUInt64)"), + ("i64.rotl", "makeBinary(s, BinaryOp::RotLInt64)"), + ("i64.rotr", "makeBinary(s, BinaryOp::RotRInt64)"), + ("f32.abs", "makeUnary(s, UnaryOp::AbsFloat32)"), + ("f32.neg", "makeUnary(s, UnaryOp::NegFloat32)"), + ("f32.ceil", "makeUnary(s, UnaryOp::CeilFloat32)"), + ("f32.floor", "makeUnary(s, UnaryOp::FloorFloat32)"), + ("f32.trunc", "makeUnary(s, UnaryOp::TruncFloat32)"), + ("f32.nearest", "makeUnary(s, UnaryOp::NearestFloat32)"), + ("f32.sqrt", "makeUnary(s, UnaryOp::SqrtFloat32)"), + ("f32.add", "makeBinary(s, BinaryOp::AddFloat32)"), + ("f32.sub", "makeBinary(s, BinaryOp::SubFloat32)"), + ("f32.mul", "makeBinary(s, BinaryOp::MulFloat32)"), + ("f32.div", "makeBinary(s, BinaryOp::DivFloat32)"), + ("f32.min", "makeBinary(s, BinaryOp::MinFloat32)"), + ("f32.max", "makeBinary(s, BinaryOp::MaxFloat32)"), + ("f32.copysign", "makeBinary(s, BinaryOp::CopySignFloat32)"), + ("f64.abs", "makeUnary(s, UnaryOp::AbsFloat64)"), + ("f64.neg", "makeUnary(s, UnaryOp::NegFloat64)"), + ("f64.ceil", "makeUnary(s, UnaryOp::CeilFloat64)"), + ("f64.floor", "makeUnary(s, UnaryOp::FloorFloat64)"), + ("f64.trunc", "makeUnary(s, UnaryOp::TruncFloat64)"), + ("f64.nearest", "makeUnary(s, UnaryOp::NearestFloat64)"), + ("f64.sqrt", "makeUnary(s, UnaryOp::SqrtFloat64)"), + ("f64.add", "makeBinary(s, BinaryOp::AddFloat64)"), + ("f64.sub", "makeBinary(s, BinaryOp::SubFloat64)"), + ("f64.mul", "makeBinary(s, BinaryOp::MulFloat64)"), + ("f64.div", "makeBinary(s, BinaryOp::DivFloat64)"), + ("f64.min", "makeBinary(s, BinaryOp::MinFloat64)"), + ("f64.max", "makeBinary(s, BinaryOp::MaxFloat64)"), + ("f64.copysign", "makeBinary(s, BinaryOp::CopySignFloat64)"), + ("i32.wrap/i64", "makeUnary(s, UnaryOp::WrapInt64)"), + ("i32.trunc_s/f32", "makeUnary(s, UnaryOp::TruncSFloat32ToInt32)"), + ("i32.trunc_u/f32", "makeUnary(s, UnaryOp::TruncUFloat32ToInt32)"), + ("i32.trunc_s/f64", "makeUnary(s, UnaryOp::TruncSFloat64ToInt32)"), + ("i32.trunc_u/f64", "makeUnary(s, UnaryOp::TruncUFloat64ToInt32)"), + ("i64.extend_s/i32", "makeUnary(s, UnaryOp::ExtendSInt32)"), + ("i64.extend_u/i32", "makeUnary(s, UnaryOp::ExtendUInt32)"), + ("i64.trunc_s/f32", "makeUnary(s, UnaryOp::TruncSFloat32ToInt64)"), + ("i64.trunc_u/f32", "makeUnary(s, UnaryOp::TruncUFloat32ToInt64)"), + ("i64.trunc_s/f64", "makeUnary(s, UnaryOp::TruncSFloat64ToInt64)"), + ("i64.trunc_u/f64", "makeUnary(s, UnaryOp::TruncUFloat64ToInt64)"), + ("f32.convert_s/i32", "makeUnary(s, UnaryOp::ConvertSInt32ToFloat32)"), + ("f32.convert_u/i32", "makeUnary(s, UnaryOp::ConvertUInt32ToFloat32)"), + ("f32.convert_s/i64", "makeUnary(s, UnaryOp::ConvertSInt64ToFloat32)"), + ("f32.convert_u/i64", "makeUnary(s, UnaryOp::ConvertUInt64ToFloat32)"), + ("f32.demote/f64", "makeUnary(s, UnaryOp::DemoteFloat64)"), + ("f64.convert_s/i32", "makeUnary(s, UnaryOp::ConvertSInt32ToFloat64)"), + ("f64.convert_u/i32", "makeUnary(s, UnaryOp::ConvertUInt32ToFloat64)"), + ("f64.convert_s/i64", "makeUnary(s, UnaryOp::ConvertSInt64ToFloat64)"), + ("f64.convert_u/i64", "makeUnary(s, UnaryOp::ConvertUInt64ToFloat64)"), + ("f64.promote/f32", "makeUnary(s, UnaryOp::PromoteFloat32)"), + ("i32.reinterpret/f32", "makeUnary(s, UnaryOp::ReinterpretFloat32)"), + ("i64.reinterpret/f64", "makeUnary(s, UnaryOp::ReinterpretFloat64)"), + ("f32.reinterpret/i32", "makeUnary(s, UnaryOp::ReinterpretInt32)"), + ("f64.reinterpret/i64", "makeUnary(s, UnaryOp::ReinterpretInt64)"), + ("i32.extend8_s", "makeUnary(s, UnaryOp::ExtendS8Int32)"), + ("i32.extend16_s", "makeUnary(s, UnaryOp::ExtendS16Int32)"), + ("i64.extend8_s", "makeUnary(s, UnaryOp::ExtendS8Int64)"), + ("i64.extend16_s", "makeUnary(s, UnaryOp::ExtendS16Int64)"), + ("i64.extend32_s", "makeUnary(s, UnaryOp::ExtendS32Int64)"), + # atomic instructions + ("wake", "makeAtomicWake(s)"), + ("i32.wait", "makeAtomicWait(s, i32)"), + ("i64.wait", "makeAtomicWait(s, i64)"), + ("i32.atomic.load8_u", "makeLoad(s, i32, /*isAtomic=*/true)"), + ("i32.atomic.load16_u", "makeLoad(s, i32, /*isAtomic=*/true)"), + ("i32.atomic.load", "makeLoad(s, i32, /*isAtomic=*/true)"), + ("i64.atomic.load8_u", "makeLoad(s, i64, /*isAtomic=*/true)"), + ("i64.atomic.load16_u", "makeLoad(s, i64, /*isAtomic=*/true)"), + ("i64.atomic.load32_u", "makeLoad(s, i64, /*isAtomic=*/true)"), + ("i64.atomic.load", "makeLoad(s, i64, /*isAtomic=*/true)"), + ("i32.atomic.store8", "makeStore(s, i32, /*isAtomic=*/true)"), + ("i32.atomic.store16", "makeStore(s, i32, /*isAtomic=*/true)"), + ("i32.atomic.store", "makeStore(s, i32, /*isAtomic=*/true)"), + ("i64.atomic.store8", "makeStore(s, i64, /*isAtomic=*/true)"), + ("i64.atomic.store16", "makeStore(s, i64, /*isAtomic=*/true)"), + ("i64.atomic.store32", "makeStore(s, i64, /*isAtomic=*/true)"), + ("i64.atomic.store", "makeStore(s, i64, /*isAtomic=*/true)"), + ("i32.atomic.rmw8_u.add", "makeAtomicRMWOrCmpxchg(s, i32)"), + ("i32.atomic.rmw16_u.add", "makeAtomicRMWOrCmpxchg(s, i32)"), + ("i32.atomic.rmw.add", "makeAtomicRMWOrCmpxchg(s, i32)"), + ("i64.atomic.rmw8_u.add", "makeAtomicRMWOrCmpxchg(s, i64)"), + ("i64.atomic.rmw16_u.add", "makeAtomicRMWOrCmpxchg(s, i64)"), + ("i64.atomic.rmw32_u.add", "makeAtomicRMWOrCmpxchg(s, i64)"), + ("i64.atomic.rmw.add", "makeAtomicRMWOrCmpxchg(s, i64)"), + ("i32.atomic.rmw8_u.sub", "makeAtomicRMWOrCmpxchg(s, i32)"), + ("i32.atomic.rmw16_u.sub", "makeAtomicRMWOrCmpxchg(s, i32)"), + ("i32.atomic.rmw.sub", "makeAtomicRMWOrCmpxchg(s, i32)"), + ("i64.atomic.rmw8_u.sub", "makeAtomicRMWOrCmpxchg(s, i64)"), + ("i64.atomic.rmw16_u.sub", "makeAtomicRMWOrCmpxchg(s, i64)"), + ("i64.atomic.rmw32_u.sub", "makeAtomicRMWOrCmpxchg(s, i64)"), + ("i64.atomic.rmw.sub", "makeAtomicRMWOrCmpxchg(s, i64)"), + ("i32.atomic.rmw8_u.and", "makeAtomicRMWOrCmpxchg(s, i32)"), + ("i32.atomic.rmw16_u.and", "makeAtomicRMWOrCmpxchg(s, i32)"), + ("i32.atomic.rmw.and", "makeAtomicRMWOrCmpxchg(s, i32)"), + ("i64.atomic.rmw8_u.and", "makeAtomicRMWOrCmpxchg(s, i64)"), + ("i64.atomic.rmw16_u.and", "makeAtomicRMWOrCmpxchg(s, i64)"), + ("i64.atomic.rmw32_u.and", "makeAtomicRMWOrCmpxchg(s, i64)"), + ("i64.atomic.rmw.and", "makeAtomicRMWOrCmpxchg(s, i64)"), + ("i32.atomic.rmw8_u.or", "makeAtomicRMWOrCmpxchg(s, i32)"), + ("i32.atomic.rmw16_u.or", "makeAtomicRMWOrCmpxchg(s, i32)"), + ("i32.atomic.rmw.or", "makeAtomicRMWOrCmpxchg(s, i32)"), + ("i64.atomic.rmw8_u.or", "makeAtomicRMWOrCmpxchg(s, i64)"), + ("i64.atomic.rmw16_u.or", "makeAtomicRMWOrCmpxchg(s, i64)"), + ("i64.atomic.rmw32_u.or", "makeAtomicRMWOrCmpxchg(s, i64)"), + ("i64.atomic.rmw.or", "makeAtomicRMWOrCmpxchg(s, i64)"), + ("i32.atomic.rmw8_u.xor", "makeAtomicRMWOrCmpxchg(s, i32)"), + ("i32.atomic.rmw16_u.xor", "makeAtomicRMWOrCmpxchg(s, i32)"), + ("i32.atomic.rmw.xor", "makeAtomicRMWOrCmpxchg(s, i32)"), + ("i64.atomic.rmw8_u.xor", "makeAtomicRMWOrCmpxchg(s, i64)"), + ("i64.atomic.rmw16_u.xor", "makeAtomicRMWOrCmpxchg(s, i64)"), + ("i64.atomic.rmw32_u.xor", "makeAtomicRMWOrCmpxchg(s, i64)"), + ("i64.atomic.rmw.xor", "makeAtomicRMWOrCmpxchg(s, i64)"), + ("i32.atomic.rmw8_u.xchg", "makeAtomicRMWOrCmpxchg(s, i32)"), + ("i32.atomic.rmw16_u.xchg", "makeAtomicRMWOrCmpxchg(s, i32)"), + ("i32.atomic.rmw.xchg", "makeAtomicRMWOrCmpxchg(s, i32)"), + ("i64.atomic.rmw8_u.xchg", "makeAtomicRMWOrCmpxchg(s, i64)"), + ("i64.atomic.rmw16_u.xchg", "makeAtomicRMWOrCmpxchg(s, i64)"), + ("i64.atomic.rmw32_u.xchg", "makeAtomicRMWOrCmpxchg(s, i64)"), + ("i64.atomic.rmw.xchg", "makeAtomicRMWOrCmpxchg(s, i64)"), + ("i32.atomic.rmw8_u.cmpxchg", "makeAtomicRMWOrCmpxchg(s, i32)"), + ("i32.atomic.rmw16_u.cmpxchg", "makeAtomicRMWOrCmpxchg(s, i32)"), + ("i32.atomic.rmw.cmpxchg", "makeAtomicRMWOrCmpxchg(s, i32)"), + ("i64.atomic.rmw8_u.cmpxchg", "makeAtomicRMWOrCmpxchg(s, i64)"), + ("i64.atomic.rmw16_u.cmpxchg", "makeAtomicRMWOrCmpxchg(s, i64)"), + ("i64.atomic.rmw32_u.cmpxchg", "makeAtomicRMWOrCmpxchg(s, i64)"), + ("i64.atomic.rmw.cmpxchg", "makeAtomicRMWOrCmpxchg(s, i64)") +] + + +class CodePrinter: + indents = 0 + + def __enter__(self): + CodePrinter.indents += 1 + + def __exit__(self, *args): + CodePrinter.indents -= 1 + + def indent(self): + # call in a 'with' statement + return self + + def print_line(self, line): + print(" " * CodePrinter.indents + line) + + +class Node: + def __init__(self, expr=None, children=None, inst=None): + # the expression to return if this is the string has ended + self.expr = expr + # map unique strings to children nodes + self.children = children if children else {} + # full instruction leading to this node + self.inst = inst + + def _common_prefix(a, b): + """Return the common prefix of two strings.""" + prefix = [] + while a and b and a[0] == b[0]: + prefix.append(a[0]) + a = a[1:] + b = b[1:] + return "".join(prefix) + + def do_insert(self, full_inst, inst, expr): + if inst is "": + assert self.expr is None, "Repeated instruction" + self.expr = expr + self.inst = full_inst + return + # find key with shared prefix + prefix, key = "", None + for k in self.children: + prefix = Node._common_prefix(inst, k) + if prefix is not "": + key = k + break + if key is None: + # unique prefix, insert and stop + self.children[inst] = Node(expr, inst=full_inst) + return + key_remainder = key[len(prefix):] + if key_remainder is not "": + # split key and move everything after the prefix to a new node + child = self.children.pop(key) + self.children[prefix] = Node(children={key_remainder: child}) + # update key for recursive insert + key = prefix + # chop off prefix and recurse + self.children[key].do_insert(full_inst, inst[len(key):], expr) + + def insert(self, inst, expr): + self.do_insert(inst, inst, expr) + + def prune(self): + """Deduplicate siblings that would lead to the same expression.""" + if not self.children: + return + for child in self.children.values(): + child.prune() + exprs = set(self.expr) if self.expr else set() + for child in self.children.values(): + # only prune if all children are terminal + if child.children: + return + exprs.add(child.expr) + if len(exprs) != 1: + # children have different expressions, can't prune + return + # do prune + self.expr = exprs.pop() + self.inst = " | ".join(sorted(c.inst for c in self.children.values())) + self.children = {} + + +def instruction_parser(): + """Build a trie out of all the instructions, then emit it as C++ code.""" + trie = Node() + inst_length = 0 + for inst, expr in instructions: + inst_length = max(inst_length, len(inst)) + trie.insert(inst, expr) + trie.prune() + + printer = CodePrinter() + + printer.print_line("char op[{}] = {{'\\0'}};".format(inst_length + 1)) + printer.print_line("strncpy(op, s[0]->c_str(), {});".format(inst_length)) + + def emit(node, idx=0): + assert node.children + printer.print_line("switch (op[{}]) {{".format(idx)) + with printer.indent(): + if node.expr: + printer.print_line("case '\\0': return {}; // {}".format(node.expr, node.inst)) + children = sorted(node.children.items(), key=lambda pair: pair[0]) + for prefix, child in children: + if child.children: + printer.print_line("case '{}': {{".format(prefix[0])) + with printer.indent(): + emit(child, idx + len(prefix)) + printer.print_line("}") + else: + assert child.expr + printer.print_line("case '{}': return {}; // {}" + .format(prefix[0], child.expr, child.inst)) + printer.print_line("default: goto parse_error;") + printer.print_line("}") + + emit(trie) + printer.print_line("parse_error:") + with printer.indent(): + printer.print_line("throw ParseException(std::string(op));") + + +def print_header(): + print("// DO NOT EDIT! This file generated by scripts/gen-s-parser.py\n") + + +def generate_with_guard(generator, guard): + print("#ifdef {}".format(guard)) + print("#undef {}".format(guard)) + generator() + print("#endif // {}".format(guard)) + + +def main(): + print_header() + generate_with_guard(instruction_parser, "INSTRUCTION_PARSER") + + +if __name__ == "__main__": + main() diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc new file mode 100644 index 000000000..5b50d6091 --- /dev/null +++ b/src/gen-s-parser.inc @@ -0,0 +1,688 @@ +// DO NOT EDIT! This file generated by scripts/gen-s-parser.py + +#ifdef INSTRUCTION_PARSER +#undef INSTRUCTION_PARSER +char op[27] = {'\0'}; +strncpy(op, s[0]->c_str(), 26); +switch (op[0]) { + case 'b': { + switch (op[1]) { + case 'l': return makeBlock(s); // block + case 'r': { + switch (op[2]) { + case '\0': return makeBreak(s); // br + case '_': { + switch (op[3]) { + case 'i': return makeBreak(s); // br_if + case 't': return makeBreakTable(s); // br_table + default: goto parse_error; + } + } + default: goto parse_error; + } + } + default: goto parse_error; + } + } + case 'c': { + switch (op[1]) { + case 'a': { + switch (op[4]) { + case '\0': return makeCall(s); // call + case '_': return makeCallIndirect(s); // call_indirect + default: goto parse_error; + } + } + case 'u': return makeHost(s, HostOp::CurrentMemory); // current_memory + default: goto parse_error; + } + } + case 'd': return makeDrop(s); // drop + case 'e': return makeThenOrElse(s); // else + case 'f': { + switch (op[1]) { + case '3': { + switch (op[4]) { + case 'a': { + switch (op[5]) { + case 'b': return makeUnary(s, UnaryOp::AbsFloat32); // f32.abs + case 'd': return makeBinary(s, BinaryOp::AddFloat32); // f32.add + default: goto parse_error; + } + } + case 'c': { + switch (op[5]) { + case 'e': return makeUnary(s, UnaryOp::CeilFloat32); // f32.ceil + case 'o': { + switch (op[6]) { + case 'n': { + switch (op[7]) { + case 's': return makeConst(s, f32); // f32.const + case 'v': { + switch (op[12]) { + case 's': { + switch (op[15]) { + case '3': return makeUnary(s, UnaryOp::ConvertSInt32ToFloat32); // f32.convert_s/i32 + case '6': return makeUnary(s, UnaryOp::ConvertSInt64ToFloat32); // f32.convert_s/i64 + default: goto parse_error; + } + } + case 'u': { + switch (op[15]) { + case '3': return makeUnary(s, UnaryOp::ConvertUInt32ToFloat32); // f32.convert_u/i32 + case '6': return makeUnary(s, UnaryOp::ConvertUInt64ToFloat32); // f32.convert_u/i64 + default: goto parse_error; + } + } + default: goto parse_error; + } + } + default: goto parse_error; + } + } + case 'p': return makeBinary(s, BinaryOp::CopySignFloat32); // f32.copysign + default: goto parse_error; + } + } + default: goto parse_error; + } + } + case 'd': { + switch (op[5]) { + case 'e': return makeUnary(s, UnaryOp::DemoteFloat64); // f32.demote/f64 + case 'i': return makeBinary(s, BinaryOp::DivFloat32); // f32.div + default: goto parse_error; + } + } + case 'e': return makeBinary(s, BinaryOp::EqFloat32); // f32.eq + case 'f': return makeUnary(s, UnaryOp::FloorFloat32); // f32.floor + case 'g': { + switch (op[5]) { + case 'e': return makeBinary(s, BinaryOp::GeFloat32); // f32.ge + case 't': return makeBinary(s, BinaryOp::GtFloat32); // f32.gt + default: goto parse_error; + } + } + case 'l': { + switch (op[5]) { + case 'e': return makeBinary(s, BinaryOp::LeFloat32); // f32.le + case 'o': return makeLoad(s, f32, /*isAtomic=*/false); // f32.load + case 't': return makeBinary(s, BinaryOp::LtFloat32); // f32.lt + default: goto parse_error; + } + } + case 'm': { + switch (op[5]) { + case 'a': return makeBinary(s, BinaryOp::MaxFloat32); // f32.max + case 'i': return makeBinary(s, BinaryOp::MinFloat32); // f32.min + case 'u': return makeBinary(s, BinaryOp::MulFloat32); // f32.mul + default: goto parse_error; + } + } + case 'n': { + switch (op[6]) { + case '\0': return makeBinary(s, BinaryOp::NeFloat32); // f32.ne + case 'a': return makeUnary(s, UnaryOp::NearestFloat32); // f32.nearest + case 'g': return makeUnary(s, UnaryOp::NegFloat32); // f32.neg + default: goto parse_error; + } + } + case 'r': return makeUnary(s, UnaryOp::ReinterpretInt32); // f32.reinterpret/i32 + case 's': { + switch (op[5]) { + case 'q': return makeUnary(s, UnaryOp::SqrtFloat32); // f32.sqrt + case 't': return makeStore(s, f32, /*isAtomic=*/false); // f32.store + case 'u': return makeBinary(s, BinaryOp::SubFloat32); // f32.sub + default: goto parse_error; + } + } + case 't': return makeUnary(s, UnaryOp::TruncFloat32); // f32.trunc + default: goto parse_error; + } + } + case '6': { + switch (op[4]) { + case 'a': { + switch (op[5]) { + case 'b': return makeUnary(s, UnaryOp::AbsFloat64); // f64.abs + case 'd': return makeBinary(s, BinaryOp::AddFloat64); // f64.add + default: goto parse_error; + } + } + case 'c': { + switch (op[5]) { + case 'e': return makeUnary(s, UnaryOp::CeilFloat64); // f64.ceil + case 'o': { + switch (op[6]) { + case 'n': { + switch (op[7]) { + case 's': return makeConst(s, f64); // f64.const + case 'v': { + switch (op[12]) { + case 's': { + switch (op[15]) { + case '3': return makeUnary(s, UnaryOp::ConvertSInt32ToFloat64); // f64.convert_s/i32 + case '6': return makeUnary(s, UnaryOp::ConvertSInt64ToFloat64); // f64.convert_s/i64 + default: goto parse_error; + } + } + case 'u': { + switch (op[15]) { + case '3': return makeUnary(s, UnaryOp::ConvertUInt32ToFloat64); // f64.convert_u/i32 + case '6': return makeUnary(s, UnaryOp::ConvertUInt64ToFloat64); // f64.convert_u/i64 + default: goto parse_error; + } + } + default: goto parse_error; + } + } + default: goto parse_error; + } + } + case 'p': return makeBinary(s, BinaryOp::CopySignFloat64); // f64.copysign + default: goto parse_error; + } + } + default: goto parse_error; + } + } + case 'd': return makeBinary(s, BinaryOp::DivFloat64); // f64.div + case 'e': return makeBinary(s, BinaryOp::EqFloat64); // f64.eq + case 'f': return makeUnary(s, UnaryOp::FloorFloat64); // f64.floor + case 'g': { + switch (op[5]) { + case 'e': return makeBinary(s, BinaryOp::GeFloat64); // f64.ge + case 't': return makeBinary(s, BinaryOp::GtFloat64); // f64.gt + default: goto parse_error; + } + } + case 'l': { + switch (op[5]) { + case 'e': return makeBinary(s, BinaryOp::LeFloat64); // f64.le + case 'o': return makeLoad(s, f64, /*isAtomic=*/false); // f64.load + case 't': return makeBinary(s, BinaryOp::LtFloat64); // f64.lt + default: goto parse_error; + } + } + case 'm': { + switch (op[5]) { + case 'a': return makeBinary(s, BinaryOp::MaxFloat64); // f64.max + case 'i': return makeBinary(s, BinaryOp::MinFloat64); // f64.min + case 'u': return makeBinary(s, BinaryOp::MulFloat64); // f64.mul + default: goto parse_error; + } + } + case 'n': { + switch (op[6]) { + case '\0': return makeBinary(s, BinaryOp::NeFloat64); // f64.ne + case 'a': return makeUnary(s, UnaryOp::NearestFloat64); // f64.nearest + case 'g': return makeUnary(s, UnaryOp::NegFloat64); // f64.neg + default: goto parse_error; + } + } + case 'p': return makeUnary(s, UnaryOp::PromoteFloat32); // f64.promote/f32 + case 'r': return makeUnary(s, UnaryOp::ReinterpretInt64); // f64.reinterpret/i64 + case 's': { + switch (op[5]) { + case 'q': return makeUnary(s, UnaryOp::SqrtFloat64); // f64.sqrt + case 't': return makeStore(s, f64, /*isAtomic=*/false); // f64.store + case 'u': return makeBinary(s, BinaryOp::SubFloat64); // f64.sub + default: goto parse_error; + } + } + case 't': return makeUnary(s, UnaryOp::TruncFloat64); // f64.trunc + default: goto parse_error; + } + } + default: goto parse_error; + } + } + case 'g': { + switch (op[1]) { + case 'e': { + switch (op[4]) { + case 'g': return makeGetGlobal(s); // get_global + case 'l': return makeGetLocal(s); // get_local + default: goto parse_error; + } + } + case 'r': return makeHost(s, HostOp::GrowMemory); // grow_memory + default: goto parse_error; + } + } + case 'i': { + switch (op[1]) { + case '3': { + switch (op[4]) { + case 'a': { + switch (op[5]) { + case 'd': return makeBinary(s, BinaryOp::AddInt32); // i32.add + case 'n': return makeBinary(s, BinaryOp::AndInt32); // i32.and + case 't': { + switch (op[11]) { + case 'l': { + switch (op[15]) { + case '\0': return makeLoad(s, i32, /*isAtomic=*/true); // i32.atomic.load + case '1': return makeLoad(s, i32, /*isAtomic=*/true); // i32.atomic.load16_u + case '8': return makeLoad(s, i32, /*isAtomic=*/true); // i32.atomic.load8_u + default: goto parse_error; + } + } + case 'r': return makeAtomicRMWOrCmpxchg(s, i32); // i32.atomic.rmw.add | i32.atomic.rmw.and | i32.atomic.rmw.cmpxchg | i32.atomic.rmw.or | i32.atomic.rmw.sub | i32.atomic.rmw.xchg | i32.atomic.rmw.xor | i32.atomic.rmw16_u.add | i32.atomic.rmw16_u.and | i32.atomic.rmw16_u.cmpxchg | i32.atomic.rmw16_u.or | i32.atomic.rmw16_u.sub | i32.atomic.rmw16_u.xchg | i32.atomic.rmw16_u.xor | i32.atomic.rmw8_u.add | i32.atomic.rmw8_u.and | i32.atomic.rmw8_u.cmpxchg | i32.atomic.rmw8_u.or | i32.atomic.rmw8_u.sub | i32.atomic.rmw8_u.xchg | i32.atomic.rmw8_u.xor + case 's': { + switch (op[16]) { + case '\0': return makeStore(s, i32, /*isAtomic=*/true); // i32.atomic.store + case '1': return makeStore(s, i32, /*isAtomic=*/true); // i32.atomic.store16 + case '8': return makeStore(s, i32, /*isAtomic=*/true); // i32.atomic.store8 + default: goto parse_error; + } + } + default: goto parse_error; + } + } + default: goto parse_error; + } + } + case 'c': { + switch (op[5]) { + case 'l': return makeUnary(s, UnaryOp::ClzInt32); // i32.clz + case 'o': return makeConst(s, i32); // i32.const + case 't': return makeUnary(s, UnaryOp::CtzInt32); // i32.ctz + default: goto parse_error; + } + } + case 'd': { + switch (op[8]) { + case 's': return makeBinary(s, BinaryOp::DivSInt32); // i32.div_s + case 'u': return makeBinary(s, BinaryOp::DivUInt32); // i32.div_u + default: goto parse_error; + } + } + case 'e': { + switch (op[5]) { + case 'q': { + switch (op[6]) { + case '\0': return makeBinary(s, BinaryOp::EqInt32); // i32.eq + case 'z': return makeUnary(s, UnaryOp::EqZInt32); // i32.eqz + default: goto parse_error; + } + } + case 'x': { + switch (op[10]) { + case '1': return makeUnary(s, UnaryOp::ExtendS16Int32); // i32.extend16_s + case '8': return makeUnary(s, UnaryOp::ExtendS8Int32); // i32.extend8_s + default: goto parse_error; + } + } + default: goto parse_error; + } + } + case 'g': { + switch (op[5]) { + case 'e': { + switch (op[7]) { + case 's': return makeBinary(s, BinaryOp::GeSInt32); // i32.ge_s + case 'u': return makeBinary(s, BinaryOp::GeUInt32); // i32.ge_u + default: goto parse_error; + } + } + case 't': { + switch (op[7]) { + case 's': return makeBinary(s, BinaryOp::GtSInt32); // i32.gt_s + case 'u': return makeBinary(s, BinaryOp::GtUInt32); // i32.gt_u + default: goto parse_error; + } + } + default: goto parse_error; + } + } + case 'l': { + switch (op[5]) { + case 'e': { + switch (op[7]) { + case 's': return makeBinary(s, BinaryOp::LeSInt32); // i32.le_s + case 'u': return makeBinary(s, BinaryOp::LeUInt32); // i32.le_u + default: goto parse_error; + } + } + case 'o': { + switch (op[8]) { + case '\0': return makeLoad(s, i32, /*isAtomic=*/false); // i32.load + case '1': return makeLoad(s, i32, /*isAtomic=*/false); // i32.load16_s | i32.load16_u + case '8': return makeLoad(s, i32, /*isAtomic=*/false); // i32.load8_s | i32.load8_u + default: goto parse_error; + } + } + case 't': { + switch (op[7]) { + case 's': return makeBinary(s, BinaryOp::LtSInt32); // i32.lt_s + case 'u': return makeBinary(s, BinaryOp::LtUInt32); // i32.lt_u + default: goto parse_error; + } + } + default: goto parse_error; + } + } + case 'm': return makeBinary(s, BinaryOp::MulInt32); // i32.mul + case 'n': return makeBinary(s, BinaryOp::NeInt32); // i32.ne + case 'o': return makeBinary(s, BinaryOp::OrInt32); // i32.or + case 'p': return makeUnary(s, UnaryOp::PopcntInt32); // i32.popcnt + case 'r': { + switch (op[5]) { + case 'e': { + switch (op[6]) { + case 'i': return makeUnary(s, UnaryOp::ReinterpretFloat32); // i32.reinterpret/f32 + case 'm': { + switch (op[8]) { + case 's': return makeBinary(s, BinaryOp::RemSInt32); // i32.rem_s + case 'u': return makeBinary(s, BinaryOp::RemUInt32); // i32.rem_u + default: goto parse_error; + } + } + default: goto parse_error; + } + } + case 'o': { + switch (op[7]) { + case 'l': return makeBinary(s, BinaryOp::RotLInt32); // i32.rotl + case 'r': return makeBinary(s, BinaryOp::RotRInt32); // i32.rotr + default: goto parse_error; + } + } + default: goto parse_error; + } + } + case 's': { + switch (op[5]) { + case 'h': { + switch (op[6]) { + case 'l': return makeBinary(s, BinaryOp::ShlInt32); // i32.shl + case 'r': { + switch (op[8]) { + case 's': return makeBinary(s, BinaryOp::ShrSInt32); // i32.shr_s + case 'u': return makeBinary(s, BinaryOp::ShrUInt32); // i32.shr_u + default: goto parse_error; + } + } + default: goto parse_error; + } + } + case 't': { + switch (op[9]) { + case '\0': return makeStore(s, i32, /*isAtomic=*/false); // i32.store + case '1': return makeStore(s, i32, /*isAtomic=*/false); // i32.store16 + case '8': return makeStore(s, i32, /*isAtomic=*/false); // i32.store8 + default: goto parse_error; + } + } + case 'u': return makeBinary(s, BinaryOp::SubInt32); // i32.sub + default: goto parse_error; + } + } + case 't': { + switch (op[10]) { + case 's': { + switch (op[13]) { + case '3': return makeUnary(s, UnaryOp::TruncSFloat32ToInt32); // i32.trunc_s/f32 + case '6': return makeUnary(s, UnaryOp::TruncSFloat64ToInt32); // i32.trunc_s/f64 + default: goto parse_error; + } + } + case 'u': { + switch (op[13]) { + case '3': return makeUnary(s, UnaryOp::TruncUFloat32ToInt32); // i32.trunc_u/f32 + case '6': return makeUnary(s, UnaryOp::TruncUFloat64ToInt32); // i32.trunc_u/f64 + default: goto parse_error; + } + } + default: goto parse_error; + } + } + case 'w': { + switch (op[5]) { + case 'a': return makeAtomicWait(s, i32); // i32.wait + case 'r': return makeUnary(s, UnaryOp::WrapInt64); // i32.wrap/i64 + default: goto parse_error; + } + } + case 'x': return makeBinary(s, BinaryOp::XorInt32); // i32.xor + default: goto parse_error; + } + } + case '6': { + switch (op[4]) { + case 'a': { + switch (op[5]) { + case 'd': return makeBinary(s, BinaryOp::AddInt64); // i64.add + case 'n': return makeBinary(s, BinaryOp::AndInt64); // i64.and + case 't': { + switch (op[11]) { + case 'l': { + switch (op[15]) { + case '\0': return makeLoad(s, i64, /*isAtomic=*/true); // i64.atomic.load + case '1': return makeLoad(s, i64, /*isAtomic=*/true); // i64.atomic.load16_u + case '3': return makeLoad(s, i64, /*isAtomic=*/true); // i64.atomic.load32_u + case '8': return makeLoad(s, i64, /*isAtomic=*/true); // i64.atomic.load8_u + default: goto parse_error; + } + } + case 'r': return makeAtomicRMWOrCmpxchg(s, i64); // i64.atomic.rmw.add | i64.atomic.rmw.and | i64.atomic.rmw.cmpxchg | i64.atomic.rmw.or | i64.atomic.rmw.sub | i64.atomic.rmw.xchg | i64.atomic.rmw.xor | i64.atomic.rmw16_u.add | i64.atomic.rmw16_u.and | i64.atomic.rmw16_u.cmpxchg | i64.atomic.rmw16_u.or | i64.atomic.rmw16_u.sub | i64.atomic.rmw16_u.xchg | i64.atomic.rmw16_u.xor | i64.atomic.rmw32_u.add | i64.atomic.rmw32_u.and | i64.atomic.rmw32_u.cmpxchg | i64.atomic.rmw32_u.or | i64.atomic.rmw32_u.sub | i64.atomic.rmw32_u.xchg | i64.atomic.rmw32_u.xor | i64.atomic.rmw8_u.add | i64.atomic.rmw8_u.and | i64.atomic.rmw8_u.cmpxchg | i64.atomic.rmw8_u.or | i64.atomic.rmw8_u.sub | i64.atomic.rmw8_u.xchg | i64.atomic.rmw8_u.xor + case 's': { + switch (op[16]) { + case '\0': return makeStore(s, i64, /*isAtomic=*/true); // i64.atomic.store + case '1': return makeStore(s, i64, /*isAtomic=*/true); // i64.atomic.store16 + case '3': return makeStore(s, i64, /*isAtomic=*/true); // i64.atomic.store32 + case '8': return makeStore(s, i64, /*isAtomic=*/true); // i64.atomic.store8 + default: goto parse_error; + } + } + default: goto parse_error; + } + } + default: goto parse_error; + } + } + case 'c': { + switch (op[5]) { + case 'l': return makeUnary(s, UnaryOp::ClzInt64); // i64.clz + case 'o': return makeConst(s, i64); // i64.const + case 't': return makeUnary(s, UnaryOp::CtzInt64); // i64.ctz + default: goto parse_error; + } + } + case 'd': { + switch (op[8]) { + case 's': return makeBinary(s, BinaryOp::DivSInt64); // i64.div_s + case 'u': return makeBinary(s, BinaryOp::DivUInt64); // i64.div_u + default: goto parse_error; + } + } + case 'e': { + switch (op[5]) { + case 'q': { + switch (op[6]) { + case '\0': return makeBinary(s, BinaryOp::EqInt64); // i64.eq + case 'z': return makeUnary(s, UnaryOp::EqZInt64); // i64.eqz + default: goto parse_error; + } + } + case 'x': { + switch (op[10]) { + case '1': return makeUnary(s, UnaryOp::ExtendS16Int64); // i64.extend16_s + case '3': return makeUnary(s, UnaryOp::ExtendS32Int64); // i64.extend32_s + case '8': return makeUnary(s, UnaryOp::ExtendS8Int64); // i64.extend8_s + case '_': { + switch (op[11]) { + case 's': return makeUnary(s, UnaryOp::ExtendSInt32); // i64.extend_s/i32 + case 'u': return makeUnary(s, UnaryOp::ExtendUInt32); // i64.extend_u/i32 + default: goto parse_error; + } + } + default: goto parse_error; + } + } + default: goto parse_error; + } + } + case 'g': { + switch (op[5]) { + case 'e': { + switch (op[7]) { + case 's': return makeBinary(s, BinaryOp::GeSInt64); // i64.ge_s + case 'u': return makeBinary(s, BinaryOp::GeUInt64); // i64.ge_u + default: goto parse_error; + } + } + case 't': { + switch (op[7]) { + case 's': return makeBinary(s, BinaryOp::GtSInt64); // i64.gt_s + case 'u': return makeBinary(s, BinaryOp::GtUInt64); // i64.gt_u + default: goto parse_error; + } + } + default: goto parse_error; + } + } + case 'l': { + switch (op[5]) { + case 'e': { + switch (op[7]) { + case 's': return makeBinary(s, BinaryOp::LeSInt64); // i64.le_s + case 'u': return makeBinary(s, BinaryOp::LeUInt64); // i64.le_u + default: goto parse_error; + } + } + case 'o': { + switch (op[8]) { + case '\0': return makeLoad(s, i64, /*isAtomic=*/false); // i64.load + case '1': return makeLoad(s, i64, /*isAtomic=*/false); // i64.load16_s | i64.load16_u + case '3': return makeLoad(s, i64, /*isAtomic=*/false); // i64.load32_s | i64.load32_u + case '8': return makeLoad(s, i64, /*isAtomic=*/false); // i64.load8_s | i64.load8_u + default: goto parse_error; + } + } + case 't': { + switch (op[7]) { + case 's': return makeBinary(s, BinaryOp::LtSInt64); // i64.lt_s + case 'u': return makeBinary(s, BinaryOp::LtUInt64); // i64.lt_u + default: goto parse_error; + } + } + default: goto parse_error; + } + } + case 'm': return makeBinary(s, BinaryOp::MulInt64); // i64.mul + case 'n': return makeBinary(s, BinaryOp::NeInt64); // i64.ne + case 'o': return makeBinary(s, BinaryOp::OrInt64); // i64.or + case 'p': return makeUnary(s, UnaryOp::PopcntInt64); // i64.popcnt + case 'r': { + switch (op[5]) { + case 'e': { + switch (op[6]) { + case 'i': return makeUnary(s, UnaryOp::ReinterpretFloat64); // i64.reinterpret/f64 + case 'm': { + switch (op[8]) { + case 's': return makeBinary(s, BinaryOp::RemSInt64); // i64.rem_s + case 'u': return makeBinary(s, BinaryOp::RemUInt64); // i64.rem_u + default: goto parse_error; + } + } + default: goto parse_error; + } + } + case 'o': { + switch (op[7]) { + case 'l': return makeBinary(s, BinaryOp::RotLInt64); // i64.rotl + case 'r': return makeBinary(s, BinaryOp::RotRInt64); // i64.rotr + default: goto parse_error; + } + } + default: goto parse_error; + } + } + case 's': { + switch (op[5]) { + case 'h': { + switch (op[6]) { + case 'l': return makeBinary(s, BinaryOp::ShlInt64); // i64.shl + case 'r': { + switch (op[8]) { + case 's': return makeBinary(s, BinaryOp::ShrSInt64); // i64.shr_s + case 'u': return makeBinary(s, BinaryOp::ShrUInt64); // i64.shr_u + default: goto parse_error; + } + } + default: goto parse_error; + } + } + case 't': { + switch (op[9]) { + case '\0': return makeStore(s, i64, /*isAtomic=*/false); // i64.store + case '1': return makeStore(s, i64, /*isAtomic=*/false); // i64.store16 + case '3': return makeStore(s, i64, /*isAtomic=*/false); // i64.store32 + case '8': return makeStore(s, i64, /*isAtomic=*/false); // i64.store8 + default: goto parse_error; + } + } + case 'u': return makeBinary(s, BinaryOp::SubInt64); // i64.sub + default: goto parse_error; + } + } + case 't': { + switch (op[10]) { + case 's': { + switch (op[13]) { + case '3': return makeUnary(s, UnaryOp::TruncSFloat32ToInt64); // i64.trunc_s/f32 + case '6': return makeUnary(s, UnaryOp::TruncSFloat64ToInt64); // i64.trunc_s/f64 + default: goto parse_error; + } + } + case 'u': { + switch (op[13]) { + case '3': return makeUnary(s, UnaryOp::TruncUFloat32ToInt64); // i64.trunc_u/f32 + case '6': return makeUnary(s, UnaryOp::TruncUFloat64ToInt64); // i64.trunc_u/f64 + default: goto parse_error; + } + } + default: goto parse_error; + } + } + case 'w': return makeAtomicWait(s, i64); // i64.wait + case 'x': return makeBinary(s, BinaryOp::XorInt64); // i64.xor + default: goto parse_error; + } + } + case 'f': return makeIf(s); // if + default: goto parse_error; + } + } + case 'l': return makeLoop(s); // loop + case 'n': return makeNop(); // nop + case 'r': return makeReturn(s); // return + case 's': { + switch (op[2]) { + case 'l': return makeSelect(s); // select + case 't': { + switch (op[4]) { + case 'g': return makeSetGlobal(s); // set_global + case 'l': return makeSetLocal(s); // set_local + default: goto parse_error; + } + } + default: goto parse_error; + } + } + case 't': { + switch (op[1]) { + case 'e': return makeTeeLocal(s); // tee_local + case 'h': return makeThenOrElse(s); // then + default: goto parse_error; + } + } + case 'u': return makeUnreachable(); // unreachable + case 'w': return makeAtomicWake(s); // wake + default: goto parse_error; +} +parse_error: + throw ParseException(std::string(op)); +#endif // INSTRUCTION_PARSER diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index c27f63f1b..517398c5c 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -167,8 +167,10 @@ public: private: Expression* makeExpression(Element& s); - Expression* makeBinary(Element& s, BinaryOp op, Type type); - Expression* makeUnary(Element& s, UnaryOp op, Type type); + Expression* makeUnreachable(); + Expression* makeNop(); + Expression* makeBinary(Element& s, BinaryOp op); + Expression* makeUnary(Element& s, UnaryOp op); Expression* makeSelect(Element& s); Expression* makeDrop(Element& s); Expression* makeHost(Element& s, HostOp op); diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 79407a3f8..34022b72b 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -658,241 +658,19 @@ Expression* SExpressionWasmBuilder::parseExpression(Element& s) { } Expression* SExpressionWasmBuilder::makeExpression(Element& s) { - IString id = s[0]->str(); - const char *str = id.str; - const char *dot = strchr(str, '.'); - if (dot) { - // type.operation (e.g. i32.add) - Type type = stringToType(str, false, true); - // Local copy to index into op without bounds checking. - enum { maxNameSize = 15 }; - char op[maxNameSize + 1] = {'\0'}; - strncpy(op, dot + 1, maxNameSize); -#define BINARY_INT_OR_FLOAT(op) (type == i32 ? BinaryOp::op##Int32 : (type == i64 ? BinaryOp::op##Int64 : (type == f32 ? BinaryOp::op##Float32 : BinaryOp::op##Float64))) -#define BINARY_INT(op) (type == i32 ? BinaryOp::op##Int32 : BinaryOp::op##Int64) -#define BINARY_FLOAT(op) (type == f32 ? BinaryOp::op##Float32 : BinaryOp::op##Float64) - switch (op[0]) { - case 'a': { - if (op[1] == 'b') return makeUnary(s, type == f32 ? UnaryOp::AbsFloat32 : UnaryOp::AbsFloat64, type); - if (op[1] == 'd') return makeBinary(s, BINARY_INT_OR_FLOAT(Add), type); - if (op[1] == 'n') return makeBinary(s, BINARY_INT(And), type); - if (op[1] == 't' && !strncmp(op, "atomic.", strlen("atomic."))) { - if (op[7] == 'l') return makeLoad(s, type, /*isAtomic=*/true); - if (op[7] == 's') return makeStore(s, type, /*isAtomic=*/true); - if (op[7] == 'r') return makeAtomicRMWOrCmpxchg(s, type); - } - abort_on(op); - } - case 'c': { - if (op[1] == 'e') return makeUnary(s, type == f32 ? UnaryOp::CeilFloat32 : UnaryOp::CeilFloat64, type); - if (op[1] == 'l') return makeUnary(s, type == i32 ? UnaryOp::ClzInt32 : UnaryOp::ClzInt64, type); - if (op[1] == 'o') { - if (op[2] == 'p') return makeBinary(s, BINARY_FLOAT(CopySign), type); - if (op[2] == 'n') { - if (op[3] == 'v') { - if (op[8] == 's') return makeUnary(s, op[11] == '3' ? (type == f32 ? UnaryOp::ConvertSInt32ToFloat32 : UnaryOp::ConvertSInt32ToFloat64) : (type == f32 ? UnaryOp::ConvertSInt64ToFloat32 : UnaryOp::ConvertSInt64ToFloat64), type); - if (op[8] == 'u') return makeUnary(s, op[11] == '3' ? (type == f32 ? UnaryOp::ConvertUInt32ToFloat32 : UnaryOp::ConvertUInt32ToFloat64) : (type == f32 ? UnaryOp::ConvertUInt64ToFloat32 : UnaryOp::ConvertUInt64ToFloat64), type); - } - if (op[3] == 's') return makeConst(s, type); - } - } - if (op[1] == 't') return makeUnary(s, type == i32 ? UnaryOp::CtzInt32 : UnaryOp::CtzInt64, type); - abort_on(op); - } - case 'd': { - if (op[1] == 'i') { - if (op[3] == '_') return makeBinary(s, op[4] == 'u' ? BINARY_INT(DivU) : BINARY_INT(DivS), type); - if (op[3] == 0) return makeBinary(s, BINARY_FLOAT(Div), type); - } - if (op[1] == 'e') return makeUnary(s, UnaryOp::DemoteFloat64, type); - abort_on(op); - } - case 'e': { - if (op[1] == 'q') { - if (op[2] == 0) return makeBinary(s, BINARY_INT_OR_FLOAT(Eq), type); - if (op[2] == 'z') return makeUnary(s, type == i32 ? UnaryOp::EqZInt32 : UnaryOp::EqZInt64, type); - } - if (op[1] == 'x') { - if (op[6] == '8') return makeUnary(s, type == i32 ? UnaryOp::ExtendS8Int32 : UnaryOp::ExtendS8Int64, type); - if (op[6] == '1') return makeUnary(s, type == i32 ? UnaryOp::ExtendS16Int32 : UnaryOp::ExtendS16Int64, type); - if (op[6] == '3') return makeUnary(s, UnaryOp::ExtendS32Int64, type); - return makeUnary(s, op[7] == 'u' ? UnaryOp::ExtendUInt32 : UnaryOp::ExtendSInt32, type); - } - abort_on(op); - } - case 'f': { - if (op[1] == 'l') return makeUnary(s, type == f32 ? UnaryOp::FloorFloat32 : UnaryOp::FloorFloat64, type); - abort_on(op); - } - case 'g': { - if (op[1] == 't') { - if (op[2] == '_') return makeBinary(s, op[3] == 'u' ? BINARY_INT(GtU) : BINARY_INT(GtS), type); - if (op[2] == 0) return makeBinary(s, BINARY_FLOAT(Gt), type); - } - if (op[1] == 'e') { - if (op[2] == '_') return makeBinary(s, op[3] == 'u' ? BINARY_INT(GeU) : BINARY_INT(GeS), type); - if (op[2] == 0) return makeBinary(s, BINARY_FLOAT(Ge), type); - } - abort_on(op); - } - case 'l': { - if (op[1] == 't') { - if (op[2] == '_') return makeBinary(s, op[3] == 'u' ? BINARY_INT(LtU) : BINARY_INT(LtS), type); - if (op[2] == 0) return makeBinary(s, BINARY_FLOAT(Lt), type); - } - if (op[1] == 'e') { - if (op[2] == '_') return makeBinary(s, op[3] == 'u' ? BINARY_INT(LeU) : BINARY_INT(LeS), type); - if (op[2] == 0) return makeBinary(s, BINARY_FLOAT(Le), type); - } - if (op[1] == 'o') return makeLoad(s, type, /*isAtomic=*/false); - abort_on(op); - } - case 'm': { - if (op[1] == 'i') return makeBinary(s, BINARY_FLOAT(Min), type); - if (op[1] == 'a') return makeBinary(s, BINARY_FLOAT(Max), type); - if (op[1] == 'u') return makeBinary(s, BINARY_INT_OR_FLOAT(Mul), type); - abort_on(op); - } - case 'n': { - if (op[1] == 'e') { - if (op[2] == 0) return makeBinary(s, BINARY_INT_OR_FLOAT(Ne), type); - if (op[2] == 'a') return makeUnary(s, type == f32 ? UnaryOp::NearestFloat32 : UnaryOp::NearestFloat64, type); - if (op[2] == 'g') return makeUnary(s, type == f32 ? UnaryOp::NegFloat32 : UnaryOp::NegFloat64, type); - } - abort_on(op); - } - case 'o': { - if (op[1] == 'r') return makeBinary(s, BINARY_INT(Or), type); - abort_on(op); - } - case 'p': { - if (op[1] == 'r') return makeUnary(s, UnaryOp::PromoteFloat32, type); - if (op[1] == 'o') return makeUnary(s, type == i32 ? UnaryOp::PopcntInt32 : UnaryOp::PopcntInt64, type); - abort_on(op); - } - case 'r': { - if (op[1] == 'e') { - if (op[2] == 'm') return makeBinary(s, op[4] == 'u' ? BINARY_INT(RemU) : BINARY_INT(RemS), type); - if (op[2] == 'i') return makeUnary(s, isFloatType(type) ? (type == f32 ? UnaryOp::ReinterpretInt32 : UnaryOp::ReinterpretInt64) : (type == i32 ? UnaryOp::ReinterpretFloat32 : UnaryOp::ReinterpretFloat64), type); - } - if (op[1] == 'o' && op[2] == 't') { - return makeBinary(s, op[3] == 'l' ? BINARY_INT(RotL) : BINARY_INT(RotR), type); - } - abort_on(op); - } - case 's': { - if (op[1] == 'h') { - if (op[2] == 'l') return makeBinary(s, BINARY_INT(Shl), type); - return makeBinary(s, op[4] == 'u' ? BINARY_INT(ShrU) : BINARY_INT(ShrS), type); - } - if (op[1] == 'u') return makeBinary(s, BINARY_INT_OR_FLOAT(Sub), type); - if (op[1] == 'q') return makeUnary(s, type == f32 ? UnaryOp::SqrtFloat32 : UnaryOp::SqrtFloat64, type); - if (op[1] == 't') return makeStore(s, type, /*isAtomic=*/false); - abort_on(op); - } - case 't': { - if (op[1] == 'r') { - if (op[6] == 's') return makeUnary(s, op[9] == '3' ? (type == i32 ? UnaryOp::TruncSFloat32ToInt32 : UnaryOp::TruncSFloat32ToInt64) : (type == i32 ? UnaryOp::TruncSFloat64ToInt32 : UnaryOp::TruncSFloat64ToInt64), type); - if (op[6] == 'u') return makeUnary(s, op[9] == '3' ? (type == i32 ? UnaryOp::TruncUFloat32ToInt32 : UnaryOp::TruncUFloat32ToInt64) : (type == i32 ? UnaryOp::TruncUFloat64ToInt32 : UnaryOp::TruncUFloat64ToInt64), type); - if (op[2] == 'u') return makeUnary(s, type == f32 ? UnaryOp::TruncFloat32 : UnaryOp::TruncFloat64, type); - } - abort_on(op); - } - case 'w': { - if (!strncmp(op, "wait", strlen("wait"))) return makeAtomicWait(s, type); - if (op[1] == 'r') return makeUnary(s, UnaryOp::WrapInt64, type); - abort_on(op); - } - case 'x': { - if (op[1] == 'o') return makeBinary(s, BINARY_INT(Xor), type); - abort_on(op); - } - default: abort_on(op); - } - } else { - // other expression - switch (str[0]) { - case 'b': { - if (str[1] == 'l') return makeBlock(s); - if (str[1] == 'r') { - if (str[2] == '_' && str[3] == 't') return makeBreakTable(s); - return makeBreak(s); - } - abort_on(str); - } - case 'c': { - if (str[1] == 'a') { - if (id == CALL) return makeCall(s); - if (id == CALL_INDIRECT) return makeCallIndirect(s); - } else if (str[1] == 'u') return makeHost(s, HostOp::CurrentMemory); - abort_on(str); - } - case 'd': { - if (str[1] == 'r') return makeDrop(s); - abort_on(str); - } - case 'e': { - if (str[1] == 'l') return makeThenOrElse(s); - abort_on(str); - } - case 'g': { - if (str[1] == 'e') { - if (str[4] == 'l') return makeGetLocal(s); - if (str[4] == 'g') return makeGetGlobal(s); - } - if (str[1] == 'r') return makeHost(s, HostOp::GrowMemory); - abort_on(str); - } - case 'h': { - abort_on(str); - } - case 'i': { - if (str[1] == 'f') return makeIf(s); - abort_on(str); - } - case 'l': { - if (str[1] == 'o') return makeLoop(s); - abort_on(str); - } - case 'n': { - if (str[1] == 'o') return allocator.alloc<Nop>(); - abort_on(str); - } - case 'p': { - abort_on(str); - } - case 's': { - if (str[1] == 'e' && str[2] == 't') { - if (str[4] == 'l') return makeSetLocal(s); - if (str[4] == 'g') return makeSetGlobal(s); - } - if (str[1] == 'e' && str[2] == 'l') return makeSelect(s); - abort_on(str); - } - case 'r': { - if (str[1] == 'e') return makeReturn(s); - abort_on(str); - } - case 't': { - if (str[1] == 'h') return makeThenOrElse(s); - if (str[1] == 'e' && str[2] == 'e') return makeTeeLocal(s); - abort_on(str); - } - case 'u': { - if (str[1] == 'n') return allocator.alloc<Unreachable>(); - abort_on(str); - } - case 'w': { - if (!strncmp(str, "wake", strlen("wake"))) return makeAtomicWake(s); - abort_on(str); - } - default: abort_on(str); - } - } - abort_on("unrecognized input string for parsing"); +#define INSTRUCTION_PARSER +#include "gen-s-parser.inc" } -Expression* SExpressionWasmBuilder::makeBinary(Element& s, BinaryOp op, Type type) { +Expression* SExpressionWasmBuilder::makeUnreachable() { + return allocator.alloc<Unreachable>(); +} + +Expression* SExpressionWasmBuilder::makeNop() { + return allocator.alloc<Nop>(); +} + +Expression* SExpressionWasmBuilder::makeBinary(Element& s, BinaryOp op) { auto ret = allocator.alloc<Binary>(); ret->op = op; ret->left = parseExpression(s[1]); @@ -902,67 +680,11 @@ Expression* SExpressionWasmBuilder::makeBinary(Element& s, BinaryOp op, Type typ } -Expression* SExpressionWasmBuilder::makeUnary(Element& s, UnaryOp op, Type type) { +Expression* SExpressionWasmBuilder::makeUnary(Element& s, UnaryOp op) { auto ret = allocator.alloc<Unary>(); ret->op = op; ret->value = parseExpression(s[1]); ret->finalize(); - // type is the reported type, e.g. i64.ctz reports i64 (but has a return type of i32, in this case) - // verify the reported type is correct - switch (op) { - case EqZInt32: - case NegFloat32: - case AbsFloat32: - case CeilFloat32: - case FloorFloat32: - case TruncFloat32: - case NearestFloat32: - case SqrtFloat32: - case ClzInt32: - case CtzInt32: - case PopcntInt32: - case EqZInt64: - case NegFloat64: - case AbsFloat64: - case CeilFloat64: - case FloorFloat64: - case TruncFloat64: - case NearestFloat64: - case SqrtFloat64: - case ClzInt64: - case CtzInt64: - case PopcntInt64: { - if (ret->value->type != unreachable && type != ret->value->type) throw ParseException(std::string("bad type for ") + getExpressionName(ret) + ": " + printType(type) + " vs value type " + printType(ret->value->type), s.line, s.col); - break; - } - case ExtendSInt32: case ExtendUInt32: - case ExtendS8Int32: case ExtendS16Int32: - case ExtendS8Int64: case ExtendS16Int64: case ExtendS32Int64: - case WrapInt64: - case PromoteFloat32: - case DemoteFloat64: - case TruncSFloat32ToInt32: - case TruncUFloat32ToInt32: - case TruncSFloat64ToInt32: - case TruncUFloat64ToInt32: - case ReinterpretFloat32: - case TruncSFloat32ToInt64: - case TruncUFloat32ToInt64: - case TruncSFloat64ToInt64: - case TruncUFloat64ToInt64: - case ReinterpretFloat64: - case ReinterpretInt32: - case ConvertSInt32ToFloat32: - case ConvertUInt32ToFloat32: - case ConvertSInt64ToFloat32: - case ConvertUInt64ToFloat32: - case ReinterpretInt64: - case ConvertSInt32ToFloat64: - case ConvertUInt32ToFloat64: - case ConvertSInt64ToFloat64: - case ConvertUInt64ToFloat64: break; - default: WASM_UNREACHABLE(); - } return ret; } diff --git a/test/ctor-eval/basics-flatten.wast b/test/ctor-eval/basics-flatten.wast index da8b76163..07078bfc3 100644 --- a/test/ctor-eval/basics-flatten.wast +++ b/test/ctor-eval/basics-flatten.wast @@ -17,8 +17,10 @@ ) (func $test2 (drop (i32.load (i32.const 12))) ;; a safe load - (drop (i32.load16 (i32.const 12))) - (drop (i32.load8 (i32.const 12))) + (drop (i32.load16_s (i32.const 12))) + (drop (i32.load8_s (i32.const 12))) + (drop (i32.load16_u (i32.const 12))) + (drop (i32.load8_u (i32.const 12))) ) (func $test3 (i32.store (i32.const 12) (i32.const 115)) ;; a safe store, should alter memory diff --git a/test/ctor-eval/basics.wast b/test/ctor-eval/basics.wast index 997e2cd11..73988c753 100644 --- a/test/ctor-eval/basics.wast +++ b/test/ctor-eval/basics.wast @@ -14,8 +14,10 @@ ) (func $test2 (drop (i32.load (i32.const 12))) ;; a safe load - (drop (i32.load16 (i32.const 12))) - (drop (i32.load8 (i32.const 12))) + (drop (i32.load16_s (i32.const 12))) + (drop (i32.load8_s (i32.const 12))) + (drop (i32.load16_u (i32.const 12))) + (drop (i32.load8_u (i32.const 12))) ) (func $test3 (i32.store (i32.const 12) (i32.const 115)) ;; a safe store, should alter memory diff --git a/test/passes/duplicate-function-elimination_optimize-level=1.wast b/test/passes/duplicate-function-elimination_optimize-level=1.wast index b79032155..05dbe646b 100644 --- a/test/passes/duplicate-function-elimination_optimize-level=1.wast +++ b/test/passes/duplicate-function-elimination_optimize-level=1.wast @@ -733,7 +733,7 @@ (memory 10) (type $0 (func)) (func $keep2 (type $0) - (i32.store32 offset=3 + (i32.store offset=3 (i32.const 0) (i32.const 100) ) diff --git a/test/passes/duplicate-function-elimination_optimize-level=2.wast b/test/passes/duplicate-function-elimination_optimize-level=2.wast index b79032155..05dbe646b 100644 --- a/test/passes/duplicate-function-elimination_optimize-level=2.wast +++ b/test/passes/duplicate-function-elimination_optimize-level=2.wast @@ -733,7 +733,7 @@ (memory 10) (type $0 (func)) (func $keep2 (type $0) - (i32.store32 offset=3 + (i32.store offset=3 (i32.const 0) (i32.const 100) ) diff --git a/test/wasm2js/br_table_temp.wast b/test/wasm2js/br_table_temp.wast index abd66053a..1ab685a7b 100644 --- a/test/wasm2js/br_table_temp.wast +++ b/test/wasm2js/br_table_temp.wast @@ -1057,7 +1057,7 @@ ) (func (export "as-unary-operand") (result i32) - (block i32 (i32.neg (br_table 0 (i32.const 3) (i32.const 0)))) + (block i32 (i32.ctz (br_table 0 (i32.const 3) (i32.const 0)))) ) (func (export "as-binary-left") (result i32) |