From ab7e6251ad228a9a227572a1c0d399df7ad0dbb9 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Mon, 1 Feb 2021 15:24:18 -0800 Subject: [PATCH] Prototype i32x4.widen_i8x16_{s,u} As proposed in https://github.com/WebAssembly/simd/pull/395. Note that the other instructions in the proposal have not been implemented in LLVM or in V8, so there is no need to implement them in Binaryen right now either. This PR introduces a new expression class for the new instructions because they uniquely take an immediate argument identifying which portion of the input vector to widen. --- scripts/gen-s-parser.py | 2 + src/gen-s-parser.inc | 11 ++++ src/ir/ReFinalize.cpp | 1 + src/ir/cost.h | 1 + src/ir/effects.h | 1 + src/passes/Print.cpp | 20 ++++++++ src/wasm-binary.h | 4 ++ src/wasm-delegations-fields.h | 8 +++ src/wasm-delegations.h | 1 + src/wasm-interpreter.h | 1 + src/wasm-s-parser.h | 1 + src/wasm.h | 20 +++++++- src/wasm/wasm-binary.cpp | 24 +++++++++ src/wasm/wasm-s-parser.cpp | 9 ++++ src/wasm/wasm-stack.cpp | 13 +++++ src/wasm/wasm.cpp | 5 ++ src/wasm2js.h | 4 ++ test/binaryen.js/exception-handling.js.txt | 6 +-- test/binaryen.js/kitchen-sink.js.txt | 58 +++++++++++----------- test/simd.wast | 10 ++++ test/simd.wast.from-wast | 10 ++++ test/simd.wast.fromBinary | 10 ++++ test/simd.wast.fromBinary.noDebugInfo | 10 ++++ 23 files changed, 197 insertions(+), 33 deletions(-) diff --git a/scripts/gen-s-parser.py b/scripts/gen-s-parser.py index af29f402001..c65e5ddc45a 100755 --- a/scripts/gen-s-parser.py +++ b/scripts/gen-s-parser.py @@ -522,6 +522,8 @@ ("i32x4.trunc_sat_f64x2_zero_u", "makeUnary(s, UnaryOp::TruncSatZeroUVecF64x2ToVecI32x4)"), ("f32x4.demote_f64x2_zero", "makeUnary(s, UnaryOp::DemoteZeroVecF64x2ToVecF32x4)"), ("f64x2.promote_low_f32x4", "makeUnary(s, UnaryOp::PromoteLowVecF32x4ToVecF64x2)"), + ("i32x4.widen_i8x16_s", "makeSIMDWiden(s, SIMDWidenOp::WidenSVecI8x16ToVecI32x4)"), + ("i32x4.widen_i8x16_u", "makeSIMDWiden(s, SIMDWidenOp::WidenUVecI8x16ToVecI32x4)"), # prefetch instructions ("prefetch.t", "makePrefetch(s, PrefetchOp::PrefetchTemporal)"), diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc index 281a2cfcae5..4e5a61d9277 100644 --- a/src/gen-s-parser.inc +++ b/src/gen-s-parser.inc @@ -1832,6 +1832,17 @@ switch (op[0]) { default: goto parse_error; } } + case 'i': { + switch (op[18]) { + case 's': + if (strcmp(op, "i32x4.widen_i8x16_s") == 0) { return makeSIMDWiden(s, SIMDWidenOp::WidenSVecI8x16ToVecI32x4); } + goto parse_error; + case 'u': + if (strcmp(op, "i32x4.widen_i8x16_u") == 0) { return makeSIMDWiden(s, SIMDWidenOp::WidenUVecI8x16ToVecI32x4); } + goto parse_error; + default: goto parse_error; + } + } case 'l': { switch (op[22]) { case 's': diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp index 808b9d41ba3..de418459610 100644 --- a/src/ir/ReFinalize.cpp +++ b/src/ir/ReFinalize.cpp @@ -112,6 +112,7 @@ void ReFinalize::visitSIMDLoad(SIMDLoad* curr) { curr->finalize(); } void ReFinalize::visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) { curr->finalize(); } +void ReFinalize::visitSIMDWiden(SIMDWiden* curr) { curr->finalize(); } void ReFinalize::visitPrefetch(Prefetch* curr) { curr->finalize(); } void ReFinalize::visitMemoryInit(MemoryInit* curr) { curr->finalize(); } void ReFinalize::visitDataDrop(DataDrop* curr) { curr->finalize(); } diff --git a/src/ir/cost.h b/src/ir/cost.h index 212933ffbef..50941bdd3c5 100644 --- a/src/ir/cost.h +++ b/src/ir/cost.h @@ -540,6 +540,7 @@ struct CostAnalyzer : public OverriddenVisitor { Index visitSIMDShift(SIMDShift* curr) { return 1 + visit(curr->vec) + visit(curr->shift); } + Index visitSIMDWiden(SIMDWiden* curr) { return 1 + visit(curr->vec); } Index visitSIMDShuffle(SIMDShuffle* curr) { return 1 + visit(curr->left) + visit(curr->right); } diff --git a/src/ir/effects.h b/src/ir/effects.h index c7142dc3421..d52fcfb1578 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -430,6 +430,7 @@ class EffectAnalyzer { } parent.implicitTrap = true; } + void visitSIMDWiden(SIMDWiden* curr) {} void visitPrefetch(Prefetch* curr) { // Do not reorder with respect to other memory ops parent.writesMemory = true; diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 39c66cc57fe..74bd5598f9a 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -671,6 +671,19 @@ struct PrintExpressionContents } o << " " << int(curr->index); } + void visitSIMDWiden(SIMDWiden* curr) { + prepareColor(o); + switch (curr->op) { + case WidenSVecI8x16ToVecI32x4: + o << "i32x4.widen_i8x16_s "; + break; + case WidenUVecI8x16ToVecI32x4: + o << "i32x4.widen_i8x16_u "; + break; + } + restoreNormalColor(o); + o << int(curr->index); + } void visitPrefetch(Prefetch* curr) { prepareColor(o); switch (curr->op) { @@ -2303,6 +2316,13 @@ struct PrintSExpression : public OverriddenVisitor { printFullLine(curr->vec); decIndent(); } + void visitSIMDWiden(SIMDWiden* curr) { + o << '('; + PrintExpressionContents(currFunction, o).visit(curr); + incIndent(); + printFullLine(curr->vec); + decIndent(); + } void visitPrefetch(Prefetch* curr) { o << '('; PrintExpressionContents(currFunction, o).visit(curr); diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 0213baffeca..6a409f7db46 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -979,6 +979,9 @@ enum ASTNodes { F32x4DemoteZeroF64x2 = 0x57, F64x2PromoteLowF32x4 = 0x69, + I32x4WidenSI8x16 = 0x67, + I32x4WidenUI8x16 = 0x68, + // prefetch opcodes PrefetchT = 0xc5, @@ -1521,6 +1524,7 @@ class WasmBinaryBuilder { bool maybeVisitSIMDShift(Expression*& out, uint32_t code); bool maybeVisitSIMDLoad(Expression*& out, uint32_t code); bool maybeVisitSIMDLoadStoreLane(Expression*& out, uint32_t code); + bool maybeVisitSIMDWiden(Expression*& out, uint32_t code); bool maybeVisitPrefetch(Expression*& out, uint32_t code); bool maybeVisitMemoryInit(Expression*& out, uint32_t code); bool maybeVisitDataDrop(Expression*& out, uint32_t code); diff --git a/src/wasm-delegations-fields.h b/src/wasm-delegations-fields.h index d4ea25b3d14..b99ebef40d9 100644 --- a/src/wasm-delegations-fields.h +++ b/src/wasm-delegations-fields.h @@ -386,6 +386,14 @@ switch (DELEGATE_ID) { DELEGATE_END(SIMDLoadStoreLane); break; } + case Expression::Id::SIMDWidenId: { + DELEGATE_START(SIMDWiden); + DELEGATE_FIELD_CHILD(SIMDWiden, vec); + DELEGATE_FIELD_INT(SIMDWiden, op); + DELEGATE_FIELD_INT(SIMDWiden, index); + DELEGATE_END(SIMDWiden); + break; + } case Expression::Id::PrefetchId: { DELEGATE_START(Prefetch); DELEGATE_FIELD_CHILD(Prefetch, ptr); diff --git a/src/wasm-delegations.h b/src/wasm-delegations.h index fd419f508b1..5a05a731fb1 100644 --- a/src/wasm-delegations.h +++ b/src/wasm-delegations.h @@ -40,6 +40,7 @@ DELEGATE(SIMDTernary); DELEGATE(SIMDShift); DELEGATE(SIMDLoad); DELEGATE(SIMDLoadStoreLane); +DELEGATE(SIMDWiden); DELEGATE(Prefetch); DELEGATE(MemoryInit); DELEGATE(DataDrop); diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index ac64a30c0b3..6cd6f4b500b 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -1132,6 +1132,7 @@ class ExpressionRunner : public OverriddenVisitor { } WASM_UNREACHABLE("invalid op"); } + Flow visitSIMDWiden(SIMDWiden* curr) { WASM_UNREACHABLE("unimp"); } Flow visitSelect(Select* curr) { NOTE_ENTER("Select"); Flow ifTrue = visit(curr->ifTrue); diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index 921ef6b4a9b..031589894df 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -220,6 +220,7 @@ class SExpressionWasmBuilder { Expression* makeSIMDShift(Element& s, SIMDShiftOp op); Expression* makeSIMDLoad(Element& s, SIMDLoadOp op); Expression* makeSIMDLoadStoreLane(Element& s, SIMDLoadStoreLaneOp op); + Expression* makeSIMDWiden(Element& s, SIMDWidenOp op); Expression* makePrefetch(Element& s, PrefetchOp op); Expression* makeMemoryInit(Element& s); Expression* makeDataDrop(Element& s); diff --git a/src/wasm.h b/src/wasm.h index bd2e3181439..0050fcc281c 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -481,7 +481,7 @@ enum SIMDReplaceOp { ReplaceLaneVecI32x4, ReplaceLaneVecI64x2, ReplaceLaneVecF32x4, - ReplaceLaneVecF64x2 + ReplaceLaneVecF64x2, }; enum SIMDShiftOp { @@ -537,6 +537,11 @@ enum SIMDTernaryOp { SignSelectVec64x2 }; +enum SIMDWidenOp { + WidenSVecI8x16ToVecI32x4, + WidenUVecI8x16ToVecI32x4, +}; + enum PrefetchOp { PrefetchTemporal, PrefetchNontemporal, @@ -625,6 +630,7 @@ class Expression { SIMDShiftId, SIMDLoadId, SIMDLoadStoreLaneId, + SIMDWidenId, MemoryInitId, DataDropId, MemoryCopyId, @@ -1072,6 +1078,18 @@ class SIMDLoadStoreLane void finalize(); }; +class SIMDWiden : public SpecificExpression { +public: + SIMDWiden() = default; + SIMDWiden(MixedArena& allocator) {} + + SIMDWidenOp op; + uint8_t index; + Expression* vec; + + void finalize(); +}; + class Prefetch : public SpecificExpression { public: Prefetch() = default; diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index b7ffcd8d772..2c473a254eb 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -3005,6 +3005,9 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { if (maybeVisitSIMDLoadStoreLane(curr, opcode)) { break; } + if (maybeVisitSIMDWiden(curr, opcode)) { + break; + } if (maybeVisitPrefetch(curr, opcode)) { break; } @@ -5466,6 +5469,27 @@ bool WasmBinaryBuilder::maybeVisitSIMDLoadStoreLane(Expression*& out, return true; } +bool WasmBinaryBuilder::maybeVisitSIMDWiden(Expression*& out, uint32_t code) { + SIMDWidenOp op; + switch (code) { + case BinaryConsts::I32x4WidenSI8x16: + op = WidenSVecI8x16ToVecI32x4; + break; + case BinaryConsts::I32x4WidenUI8x16: + op = WidenUVecI8x16ToVecI32x4; + break; + default: + return false; + } + auto* curr = allocator.alloc(); + curr->op = op; + curr->index = getLaneIndex(4); + curr->vec = popNonVoidExpression(); + curr->finalize(); + out = curr; + return true; +} + bool WasmBinaryBuilder::maybeVisitPrefetch(Expression*& out, uint32_t code) { PrefetchOp op; switch (code) { diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 3075f70d7a6..0dc121f2c90 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -1666,6 +1666,15 @@ SExpressionWasmBuilder::makeSIMDLoadStoreLane(Element& s, return ret; } +Expression* SExpressionWasmBuilder::makeSIMDWiden(Element& s, SIMDWidenOp op) { + auto* ret = allocator.alloc(); + ret->op = op; + ret->index = parseLaneIndex(s[1], 4); + ret->vec = parseExpression(s[2]); + ret->finalize(); + return ret; +} + Expression* SExpressionWasmBuilder::makePrefetch(Element& s, PrefetchOp op) { Address offset, align; size_t i = parseMemAttributes(s, offset, align, /*defaultAlign*/ 1); diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index d42080bac7f..cb8702d0c0d 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -691,6 +691,19 @@ void BinaryInstWriter::visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) { o << curr->index; } +void BinaryInstWriter::visitSIMDWiden(SIMDWiden* curr) { + o << int8_t(BinaryConsts::SIMDPrefix); + switch (curr->op) { + case WidenSVecI8x16ToVecI32x4: + o << U32LEB(BinaryConsts::I32x4WidenSI8x16); + break; + case WidenUVecI8x16ToVecI32x4: + o << U32LEB(BinaryConsts::I32x4WidenUI8x16); + break; + } + o << uint8_t(curr->index); +} + void BinaryInstWriter::visitPrefetch(Prefetch* curr) { o << int8_t(BinaryConsts::SIMDPrefix); switch (curr->op) { diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index f8d8890ec94..e9d2bf1161d 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -496,6 +496,11 @@ void SIMDLoadStoreLane::finalize() { } } +void SIMDWiden::finalize() { + assert(vec); + type = vec->type == Type::unreachable ? Type::unreachable : Type::v128; +} + Index SIMDLoadStoreLane::getMemBytes() { switch (op) { case LoadLaneVec8x16: diff --git a/src/wasm2js.h b/src/wasm2js.h index 43c5f4cbdd5..db25fd47205 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -2119,6 +2119,10 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, unimplemented(curr); WASM_UNREACHABLE("unimp"); } + Ref visitSIMDWiden(SIMDWiden* curr) { + unimplemented(curr); + WASM_UNREACHABLE("unimp"); + } Ref visitMemoryInit(MemoryInit* curr) { ABI::wasm2js::ensureHelpers(module, ABI::wasm2js::MEMORY_INIT); return ValueBuilder::makeCall(ABI::wasm2js::MEMORY_INIT, diff --git a/test/binaryen.js/exception-handling.js.txt b/test/binaryen.js/exception-handling.js.txt index e7c72e6a76f..69b619564e6 100644 --- a/test/binaryen.js/exception-handling.js.txt +++ b/test/binaryen.js/exception-handling.js.txt @@ -19,6 +19,6 @@ ) ) -getExpressionInfo(throw) = {"id":47,"type":1,"event":"e"} -getExpressionInfo(rethrow) = {"id":48,"type":1,"depth":0} -getExpressionInfo(try) = {"id":46,"type":1,"hasCatchAll":0} +getExpressionInfo(throw) = {"id":48,"type":1,"event":"e"} +getExpressionInfo(rethrow) = {"id":49,"type":1,"depth":0} +getExpressionInfo(try) = {"id":47,"type":1,"hasCatchAll":0} diff --git a/test/binaryen.js/kitchen-sink.js.txt b/test/binaryen.js/kitchen-sink.js.txt index 5ad60b286bf..9b45d9a0197 100644 --- a/test/binaryen.js/kitchen-sink.js.txt +++ b/test/binaryen.js/kitchen-sink.js.txt @@ -76,35 +76,35 @@ SIMDShuffleId: 32 SIMDTernaryId: 33 SIMDShiftId: 34 SIMDLoadId: 35 -MemoryInitId: 37 -DataDropId: 38 -MemoryCopyId: 39 -MemoryFillId: 40 -PopId: 41 -RefNullId: 42 -RefIsId: 43 -RefFuncId: 44 -RefEqId: 45 -TryId: 46 -ThrowId: 47 -RethrowId: 48 -TupleMakeId: 49 -TupleExtractId: 50 -I31NewId: 51 -I31GetId: 52 -CallRefId: 53 -RefTestId: 54 -RefCastId: 55 -BrOnId: 56 -RttCanonId: 57 -RttSubId: 58 -StructNewId: 59 -StructGetId: 60 -StructSetId: 61 -ArrayNewId: 62 -ArrayGetId: 63 -ArraySetId: 64 -ArrayLenId: 65 +MemoryInitId: 38 +DataDropId: 39 +MemoryCopyId: 40 +MemoryFillId: 41 +PopId: 42 +RefNullId: 43 +RefIsId: 44 +RefFuncId: 45 +RefEqId: 46 +TryId: 47 +ThrowId: 48 +RethrowId: 49 +TupleMakeId: 50 +TupleExtractId: 51 +I31NewId: 52 +I31GetId: 53 +CallRefId: 54 +RefTestId: 55 +RefCastId: 56 +BrOnId: 57 +RttCanonId: 58 +RttSubId: 59 +StructNewId: 60 +StructGetId: 61 +StructSetId: 62 +ArrayNewId: 63 +ArrayGetId: 64 +ArraySetId: 65 +ArrayLenId: 66 getExpressionInfo={"id":15,"type":4,"op":6} (f32.neg (f32.const -33.61199951171875) diff --git a/test/simd.wast b/test/simd.wast index a171c53983d..8d12ea0ac0a 100644 --- a/test/simd.wast +++ b/test/simd.wast @@ -1436,4 +1436,14 @@ (local.get $0) ) ) + (func $i32x4.widen_i8x16_s (param $0 v128) (result v128) + (i32x4.widen_i8x16_s 0 + (local.get $0) + ) + ) + (func $i32x4.widen_i8x16_u (param $0 v128) (result v128) + (i32x4.widen_i8x16_u 0 + (local.get $0) + ) + ) ) diff --git a/test/simd.wast.from-wast b/test/simd.wast.from-wast index 223191c18da..6fbacea1155 100644 --- a/test/simd.wast.from-wast +++ b/test/simd.wast.from-wast @@ -1454,4 +1454,14 @@ (local.get $0) ) ) + (func $i32x4.widen_i8x16_s (param $0 v128) (result v128) + (i32x4.widen_i8x16_s 0 + (local.get $0) + ) + ) + (func $i32x4.widen_i8x16_u (param $0 v128) (result v128) + (i32x4.widen_i8x16_u 0 + (local.get $0) + ) + ) ) diff --git a/test/simd.wast.fromBinary b/test/simd.wast.fromBinary index 1c0b12493f0..4c84b5b2542 100644 --- a/test/simd.wast.fromBinary +++ b/test/simd.wast.fromBinary @@ -1454,5 +1454,15 @@ (local.get $0) ) ) + (func $i32x4.widen_i8x16_s (param $0 v128) (result v128) + (i32x4.widen_i8x16_s 0 + (local.get $0) + ) + ) + (func $i32x4.widen_i8x16_u (param $0 v128) (result v128) + (i32x4.widen_i8x16_u 0 + (local.get $0) + ) + ) ) diff --git a/test/simd.wast.fromBinary.noDebugInfo b/test/simd.wast.fromBinary.noDebugInfo index 3feb5a7b41f..bb5cdd5e056 100644 --- a/test/simd.wast.fromBinary.noDebugInfo +++ b/test/simd.wast.fromBinary.noDebugInfo @@ -1454,5 +1454,15 @@ (local.get $0) ) ) + (func $256 (param $0 v128) (result v128) + (i32x4.widen_i8x16_s 0 + (local.get $0) + ) + ) + (func $257 (param $0 v128) (result v128) + (i32x4.widen_i8x16_u 0 + (local.get $0) + ) + ) )