summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rwxr-xr-xbuild-js.sh1
-rw-r--r--scripts/test/shared.py9
-rw-r--r--src/asm2wasm.h1
-rw-r--r--src/binaryen-c.cpp6
-rw-r--r--src/ir/memory-utils.h6
-rw-r--r--src/ir/module-utils.h69
-rw-r--r--src/pass.h5
-rw-r--r--src/passes/CMakeLists.txt1
-rw-r--r--src/passes/MemoryPacking.cpp2
-rw-r--r--src/passes/Print.cpp1
-rw-r--r--src/passes/SafeHeap.cpp2
-rw-r--r--src/passes/Strip.cpp5
-rw-r--r--src/passes/StripTargetFeatures.cpp33
-rw-r--r--src/passes/pass.cpp4
-rw-r--r--src/support/file.cpp3
-rw-r--r--src/tools/asm2wasm.cpp6
-rw-r--r--src/tools/fuzzing.h31
-rw-r--r--src/tools/optimization-options.h1
-rw-r--r--src/tools/tool-options.h40
-rw-r--r--src/tools/wasm-as.cpp4
-rw-r--r--src/tools/wasm-ctor-eval.cpp3
-rw-r--r--src/tools/wasm-emscripten-finalize.cpp4
-rw-r--r--src/tools/wasm-opt.cpp12
-rw-r--r--src/tools/wasm-reduce.cpp2
-rw-r--r--src/tools/wasm-shell.cpp7
-rw-r--r--src/tools/wasm2js.cpp4
-rw-r--r--src/wasm-binary.h11
-rw-r--r--src/wasm-emscripten.h4
-rw-r--r--src/wasm-features.h6
-rw-r--r--src/wasm-validator.h2
-rw-r--r--src/wasm.h7
-rw-r--r--src/wasm/wasm-binary.cpp86
-rw-r--r--src/wasm/wasm-emscripten.cpp5
-rw-r--r--src/wasm/wasm-validator.cpp56
-rw-r--r--test/debugInfo.fromasm.clamp.no-opts.map2
-rw-r--r--test/debugInfo.fromasm.imprecise.no-opts.map2
-rw-r--r--test/debugInfo.fromasm.no-opts.map2
-rw-r--r--test/lld/duplicate_imports.wast.out6
-rw-r--r--test/lld/em_asm.wast.mem.out6
-rw-r--r--test/lld/em_asm.wast.out6
-rw-r--r--test/lld/em_asm_O0.wast.out6
-rw-r--r--test/lld/em_asm_shared.wast.out6
-rw-r--r--test/lld/em_asm_table.wast.out6
-rw-r--r--test/lld/em_js_O0.wast.out6
-rw-r--r--test/lld/hello_world.wast.mem.out6
-rw-r--r--test/lld/hello_world.wast.out6
-rw-r--r--test/lld/init.wast.out6
-rw-r--r--test/lld/recursive.wast.out6
-rw-r--r--test/lld/reserved_func_ptr.wast.out6
-rw-r--r--test/lld/shared.wast.out6
-rw-r--r--test/passes/limit-segments_disable-bulk-memory.txt (renamed from test/passes/limit-segments.txt)0
-rw-r--r--test/passes/limit-segments_disable-bulk-memory.wast (renamed from test/passes/limit-segments.wast)0
-rw-r--r--test/spec/globals.wast20
-rw-r--r--test/unit/test_features.py14
55 files changed, 294 insertions, 264 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e4b6a725d..17a69e053 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,7 @@ Current Trunk
- Add --side-module option to wasm-emscripten-finalize.
- Add `segmentPassive` argument to `BinaryenSetMemory` for marking segments
passive.
+- Make `-o -` print to stdout instead of a file named "-".
v73
---
diff --git a/build-js.sh b/build-js.sh
index f4f93a469..1ca2a1129 100755
--- a/build-js.sh
+++ b/build-js.sh
@@ -144,6 +144,7 @@ echo "building shared bitcode"
$BINARYEN_SRC/passes/SSAify.cpp \
$BINARYEN_SRC/passes/StackIR.cpp \
$BINARYEN_SRC/passes/Strip.cpp \
+ $BINARYEN_SRC/passes/StripTargetFeatures.cpp \
$BINARYEN_SRC/passes/TrapMode.cpp \
$BINARYEN_SRC/passes/Untee.cpp \
$BINARYEN_SRC/passes/Vacuum.cpp \
diff --git a/scripts/test/shared.py b/scripts/test/shared.py
index 7723f7cee..227b7b65e 100644
--- a/scripts/test/shared.py
+++ b/scripts/test/shared.py
@@ -312,10 +312,7 @@ class Py2CompletedProcess:
output=self.stdout, stderr=self.stderr)
-def run_process(cmd, check=True, input=None, universal_newlines=True,
- capture_output=False, *args, **kw):
- kw.setdefault('universal_newlines', True)
-
+def run_process(cmd, check=True, input=None, capture_output=False, *args, **kw):
if hasattr(subprocess, "run"):
ret = subprocess.run(cmd, check=check, input=input, *args, **kw)
return ret
@@ -419,9 +416,7 @@ def minify_check(wast, verify_final_result=True):
print ' (minify check)'
cmd = WASM_OPT + [wast, '--print-minified']
print ' ', ' '.join(cmd)
- subprocess.check_call(
- WASM_OPT + [wast, '--print-minified'],
- stdout=open('a.wast', 'w'), stderr=subprocess.PIPE)
+ subprocess.check_call(cmd, stdout=open('a.wast', 'w'), stderr=subprocess.PIPE)
assert os.path.exists('a.wast')
subprocess.check_call(
WASM_OPT + ['a.wast', '--print-minified'],
diff --git a/src/asm2wasm.h b/src/asm2wasm.h
index 9a68542a5..a981cb660 100644
--- a/src/asm2wasm.h
+++ b/src/asm2wasm.h
@@ -1450,7 +1450,6 @@ void Asm2WasmBuilder::processAsm(Ref ast) {
PassRunner passRunner(&wasm, passOptions);
passRunner.options.lowMemoryUnused = true;
- passRunner.setFeatures(passOptions.features);
if (debug) {
passRunner.setDebug(true);
passRunner.setValidateGlobally(false);
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp
index 2ae3da650..aa94d1d30 100644
--- a/src/binaryen-c.cpp
+++ b/src/binaryen-c.cpp
@@ -2486,9 +2486,9 @@ int BinaryenModuleValidate(BinaryenModuleRef module) {
}
Module* wasm = (Module*)module;
- // TODO add feature selection support to C API
- FeatureSet features = FeatureSet::All;
- return WasmValidator().validate(*wasm, features) ? 1 : 0;
+ // TODO(tlively): Add C API for managing features
+ wasm->features = FeatureSet::All;
+ return WasmValidator().validate(*wasm) ? 1 : 0;
}
void BinaryenModuleOptimize(BinaryenModuleRef module) {
diff --git a/src/ir/memory-utils.h b/src/ir/memory-utils.h
index 0bad50c5a..fbf1f646c 100644
--- a/src/ir/memory-utils.h
+++ b/src/ir/memory-utils.h
@@ -69,6 +69,12 @@ namespace MemoryUtils {
return true;
}
+ // Conservatively refuse to change segments if there might be memory.init
+ // and data.drop instructions.
+ if (module.features.hasBulkMemory()) {
+ return false;
+ }
+
auto isEmpty = [](Memory::Segment& segment) {
return segment.data.size() == 0;
};
diff --git a/src/ir/module-utils.h b/src/ir/module-utils.h
index 259ec9fea..fe99a458f 100644
--- a/src/ir/module-utils.h
+++ b/src/ir/module-utils.h
@@ -69,75 +69,6 @@ struct BinaryIndexes {
}
};
-// Read the features section into the out param, if it is present. If it is not
-// present, return false
-inline bool readFeaturesSection(const Module& module, FeatureSet& features) try {
- features = FeatureSet::MVP;
-
- const UserSection* section = nullptr;
- for (auto &s : module.userSections) {
- if (s.name == BinaryConsts::UserSections::TargetFeatures) {
- section = &s;
- break;
- }
- }
-
- if (!section) {
- return false;
- }
-
- // read target features section
- size_t index = 0;
- auto next_byte = [&]() {
- return section->data.at(index++);
- };
-
- size_t num_feats = U32LEB().read(next_byte).value;
- for (size_t i = 0; i < num_feats; ++i) {
- uint8_t prefix = section->data.at(index++);
- if (prefix != '=' && prefix != '+' && prefix != '-') {
- // unrecognized prefix, silently fail
- features = FeatureSet::MVP;
- return false;
- }
-
- size_t len = U32LEB().read(next_byte).value;
- if (index + len > section->data.size()) {
- // ill-formed string, silently fail
- features = FeatureSet::MVP;
- return false;
- }
- std::string name(&section->data[index], len);
- index += len;
-
- if (prefix == '-') {
- continue;
- }
-
- if (name == BinaryConsts::UserSections::AtomicsFeature) {
- features.setAtomics();
- } else if (name == BinaryConsts::UserSections::BulkMemoryFeature) {
- features.setBulkMemory();
- } else if (name == BinaryConsts::UserSections::ExceptionHandlingFeature) {
- WASM_UNREACHABLE(); // TODO: exception handling
- } else if (name == BinaryConsts::UserSections::TruncSatFeature) {
- features.setTruncSat();
- } else if (name == BinaryConsts::UserSections::SignExtFeature) {
- features.setSignExt();
- } else if (name == BinaryConsts::UserSections::SIMD128Feature) {
- features.setSIMD();
- }
- }
-
- return true;
-
-// silently fail to read features. Maybe this should warn?
-} catch (std::out_of_range& e) {
- features = FeatureSet::MVP;
- return false;
-}
-
-
inline Function* copyFunction(Function* func, Module& out) {
auto* ret = new Function();
ret->name = func->name;
diff --git a/src/pass.h b/src/pass.h
index 6116a9cca..65bf7bf52 100644
--- a/src/pass.h
+++ b/src/pass.h
@@ -74,8 +74,6 @@ struct PassOptions {
enum { LowMemoryBound = 1024 };
// Whether to try to preserve debug info through, which are special calls.
bool debugInfo = false;
- // Which wasm features to accept, and be allowed to use.
- FeatureSet features = FeatureSet::All;
// Arbitrary string arguments from the commandline, which we forward to passes.
std::map<std::string, std::string> arguments;
@@ -129,9 +127,6 @@ struct PassRunner {
void setValidateGlobally(bool validate) {
options.validateGlobally = validate;
}
- void setFeatures(FeatureSet features) {
- options.features = features;
- }
void add(std::string passName) {
auto pass = PassRegistry::get()->createPass(passName);
diff --git a/src/passes/CMakeLists.txt b/src/passes/CMakeLists.txt
index d2e713e2b..10fdc7fe4 100644
--- a/src/passes/CMakeLists.txt
+++ b/src/passes/CMakeLists.txt
@@ -42,6 +42,7 @@ SET(passes_SOURCES
PrintCallGraph.cpp
StackIR.cpp
Strip.cpp
+ StripTargetFeatures.cpp
RedundantSetElimination.cpp
RelooperJumpThreading.cpp
ReReloop.cpp
diff --git a/src/passes/MemoryPacking.cpp b/src/passes/MemoryPacking.cpp
index ef150e2de..704f57d89 100644
--- a/src/passes/MemoryPacking.cpp
+++ b/src/passes/MemoryPacking.cpp
@@ -32,7 +32,7 @@ struct MemoryPacking : public Pass {
// avoid invalidating segment indices or segment contents referenced from
// memory.init instructions.
// TODO: optimize in the presence of memory.init instructions
- if (!module->memory.exists || runner->options.features.hasBulkMemory()) {
+ if (!module->memory.exists || module->features.hasBulkMemory()) {
return;
}
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index ebfb6c311..29d867d76 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -1456,7 +1456,6 @@ Pass* createPrintStackIRPass() {
std::ostream& WasmPrinter::printModule(Module* module, std::ostream& o) {
PassRunner passRunner(module);
- passRunner.setFeatures(FeatureSet::All);
passRunner.setIsNested(true);
passRunner.add<Printer>(&o);
passRunner.run();
diff --git a/src/passes/SafeHeap.cpp b/src/passes/SafeHeap.cpp
index 852af0c16..738a201ee 100644
--- a/src/passes/SafeHeap.cpp
+++ b/src/passes/SafeHeap.cpp
@@ -113,7 +113,7 @@ struct SafeHeap : public Pass {
instrumenter.add<AccessInstrumenter>();
instrumenter.run();
// add helper checking funcs and imports
- addGlobals(module, runner->options.features);
+ addGlobals(module, module->features);
}
Name dynamicTopPtr, segfault, alignfault;
diff --git a/src/passes/Strip.cpp b/src/passes/Strip.cpp
index 40e5a9e9d..edc171ab3 100644
--- a/src/passes/Strip.cpp
+++ b/src/passes/Strip.cpp
@@ -75,9 +75,4 @@ Pass *createStripProducersPass() {
});
}
-Pass *createStripTargetFeaturesPass() {
- return new Strip([&](const UserSection& curr) {
- return curr.name == BinaryConsts::UserSections::TargetFeatures;
- });
-}
} // namespace wasm
diff --git a/src/passes/StripTargetFeatures.cpp b/src/passes/StripTargetFeatures.cpp
new file mode 100644
index 000000000..645fbb11c
--- /dev/null
+++ b/src/passes/StripTargetFeatures.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2019 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "pass.h"
+
+using namespace std;
+
+namespace wasm {
+
+struct StripTargetFeatures : public Pass {
+ void run(PassRunner* runner, Module* module) override {
+ module->hasFeaturesSection = false;
+ }
+};
+
+Pass *createStripTargetFeaturesPass() {
+ return new StripTargetFeatures();
+}
+
+} // namespace wasm
diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp
index 786a39474..fd5359206 100644
--- a/src/passes/pass.cpp
+++ b/src/passes/pass.cpp
@@ -306,7 +306,7 @@ void PassRunner::run() {
if (options.validate) {
// validate, ignoring the time
std::cerr << "[PassRunner] (validating)\n";
- if (!WasmValidator().validate(*wasm, options.features, validationFlags)) {
+ if (!WasmValidator().validate(*wasm, validationFlags)) {
WasmPrinter::printModule(wasm);
if (passDebug >= 2) {
std::cerr << "Last pass (" << pass->name << ") broke validation. Here is the module before: \n" << moduleBefore.str() << "\n";
@@ -323,7 +323,7 @@ void PassRunner::run() {
std::cerr << "[PassRunner] passes took " << totalTime.count() << " seconds." << std::endl;
if (options.validate) {
std::cerr << "[PassRunner] (final validation)\n";
- if (!WasmValidator().validate(*wasm, options.features, validationFlags)) {
+ if (!WasmValidator().validate(*wasm, validationFlags)) {
WasmPrinter::printModule(wasm);
std::cerr << "final module does not validate\n";
abort();
diff --git a/src/support/file.cpp b/src/support/file.cpp
index 2fe636cfd..c10646720 100644
--- a/src/support/file.cpp
+++ b/src/support/file.cpp
@@ -68,6 +68,9 @@ template std::vector<char> wasm::read_file<>(const std::string& , Flags::BinaryO
wasm::Output::Output(const std::string& filename, Flags::BinaryOption binary, Flags::DebugOption debug)
: outfile(), out([this, filename, binary, debug]() {
+ if (filename == "-") {
+ return std::cout.rdbuf();
+ }
std::streambuf *buffer;
if (filename.size()) {
if (debug == Flags::Debug) std::cerr << "Opening '" << filename << "'" << std::endl;
diff --git a/src/tools/asm2wasm.cpp b/src/tools/asm2wasm.cpp
index 9ec083db2..547bf4ab1 100644
--- a/src/tools/asm2wasm.cpp
+++ b/src/tools/asm2wasm.cpp
@@ -177,6 +177,9 @@ int main(int argc, const char *argv[]) {
wasm.memory.segments.emplace_back(init, data);
}
+ // set up the module's features, needed by optimization and validation passes
+ options.applyFeatures(wasm);
+
// compile the code
Asm2WasmBuilder asm2wasm(wasm, pre, options.debug, trapMode, options.passOptions, legalizeJavaScriptFFI, options.runningDefaultOptimizationPasses(), wasmOnly);
asm2wasm.processAsm(asmjs);
@@ -185,7 +188,6 @@ int main(int argc, const char *argv[]) {
if (memInit != options.extra.end()) {
if (options.runningDefaultOptimizationPasses()) {
PassRunner runner(&wasm);
- runner.setFeatures(options.passOptions.features);
runner.add("memory-packing");
runner.run();
}
@@ -213,7 +215,7 @@ int main(int argc, const char *argv[]) {
}
if (options.passOptions.validate) {
- if (!WasmValidator().validate(wasm, options.passOptions.features)) {
+ if (!WasmValidator().validate(wasm)) {
WasmPrinter::printModule(&wasm);
Fatal() << "error in validating output";
}
diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h
index 7b47dea8f..b7056f964 100644
--- a/src/tools/fuzzing.h
+++ b/src/tools/fuzzing.h
@@ -124,10 +124,6 @@ public:
std::cout << "shrink level: " << options.passOptions.shrinkLevel << '\n';
}
- void setFeatures(FeatureSet features_) {
- features = features_;
- }
-
void setAllowNaNs(bool allowNaNs_) {
allowNaNs = allowNaNs_;
}
@@ -197,9 +193,6 @@ private:
// Whether to emit memory operations like loads and stores.
bool allowMemory = true;
- // Features allowed to be emitted
- FeatureSet features = FeatureSet::All;
-
// Whether to emit atomic waits (which in single-threaded mode, may hang...)
static const bool ATOMIC_WAITS = false;
@@ -257,7 +250,7 @@ private:
void setupMemory() {
// Add memory itself
MemoryUtils::ensureExists(wasm.memory);
- if (features.hasBulkMemory()) {
+ if (wasm.features.hasBulkMemory()) {
size_t memCovered = 0;
// need at least one segment for memory.inits
size_t numSegments = upTo(8) + 1;
@@ -1198,7 +1191,7 @@ private:
return builder.makeLoad(8, false, offset, pick(1, 2, 4, 8), ptr, type);
}
case v128: {
- if (!features.hasSIMD()) {
+ if (!wasm.features.hasSIMD()) {
return makeTrivial(type);
}
return builder.makeLoad(16, false, offset, pick(1, 2, 4, 8, 16), ptr, type);
@@ -1213,7 +1206,7 @@ private:
if (!allowMemory) return makeTrivial(type);
auto* ret = makeNonAtomicLoad(type);
if (type != i32 && type != i64) return ret;
- if (!features.hasAtomics() || oneIn(2)) return ret;
+ if (!wasm.features.hasAtomics() || oneIn(2)) return ret;
// make it atomic
auto* load = ret->cast<Load>();
wasm.memory.shared = true;
@@ -1270,7 +1263,7 @@ private:
return builder.makeStore(8, offset, pick(1, 2, 4, 8), ptr, value, type);
}
case v128: {
- if (!features.hasSIMD()) {
+ if (!wasm.features.hasSIMD()) {
return makeTrivial(type);
}
return builder.makeStore(16, offset, pick(1, 2, 4, 8, 16), ptr, value, type);
@@ -1287,7 +1280,7 @@ private:
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;
+ if (!wasm.features.hasAtomics() || oneIn(2)) return store;
// make it atomic
wasm.memory.shared = true;
store->isAtomic = true;
@@ -1482,7 +1475,7 @@ private:
return buildUnary({ op, make(f64) });
}
case v128: {
- assert(features.hasSIMD());
+ assert(wasm.features.hasSIMD());
return buildUnary({ pick(AnyTrueVecI8x16, AllTrueVecI8x16, AnyTrueVecI16x8, AllTrueVecI16x8,
AnyTrueVecI32x4, AllTrueVecI32x4, AnyTrueVecI64x2, AllTrueVecI64x2),
make(v128) });
@@ -1541,7 +1534,7 @@ private:
WASM_UNREACHABLE();
}
case v128: {
- assert(features.hasSIMD());
+ assert(wasm.features.hasSIMD());
switch (upTo(5)) {
case 0: return buildUnary({ pick(SplatVecI8x16, SplatVecI16x8, SplatVecI32x4), make(i32) });
case 1: return buildUnary({ SplatVecI64x2, make(i64) });
@@ -1594,7 +1587,7 @@ private:
return makeDeNanOp(buildBinary({ pick(AddFloat64, SubFloat64, MulFloat64, DivFloat64, CopySignFloat64, MinFloat64, MaxFloat64), make(f64), make(f64) }));
}
case v128: {
- assert(features.hasSIMD());
+ assert(wasm.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,
@@ -1669,7 +1662,7 @@ private:
}
Expression* makeAtomic(Type type) {
- assert(features.hasAtomics());
+ assert(wasm.features.hasAtomics());
if (!allowMemory) return makeTrivial(type);
wasm.memory.shared = true;
if (type == i32 && oneIn(2)) {
@@ -1722,7 +1715,7 @@ private:
}
Expression* makeSIMD(Type type) {
- assert(features.hasSIMD());
+ assert(wasm.features.hasSIMD());
if (type != v128) {
return makeSIMDExtract(type);
}
@@ -1809,7 +1802,7 @@ private:
Expression* makeBulkMemory(Type type) {
if (!allowMemory) return makeTrivial(type);
- assert(features.hasBulkMemory());
+ assert(wasm.features.hasBulkMemory());
assert(type == none);
switch (upTo(4)) {
case 0: return makeMemoryInit();
@@ -1988,7 +1981,7 @@ private:
const T pick(FeatureOptions<T>& picker) {
std::vector<T> matches;
for (const auto& item : picker.options) {
- if (features.has(item.first)) {
+ if (wasm.features.has(item.first)) {
matches.reserve(matches.size() + item.second.size());
matches.insert(matches.end(), item.second.begin(), item.second.end());
}
diff --git a/src/tools/optimization-options.h b/src/tools/optimization-options.h
index dfc61b0e3..cf7c612ea 100644
--- a/src/tools/optimization-options.h
+++ b/src/tools/optimization-options.h
@@ -141,7 +141,6 @@ struct OptimizationOptions : public ToolOptions {
void runPasses(Module& wasm) {
PassRunner passRunner(&wasm, passOptions);
if (debug) passRunner.setDebug(true);
- passRunner.setFeatures(passOptions.features);
for (auto& pass : passes) {
if (pass == DEFAULT_OPT_PASSES) {
passRunner.addDefaultOptimizationPasses();
diff --git a/src/tools/tool-options.h b/src/tools/tool-options.h
index 6c2613407..dd585612f 100644
--- a/src/tools/tool-options.h
+++ b/src/tools/tool-options.h
@@ -34,24 +34,18 @@ struct ToolOptions : public Options {
Arguments::Zero,
[this](Options*, const std::string&) {
hasFeatureOptions = true;
- passOptions.features.makeMVP();
enabledFeatures.makeMVP();
disabledFeatures.setAll();
})
- .add("--all-features", "-all", "Enable all features "
- "(default if there is no target features section"
- " or if any feature options are provided)",
+ .add("--all-features", "-all", "Enable all features",
Arguments::Zero,
[this](Options*, const std::string&) {
hasFeatureOptions = true;
- passOptions.features.setAll();
enabledFeatures.setAll();
disabledFeatures.makeMVP();
})
.add("--detect-features", "",
- "Use features from the target features section"
- "(default if there is a target features section"
- " and no feature options are provided)",
+ "Use features from the target features section, or MVP (default)",
Arguments::Zero,
[this](Options*, const std::string&) {
hasFeatureOptions = true;
@@ -76,13 +70,11 @@ struct ToolOptions : public Options {
ToolOptions& addFeature(FeatureSet::Feature feature,
const std::string& description) {
-
(*this)
.add(std::string("--enable-") + FeatureSet::toString(feature), "",
std::string("Enable ") + description, Arguments::Zero,
[=](Options*, const std::string&) {
hasFeatureOptions = true;
- passOptions.features.set(feature, true);
enabledFeatures.set(feature, true);
disabledFeatures.set(feature, false);
})
@@ -91,36 +83,32 @@ struct ToolOptions : public Options {
std::string("Disable ") + description, Arguments::Zero,
[=](Options*, const std::string&) {
hasFeatureOptions = true;
- passOptions.features.set(feature, false);
enabledFeatures.set(feature, false);
disabledFeatures.set(feature, true);
});
return *this;
}
- void calculateFeatures(const Module& module) {
- FeatureSet wasmFeatures;
- bool wasmHasFeatures =
- ModuleUtils::readFeaturesSection(module, wasmFeatures);
-
+ void applyFeatures(Module& module) {
if (hasFeatureOptions) {
- if (detectFeatures) {
- wasmFeatures.enable(enabledFeatures);
- wasmFeatures.disable(disabledFeatures);
- passOptions.features = wasmFeatures;
- } else if (!(wasmFeatures <= passOptions.features)) {
- Fatal() << "module uses features not explicitly specified, "
- << "use --detect-features to resolve";
+ if (!detectFeatures && module.hasFeaturesSection) {
+ FeatureSet optionsFeatures = FeatureSet::All;
+ optionsFeatures.enable(enabledFeatures);
+ optionsFeatures.disable(disabledFeatures);
+ if (!(module.features <= optionsFeatures)) {
+ Fatal() << "module uses features not explicitly specified, "
+ << "use --detect-features to resolve";
+ }
}
- } else if (wasmHasFeatures) {
- passOptions.features = wasmFeatures;
+ module.features.enable(enabledFeatures);
+ module.features.disable(disabledFeatures);
}
}
private:
bool hasFeatureOptions = false;
bool detectFeatures = false;
- FeatureSet enabledFeatures = FeatureSet::MVP;
+ FeatureSet enabledFeatures = FeatureSet::All;
FeatureSet disabledFeatures = FeatureSet::MVP;
};
diff --git a/src/tools/wasm-as.cpp b/src/tools/wasm-as.cpp
index c504c2a81..fb79eb7cf 100644
--- a/src/tools/wasm-as.cpp
+++ b/src/tools/wasm-as.cpp
@@ -91,9 +91,11 @@ int main(int argc, const char *argv[]) {
Fatal() << "error in parsing input";
}
+ wasm.features = FeatureSet::All;
+
if (options.extra["validate"] != "none") {
if (options.debug) std::cerr << "Validating..." << std::endl;
- if (!wasm::WasmValidator().validate(wasm, FeatureSet::All,
+ if (!wasm::WasmValidator().validate(wasm,
WasmValidator::Globally | (options.extra["validate"] == "web" ? WasmValidator::Web : 0))) {
WasmPrinter::printModule(&wasm);
Fatal() << "Error: input module is not valid.\n";
diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp
index be1de7684..7509eb84f 100644
--- a/src/tools/wasm-ctor-eval.cpp
+++ b/src/tools/wasm-ctor-eval.cpp
@@ -425,7 +425,7 @@ int main(int argc, const char* argv[]) {
}
}
- options.calculateFeatures(wasm);
+ options.applyFeatures(wasm);
if (!WasmValidator().validate(wasm)) {
WasmPrinter::printModule(&wasm);
@@ -444,7 +444,6 @@ int main(int argc, const char* argv[]) {
// Do some useful optimizations after the evalling
{
PassRunner passRunner(&wasm);
- passRunner.setFeatures(options.passOptions.features);
passRunner.add("memory-packing"); // we flattened it, so re-optimize
passRunner.add("remove-unused-names");
passRunner.add("dce");
diff --git a/src/tools/wasm-emscripten-finalize.cpp b/src/tools/wasm-emscripten-finalize.cpp
index e39d9e2d6..46fb0533b 100644
--- a/src/tools/wasm-emscripten-finalize.cpp
+++ b/src/tools/wasm-emscripten-finalize.cpp
@@ -130,7 +130,7 @@ int main(int argc, const char *argv[]) {
Fatal() << "error in parsing wasm source map";
}
- options.calculateFeatures(wasm);
+ options.applyFeatures(wasm);
if (options.debug) {
std::cerr << "Module before:\n";
@@ -210,7 +210,7 @@ int main(int argc, const char *argv[]) {
}
// Substantial changes to the wasm are done, enough to create the metadata.
- std::string metadata = generator.generateEmscriptenMetadata(dataSize, initializerFunctions, options.passOptions.features);
+ std::string metadata = generator.generateEmscriptenMetadata(dataSize, initializerFunctions);
// Finally, separate out data segments if relevant (they may have been needed
// for metadata).
diff --git a/src/tools/wasm-opt.cpp b/src/tools/wasm-opt.cpp
index d36459a4a..6fce0f89b 100644
--- a/src/tools/wasm-opt.cpp
+++ b/src/tools/wasm-opt.cpp
@@ -162,26 +162,26 @@ int main(int argc, const char* argv[]) {
Fatal() << "error in building module, std::bad_alloc (possibly invalid request for silly amounts of memory)";
}
- options.calculateFeatures(wasm);
+ options.applyFeatures(wasm);
if (options.passOptions.validate) {
- if (!WasmValidator().validate(wasm, options.passOptions.features)) {
+ if (!WasmValidator().validate(wasm)) {
WasmPrinter::printModule(&wasm);
Fatal() << "error in validating input";
}
}
} else {
// translate-to-fuzz
+ options.applyFeatures(wasm);
TranslateToFuzzReader reader(wasm, options.extra["infile"]);
if (fuzzPasses) {
reader.pickPasses(options);
}
- reader.setFeatures(options.passOptions.features);
reader.setAllowNaNs(fuzzNaNs);
reader.setAllowMemory(fuzzMemory);
reader.build();
if (options.passOptions.validate) {
- if (!WasmValidator().validate(wasm, options.passOptions.features)) {
+ if (!WasmValidator().validate(wasm)) {
WasmPrinter::printModule(&wasm);
std::cerr << "translate-to-fuzz must always generate a valid module";
abort();
@@ -241,7 +241,7 @@ int main(int argc, const char* argv[]) {
WasmBinaryBuilder parser(other, input, false);
parser.read();
if (options.passOptions.validate) {
- bool valid = WasmValidator().validate(other, options.passOptions.features);
+ bool valid = WasmValidator().validate(other);
if (!valid) {
WasmPrinter::printModule(&other);
}
@@ -256,7 +256,7 @@ int main(int argc, const char* argv[]) {
options.runPasses(*curr);
if (options.passOptions.validate) {
bool valid =
- WasmValidator().validate(*curr, options.passOptions.features);
+ WasmValidator().validate(*curr);
if (!valid) {
WasmPrinter::printModule(&*curr);
}
diff --git a/src/tools/wasm-reduce.cpp b/src/tools/wasm-reduce.cpp
index 36a44b056..1f58da0d5 100644
--- a/src/tools/wasm-reduce.cpp
+++ b/src/tools/wasm-reduce.cpp
@@ -819,7 +819,7 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<
FunctionReferenceRemover referenceRemover(names);
referenceRemover.walkModule(module.get());
- if (WasmValidator().validate(*module, FeatureSet::All, WasmValidator::Globally | WasmValidator::Quiet) &&
+ if (WasmValidator().validate(*module, WasmValidator::Globally | WasmValidator::Quiet) &&
writeAndTestReduction()) {
std::cerr << "| removed " << names.size() << " functions\n";
return true;
diff --git a/src/tools/wasm-shell.cpp b/src/tools/wasm-shell.cpp
index 0d7ff61de..0c95b39ea 100644
--- a/src/tools/wasm-shell.cpp
+++ b/src/tools/wasm-shell.cpp
@@ -25,12 +25,12 @@
#include "execution-results.h"
#include "pass.h"
#include "shell-interface.h"
+#include "support/command-line.h"
#include "support/file.h"
#include "wasm-interpreter.h"
#include "wasm-printing.h"
#include "wasm-s-parser.h"
#include "wasm-validator.h"
-#include "tool-options.h"
using namespace cashew;
using namespace wasm;
@@ -233,7 +233,7 @@ int main(int argc, const char* argv[]) {
Name entry;
std::set<size_t> skipped;
- ToolOptions options("wasm-shell", "Execute .wast files");
+ Options options("wasm-shell", "Execute .wast files");
options
.add(
"--entry", "-e", "Call the entry point after parsing the module",
@@ -292,7 +292,8 @@ int main(int argc, const char* argv[]) {
builders[moduleName].swap(builder);
modules[moduleName].swap(module);
i++;
- bool valid = WasmValidator().validate(*modules[moduleName], options.passOptions.features);
+ modules[moduleName]->features = FeatureSet::All;
+ bool valid = WasmValidator().validate(*modules[moduleName]);
if (!valid) {
WasmPrinter::printModule(modules[moduleName].get());
}
diff --git a/src/tools/wasm2js.cpp b/src/tools/wasm2js.cpp
index 8704dbf49..67a5d33b6 100644
--- a/src/tools/wasm2js.cpp
+++ b/src/tools/wasm2js.cpp
@@ -407,7 +407,7 @@ int main(int argc, const char *argv[]) {
ModuleReader reader;
reader.setDebug(options.debug);
reader.read(input, wasm, "");
- options.calculateFeatures(wasm);
+ options.applyFeatures(wasm);
} else {
auto input(
read_file<std::vector<char>>(options.extra["infile"], Flags::Text, options.debug ? Flags::Debug : Flags::Release));
@@ -426,7 +426,7 @@ int main(int argc, const char *argv[]) {
}
if (options.passOptions.validate) {
- if (!WasmValidator().validate(wasm, options.passOptions.features)) {
+ if (!WasmValidator().validate(wasm)) {
WasmPrinter::printModule(&wasm);
Fatal() << "error in validating input";
}
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index f13b79654..50b78fbae 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -365,8 +365,7 @@ enum Subsection {
NameLocal = 2,
};
-
-}
+} // namespace UserSections
enum ASTNodes {
Unreachable = 0x00,
@@ -819,6 +818,12 @@ enum MemoryFlags {
IsShared = 1 << 1
};
+enum FeaturePrefix {
+ FeatureUsed = '+',
+ FeatureRequired = '=',
+ FeatureDisallowed = '-'
+};
+
} // namespace BinaryConsts
@@ -901,6 +906,7 @@ public:
void writeEarlyUserSections();
void writeLateUserSections();
void writeUserSection(const UserSection& section);
+ void writeFeaturesSection();
void initializeDebugInfo();
void writeSourceMapProlog();
@@ -1085,6 +1091,7 @@ public:
void readFunctionTableDeclaration();
void readTableElements();
void readNames(size_t);
+ void readFeatures(size_t);
// Debug information reading helpers
void setDebugLocations(std::istream* sourceMap_) {
diff --git a/src/wasm-emscripten.h b/src/wasm-emscripten.h
index 36c730be8..7d86031c7 100644
--- a/src/wasm-emscripten.h
+++ b/src/wasm-emscripten.h
@@ -49,8 +49,8 @@ public:
void replaceStackPointerGlobal();
std::string generateEmscriptenMetadata(
- Address staticBump, std::vector<Name> const& initializerFunctions,
- FeatureSet features);
+ Address staticBump, std::vector<Name> const& initializerFunctions);
+
void fixInvokeFunctionNames();
diff --git a/src/wasm-features.h b/src/wasm-features.h
index 6c58f1642..a1e7a321d 100644
--- a/src/wasm-features.h
+++ b/src/wasm-features.h
@@ -77,14 +77,14 @@ struct FeatureSet {
template<typename F>
void iterFeatures(F f) {
if (hasAtomics()) f(Atomics);
+ if (hasBulkMemory()) f(BulkMemory);
if (hasMutableGlobals()) f(MutableGlobals);
if (hasTruncSat()) f(TruncSat);
- if (hasSIMD()) f(SIMD);
- if (hasBulkMemory()) f(BulkMemory);
if (hasSignExt()) f(SignExt);
+ if (hasSIMD()) f(SIMD);
}
- bool operator<=(const FeatureSet& other) {
+ bool operator<=(const FeatureSet& other) const {
return !(features & ~other.features);
}
diff --git a/src/wasm-validator.h b/src/wasm-validator.h
index 3c0b6018b..9f9cddb02 100644
--- a/src/wasm-validator.h
+++ b/src/wasm-validator.h
@@ -57,7 +57,7 @@ struct WasmValidator {
};
typedef uint32_t Flags;
- bool validate(Module& module, FeatureSet features = FeatureSet::MVP, Flags flags = Globally);
+ bool validate(Module& module, Flags flags = Globally);
};
} // namespace wasm
diff --git a/src/wasm.h b/src/wasm.h
index 700938ef9..2171eeb12 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -924,6 +924,13 @@ public:
std::vector<UserSection> userSections;
std::vector<std::string> debugInfoFileNames;
+ // `features` are the features allowed to be used in this module and should be
+ // respected regardless of the value of`hasFeaturesSection`.
+ // `hasFeaturesSection` means we read a features section and will emit one
+ // too.
+ FeatureSet features = FeatureSet::All;
+ bool hasFeaturesSection = false;
+
MixedArena allocator;
private:
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index 2ee528392..b351a46e5 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -69,6 +69,7 @@ void WasmBinaryWriter::write() {
}
writeLateUserSections();
+ writeFeaturesSection();
finishUp();
}
@@ -356,7 +357,9 @@ void WasmBinaryWriter::writeFunctionTableDeclaration() {
}
void WasmBinaryWriter::writeTableElements() {
- if (!wasm->table.exists) return;
+ if (!wasm->table.exists || wasm->table.segments.size() == 0) {
+ return;
+ }
if (debug) std::cerr << "== writeTableElements" << std::endl;
auto start = startSection(BinaryConsts::Section::Element);
@@ -486,7 +489,7 @@ void WasmBinaryWriter::writeLateUserSections() {
}
void WasmBinaryWriter::writeUserSection(const UserSection& section) {
- auto start = startSection(0);
+ auto start = startSection(BinaryConsts::User);
writeInlineString(section.name.c_str());
for (size_t i = 0; i < section.data.size(); i++) {
o << uint8_t(section.data[i]);
@@ -494,6 +497,41 @@ void WasmBinaryWriter::writeUserSection(const UserSection& section) {
finishSection(start);
}
+void WasmBinaryWriter::writeFeaturesSection() {
+ if (!wasm->hasFeaturesSection || wasm->features.isMVP()) {
+ return;
+ }
+
+ // TODO(tlively): unify feature names with rest of toolchain and use
+ // FeatureSet::toString()
+ auto toString = [](FeatureSet::Feature f) {
+ switch (f) {
+ case FeatureSet::Atomics: return "atomics";
+ case FeatureSet::MutableGlobals: return "mutable-globals";
+ case FeatureSet::TruncSat: return "nontrapping-fptoint";
+ case FeatureSet::SIMD: return "simd128";
+ case FeatureSet::BulkMemory: return "bulk-memory";
+ case FeatureSet::SignExt: return "sign-ext";
+ default: WASM_UNREACHABLE();
+ }
+ };
+
+ std::vector<const char*> features;
+ wasm->features.iterFeatures([&](FeatureSet::Feature f) {
+ features.push_back(toString(f));
+ });
+
+ auto start = startSection(BinaryConsts::User);
+ writeInlineString(BinaryConsts::UserSections::TargetFeatures);
+ o << U32LEB(features.size());
+ for (auto& f : features) {
+ o << uint8_t(BinaryConsts::FeatureUsed);
+ writeInlineString(f);
+ }
+ finishSection(start);
+}
+
+
void WasmBinaryWriter::writeDebugLocation(const Function::DebugLocation& loc) {
if (loc == lastDebugLocation) {
return;
@@ -648,6 +686,8 @@ void WasmBinaryBuilder::readUserSection(size_t payloadLen) {
payloadLen -= read;
if (sectionName.equals(BinaryConsts::UserSections::Name)) {
readNames(payloadLen);
+ } else if (sectionName.equals(BinaryConsts::UserSections::TargetFeatures)) {
+ readFeatures(payloadLen);
} else {
// an unfamiliar custom section
if (sectionName.equals(BinaryConsts::UserSections::Linking)) {
@@ -1600,6 +1640,48 @@ void WasmBinaryBuilder::readNames(size_t payloadLen) {
}
}
+void WasmBinaryBuilder::readFeatures(size_t payloadLen) {
+ wasm.hasFeaturesSection = true;
+ wasm.features = FeatureSet::MVP;
+
+ auto sectionPos = pos;
+ size_t num_feats = getU32LEB();
+ for (size_t i = 0; i < num_feats; ++i) {
+ uint8_t prefix = getInt8();
+ if (prefix != BinaryConsts::FeatureUsed) {
+ if (prefix == BinaryConsts::FeatureRequired) {
+ throwError("Required features not supported");
+ } else if (prefix == BinaryConsts::FeatureDisallowed) {
+ throwError("Disallowed features not supported");
+ } else {
+ throwError("Unrecognized feature policy prefix");
+ }
+ }
+
+ Name name = getInlineString();
+ if (pos > sectionPos + payloadLen) {
+ throwError("ill-formed string extends beyond section");
+ }
+
+ if (name == BinaryConsts::UserSections::AtomicsFeature) {
+ wasm.features.setAtomics();
+ } else if (name == BinaryConsts::UserSections::BulkMemoryFeature) {
+ wasm.features.setBulkMemory();
+ } else if (name == BinaryConsts::UserSections::ExceptionHandlingFeature) {
+ WASM_UNREACHABLE(); // TODO: exception handling
+ } else if (name == BinaryConsts::UserSections::TruncSatFeature) {
+ wasm.features.setTruncSat();
+ } else if (name == BinaryConsts::UserSections::SignExtFeature) {
+ wasm.features.setSignExt();
+ } else if (name == BinaryConsts::UserSections::SIMD128Feature) {
+ wasm.features.setSIMD();
+ }
+ }
+ if (pos != sectionPos + payloadLen) {
+ throwError("bad features section size");
+ }
+}
+
BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) {
if (pos == endOfFunction) {
throwError("Reached function end without seeing End opcode");
diff --git a/src/wasm/wasm-emscripten.cpp b/src/wasm/wasm-emscripten.cpp
index 1b40b871e..da2604383 100644
--- a/src/wasm/wasm-emscripten.cpp
+++ b/src/wasm/wasm-emscripten.cpp
@@ -823,8 +823,7 @@ void printSet(std::ostream& o, C& c) {
}
std::string EmscriptenGlueGenerator::generateEmscriptenMetadata(
- Address staticBump, std::vector<Name> const& initializerFunctions,
- FeatureSet features) {
+ Address staticBump, std::vector<Name> const& initializerFunctions) {
bool commaFirst;
auto nextElement = [&commaFirst]() {
if (commaFirst) {
@@ -965,7 +964,7 @@ std::string EmscriptenGlueGenerator::generateEmscriptenMetadata(
meta << " \"features\": [";
commaFirst = true;
meta << nextElement() << "\"--mvp-features\"";
- features.iterFeatures([&](FeatureSet::Feature f) {
+ wasm.features.iterFeatures([&](FeatureSet::Feature f) {
meta << nextElement() << "\"--enable-" << FeatureSet::toString(f) << '"';
});
meta << "\n ]\n";
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index a32dd5b0c..1e94ead0a 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -50,7 +50,6 @@ inline std::ostream& printModuleComponent(Expression* curr, std::ostream& stream
struct ValidationInfo {
bool validateWeb;
bool validateGlobally;
- FeatureSet features;
bool quiet;
std::atomic<bool> valid;
@@ -477,7 +476,7 @@ void FunctionValidator::visitCallIndirect(CallIndirect* curr) {
}
void FunctionValidator::visitConst(Const* curr) {
- shouldBeTrue(getFeatures(curr->type) <= info.features, curr,
+ shouldBeTrue(getFeatures(curr->type) <= getModule()->features, curr,
"all used features should be allowed");
}
@@ -514,10 +513,10 @@ void FunctionValidator::visitSetGlobal(SetGlobal* curr) {
void FunctionValidator::visitLoad(Load* curr) {
shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
if (curr->isAtomic) {
- shouldBeTrue(info.features.hasAtomics(), curr, "Atomic operation (atomics are disabled)");
+ shouldBeTrue(getModule()->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)");
+ if (curr->type == v128) shouldBeTrue(getModule()->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);
validateAlignment(curr->align, curr->type, curr->bytes, curr->isAtomic, curr);
@@ -531,10 +530,10 @@ void FunctionValidator::visitLoad(Load* curr) {
void FunctionValidator::visitStore(Store* curr) {
shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
if (curr->isAtomic) {
- shouldBeTrue(info.features.hasAtomics(), curr, "Atomic operation (atomics are disabled)");
+ shouldBeTrue(getModule()->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)");
+ if (curr->valueType == v128) shouldBeTrue(getModule()->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);
validateAlignment(curr->align, curr->valueType, curr->bytes, curr->isAtomic, curr);
@@ -548,7 +547,7 @@ void FunctionValidator::visitStore(Store* curr) {
void FunctionValidator::visitAtomicRMW(AtomicRMW* curr) {
shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
- shouldBeTrue(info.features.hasAtomics(), curr, "Atomic operation (atomics are disabled)");
+ shouldBeTrue(getModule()->features.hasAtomics(), curr, "Atomic operation (atomics are disabled)");
shouldBeFalse(!getModule()->memory.shared, curr, "Atomic operation with non-shared memory");
validateMemBytes(curr->bytes, curr->type, curr);
shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "AtomicRMW pointer type must be i32");
@@ -558,7 +557,7 @@ void FunctionValidator::visitAtomicRMW(AtomicRMW* curr) {
void FunctionValidator::visitAtomicCmpxchg(AtomicCmpxchg* curr) {
shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
- shouldBeTrue(info.features.hasAtomics(), curr, "Atomic operation (atomics are disabled)");
+ shouldBeTrue(getModule()->features.hasAtomics(), curr, "Atomic operation (atomics are disabled)");
shouldBeFalse(!getModule()->memory.shared, curr, "Atomic operation with non-shared memory");
validateMemBytes(curr->bytes, curr->type, curr);
shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "cmpxchg pointer type must be i32");
@@ -572,7 +571,7 @@ void FunctionValidator::visitAtomicCmpxchg(AtomicCmpxchg* curr) {
void FunctionValidator::visitAtomicWait(AtomicWait* curr) {
shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
- shouldBeTrue(info.features.hasAtomics(), curr, "Atomic operation (atomics are disabled)");
+ shouldBeTrue(getModule()->features.hasAtomics(), curr, "Atomic operation (atomics are disabled)");
shouldBeFalse(!getModule()->memory.shared, curr, "Atomic operation with non-shared memory");
shouldBeEqualOrFirstIsUnreachable(curr->type, i32, curr, "AtomicWait must have type i32");
shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "AtomicWait pointer type must be i32");
@@ -583,7 +582,7 @@ void FunctionValidator::visitAtomicWait(AtomicWait* curr) {
void FunctionValidator::visitAtomicNotify(AtomicNotify* curr) {
shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
- shouldBeTrue(info.features.hasAtomics(), curr, "Atomic operation (atomics are disabled)");
+ shouldBeTrue(getModule()->features.hasAtomics(), curr, "Atomic operation (atomics are disabled)");
shouldBeFalse(!getModule()->memory.shared, curr, "Atomic operation with non-shared memory");
shouldBeEqualOrFirstIsUnreachable(curr->type, i32, curr, "AtomicNotify must have type i32");
shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "AtomicNotify pointer type must be i32");
@@ -591,7 +590,7 @@ void FunctionValidator::visitAtomicNotify(AtomicNotify* curr) {
}
void FunctionValidator::visitSIMDExtract(SIMDExtract* curr) {
- shouldBeTrue(info.features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
+ shouldBeTrue(getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
shouldBeEqualOrFirstIsUnreachable(curr->vec->type, v128, curr, "extract_lane must operate on a v128");
Type lane_t = none;
size_t lanes = 0;
@@ -610,7 +609,7 @@ void FunctionValidator::visitSIMDExtract(SIMDExtract* curr) {
}
void FunctionValidator::visitSIMDReplace(SIMDReplace* curr) {
- shouldBeTrue(info.features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
+ shouldBeTrue(getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
shouldBeEqualOrFirstIsUnreachable(curr->type, v128, curr, "replace_lane must have type v128");
shouldBeEqualOrFirstIsUnreachable(curr->vec->type, v128, curr, "replace_lane must operate on a v128");
Type lane_t = none;
@@ -628,7 +627,7 @@ void FunctionValidator::visitSIMDReplace(SIMDReplace* curr) {
}
void FunctionValidator::visitSIMDShuffle(SIMDShuffle* curr) {
- shouldBeTrue(info.features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
+ shouldBeTrue(getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
shouldBeEqualOrFirstIsUnreachable(curr->type, v128, curr, "v128.shuffle must have type v128");
shouldBeEqualOrFirstIsUnreachable(curr->left->type, v128, curr, "expected operand of type v128");
shouldBeEqualOrFirstIsUnreachable(curr->right->type, v128, curr, "expected operand of type v128");
@@ -638,7 +637,7 @@ void FunctionValidator::visitSIMDShuffle(SIMDShuffle* curr) {
}
void FunctionValidator::visitSIMDBitselect(SIMDBitselect* curr) {
- shouldBeTrue(info.features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
+ shouldBeTrue(getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
shouldBeEqualOrFirstIsUnreachable(curr->type, v128, curr, "v128.bitselect must have type v128");
shouldBeEqualOrFirstIsUnreachable(curr->left->type, v128, curr, "expected operand of type v128");
shouldBeEqualOrFirstIsUnreachable(curr->right->type, v128, curr, "expected operand of type v128");
@@ -646,7 +645,7 @@ void FunctionValidator::visitSIMDBitselect(SIMDBitselect* curr) {
}
void FunctionValidator::visitSIMDShift(SIMDShift* curr) {
- shouldBeTrue(info.features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
+ shouldBeTrue(getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
shouldBeEqualOrFirstIsUnreachable(curr->type, v128, curr, "vector shift must have type v128");
shouldBeEqualOrFirstIsUnreachable(curr->vec->type, v128, curr, "expected operand of type v128");
shouldBeEqualOrFirstIsUnreachable(curr->shift->type, i32, curr, "expected shift amount to have type i32");
@@ -654,7 +653,7 @@ void FunctionValidator::visitSIMDShift(SIMDShift* curr) {
void FunctionValidator::visitMemoryInit(MemoryInit* curr) {
shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
- shouldBeTrue(info.features.hasBulkMemory(), curr, "Bulk memory operation (bulk memory is disabled)");
+ shouldBeTrue(getModule()->features.hasBulkMemory(), curr, "Bulk memory operation (bulk memory is disabled)");
shouldBeEqualOrFirstIsUnreachable(curr->type, none, curr, "memory.init must have type none");
shouldBeEqualOrFirstIsUnreachable(curr->dest->type, i32, curr, "memory.init dest must be an i32");
shouldBeEqualOrFirstIsUnreachable(curr->offset->type, i32, curr, "memory.init offset must be an i32");
@@ -664,14 +663,14 @@ void FunctionValidator::visitMemoryInit(MemoryInit* curr) {
void FunctionValidator::visitDataDrop(DataDrop* curr) {
shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
- shouldBeTrue(info.features.hasBulkMemory(), curr, "Bulk memory operation (bulk memory is disabled)");
+ shouldBeTrue(getModule()->features.hasBulkMemory(), curr, "Bulk memory operation (bulk memory is disabled)");
shouldBeEqualOrFirstIsUnreachable(curr->type, none, curr, "data.drop must have type none");
shouldBeTrue(curr->segment < getModule()->memory.segments.size(), curr, "data.drop segment index out of bounds");
}
void FunctionValidator::visitMemoryCopy(MemoryCopy* curr) {
shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
- shouldBeTrue(info.features.hasBulkMemory(), curr, "Bulk memory operation (bulk memory is disabled)");
+ shouldBeTrue(getModule()->features.hasBulkMemory(), curr, "Bulk memory operation (bulk memory is disabled)");
shouldBeEqualOrFirstIsUnreachable(curr->type, none, curr, "memory.copy must have type none");
shouldBeEqualOrFirstIsUnreachable(curr->dest->type, i32, curr, "memory.copy dest must be an i32");
shouldBeEqualOrFirstIsUnreachable(curr->source->type, i32, curr, "memory.copy source must be an i32");
@@ -680,7 +679,7 @@ void FunctionValidator::visitMemoryCopy(MemoryCopy* curr) {
void FunctionValidator::visitMemoryFill(MemoryFill* curr) {
shouldBeTrue(getModule()->memory.exists, curr, "Memory operations require a memory");
- shouldBeTrue(info.features.hasBulkMemory(), curr, "Bulk memory operation (bulk memory is disabled)");
+ shouldBeTrue(getModule()->features.hasBulkMemory(), curr, "Bulk memory operation (bulk memory is disabled)");
shouldBeEqualOrFirstIsUnreachable(curr->type, none, curr, "memory.fill must have type none");
shouldBeEqualOrFirstIsUnreachable(curr->dest->type, i32, curr, "memory.fill dest must be an i32");
shouldBeEqualOrFirstIsUnreachable(curr->value->type, i32, curr, "memory.fill value must be an i32");
@@ -874,7 +873,7 @@ void FunctionValidator::visitBinary(Binary* curr) {
}
case InvalidBinary: WASM_UNREACHABLE();
}
- shouldBeTrue(Features::get(curr->op) <= info.features, curr, "all used features should be allowed");
+ shouldBeTrue(Features::get(curr->op) <= getModule()->features, curr, "all used features should be allowed");
}
void FunctionValidator::visitUnary(Unary* curr) {
@@ -1057,7 +1056,7 @@ void FunctionValidator::visitUnary(Unary* curr) {
break;
case InvalidUnary: WASM_UNREACHABLE();
}
- shouldBeTrue(Features::get(curr->op) <= info.features, curr, "all used features should be allowed");
+ shouldBeTrue(Features::get(curr->op) <= getModule()->features, curr, "all used features should be allowed");
}
void FunctionValidator::visitSelect(Select* curr) {
@@ -1106,7 +1105,7 @@ void FunctionValidator::visitFunction(Function* curr) {
typeFeatures |= getFeatures(type);
shouldBeTrue(isConcreteType(type), curr, "vars must be concretely typed");
}
- shouldBeTrue(typeFeatures <= info.features, curr,
+ shouldBeTrue(typeFeatures <= getModule()->features, curr,
"all used types should be allowed");
// if function has no result, it is ignored
// if body is unreachable, it might be e.g. a return
@@ -1241,7 +1240,7 @@ static void validateImports(Module& module, ValidationInfo& info) {
}
}
});
- if (!info.features.hasMutableGlobals()) {
+ if (!module.features.hasMutableGlobals()) {
ModuleUtils::iterImportedGlobals(module, [&](Global* curr) {
info.shouldBeFalse(curr->mutable_, curr->name, "Imported global cannot be mutable");
});
@@ -1258,7 +1257,7 @@ static void validateExports(Module& module, ValidationInfo& info) {
info.shouldBeUnequal(param, i64, f->name, "Exported function must not have i64 parameters");
}
}
- } else if (curr->kind == ExternalKind::Global && !info.features.hasMutableGlobals()) {
+ } else if (curr->kind == ExternalKind::Global && !module.features.hasMutableGlobals()) {
if (Global* g = module.getGlobalOrNull(curr->value)) {
info.shouldBeFalse(g->mutable_, g->name, "Exported global cannot be mutable");
}
@@ -1286,7 +1285,7 @@ static void validateExports(Module& module, ValidationInfo& info) {
static void validateGlobals(Module& module, ValidationInfo& info) {
ModuleUtils::iterDefinedGlobals(module, [&](Global* curr) {
- info.shouldBeTrue(getFeatures(curr->type) <= info.features, curr->name,
+ info.shouldBeTrue(getFeatures(curr->type) <= module.features, curr->name,
"all used types should be allowed");
info.shouldBeTrue(curr->init != nullptr, curr->name, "global init must be non-null");
info.shouldBeTrue(curr->init->is<Const>() || curr->init->is<GetGlobal>(), curr->name, "global init must be valid");
@@ -1302,11 +1301,11 @@ static void validateMemory(Module& module, ValidationInfo& info) {
info.shouldBeTrue(curr.initial <= Memory::kMaxSize, "memory", "initial memory must be <= 4GB");
info.shouldBeTrue(!curr.hasMax() || curr.max <= Memory::kMaxSize, "memory", "max memory must be <= 4GB, or unlimited");
info.shouldBeTrue(!curr.shared || curr.hasMax(), "memory", "shared memory must have max size");
- if (curr.shared) info.shouldBeTrue(info.features.hasAtomics(), "memory", "memory is shared, but atomics are disabled");
+ if (curr.shared) info.shouldBeTrue(module.features.hasAtomics(), "memory", "memory is shared, but atomics are disabled");
for (auto& segment : curr.segments) {
Index size = segment.data.size();
if (segment.isPassive) {
- info.shouldBeTrue(info.features.hasBulkMemory(), segment.offset, "nonzero segment flags (bulk memory is disabled)");
+ info.shouldBeTrue(module.features.hasBulkMemory(), segment.offset, "nonzero segment flags (bulk memory is disabled)");
info.shouldBeEqual(segment.offset, (Expression*)nullptr, segment.offset, "passive segment should not have an offset");
} else {
if (!info.shouldBeEqual(segment.offset->type, i32, segment.offset, "segment offset should be i32")) continue;
@@ -1351,11 +1350,10 @@ static void validateModule(Module& module, ValidationInfo& info) {
// TODO: If we want the validator to be part of libwasm rather than libpasses, then
// Using PassRunner::getPassDebug causes a circular dependence. We should fix that,
// perhaps by moving some of the pass infrastructure into libsupport.
-bool WasmValidator::validate(Module& module, FeatureSet features, Flags flags) {
+bool WasmValidator::validate(Module& module, Flags flags) {
ValidationInfo info;
info.validateWeb = (flags & Web) != 0;
info.validateGlobally = (flags & Globally) != 0;
- info.features = features;
info.quiet = (flags & Quiet) != 0;
// parallel wasm logic validation
PassRunner runner(&module);
diff --git a/test/debugInfo.fromasm.clamp.no-opts.map b/test/debugInfo.fromasm.clamp.no-opts.map
index 0b394eb99..091513249 100644
--- a/test/debugInfo.fromasm.clamp.no-opts.map
+++ b/test/debugInfo.fromasm.clamp.no-opts.map
@@ -1 +1 @@
-{"version":3,"sources":["tests/hello_world.c","tests/other_file.cpp","return.cpp","even-opted.cpp","fib.c","/tmp/emscripten_test_binaryen2_28hnAe/src.c","(unknown)"],"names":[],"mappings":"yLAIA,IACA,ICyylTA,aC7vlTA,OAkDA,0BCnGA,OACA,OACA,uBCAA,4BAKA,QAJA,OADA,8CAKA,0ICsi1DA,MCrvyDA"} \ No newline at end of file
+{"version":3,"sources":["tests/hello_world.c","tests/other_file.cpp","return.cpp","even-opted.cpp","fib.c","/tmp/emscripten_test_binaryen2_28hnAe/src.c","(unknown)"],"names":[],"mappings":"sLAIA,IACA,ICyylTA,aC7vlTA,OAkDA,0BCnGA,OACA,OACA,uBCAA,4BAKA,QAJA,OADA,8CAKA,0ICsi1DA,MCrvyDA"} \ No newline at end of file
diff --git a/test/debugInfo.fromasm.imprecise.no-opts.map b/test/debugInfo.fromasm.imprecise.no-opts.map
index 75f8f7d42..bce1d3419 100644
--- a/test/debugInfo.fromasm.imprecise.no-opts.map
+++ b/test/debugInfo.fromasm.imprecise.no-opts.map
@@ -1 +1 @@
-{"version":3,"sources":["tests/hello_world.c","tests/other_file.cpp","return.cpp","even-opted.cpp","fib.c","/tmp/emscripten_test_binaryen2_28hnAe/src.c","(unknown)"],"names":[],"mappings":"wLAIA,IACA,ICyylTA,aC7vlTA,OAkDA,SCnGA,OACA,OACA,sBCAA,4BAKA,QAJA,OADA,8CAKA,0ICsi1DA,MCrvyDA"} \ No newline at end of file
+{"version":3,"sources":["tests/hello_world.c","tests/other_file.cpp","return.cpp","even-opted.cpp","fib.c","/tmp/emscripten_test_binaryen2_28hnAe/src.c","(unknown)"],"names":[],"mappings":"qLAIA,IACA,ICyylTA,aC7vlTA,OAkDA,SCnGA,OACA,OACA,sBCAA,4BAKA,QAJA,OADA,8CAKA,0ICsi1DA,MCrvyDA"} \ No newline at end of file
diff --git a/test/debugInfo.fromasm.no-opts.map b/test/debugInfo.fromasm.no-opts.map
index 0b394eb99..091513249 100644
--- a/test/debugInfo.fromasm.no-opts.map
+++ b/test/debugInfo.fromasm.no-opts.map
@@ -1 +1 @@
-{"version":3,"sources":["tests/hello_world.c","tests/other_file.cpp","return.cpp","even-opted.cpp","fib.c","/tmp/emscripten_test_binaryen2_28hnAe/src.c","(unknown)"],"names":[],"mappings":"yLAIA,IACA,ICyylTA,aC7vlTA,OAkDA,0BCnGA,OACA,OACA,uBCAA,4BAKA,QAJA,OADA,8CAKA,0ICsi1DA,MCrvyDA"} \ No newline at end of file
+{"version":3,"sources":["tests/hello_world.c","tests/other_file.cpp","return.cpp","even-opted.cpp","fib.c","/tmp/emscripten_test_binaryen2_28hnAe/src.c","(unknown)"],"names":[],"mappings":"sLAIA,IACA,ICyylTA,aC7vlTA,OAkDA,0BCnGA,OACA,OACA,uBCAA,4BAKA,QAJA,OADA,8CAKA,0ICsi1DA,MCrvyDA"} \ No newline at end of file
diff --git a/test/lld/duplicate_imports.wast.out b/test/lld/duplicate_imports.wast.out
index 88f60d664..cbb1db44a 100644
--- a/test/lld/duplicate_imports.wast.out
+++ b/test/lld/duplicate_imports.wast.out
@@ -140,11 +140,11 @@
"features": [
"--mvp-features",
"--enable-threads",
+ "--enable-bulk-memory",
"--enable-mutable-globals",
"--enable-nontrapping-float-to-int",
- "--enable-simd",
- "--enable-bulk-memory",
- "--enable-sign-ext"
+ "--enable-sign-ext",
+ "--enable-simd"
]
}
-- END METADATA --
diff --git a/test/lld/em_asm.wast.mem.out b/test/lld/em_asm.wast.mem.out
index c8c193574..13484ff3d 100644
--- a/test/lld/em_asm.wast.mem.out
+++ b/test/lld/em_asm.wast.mem.out
@@ -264,11 +264,11 @@
"features": [
"--mvp-features",
"--enable-threads",
+ "--enable-bulk-memory",
"--enable-mutable-globals",
"--enable-nontrapping-float-to-int",
- "--enable-simd",
- "--enable-bulk-memory",
- "--enable-sign-ext"
+ "--enable-sign-ext",
+ "--enable-simd"
]
}
-- END METADATA --
diff --git a/test/lld/em_asm.wast.out b/test/lld/em_asm.wast.out
index bbacde8b7..7986466cd 100644
--- a/test/lld/em_asm.wast.out
+++ b/test/lld/em_asm.wast.out
@@ -265,11 +265,11 @@
"features": [
"--mvp-features",
"--enable-threads",
+ "--enable-bulk-memory",
"--enable-mutable-globals",
"--enable-nontrapping-float-to-int",
- "--enable-simd",
- "--enable-bulk-memory",
- "--enable-sign-ext"
+ "--enable-sign-ext",
+ "--enable-simd"
]
}
-- END METADATA --
diff --git a/test/lld/em_asm_O0.wast.out b/test/lld/em_asm_O0.wast.out
index 38074f724..45868e567 100644
--- a/test/lld/em_asm_O0.wast.out
+++ b/test/lld/em_asm_O0.wast.out
@@ -125,11 +125,11 @@
"features": [
"--mvp-features",
"--enable-threads",
+ "--enable-bulk-memory",
"--enable-mutable-globals",
"--enable-nontrapping-float-to-int",
- "--enable-simd",
- "--enable-bulk-memory",
- "--enable-sign-ext"
+ "--enable-sign-ext",
+ "--enable-simd"
]
}
-- END METADATA --
diff --git a/test/lld/em_asm_shared.wast.out b/test/lld/em_asm_shared.wast.out
index 725ef618d..19eabedc8 100644
--- a/test/lld/em_asm_shared.wast.out
+++ b/test/lld/em_asm_shared.wast.out
@@ -257,11 +257,11 @@
"features": [
"--mvp-features",
"--enable-threads",
+ "--enable-bulk-memory",
"--enable-mutable-globals",
"--enable-nontrapping-float-to-int",
- "--enable-simd",
- "--enable-bulk-memory",
- "--enable-sign-ext"
+ "--enable-sign-ext",
+ "--enable-simd"
]
}
-- END METADATA --
diff --git a/test/lld/em_asm_table.wast.out b/test/lld/em_asm_table.wast.out
index 72002d34e..1ed2cd5bd 100644
--- a/test/lld/em_asm_table.wast.out
+++ b/test/lld/em_asm_table.wast.out
@@ -95,11 +95,11 @@
"features": [
"--mvp-features",
"--enable-threads",
+ "--enable-bulk-memory",
"--enable-mutable-globals",
"--enable-nontrapping-float-to-int",
- "--enable-simd",
- "--enable-bulk-memory",
- "--enable-sign-ext"
+ "--enable-sign-ext",
+ "--enable-simd"
]
}
-- END METADATA --
diff --git a/test/lld/em_js_O0.wast.out b/test/lld/em_js_O0.wast.out
index 26e4ce5af..3db4c83d7 100644
--- a/test/lld/em_js_O0.wast.out
+++ b/test/lld/em_js_O0.wast.out
@@ -75,11 +75,11 @@
"features": [
"--mvp-features",
"--enable-threads",
+ "--enable-bulk-memory",
"--enable-mutable-globals",
"--enable-nontrapping-float-to-int",
- "--enable-simd",
- "--enable-bulk-memory",
- "--enable-sign-ext"
+ "--enable-sign-ext",
+ "--enable-simd"
]
}
-- END METADATA --
diff --git a/test/lld/hello_world.wast.mem.out b/test/lld/hello_world.wast.mem.out
index 133e66aa7..79ef1342a 100644
--- a/test/lld/hello_world.wast.mem.out
+++ b/test/lld/hello_world.wast.mem.out
@@ -100,11 +100,11 @@
"features": [
"--mvp-features",
"--enable-threads",
+ "--enable-bulk-memory",
"--enable-mutable-globals",
"--enable-nontrapping-float-to-int",
- "--enable-simd",
- "--enable-bulk-memory",
- "--enable-sign-ext"
+ "--enable-sign-ext",
+ "--enable-simd"
]
}
-- END METADATA --
diff --git a/test/lld/hello_world.wast.out b/test/lld/hello_world.wast.out
index f964276d9..c4a54667b 100644
--- a/test/lld/hello_world.wast.out
+++ b/test/lld/hello_world.wast.out
@@ -101,11 +101,11 @@
"features": [
"--mvp-features",
"--enable-threads",
+ "--enable-bulk-memory",
"--enable-mutable-globals",
"--enable-nontrapping-float-to-int",
- "--enable-simd",
- "--enable-bulk-memory",
- "--enable-sign-ext"
+ "--enable-sign-ext",
+ "--enable-simd"
]
}
-- END METADATA --
diff --git a/test/lld/init.wast.out b/test/lld/init.wast.out
index 5f46bee3c..676f00327 100644
--- a/test/lld/init.wast.out
+++ b/test/lld/init.wast.out
@@ -112,11 +112,11 @@
"features": [
"--mvp-features",
"--enable-threads",
+ "--enable-bulk-memory",
"--enable-mutable-globals",
"--enable-nontrapping-float-to-int",
- "--enable-simd",
- "--enable-bulk-memory",
- "--enable-sign-ext"
+ "--enable-sign-ext",
+ "--enable-simd"
]
}
-- END METADATA --
diff --git a/test/lld/recursive.wast.out b/test/lld/recursive.wast.out
index 8ac3105d4..277d3cf1f 100644
--- a/test/lld/recursive.wast.out
+++ b/test/lld/recursive.wast.out
@@ -158,11 +158,11 @@
"features": [
"--mvp-features",
"--enable-threads",
+ "--enable-bulk-memory",
"--enable-mutable-globals",
"--enable-nontrapping-float-to-int",
- "--enable-simd",
- "--enable-bulk-memory",
- "--enable-sign-ext"
+ "--enable-sign-ext",
+ "--enable-simd"
]
}
-- END METADATA --
diff --git a/test/lld/reserved_func_ptr.wast.out b/test/lld/reserved_func_ptr.wast.out
index ee58ef846..5a5a6f29d 100644
--- a/test/lld/reserved_func_ptr.wast.out
+++ b/test/lld/reserved_func_ptr.wast.out
@@ -195,11 +195,11 @@
"features": [
"--mvp-features",
"--enable-threads",
+ "--enable-bulk-memory",
"--enable-mutable-globals",
"--enable-nontrapping-float-to-int",
- "--enable-simd",
- "--enable-bulk-memory",
- "--enable-sign-ext"
+ "--enable-sign-ext",
+ "--enable-simd"
]
}
-- END METADATA --
diff --git a/test/lld/shared.wast.out b/test/lld/shared.wast.out
index 076a91aab..8d44279b8 100644
--- a/test/lld/shared.wast.out
+++ b/test/lld/shared.wast.out
@@ -99,11 +99,11 @@
"features": [
"--mvp-features",
"--enable-threads",
+ "--enable-bulk-memory",
"--enable-mutable-globals",
"--enable-nontrapping-float-to-int",
- "--enable-simd",
- "--enable-bulk-memory",
- "--enable-sign-ext"
+ "--enable-sign-ext",
+ "--enable-simd"
]
}
-- END METADATA --
diff --git a/test/passes/limit-segments.txt b/test/passes/limit-segments_disable-bulk-memory.txt
index 3ef5da445..3ef5da445 100644
--- a/test/passes/limit-segments.txt
+++ b/test/passes/limit-segments_disable-bulk-memory.txt
diff --git a/test/passes/limit-segments.wast b/test/passes/limit-segments_disable-bulk-memory.wast
index 7771cacde..7771cacde 100644
--- a/test/passes/limit-segments.wast
+++ b/test/passes/limit-segments_disable-bulk-memory.wast
diff --git a/test/spec/globals.wast b/test/spec/globals.wast
index 02ef4dc04..ca3100a64 100644
--- a/test/spec/globals.wast
+++ b/test/spec/globals.wast
@@ -52,26 +52,6 @@
)
(assert_invalid
- (module (import "m" "a" (global (mut i32))))
- "mutable globals cannot be imported"
-)
-
-(assert_invalid
- (module (global (import "m" "a") (mut i32)))
- "mutable globals cannot be imported"
-)
-
-(assert_invalid
- (module (global (mut f32) (f32.const 0)) (export "a" (global 0)))
- "mutable globals cannot be exported"
-)
-
-(assert_invalid
- (module (global (export "a") (mut f32) (f32.const 0)))
- "mutable globals cannot be exported"
-)
-
-(assert_invalid
(module (global f32 (f32.neg (f32.const 0))))
"constant expression required"
)
diff --git a/test/unit/test_features.py b/test/unit/test_features.py
index f9c410414..f130e1408 100644
--- a/test/unit/test_features.py
+++ b/test/unit/test_features.py
@@ -124,23 +124,37 @@ class TargetFeaturesSectionTest(unittest.TestCase):
self.assertEqual(p.stderr, '')
return p.stdout
+ def roundtrip(self, filename):
+ path = os.path.join(options.binaryen_test, 'unit', 'input', filename)
+ p = run_process(WASM_OPT + ['-g', '-o', '-', path], check=False,
+ capture_output=True)
+ self.assertEqual(p.returncode, 0)
+ self.assertEqual(p.stderr, '')
+ with open(path, 'rb') as f:
+ self.assertEqual(str(p.stdout), str(f.read()))
+
def test_atomics(self):
+ self.roundtrip('atomics_target_feature.wasm')
module = self.disassemble('atomics_target_feature.wasm')
self.assertIn('i32.atomic.rmw.add', module)
def test_bulk_memory(self):
+ self.roundtrip('bulkmem_target_feature.wasm')
module = self.disassemble('bulkmem_target_feature.wasm')
self.assertIn('memory.copy', module)
def test_nontrapping_fptoint(self):
+ self.roundtrip('truncsat_target_feature.wasm')
module = self.disassemble('truncsat_target_feature.wasm')
self.assertIn('i32.trunc_sat_f32_u', module)
def test_sign_ext(self):
+ self.roundtrip('signext_target_feature.wasm')
module = self.disassemble('signext_target_feature.wasm')
self.assertIn('i32.extend8_s', module)
def test_simd(self):
+ self.roundtrip('simd_target_feature.wasm')
module = self.disassemble('simd_target_feature.wasm')
self.assertIn('i32x4.splat', module)