diff options
author | Thomas Lively <7121787+tlively@users.noreply.github.com> | 2018-12-14 23:57:58 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-12-14 23:57:58 -0800 |
commit | e037e7a02e3c7d739de743b891bb6e145b63156f (patch) | |
tree | 02622c4eadc2e93898feecf9e9c5504f47789839 /src | |
parent | c300e05873da1c8cafb654285f31959af1b3b9fc (diff) | |
download | binaryen-e037e7a02e3c7d739de743b891bb6e145b63156f.tar.gz binaryen-e037e7a02e3c7d739de743b891bb6e145b63156f.tar.bz2 binaryen-e037e7a02e3c7d739de743b891bb6e145b63156f.zip |
Fuzzing v128 and associated bug fixes (#1827)
* Fuzzing v128 and associated bug fixes
Diffstat (limited to 'src')
-rw-r--r-- | src/ir/ExpressionManipulator.cpp | 15 | ||||
-rw-r--r-- | src/ir/cost.h | 154 | ||||
-rw-r--r-- | src/literal.h | 2 | ||||
-rw-r--r-- | src/tools/execution-results.h | 1 | ||||
-rw-r--r-- | src/tools/feature-options.h | 4 | ||||
-rw-r--r-- | src/tools/fuzzing.h | 358 | ||||
-rw-r--r-- | src/tools/spec-wrapper.h | 2 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 2 | ||||
-rw-r--r-- | src/wasm-traversal.h | 10 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 2 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 10 |
11 files changed, 362 insertions, 198 deletions
diff --git a/src/ir/ExpressionManipulator.cpp b/src/ir/ExpressionManipulator.cpp index d65509c52..3f53bccdd 100644 --- a/src/ir/ExpressionManipulator.cpp +++ b/src/ir/ExpressionManipulator.cpp @@ -114,6 +114,21 @@ Expression* flexibleCopy(Expression* original, Module& wasm, CustomCopier custom Expression* visitAtomicWake(AtomicWake* curr) { return builder.makeAtomicWake(copy(curr->ptr), copy(curr->wakeCount), curr->offset); } + Expression* visitSIMDExtract(SIMDExtract* curr) { + return builder.makeSIMDExtract(curr->op, copy(curr->vec), curr->idx); + } + Expression* visitSIMDReplace(SIMDReplace* curr) { + return builder.makeSIMDReplace(curr->op, copy(curr->vec), curr->idx, copy(curr->value)); + } + Expression* visitSIMDShuffle(SIMDShuffle* curr) { + return builder.makeSIMDShuffle(copy(curr->left), copy(curr->right), curr->mask); + } + Expression* visitSIMDBitselect(SIMDBitselect* curr) { + return builder.makeSIMDBitselect(copy(curr->left), copy(curr->right), copy(curr->cond)); + } + Expression* visitSIMDShift(SIMDShift* curr) { + return builder.makeSIMDShift(curr->op, copy(curr->vec), copy(curr->shift)); + } Expression* visitConst(Const *curr) { return builder.makeConst(curr->value); } diff --git a/src/ir/cost.h b/src/ir/cost.h index 354f663e1..defd9413e 100644 --- a/src/ir/cost.h +++ b/src/ir/cost.h @@ -184,7 +184,7 @@ struct CostAnalyzer : public Visitor<CostAnalyzer, Index> { case ConvertSVecI32x4ToVecF32x4: case ConvertUVecI32x4ToVecF32x4: case ConvertSVecI64x2ToVecF64x2: - case ConvertUVecI64x2ToVecF64x2: assert(false && "v128 not implemented yet"); + case ConvertUVecI64x2ToVecF64x2: return 1; case InvalidUnary: WASM_UNREACHABLE(); } return ret + visit(curr->value); @@ -268,82 +268,82 @@ struct CostAnalyzer : public Visitor<CostAnalyzer, Index> { case NeFloat32: ret = 1; break; case EqFloat64: ret = 1; break; case NeFloat64: ret = 1; break; - case EqVecI8x16: - case NeVecI8x16: - case LtSVecI8x16: - case LtUVecI8x16: - case LeSVecI8x16: - case LeUVecI8x16: - case GtSVecI8x16: - case GtUVecI8x16: - case GeSVecI8x16: - case GeUVecI8x16: - case EqVecI16x8: - case NeVecI16x8: - case LtSVecI16x8: - case LtUVecI16x8: - case LeSVecI16x8: - case LeUVecI16x8: - case GtSVecI16x8: - case GtUVecI16x8: - case GeSVecI16x8: - case GeUVecI16x8: - case EqVecI32x4: - case NeVecI32x4: - case LtSVecI32x4: - case LtUVecI32x4: - case LeSVecI32x4: - case LeUVecI32x4: - case GtSVecI32x4: - case GtUVecI32x4: - case GeSVecI32x4: - case GeUVecI32x4: - case EqVecF32x4: - case NeVecF32x4: - case LtVecF32x4: - case LeVecF32x4: - case GtVecF32x4: - case GeVecF32x4: - case EqVecF64x2: - case NeVecF64x2: - case LtVecF64x2: - case LeVecF64x2: - case GtVecF64x2: - case GeVecF64x2: - case AndVec128: - case OrVec128: - case XorVec128: - case AddVecI8x16: - case AddSatSVecI8x16: - case AddSatUVecI8x16: - case SubVecI8x16: - case SubSatSVecI8x16: - case SubSatUVecI8x16: - case MulVecI8x16: - case AddVecI16x8: - case AddSatSVecI16x8: - case AddSatUVecI16x8: - case SubVecI16x8: - case SubSatSVecI16x8: - case SubSatUVecI16x8: - case MulVecI16x8: - case AddVecI32x4: - case SubVecI32x4: - case MulVecI32x4: - case AddVecI64x2: - case SubVecI64x2: - case AddVecF32x4: - case SubVecF32x4: - case MulVecF32x4: - case DivVecF32x4: - case MinVecF32x4: - case MaxVecF32x4: - case AddVecF64x2: - case SubVecF64x2: - case MulVecF64x2: - case DivVecF64x2: - case MinVecF64x2: - case MaxVecF64x2: assert(false && "v128 not implemented yet"); + case EqVecI8x16: ret = 1; break; + case NeVecI8x16: ret = 1; break; + case LtSVecI8x16: ret = 1; break; + case LtUVecI8x16: ret = 1; break; + case LeSVecI8x16: ret = 1; break; + case LeUVecI8x16: ret = 1; break; + case GtSVecI8x16: ret = 1; break; + case GtUVecI8x16: ret = 1; break; + case GeSVecI8x16: ret = 1; break; + case GeUVecI8x16: ret = 1; break; + case EqVecI16x8: ret = 1; break; + case NeVecI16x8: ret = 1; break; + case LtSVecI16x8: ret = 1; break; + case LtUVecI16x8: ret = 1; break; + case LeSVecI16x8: ret = 1; break; + case LeUVecI16x8: ret = 1; break; + case GtSVecI16x8: ret = 1; break; + case GtUVecI16x8: ret = 1; break; + case GeSVecI16x8: ret = 1; break; + case GeUVecI16x8: ret = 1; break; + case EqVecI32x4: ret = 1; break; + case NeVecI32x4: ret = 1; break; + case LtSVecI32x4: ret = 1; break; + case LtUVecI32x4: ret = 1; break; + case LeSVecI32x4: ret = 1; break; + case LeUVecI32x4: ret = 1; break; + case GtSVecI32x4: ret = 1; break; + case GtUVecI32x4: ret = 1; break; + case GeSVecI32x4: ret = 1; break; + case GeUVecI32x4: ret = 1; break; + case EqVecF32x4: ret = 1; break; + case NeVecF32x4: ret = 1; break; + case LtVecF32x4: ret = 1; break; + case LeVecF32x4: ret = 1; break; + case GtVecF32x4: ret = 1; break; + case GeVecF32x4: ret = 1; break; + case EqVecF64x2: ret = 1; break; + case NeVecF64x2: ret = 1; break; + case LtVecF64x2: ret = 1; break; + case LeVecF64x2: ret = 1; break; + case GtVecF64x2: ret = 1; break; + case GeVecF64x2: ret = 1; break; + case AndVec128: ret = 1; break; + case OrVec128: ret = 1; break; + case XorVec128: ret = 1; break; + case AddVecI8x16: ret = 1; break; + case AddSatSVecI8x16: ret = 1; break; + case AddSatUVecI8x16: ret = 1; break; + case SubVecI8x16: ret = 1; break; + case SubSatSVecI8x16: ret = 1; break; + case SubSatUVecI8x16: ret = 1; break; + case MulVecI8x16: ret = 2; break; + case AddVecI16x8: ret = 1; break; + case AddSatSVecI16x8: ret = 1; break; + case AddSatUVecI16x8: ret = 1; break; + case SubVecI16x8: ret = 1; break; + case SubSatSVecI16x8: ret = 1; break; + case SubSatUVecI16x8: ret = 1; break; + case MulVecI16x8: ret = 2; break; + case AddVecI32x4: ret = 1; break; + case SubVecI32x4: ret = 1; break; + case MulVecI32x4: ret = 2; break; + case AddVecI64x2: ret = 1; break; + case SubVecI64x2: ret = 1; break; + case AddVecF32x4: ret = 1; break; + case SubVecF32x4: ret = 1; break; + case MulVecF32x4: ret = 2; break; + case DivVecF32x4: ret = 3; break; + case MinVecF32x4: ret = 1; break; + case MaxVecF32x4: ret = 1; break; + case AddVecF64x2: ret = 1; break; + case SubVecF64x2: ret = 1; break; + case MulVecF64x2: ret = 2; break; + case DivVecF64x2: ret = 3; break; + case MinVecF64x2: ret = 1; break; + case MaxVecF64x2: ret = 1; break; case InvalidBinary: WASM_UNREACHABLE(); } return ret + visit(curr->left) + visit(curr->right); diff --git a/src/literal.h b/src/literal.h index dd5688263..6cd148b23 100644 --- a/src/literal.h +++ b/src/literal.h @@ -42,7 +42,7 @@ private: public: Literal() : type(Type::none), v128() {} - explicit Literal(Type type) : type(type), i64(0) {} + explicit Literal(Type type) : type(type), v128() {} explicit Literal(int32_t init) : type(Type::i32), i32(init) {} explicit Literal(uint32_t init) : type(Type::i32), i32(init) {} explicit Literal(int64_t init) : type(Type::i64), i64(init) {} diff --git a/src/tools/execution-results.h b/src/tools/execution-results.h index 935aff4cc..89e0440bb 100644 --- a/src/tools/execution-results.h +++ b/src/tools/execution-results.h @@ -143,4 +143,3 @@ struct ExecutionResults { }; } // namespace wasm - diff --git a/src/tools/feature-options.h b/src/tools/feature-options.h index 1bd78d9b9..282aa4d4b 100644 --- a/src/tools/feature-options.h +++ b/src/tools/feature-options.h @@ -29,12 +29,12 @@ struct FeatureOptions : public Options { FeatureOptions(const std::string& command, const std::string& description) : Options(command, description) { (*this) - .add("--mvp-features", "-mvp", "Disable all non-MVP features (default)", + .add("--mvp-features", "-mvp", "Disable all non-MVP features", Options::Arguments::Zero, [this](Options *o, const std::string& arguments) { passOptions.features = FeatureSet::MVP; }) - .add("--all-features", "-all", "Enable all features", + .add("--all-features", "-all", "Enable all features (default)", Options::Arguments::Zero, [this](Options *o, const std::string& arguments) { passOptions.features = FeatureSet::All; diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h index 999325baa..5aec51c88 100644 --- a/src/tools/fuzzing.h +++ b/src/tools/fuzzing.h @@ -177,6 +177,7 @@ private: // Optionally remove NaNs, which are a source of nondeterminism (which makes // cross-VM comparisons harder) + // TODO: de-NaN SIMD values static const bool DE_NAN = true; // Features allowed to be emitted @@ -688,8 +689,8 @@ private: case i32: case i64: case f32: - case f64: ret = _makeConcrete(type); break; - case v128: assert(false && "v128 not implemented yet"); + case f64: + case v128: ret = _makeConcrete(type); break; case none: ret = _makenone(); break; case unreachable: ret = _makeunreachable(); break; } @@ -707,24 +708,28 @@ private: if (choice < 70) return makeIf(type); if (choice < 80) return makeLoop(type); if (choice < 90) return makeBreak(type); - switch (upTo(15)) { - case 0: return makeBlock(type); - case 1: return makeIf(type); - case 2: return makeLoop(type); - case 3: return makeBreak(type); - case 4: return makeCall(type); - case 5: return makeCallIndirect(type); - case 6: return makeGetLocal(type); - case 7: return makeSetLocal(type); - case 8: return makeLoad(type); - case 9: return makeConst(type); - case 10: return makeUnary(type); - case 11: return makeBinary(type); - case 12: return makeSelect(type); - case 13: return makeGetGlobal(type); - case 14: return makeAtomic(type); - } - WASM_UNREACHABLE(); + using Self = TranslateToFuzzReader; + auto options = FeatureOptions<Expression* (Self::*)(Type)>() + .add(FeatureSet::MVP, + &Self::makeBlock, + &Self::makeIf, + &Self::makeLoop, + &Self::makeBreak, + &Self::makeCall, + &Self::makeCallIndirect, + &Self::makeGetLocal, + &Self::makeSetLocal, + &Self::makeLoad, + &Self::makeConst, + &Self::makeUnary, + &Self::makeBinary, + &Self::makeSelect, + &Self::makeGetGlobal) + .add(FeatureSet::SIMD, &Self::makeSIMD); + if (type == i32 || type == i64) { + options.add(FeatureSet::Atomics, &Self::makeAtomic); + } + return (this->*pick(options))(type); } Expression* _makenone() { @@ -881,18 +886,18 @@ private: } } + Expression* buildIf(const struct ThreeArgs& args) { + return builder.makeIf(args.a, args.b, args.c); + } + Expression* makeIf(Type type) { auto* condition = makeCondition(); hangStack.push_back(nullptr); - auto* ret = makeIf({ condition, makeMaybeBlock(type), makeMaybeBlock(type) }); + auto* ret = buildIf({ condition, makeMaybeBlock(type), makeMaybeBlock(type) }); hangStack.pop_back(); return ret; } - Expression* makeIf(const struct ThreeArgs& args) { - return builder.makeIf(args.a, args.b, args.c); - } - Expression* makeBreak(Type type) { if (breakableStack.empty()) return makeTrivial(type); Expression* condition = nullptr; @@ -1108,7 +1113,9 @@ private: case f64: { return builder.makeLoad(8, false, offset, pick(1, 2, 4, 8), ptr, type); } - case v128: assert(false && "v128 not implemented yet"); + case v128: { + return builder.makeLoad(16, false, offset, pick(1, 2, 4, 8, 16), ptr, type); + } case none: case unreachable: WASM_UNREACHABLE(); } @@ -1171,7 +1178,9 @@ private: case f64: { return builder.makeStore(8, offset, pick(1, 2, 4, 8), ptr, value, type); } - case v128: assert(false && "v128 not implemented yet"); + case v128: { + return builder.makeStore(16, offset, pick(1, 2, 4, 8, 16), ptr, value, type); + } case none: case unreachable: WASM_UNREACHABLE(); } @@ -1189,17 +1198,41 @@ private: return ret; } - Expression* makeConst(Type type) { - Literal value; + Literal makeLiteral(Type type) { + if (type == v128) { + // generate each lane individually for random lane interpretation + switch (upTo(6)) { + case 0: return Literal( + std::array<Literal, 16>{{ + makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), + makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), + makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), + makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), makeLiteral(i32) + }} + ); + case 1: return Literal( + std::array<Literal, 8>{{ + makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), + makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), makeLiteral(i32) + }} + ); + case 2: return Literal(std::array<Literal, 4>{{makeLiteral(i32), makeLiteral(i32), makeLiteral(i32), makeLiteral(i32)}}); + case 3: return Literal(std::array<Literal, 2>{{makeLiteral(i64), makeLiteral(i64)}}); + case 4: return Literal(std::array<Literal, 4>{{makeLiteral(f32), makeLiteral(f32), makeLiteral(f32), makeLiteral(f32)}}); + case 5: return Literal(std::array<Literal, 2>{{makeLiteral(f64), makeLiteral(f64)}}); + default: WASM_UNREACHABLE(); + } + } + switch (upTo(4)) { case 0: { // totally random, entire range switch (type) { - case i32: value = Literal(get32()); break; - case i64: value = Literal(get64()); break; - case f32: value = Literal(getFloat()); break; - case f64: value = Literal(getDouble()); break; - case v128: assert(false && "v128 not implemented yet"); + case i32: return Literal(get32()); + case i64: return Literal(get64()); + case f32: return Literal(getFloat()); + case f64: return Literal(getDouble()); + case v128: case none: case unreachable: WASM_UNREACHABLE(); } @@ -1218,11 +1251,11 @@ private: default: WASM_UNREACHABLE(); } switch (type) { - case i32: value = Literal(int32_t(small)); break; - case i64: value = Literal(int64_t(small)); break; - case f32: value = Literal(float(small)); break; - case f64: value = Literal(double(small)); break; - case v128: assert(false && "v128 not implemented yet"); + case i32: return Literal(int32_t(small)); + case i64: return Literal(int64_t(small)); + case f32: return Literal(float(small)); + case f64: return Literal(double(small)); + case v128: case none: case unreachable: WASM_UNREACHABLE(); } @@ -1230,6 +1263,7 @@ private: } case 2: { // special values + Literal value; switch (type) { case i32: value = Literal(pick<int32_t>(0, std::numeric_limits<int8_t>::min(), std::numeric_limits<int8_t>::max(), @@ -1260,11 +1294,9 @@ private: std::numeric_limits<int64_t>::min(), std::numeric_limits<int64_t>::max(), std::numeric_limits<uint32_t>::max(), std::numeric_limits<uint64_t>::max())); break; - case v128: assert(false && "v128 not implemented yet"); + case v128: case none: - case unreachable: { - WASM_UNREACHABLE(); - } + case unreachable: WASM_UNREACHABLE(); } // tweak around special values if (oneIn(3)) { // +- 1 @@ -1273,16 +1305,17 @@ private: if (oneIn(2)) { // flip sign value = value.mul(Literal::makeFromInt32(-1, type)); } - break; + return value; } case 3: { // powers of 2 + Literal value; switch (type) { case i32: value = Literal(int32_t(1) << upTo(32)); break; case i64: value = Literal(int64_t(1) << upTo(64)); break; case f32: value = Literal(float(int64_t(1) << upTo(64))); break; case f64: value = Literal(double(int64_t(1) << upTo(64))); break; - case v128: assert(false && "v128 not implemented yet"); + case v128: case none: case unreachable: WASM_UNREACHABLE(); } @@ -1290,15 +1323,20 @@ private: if (oneIn(2)) { value = value.mul(Literal::makeFromInt32(-1, type)); } + return value; } } + WASM_UNREACHABLE(); + } + + Expression* makeConst(Type type) { auto* ret = wasm.allocator.alloc<Const>(); - ret->value = value; - ret->type = value.type; + ret->value = makeLiteral(type); + ret->type = type; return ret; } - Expression* makeUnary(const UnaryArgs& args) { + Expression* buildUnary(const UnaryArgs& args) { return builder.makeUnary(args.a, args.b); } @@ -1312,32 +1350,40 @@ private: } switch (type) { case i32: { - switch (upTo(4)) { - case 0: { + switch (getConcreteType()) { + case i32: { auto op = pick( FeatureOptions<UnaryOp>() .add(FeatureSet::MVP, EqZInt32, ClzInt32, CtzInt32, PopcntInt32) .add(FeatureSet::Atomics, ExtendS8Int32, ExtendS16Int32) ); - return makeUnary({ op, make(i32) }); + return buildUnary({ op, make(i32) }); } - case 1: return makeUnary({ pick(EqZInt64, WrapInt64), make(i64) }); - case 2: { + case i64: return buildUnary({ pick(EqZInt64, WrapInt64), make(i64) }); + case f32: { auto op = pick( FeatureOptions<UnaryOp>() .add(FeatureSet::MVP, TruncSFloat32ToInt32, TruncUFloat32ToInt32, ReinterpretFloat32) .add(FeatureSet::TruncSat, TruncSatSFloat32ToInt32, TruncSatUFloat32ToInt32) ); - return makeUnary({ op, make(f32) }); + return buildUnary({ op, make(f32) }); } - case 3: { + case f64: { auto op = pick( FeatureOptions<UnaryOp>() .add(FeatureSet::MVP, TruncSFloat64ToInt32, TruncUFloat64ToInt32) .add(FeatureSet::TruncSat, TruncSatSFloat64ToInt32, TruncSatUFloat64ToInt32) ); - return makeUnary({ op, make(f64) }); + return buildUnary({ op, make(f64) }); } + case v128: { + assert(features.hasSIMD()); + return buildUnary({ pick(AnyTrueVecI8x16, AllTrueVecI8x16, AnyTrueVecI16x8, AllTrueVecI16x8, + AnyTrueVecI32x4, AllTrueVecI32x4, AnyTrueVecI64x2, AllTrueVecI64x2), + make(v128) }); + } + case none: + case unreachable: WASM_UNREACHABLE(); } WASM_UNREACHABLE(); } @@ -1349,16 +1395,16 @@ private: .add(FeatureSet::MVP, ClzInt64, CtzInt64, PopcntInt64) .add(FeatureSet::Atomics, ExtendS8Int64, ExtendS16Int64, ExtendS32Int64) ); - return makeUnary({ op, make(i64) }); + return buildUnary({ op, make(i64) }); } - case 1: return makeUnary({ pick(ExtendSInt32, ExtendUInt32), make(i32) }); + case 1: return buildUnary({ pick(ExtendSInt32, ExtendUInt32), make(i32) }); case 2: { auto op = pick( FeatureOptions<UnaryOp>() .add(FeatureSet::MVP, TruncSFloat32ToInt64, TruncUFloat32ToInt64) .add(FeatureSet::TruncSat, TruncSatSFloat32ToInt64, TruncSatUFloat32ToInt64) ); - return makeUnary({ op, make(f32) }); + return buildUnary({ op, make(f32) }); } case 3: { auto op = pick( @@ -1366,46 +1412,59 @@ private: .add(FeatureSet::MVP, TruncSFloat64ToInt64, TruncUFloat64ToInt64, ReinterpretFloat64) .add(FeatureSet::TruncSat, TruncSatSFloat64ToInt64, TruncSatUFloat64ToInt64) ); - return makeUnary({ op, make(f64) }); + return buildUnary({ op, make(f64) }); } } WASM_UNREACHABLE(); } case f32: { switch (upTo(4)) { - case 0: return makeDeNanOp(makeUnary({ pick(NegFloat32, AbsFloat32, CeilFloat32, FloorFloat32, TruncFloat32, NearestFloat32, SqrtFloat32), make(f32) })); - case 1: return makeDeNanOp(makeUnary({ pick(ConvertUInt32ToFloat32, ConvertSInt32ToFloat32, ReinterpretInt32), make(i32) })); - case 2: return makeDeNanOp(makeUnary({ pick(ConvertUInt64ToFloat32, ConvertSInt64ToFloat32), make(i64) })); - case 3: return makeDeNanOp(makeUnary({ DemoteFloat64, make(f64) })); + case 0: return makeDeNanOp(buildUnary({ pick(NegFloat32, AbsFloat32, CeilFloat32, FloorFloat32, TruncFloat32, NearestFloat32, SqrtFloat32), make(f32) })); + case 1: return makeDeNanOp(buildUnary({ pick(ConvertUInt32ToFloat32, ConvertSInt32ToFloat32, ReinterpretInt32), make(i32) })); + case 2: return makeDeNanOp(buildUnary({ pick(ConvertUInt64ToFloat32, ConvertSInt64ToFloat32), make(i64) })); + case 3: return makeDeNanOp(buildUnary({ DemoteFloat64, make(f64) })); } WASM_UNREACHABLE(); } case f64: { switch (upTo(4)) { - case 0: return makeDeNanOp(makeUnary({ pick(NegFloat64, AbsFloat64, CeilFloat64, FloorFloat64, TruncFloat64, NearestFloat64, SqrtFloat64), make(f64) })); - case 1: return makeDeNanOp(makeUnary({ pick(ConvertUInt32ToFloat64, ConvertSInt32ToFloat64), make(i32) })); - case 2: return makeDeNanOp(makeUnary({ pick(ConvertUInt64ToFloat64, ConvertSInt64ToFloat64, ReinterpretInt64), make(i64) })); - case 3: return makeDeNanOp(makeUnary({ PromoteFloat32, make(f32) })); + case 0: return makeDeNanOp(buildUnary({ pick(NegFloat64, AbsFloat64, CeilFloat64, FloorFloat64, TruncFloat64, NearestFloat64, SqrtFloat64), make(f64) })); + case 1: return makeDeNanOp(buildUnary({ pick(ConvertUInt32ToFloat64, ConvertSInt32ToFloat64), make(i32) })); + case 2: return makeDeNanOp(buildUnary({ pick(ConvertUInt64ToFloat64, ConvertSInt64ToFloat64, ReinterpretInt64), make(i64) })); + case 3: return makeDeNanOp(buildUnary({ PromoteFloat32, make(f32) })); } WASM_UNREACHABLE(); } - case v128: assert(false && "v128 not implemented yet"); - case none: - case unreachable: { + case v128: { + assert(features.hasSIMD()); + switch (upTo(5)) { + case 0: return buildUnary({ pick(SplatVecI8x16, SplatVecI16x8, SplatVecI32x4), make(i32) }); + case 1: return buildUnary({ SplatVecI64x2, make(i64) }); + case 2: return buildUnary({ SplatVecF32x4, make(f32) }); + case 3: return buildUnary({ SplatVecF64x2, make(f64) }); + case 4: return buildUnary({ + pick(NotVec128, NegVecI8x16, NegVecI16x8, NegVecI32x4, NegVecI64x2, + AbsVecF32x4, NegVecF32x4, SqrtVecF32x4, AbsVecF64x2, NegVecF64x2, SqrtVecF64x2, + TruncSatSVecF32x4ToVecI32x4, TruncSatUVecF32x4ToVecI32x4, TruncSatSVecF64x2ToVecI64x2, TruncSatUVecF64x2ToVecI64x2, + ConvertSVecI32x4ToVecF32x4, ConvertUVecI32x4ToVecF32x4, ConvertSVecI64x2ToVecF64x2, ConvertUVecI64x2ToVecF64x2), + make(v128) }); + } WASM_UNREACHABLE(); } + case none: + case unreachable: WASM_UNREACHABLE(); } WASM_UNREACHABLE(); } - Expression* makeBinary(const BinaryArgs& args) { + Expression* buildBinary(const BinaryArgs& args) { return builder.makeBinary(args.a, args.b, args.c); } Expression* makeBinary(Type type) { if (type == unreachable) { if (auto* binary = makeBinary(getConcreteType())->dynCast<Binary>()) { - return makeDeNanOp(makeBinary({ binary->op, make(unreachable), make(unreachable) })); + return makeDeNanOp(buildBinary({ binary->op, make(unreachable), make(unreachable) })); } // give up return makeTrivial(type); @@ -1413,35 +1472,47 @@ private: switch (type) { case i32: { switch (upTo(4)) { - case 0: return makeBinary({ pick(AddInt32, SubInt32, MulInt32, DivSInt32, DivUInt32, RemSInt32, RemUInt32, AndInt32, OrInt32, XorInt32, ShlInt32, ShrUInt32, ShrSInt32, RotLInt32, RotRInt32, EqInt32, NeInt32, LtSInt32, LtUInt32, LeSInt32, LeUInt32, GtSInt32, GtUInt32, GeSInt32, GeUInt32), make(i32), make(i32) }); - case 1: return makeBinary({ pick(EqInt64, NeInt64, LtSInt64, LtUInt64, LeSInt64, LeUInt64, GtSInt64, GtUInt64, GeSInt64, GeUInt64), make(i64), make(i64) }); - case 2: return makeBinary({ pick(EqFloat32, NeFloat32, LtFloat32, LeFloat32, GtFloat32, GeFloat32), make(f32), make(f32) }); - case 3: return makeBinary({ pick(EqFloat64, NeFloat64, LtFloat64, LeFloat64, GtFloat64, GeFloat64), make(f64), make(f64) }); + case 0: return buildBinary({ pick(AddInt32, SubInt32, MulInt32, DivSInt32, DivUInt32, RemSInt32, RemUInt32, AndInt32, OrInt32, XorInt32, ShlInt32, ShrUInt32, ShrSInt32, RotLInt32, RotRInt32, EqInt32, NeInt32, LtSInt32, LtUInt32, LeSInt32, LeUInt32, GtSInt32, GtUInt32, GeSInt32, GeUInt32), make(i32), make(i32) }); + case 1: return buildBinary({ pick(EqInt64, NeInt64, LtSInt64, LtUInt64, LeSInt64, LeUInt64, GtSInt64, GtUInt64, GeSInt64, GeUInt64), make(i64), make(i64) }); + case 2: return buildBinary({ pick(EqFloat32, NeFloat32, LtFloat32, LeFloat32, GtFloat32, GeFloat32), make(f32), make(f32) }); + case 3: return buildBinary({ pick(EqFloat64, NeFloat64, LtFloat64, LeFloat64, GtFloat64, GeFloat64), make(f64), make(f64) }); } WASM_UNREACHABLE(); } case i64: { - return makeBinary({ pick(AddInt64, SubInt64, MulInt64, DivSInt64, DivUInt64, RemSInt64, RemUInt64, AndInt64, OrInt64, XorInt64, ShlInt64, ShrUInt64, ShrSInt64, RotLInt64, RotRInt64), make(i64), make(i64) }); + return buildBinary({ pick(AddInt64, SubInt64, MulInt64, DivSInt64, DivUInt64, RemSInt64, RemUInt64, AndInt64, OrInt64, XorInt64, ShlInt64, ShrUInt64, ShrSInt64, RotLInt64, RotRInt64), make(i64), make(i64) }); } case f32: { - return makeDeNanOp(makeBinary({ pick(AddFloat32, SubFloat32, MulFloat32, DivFloat32, CopySignFloat32, MinFloat32, MaxFloat32), make(f32), make(f32) })); + return makeDeNanOp(buildBinary({ pick(AddFloat32, SubFloat32, MulFloat32, DivFloat32, CopySignFloat32, MinFloat32, MaxFloat32), make(f32), make(f32) })); } case f64: { - return makeDeNanOp(makeBinary({ pick(AddFloat64, SubFloat64, MulFloat64, DivFloat64, CopySignFloat64, MinFloat64, MaxFloat64), make(f64), make(f64) })); + return makeDeNanOp(buildBinary({ pick(AddFloat64, SubFloat64, MulFloat64, DivFloat64, CopySignFloat64, MinFloat64, MaxFloat64), make(f64), make(f64) })); + } + case v128: { + assert(features.hasSIMD()); + return buildBinary({ + pick(EqVecI8x16, NeVecI8x16, LtSVecI8x16, LtUVecI8x16, GtSVecI8x16, GtUVecI8x16, LeSVecI8x16, LeUVecI8x16, GeSVecI8x16, GeUVecI8x16, + EqVecI16x8, NeVecI16x8, LtSVecI16x8, LtUVecI16x8, GtSVecI16x8, GtUVecI16x8, LeSVecI16x8, LeUVecI16x8, GeSVecI16x8, GeUVecI16x8, + EqVecI32x4, NeVecI32x4, LtSVecI32x4, LtUVecI32x4, GtSVecI32x4, GtUVecI32x4, LeSVecI32x4, LeUVecI32x4, GeSVecI32x4, GeUVecI32x4, + EqVecF32x4, NeVecF32x4, LtVecF32x4, GtVecF32x4, LeVecF32x4, GeVecF32x4, EqVecF64x2, NeVecF64x2, LtVecF64x2, GtVecF64x2, LeVecF64x2, GeVecF64x2, + AndVec128, OrVec128, XorVec128, AddVecI8x16, AddSatSVecI8x16, AddSatUVecI8x16, SubVecI8x16, SubSatSVecI8x16, SubSatUVecI8x16, MulVecI8x16, + AddVecI16x8, AddSatSVecI16x8, AddSatUVecI16x8, SubVecI16x8, SubSatSVecI16x8, SubSatUVecI16x8, MulVecI16x8, AddVecI32x4, SubVecI32x4, MulVecI32x4, + AddVecI64x2, SubVecI64x2, AddVecF32x4, SubVecF32x4, MulVecF32x4, DivVecF32x4, MinVecF32x4, MaxVecF32x4, + AddVecF64x2, SubVecF64x2, MulVecF64x2, DivVecF64x2, MinVecF64x2, MaxVecF64x2), + make(v128), make(v128) }); } - case v128: assert(false && "v128 not implemented yet"); case none: case unreachable: WASM_UNREACHABLE(); } WASM_UNREACHABLE(); } - Expression* makeSelect(const ThreeArgs& args) { + Expression* buildSelect(const ThreeArgs& args) { return builder.makeSelect(args.a, args.b, args.c); } Expression* makeSelect(Type type) { - return makeDeNanOp(makeSelect({ make(i32), make(type), make(type) })); + return makeDeNanOp(buildSelect({ make(i32), make(type), make(type) })); } Expression* makeSwitch(Type type) { @@ -1493,7 +1564,7 @@ private: } Expression* makeAtomic(Type type) { - if (!features.hasAtomics() || (type != i32 && type != i64)) return makeTrivial(type); + assert(features.hasAtomics()); wasm.memory.shared = true; if (type == i32 && oneIn(2)) { if (ATOMIC_WAITS && oneIn(2)) { @@ -1544,6 +1615,92 @@ private: } } + Expression* makeSIMD(Type type) { + assert(features.hasSIMD()); + if (type != v128) { + return makeSIMDExtract(type); + } + switch (upTo(6)) { + case 0: return makeUnary(v128); + case 1: return makeBinary(v128); + case 2: return makeSIMDReplace(); + case 3: return makeSIMDShuffle(); + case 4: return makeSIMDBitselect(); + case 5: return makeSIMDShift(); + } + WASM_UNREACHABLE(); + } + + Expression* makeSIMDExtract(Type type) { + auto op = static_cast<SIMDExtractOp>(0); + switch (type) { + case i32: op = pick(ExtractLaneSVecI8x16, ExtractLaneUVecI8x16, ExtractLaneSVecI16x8, ExtractLaneUVecI16x8, ExtractLaneVecI32x4); break; + case i64: op = ExtractLaneVecI64x2; break; + case f32: op = ExtractLaneVecF32x4; break; + case f64: op = ExtractLaneVecF64x2; break; + case v128: + case none: + case unreachable: WASM_UNREACHABLE(); + } + Expression* vec = make(v128); + uint8_t idx = 0; + switch (op) { + case ExtractLaneSVecI8x16: + case ExtractLaneUVecI8x16: idx = upTo(16); break; + case ExtractLaneSVecI16x8: + case ExtractLaneUVecI16x8: idx = upTo(8); break; + case ExtractLaneVecI32x4: + case ExtractLaneVecF32x4: idx = upTo(4); break; + case ExtractLaneVecI64x2: + case ExtractLaneVecF64x2: idx = upTo(2); break; + } + return builder.makeSIMDExtract(op, vec, idx); + } + + Expression* makeSIMDReplace() { + SIMDReplaceOp op = pick(ReplaceLaneVecI8x16, ReplaceLaneVecI16x8, ReplaceLaneVecI32x4, + ReplaceLaneVecI64x2, ReplaceLaneVecF32x4, ReplaceLaneVecF64x2); + Expression* vec = make(v128); + uint8_t idx; + Type lane_t; + switch (op) { + case ReplaceLaneVecI8x16: idx = upTo(16); lane_t = i32; break; + case ReplaceLaneVecI16x8: idx = upTo(8); lane_t = i32; break; + case ReplaceLaneVecI32x4: idx = upTo(4); lane_t = i32; break; + case ReplaceLaneVecI64x2: idx = upTo(2); lane_t = i64; break; + case ReplaceLaneVecF32x4: idx = upTo(4); lane_t = f32; break; + case ReplaceLaneVecF64x2: idx = upTo(2); lane_t = f64; break; + default: WASM_UNREACHABLE(); + } + Expression* value = make(lane_t); + return builder.makeSIMDReplace(op, vec, idx, value); + } + + Expression* makeSIMDShuffle() { + Expression* left = make(v128); + Expression* right = make(v128); + std::array<uint8_t, 16> mask; + for (size_t i = 0; i < 16; ++i) { + mask[i] = upTo(32); + } + return builder.makeSIMDShuffle(left, right, mask); + } + + Expression* makeSIMDBitselect() { + Expression* left = make(v128); + Expression* right = make(v128); + Expression* cond = make(v128); + return builder.makeSIMDBitselect(left, right, cond); + } + + Expression* makeSIMDShift() { + SIMDShiftOp op = pick(ShlVecI8x16, ShrSVecI8x16, ShrUVecI8x16, ShlVecI16x8, ShrSVecI16x8, ShrUVecI16x8, + ShlVecI32x4, ShrSVecI32x4, ShrUVecI32x4, ShlVecI64x2, ShrSVecI64x2, ShrUVecI64x2); + Expression* vec = make(v128); + Expression* shift = make(i32); + return builder.makeSIMDShift(op, vec, shift); + } + // special makers Expression* makeLogging() { @@ -1554,36 +1711,21 @@ private: // special getters Type getType() { - switch (upTo(6)) { - case 0: return i32; - case 1: return i64; - case 2: return f32; - case 3: return f64; - case 4: return none; - case 5: return unreachable; - } - WASM_UNREACHABLE(); + return pick(FeatureOptions<Type>() + .add(FeatureSet::MVP, i32, i64, f32, f64, none, unreachable) + .add(FeatureSet::SIMD, v128)); } Type getReachableType() { - switch (upTo(5)) { - case 0: return i32; - case 1: return i64; - case 2: return f32; - case 3: return f64; - case 4: return none; - } - WASM_UNREACHABLE(); + return pick(FeatureOptions<Type>() + .add(FeatureSet::MVP, i32, i64, f32, f64, none) + .add(FeatureSet::SIMD, v128)); } Type getConcreteType() { - switch (upTo(4)) { - case 0: return i32; - case 1: return i64; - case 2: return f32; - case 3: return f64; - } - WASM_UNREACHABLE(); + return pick(FeatureOptions<Type>() + .add(FeatureSet::MVP, i32, i64, f32, f64) + .add(FeatureSet::SIMD, v128)); } // statistical distributions diff --git a/src/tools/spec-wrapper.h b/src/tools/spec-wrapper.h index d6aa0d87e..a42230fc1 100644 --- a/src/tools/spec-wrapper.h +++ b/src/tools/spec-wrapper.h @@ -34,7 +34,7 @@ static std::string generateSpecWrapper(Module& wasm) { case i64: ret += "(i64.const 0)"; break; case f32: ret += "(f32.const 0)"; break; case f64: ret += "(f64.const 0)"; break; - case v128: assert(false && "v128 not implemented yet"); + case v128: ret += "(v128.const i32 0 0 0 0)"; break; case none: case unreachable: WASM_UNREACHABLE(); } diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index e970a4f83..cec5dd983 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -589,7 +589,7 @@ public: return left.shuffleV8x16(right, curr->mask); } Flow visitSIMDBitselect(SIMDBitselect *curr) { - NOTE_ENTER("SIMDShuffle"); + NOTE_ENTER("SIMDBitselect"); Flow flow = this->visit(curr->left); if (flow.breaking()) return flow; Literal left = flow.value; diff --git a/src/wasm-traversal.h b/src/wasm-traversal.h index 200a67cb6..c79be1c10 100644 --- a/src/wasm-traversal.h +++ b/src/wasm-traversal.h @@ -591,27 +591,27 @@ struct PostWalker : public Walker<SubType, VisitorType> { } case Expression::Id::SIMDReplaceId: { self->pushTask(SubType::doVisitSIMDReplace, currp); - self->pushTask(SubType::scan, &curr->cast<SIMDReplace>()->vec); self->pushTask(SubType::scan, &curr->cast<SIMDReplace>()->value); + self->pushTask(SubType::scan, &curr->cast<SIMDReplace>()->vec); break; } case Expression::Id::SIMDShuffleId: { self->pushTask(SubType::doVisitSIMDShuffle, currp); - self->pushTask(SubType::scan, &curr->cast<SIMDShuffle>()->left); self->pushTask(SubType::scan, &curr->cast<SIMDShuffle>()->right); + self->pushTask(SubType::scan, &curr->cast<SIMDShuffle>()->left); break; } case Expression::Id::SIMDBitselectId: { self->pushTask(SubType::doVisitSIMDBitselect, currp); - self->pushTask(SubType::scan, &curr->cast<SIMDBitselect>()->left); - self->pushTask(SubType::scan, &curr->cast<SIMDBitselect>()->right); self->pushTask(SubType::scan, &curr->cast<SIMDBitselect>()->cond); + self->pushTask(SubType::scan, &curr->cast<SIMDBitselect>()->right); + self->pushTask(SubType::scan, &curr->cast<SIMDBitselect>()->left); break; } case Expression::Id::SIMDShiftId: { self->pushTask(SubType::doVisitSIMDShift, currp); - self->pushTask(SubType::scan, &curr->cast<SIMDShift>()->vec); self->pushTask(SubType::scan, &curr->cast<SIMDShift>()->shift); + self->pushTask(SubType::scan, &curr->cast<SIMDShift>()->vec); break; } case Expression::Id::ConstId: { diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index a373a5ec6..bdd8f327e 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -2544,6 +2544,7 @@ bool WasmBinaryBuilder::maybeVisitSIMDLoad(Expression*& out, uint32_t code) { curr->type = v128; curr->bytes = 16; readMemoryAccess(curr->align, curr->offset); + curr->isAtomic = false; curr->ptr = popNonVoidExpression(); curr->finalize(); out = curr; @@ -2558,6 +2559,7 @@ bool WasmBinaryBuilder::maybeVisitSIMDStore(Expression*& out, uint32_t code) { curr->bytes = 16; curr->valueType = v128; readMemoryAccess(curr->align, curr->offset); + curr->isAtomic = false; curr->value = popNonVoidExpression(); curr->ptr = popNonVoidExpression(); curr->finalize(); diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index e1838bb71..7137e0860 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -502,7 +502,10 @@ void FunctionValidator::visitSetGlobal(SetGlobal* curr) { } void FunctionValidator::visitLoad(Load* curr) { - if (curr->isAtomic) shouldBeTrue(info.features.hasAtomics(), curr, "Atomic operation (atomics are disabled)"); + if (curr->isAtomic) { + shouldBeTrue(info.features.hasAtomics(), curr, "Atomic operation (atomics are disabled)"); + shouldBeTrue(curr->type == i32 || curr->type == i64 || curr->type == unreachable, curr, "Atomic load should be i32 or i64"); + } if (curr->type == v128) shouldBeTrue(info.features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)"); shouldBeFalse(curr->isAtomic && !getModule()->memory.shared, curr, "Atomic operation with non-shared memory"); validateMemBytes(curr->bytes, curr->type, curr); @@ -515,7 +518,10 @@ void FunctionValidator::visitLoad(Load* curr) { } void FunctionValidator::visitStore(Store* curr) { - if (curr->isAtomic) shouldBeTrue(info.features.hasAtomics(), curr, "Atomic operation (atomics are disabled)"); + if (curr->isAtomic) { + shouldBeTrue(info.features.hasAtomics(), curr, "Atomic operation (atomics are disabled)"); + shouldBeTrue(curr->valueType == i32 || curr->valueType == i64 || curr->valueType == unreachable, curr, "Atomic store should be i32 or i64"); + } if (curr->valueType == v128) shouldBeTrue(info.features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)"); shouldBeFalse(curr->isAtomic && !getModule()->memory.shared, curr, "Atomic operation with non-shared memory"); validateMemBytes(curr->bytes, curr->valueType, curr); |