summaryrefslogtreecommitdiff
path: root/src/tools
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/asm2wasm.cpp2
-rw-r--r--src/tools/execution-results.h1
-rw-r--r--src/tools/feature-options.h19
-rw-r--r--src/tools/fuzzing.h412
-rw-r--r--src/tools/js-wrapper.h18
-rw-r--r--src/tools/spec-wrapper.h2
-rw-r--r--src/tools/wasm-ctor-eval.cpp6
-rw-r--r--src/tools/wasm-emscripten-finalize.cpp18
-rw-r--r--src/tools/wasm-merge.cpp2
-rw-r--r--src/tools/wasm-reduce.cpp2
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);
}