diff options
Diffstat (limited to 'src/tools')
-rw-r--r-- | src/tools/asm2wasm.cpp | 2 | ||||
-rw-r--r-- | src/tools/execution-results.h | 1 | ||||
-rw-r--r-- | src/tools/feature-options.h | 19 | ||||
-rw-r--r-- | src/tools/fuzzing.h | 412 | ||||
-rw-r--r-- | src/tools/js-wrapper.h | 18 | ||||
-rw-r--r-- | src/tools/spec-wrapper.h | 2 | ||||
-rw-r--r-- | src/tools/wasm-ctor-eval.cpp | 6 | ||||
-rw-r--r-- | src/tools/wasm-emscripten-finalize.cpp | 18 | ||||
-rw-r--r-- | src/tools/wasm-merge.cpp | 2 | ||||
-rw-r--r-- | src/tools/wasm-reduce.cpp | 2 |
10 files changed, 333 insertions, 149 deletions
diff --git a/src/tools/asm2wasm.cpp b/src/tools/asm2wasm.cpp index a001c08a6..4907033e5 100644 --- a/src/tools/asm2wasm.cpp +++ b/src/tools/asm2wasm.cpp @@ -96,7 +96,7 @@ int main(int argc, const char *argv[]) { [&wasmOnly](Options *o, const std::string& ) { wasmOnly = true; }) - .add("--no-legalize-javascript-ffi", "-nj", "Do not legalize (i64->i32, f32->f64) the imports and exports for interfacing with JS", Options::Arguments::Zero, + .add("--no-legalize-javascript-ffi", "-nj", "Do not fully legalize (i64->i32, f32->f64) the imports and exports for interfacing with JS", Options::Arguments::Zero, [&legalizeJavaScriptFFI](Options *o, const std::string& ) { legalizeJavaScriptFFI = false; }) 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 3b35656fc..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; @@ -70,7 +70,20 @@ struct FeatureOptions : public Options { Options::Arguments::Zero, [this](Options *o, const std::string& arguments) { passOptions.features.setTruncSat(false); - }); + }) + .add("--enable-simd", "", + "Enable nontrapping float-to-int operations", + Options::Arguments::Zero, + [this](Options *o, const std::string& arguments) { + passOptions.features.setSIMD(); + }) + .add("--disable-simd", "", + "Disable nontrapping float-to-int operations", + Options::Arguments::Zero, + [this](Options *o, const std::string& arguments) { + passOptions.features.setSIMD(false); + }) + ; } FeatureSet getFeatures() const { diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h index dcb47529f..56c633f14 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 @@ -313,6 +314,7 @@ private: func->base = name; func->params.push_back(type); func->result = none; + func->type = ensureFunctionType(getSig(func), &wasm)->name; wasm.addFunction(func); } } @@ -688,8 +690,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 +709,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 +887,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; @@ -1079,7 +1085,7 @@ private: return ret; } - Load* makeNonAtomicLoad(Type type) { + Expression* makeNonAtomicLoad(Type type) { auto offset = logify(get()); auto ptr = makePointer(); switch (type) { @@ -1108,7 +1114,12 @@ 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: { + if (!features.hasSIMD()) { + return makeTrivial(type); + } + return builder.makeLoad(16, false, offset, pick(1, 2, 4, 8, 16), ptr, type); + } case none: case unreachable: WASM_UNREACHABLE(); } @@ -1120,24 +1131,27 @@ private: if (type != i32 && type != i64) return ret; if (!features.hasAtomics() || oneIn(2)) return ret; // make it atomic + auto* load = ret->cast<Load>(); wasm.memory.shared = true; - ret->isAtomic = true; - ret->signed_ = false; - ret->align = ret->bytes; - return ret; + load->isAtomic = true; + load->signed_ = false; + load->align = load->bytes; + return load; } - Store* makeNonAtomicStore(Type type) { + Expression* makeNonAtomicStore(Type type) { if (type == unreachable) { // make a normal store, then make it unreachable auto* ret = makeNonAtomicStore(getConcreteType()); + auto* store = ret->dynCast<Store>(); + if (!store) return ret; switch (upTo(3)) { - case 0: ret->ptr = make(unreachable); break; - case 1: ret->value = make(unreachable); break; - case 2: ret->ptr = make(unreachable); ret->value = make(unreachable); break; + case 0: store->ptr = make(unreachable); break; + case 1: store->value = make(unreachable); break; + case 2: store->ptr = make(unreachable); store->value = make(unreachable); break; } - ret->finalize(); - return ret; + store->finalize(); + return store; } // the type is none or unreachable. we also need to pick the value // type. @@ -1171,35 +1185,66 @@ 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: { + if (!features.hasSIMD()) { + return makeTrivial(type); + } + return builder.makeStore(16, offset, pick(1, 2, 4, 8, 16), ptr, value, type); + } case none: case unreachable: WASM_UNREACHABLE(); } WASM_UNREACHABLE(); } - Store* makeStore(Type type) { + Expression* makeStore(Type type) { auto* ret = makeNonAtomicStore(type); - if (ret->value->type != i32 && ret->value->type != i64) return ret; - if (!features.hasAtomics() || oneIn(2)) return ret; + auto* store = ret->dynCast<Store>(); + if (!store) return ret; + if (store->value->type != i32 && store->value->type != i64) return store; + if (!features.hasAtomics() || oneIn(2)) return store; // make it atomic wasm.memory.shared = true; - ret->isAtomic = true; - ret->align = ret->bytes; - return ret; - } + store->isAtomic = true; + store->align = store->bytes; + return store; + } + + 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(); + } + } - Expression* makeConst(Type type) { - Literal value; 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 +1263,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 +1275,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,45 +1306,49 @@ 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 - value = value.add(LiteralUtils::makeLiteralFromInt32(upTo(3) - 1, type)); + value = value.add(Literal::makeFromInt32(upTo(3) - 1, type)); } if (oneIn(2)) { // flip sign - value = value.mul(LiteralUtils::makeLiteralFromInt32(-1, type)); + 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(); } // maybe negative if (oneIn(2)) { - value = value.mul(LiteralUtils::makeLiteralFromInt32(-1, type)); + 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 +1362,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 +1407,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 +1424,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 +1484,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 +1576,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 +1627,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 index = 0; + switch (op) { + case ExtractLaneSVecI8x16: + case ExtractLaneUVecI8x16: index = upTo(16); break; + case ExtractLaneSVecI16x8: + case ExtractLaneUVecI16x8: index = upTo(8); break; + case ExtractLaneVecI32x4: + case ExtractLaneVecF32x4: index = upTo(4); break; + case ExtractLaneVecI64x2: + case ExtractLaneVecF64x2: index = upTo(2); break; + } + return builder.makeSIMDExtract(op, vec, index); + } + + Expression* makeSIMDReplace() { + SIMDReplaceOp op = pick(ReplaceLaneVecI8x16, ReplaceLaneVecI16x8, ReplaceLaneVecI32x4, + ReplaceLaneVecI64x2, ReplaceLaneVecF32x4, ReplaceLaneVecF64x2); + Expression* vec = make(v128); + uint8_t index; + Type lane_t; + switch (op) { + case ReplaceLaneVecI8x16: index = upTo(16); lane_t = i32; break; + case ReplaceLaneVecI16x8: index = upTo(8); lane_t = i32; break; + case ReplaceLaneVecI32x4: index = upTo(4); lane_t = i32; break; + case ReplaceLaneVecI64x2: index = upTo(2); lane_t = i64; break; + case ReplaceLaneVecF32x4: index = upTo(4); lane_t = f32; break; + case ReplaceLaneVecF64x2: index = upTo(2); lane_t = f64; break; + default: WASM_UNREACHABLE(); + } + Expression* value = make(lane_t); + return builder.makeSIMDReplace(op, vec, index, 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 +1723,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/js-wrapper.h b/src/tools/js-wrapper.h index cb5c0bd5b..7cf2ffc53 100644 --- a/src/tools/js-wrapper.h +++ b/src/tools/js-wrapper.h @@ -22,10 +22,15 @@ namespace wasm { static std::string generateJSWrapper(Module& wasm) { + PassRunner runner(&wasm); + runner.add("legalize-js-interface"); + runner.run(); + std::string ret; ret += "if (typeof console === 'undefined') {\n" " console = { log: print };\n" "}\n" + "var tempRet0;\n" "var binary;\n" "if (typeof process === 'object' && typeof require === 'function' /* node.js detection */) {\n" " var args = process.argv.slice(2);\n" @@ -44,7 +49,18 @@ static std::string generateJSWrapper(Module& wasm) { " binary = read(args[0], 'binary');\n" " }\n" "}\n" - "var instance = new WebAssembly.Instance(new WebAssembly.Module(binary), {});\n"; + "var instance = new WebAssembly.Instance(new WebAssembly.Module(binary), {\n" + " 'fuzzing-support': {\n" + " 'log-i32': function(x) { console.log('i32: ' + x) },\n" + " 'log-i64': function(x, y) { console.log('i64: ' + x + ', ' + y) },\n" + " 'log-f32': function(x) { console.log('f32: ' + x) },\n" + " 'log-f64': function(x) { console.log('f64: ' + x) }\n" + " },\n" + " 'env': {\n" + " 'setTempRet0': function(x) { tempRet0 = x },\n" + " 'getTempRet0': function() { return tempRet0 },\n" + " },\n" + "});\n"; for (auto& exp : wasm.exports) { auto* func = wasm.getFunctionOrNull(exp->value); if (!func) continue; // something exported other than a function 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/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp index b0e2e2ce7..c0730877a 100644 --- a/src/tools/wasm-ctor-eval.cpp +++ b/src/tools/wasm-ctor-eval.cpp @@ -195,12 +195,12 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface { // fill in fake values for everything else, which is dangerous to use ModuleUtils::iterDefinedGlobals(wasm_, [&](Global* defined) { if (globals.find(defined->name) == globals.end()) { - globals[defined->name] = LiteralUtils::makeLiteralZero(defined->type); + globals[defined->name] = Literal::makeZero(defined->type); } }); ModuleUtils::iterImportedGlobals(wasm_, [&](Global* import) { if (globals.find(import->name) == globals.end()) { - globals[import->name] = LiteralUtils::makeLiteralZero(import->type); + globals[import->name] = Literal::makeZero(import->type); } }); } @@ -226,7 +226,7 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface { } else if (segment.offset->is<GetGlobal>()) { start = 0; } else { - WASM_UNREACHABLE(); // wasm spec only allows const and get_global there + WASM_UNREACHABLE(); // wasm spec only allows const and global.get there } auto end = start + segment.data.size(); if (start <= index && index < end) { diff --git a/src/tools/wasm-emscripten-finalize.cpp b/src/tools/wasm-emscripten-finalize.cpp index 6c5682703..3d74138cc 100644 --- a/src/tools/wasm-emscripten-finalize.cpp +++ b/src/tools/wasm-emscripten-finalize.cpp @@ -30,6 +30,7 @@ #include "wasm-io.h" #include "wasm-printing.h" #include "wasm-validator.h" +#include "abi/js.h" using namespace cashew; using namespace wasm; @@ -83,7 +84,7 @@ int main(int argc, const char *argv[]) { .add("--input-source-map", "-ism", "Consume source map from the specified file", Options::Arguments::One, [&inputSourceMapFilename](Options *o, const std::string& argument) { inputSourceMapFilename = argument; }) - .add("--no-legalize-javascript-ffi", "-nj", "Do not legalize (i64->i32, " + .add("--no-legalize-javascript-ffi", "-nj", "Do not fully legalize (i64->i32, " "f32->f64) the imports and exports for interfacing with JS", Options::Arguments::Zero, [&legalizeJavaScriptFFI](Options *o, const std::string& ) { @@ -158,13 +159,14 @@ int main(int argc, const char *argv[]) { EmscriptenGlueGenerator generator(wasm); generator.fixInvokeFunctionNames(); - if (legalizeJavaScriptFFI) { - PassRunner passRunner(&wasm); - passRunner.setDebug(options.debug); - passRunner.setDebugInfo(debugInfo); - passRunner.add("legalize-js-interface"); - passRunner.run(); - } + PassRunner passRunner(&wasm); + passRunner.setDebug(options.debug); + passRunner.setDebugInfo(debugInfo); + passRunner.add(ABI::getLegalizationPass( + legalizeJavaScriptFFI ? ABI::LegalizationLevel::Full + : ABI::LegalizationLevel::Minimal + )); + passRunner.run(); std::vector<Name> initializerFunctions; diff --git a/src/tools/wasm-merge.cpp b/src/tools/wasm-merge.cpp index 52f682e16..e9ac6d649 100644 --- a/src/tools/wasm-merge.cpp +++ b/src/tools/wasm-merge.cpp @@ -500,7 +500,7 @@ struct InputMergeable : public ExpressionStackWalker<InputMergeable, Visitor<Inp } private: - // add an offset to a get_global. we look above, and if there is already an add, + // add an offset to a global.get. we look above, and if there is already an add, // we can add into it, avoiding creating a new node void addBump(Index bump) { if (expressionStack.size() >= 2) { diff --git a/src/tools/wasm-reduce.cpp b/src/tools/wasm-reduce.cpp index 8c5df7a1b..02174bb8a 100644 --- a/src/tools/wasm-reduce.cpp +++ b/src/tools/wasm-reduce.cpp @@ -839,7 +839,7 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor< // try to replace with a trivial value Const* c = builder->makeConst(Literal(int32_t(0))); if (tryToReplaceCurrent(c)) return true; - c->value = LiteralUtils::makeLiteralFromInt32(1, curr->type); + c->value = Literal::makeFromInt32(1, curr->type); c->type = curr->type; return tryToReplaceCurrent(c); } |