summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/binaryen-c.cpp3
-rw-r--r--src/ir/module-utils.cpp32
-rw-r--r--src/ir/type-updating.cpp155
-rw-r--r--src/ir/type-updating.h5
-rw-r--r--src/parser/parse-3-implicit-types.cpp4
-rw-r--r--src/passes/MinimizeRecGroups.cpp2
-rw-r--r--src/passes/RoundTrip.cpp9
-rw-r--r--src/support/topological_sort.h27
-rw-r--r--src/tools/tool-options.h19
-rw-r--r--src/tools/wasm-as.cpp4
-rw-r--r--src/tools/wasm-ctor-eval.cpp4
-rw-r--r--src/tools/wasm-dis.cpp4
-rw-r--r--src/tools/wasm-emscripten-finalize.cpp4
-rw-r--r--src/tools/wasm-merge.cpp4
-rw-r--r--src/tools/wasm-metadce.cpp4
-rw-r--r--src/tools/wasm-opt.cpp4
-rw-r--r--src/tools/wasm-reduce.cpp8
-rw-r--r--src/tools/wasm-split/wasm-split.cpp4
-rw-r--r--src/tools/wasm2js.cpp10
-rw-r--r--src/wasm.h1
-rw-r--r--src/wasm/wasm-binary.cpp7
21 files changed, 255 insertions, 59 deletions
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp
index 31378ce22..64462c354 100644
--- a/src/binaryen-c.cpp
+++ b/src/binaryen-c.cpp
@@ -5647,6 +5647,9 @@ BinaryenModuleRef BinaryenModuleReadWithFeatures(char* input,
p.dump(std::cerr);
Fatal() << "error in parsing wasm binary";
}
+ // Do not regress code size by maintaining type order. TODO: Add an option to
+ // control this.
+ wasm->typeIndices.clear();
return wasm;
}
diff --git a/src/ir/module-utils.cpp b/src/ir/module-utils.cpp
index 58f7d4637..a16592d15 100644
--- a/src/ir/module-utils.cpp
+++ b/src/ir/module-utils.cpp
@@ -764,7 +764,39 @@ IndexedHeapTypes getOptimizedIndexedHeapTypes(Module& wasm) {
}
}
+ // If we've preserved the input type order on the module, we have to respect
+ // that first. Use the index of the first type from each group. In principle
+ // we could try to do something more robust like take the minimum index of all
+ // the types in the group, but if the groups haven't been preserved, then we
+ // won't be able to perfectly preserve the order anyway.
+ std::vector<std::optional<Index>> groupTypeIndices;
+ if (wasm.typeIndices.empty()) {
+ groupTypeIndices.resize(groups.size());
+ } else {
+ groupTypeIndices.reserve(groups.size());
+ for (auto group : groups) {
+ groupTypeIndices.emplace_back();
+ if (auto it = wasm.typeIndices.find(group[0]);
+ it != wasm.typeIndices.end()) {
+ groupTypeIndices.back() = it->second;
+ }
+ }
+ }
+
auto order = TopologicalSort::minSort(deps, [&](size_t a, size_t b) {
+ auto indexA = groupTypeIndices[a];
+ auto indexB = groupTypeIndices[b];
+ // Groups with indices must be sorted before groups without indices to
+ // ensure transitivity of this comparison relation.
+ if (indexA.has_value() != indexB.has_value()) {
+ return indexA.has_value();
+ }
+ // Sort by preserved index if we can.
+ if (indexA && *indexA != *indexB) {
+ return *indexA < *indexB;
+ }
+ // Otherwise sort by weight and break ties by the arbitrary deterministic
+ // order in which we've collected types.
auto weightA = weights[a];
auto weightB = weights[b];
if (weightA != weightB) {
diff --git a/src/ir/type-updating.cpp b/src/ir/type-updating.cpp
index 17437cf2e..467971989 100644
--- a/src/ir/type-updating.cpp
+++ b/src/ir/type-updating.cpp
@@ -20,6 +20,7 @@
#include "ir/module-utils.h"
#include "ir/names.h"
#include "ir/utils.h"
+#include "support/topological_sort.h"
#include "wasm-type-ordering.h"
#include "wasm-type.h"
#include "wasm.h"
@@ -38,44 +39,125 @@ GlobalTypeRewriter::TypeMap GlobalTypeRewriter::rebuildTypes(
// Find the heap types that are not publicly observable. Even in a closed
// world scenario, don't modify public types because we assume that they may
// be reflected on or used for linking. Figure out where each private type
- // will be located in the builder. Sort the private types so that supertypes
- // come before their subtypes.
- Index i = 0;
- auto privateTypes = ModuleUtils::getPrivateHeapTypes(wasm);
+ // will be located in the builder.
+ //
+ // There are two code paths here: a new one that is used when there are type
+ // indices to preserve and an old one that is used otherwise. The old code
+ // path is kept around to avoid unnecessary changes to test outputs while we
+ // incrementally add --preserve-type-order to tests that could benefit from
+ // it. Once we are done adding --preserve-type-order to tests, we should
+ // remove the old code path here since the new code path is strictly better.
+ if (wasm.typeIndices.size()) {
+ // New code path, currently used only with --preserve-type-order.
+ auto typeInfo = ModuleUtils::collectHeapTypeInfo(
+ wasm,
+ ModuleUtils::TypeInclusion::UsedIRTypes,
+ ModuleUtils::VisibilityHandling::FindVisibility);
+
+ std::unordered_set<HeapType> additionalSet(additionalPrivateTypes.begin(),
+ additionalPrivateTypes.end());
+
+ std::vector<std::pair<HeapType, SmallVector<HeapType, 1>>>
+ privateSupertypes;
+ privateSupertypes.reserve(typeInfo.size());
+ for (auto& [type, info] : typeInfo) {
+ if (info.visibility != ModuleUtils::Visibility::Private &&
+ !additionalSet.count(type)) {
+ continue;
+ }
+ privateSupertypes.push_back({type, {}});
+
+ if (auto super = getDeclaredSuperType(type)) {
+ auto it = typeInfo.find(*super);
+ // Record the supertype only if it is among the private types.
+ if ((it != typeInfo.end() &&
+ it->second.visibility == ModuleUtils::Visibility::Private) ||
+ additionalSet.count(*super)) {
+ privateSupertypes.back().second.push_back(*super);
+ }
+ }
+ }
+
+ // Topological sort to have subtypes first. This is the opposite of the
+ // order we need, so the comparison is the opposite of what we ultimately
+ // want.
+ std::vector<HeapType> sorted;
+ if (wasm.typeIndices.empty()) {
+ sorted = TopologicalSort::sortOf(privateSupertypes.begin(),
+ privateSupertypes.end());
+ } else {
+ sorted =
+ TopologicalSort::minSortOf(privateSupertypes.begin(),
+ privateSupertypes.end(),
+ [&](Index a, Index b) {
+ auto typeA = privateSupertypes[a].first;
+ auto typeB = privateSupertypes[b].first;
+ // Preserve type order.
+ auto itA = wasm.typeIndices.find(typeA);
+ auto itB = wasm.typeIndices.find(typeB);
+ bool hasA = itA != wasm.typeIndices.end();
+ bool hasB = itB != wasm.typeIndices.end();
+ if (hasA != hasB) {
+ // Types with preserved indices must be
+ // sorted before (after in this reversed
+ // comparison) types without indices to
+ // maintain transitivity.
+ return !hasA;
+ }
+ if (hasA && *itA != *itB) {
+ return !(itA->second < itB->second);
+ }
+ // Break ties by the arbitrary order we
+ // have collected the types in.
+ return a > b;
+ });
+ }
+ std::reverse(sorted.begin(), sorted.end());
+ Index i = 0;
+ for (auto type : sorted) {
+ typeIndices[type] = i++;
+ }
+ } else {
+ // Old code path.
- if (!additionalPrivateTypes.empty()) {
- // Only add additional private types that are not already in the list.
- std::unordered_set<HeapType> privateTypesSet(privateTypes.begin(),
- privateTypes.end());
+ auto privateTypes = ModuleUtils::getPrivateHeapTypes(wasm);
- for (auto t : additionalPrivateTypes) {
- if (!privateTypesSet.count(t)) {
- privateTypes.push_back(t);
- privateTypesSet.insert(t);
+ if (!additionalPrivateTypes.empty()) {
+ // Only add additional private types that are not already in the list.
+ std::unordered_set<HeapType> privateTypesSet(privateTypes.begin(),
+ privateTypes.end());
+
+ for (auto t : additionalPrivateTypes) {
+ if (!privateTypesSet.count(t)) {
+ privateTypes.push_back(t);
+ privateTypesSet.insert(t);
+ }
}
}
- }
- // Topological sort to have supertypes first, but we have to account for the
- // fact that we may be replacing the supertypes to get the order correct.
- struct SupertypesFirst
- : HeapTypeOrdering::SupertypesFirstBase<SupertypesFirst> {
- GlobalTypeRewriter& parent;
+ // Topological sort to have supertypes first, but we have to account for the
+ // fact that we may be replacing the supertypes to get the order correct.
+ struct SupertypesFirst
+ : HeapTypeOrdering::SupertypesFirstBase<SupertypesFirst> {
+ GlobalTypeRewriter& parent;
- SupertypesFirst(GlobalTypeRewriter& parent) : parent(parent) {}
- std::optional<HeapType> getDeclaredSuperType(HeapType type) {
- return parent.getDeclaredSuperType(type);
- }
- };
+ SupertypesFirst(GlobalTypeRewriter& parent) : parent(parent) {}
+ std::optional<HeapType> getDeclaredSuperType(HeapType type) {
+ return parent.getDeclaredSuperType(type);
+ }
+ };
- SupertypesFirst sortedTypes(*this);
- for (auto type : sortedTypes.sort(privateTypes)) {
- typeIndices[type] = i++;
+ SupertypesFirst sortedTypes(*this);
+ Index i = 0;
+ for (auto type : sortedTypes.sort(privateTypes)) {
+ typeIndices[type] = i++;
+ }
}
if (typeIndices.size() == 0) {
return {};
}
+
typeBuilder.grow(typeIndices.size());
// All the input types are distinct, so we need to make sure the output types
@@ -86,7 +168,7 @@ GlobalTypeRewriter::TypeMap GlobalTypeRewriter::rebuildTypes(
typeBuilder.createRecGroup(0, typeBuilder.size());
// Create the temporary heap types.
- i = 0;
+ Index i = 0;
auto map = [&](HeapType type) -> HeapType {
if (auto it = typeIndices.find(type); it != typeIndices.end()) {
return typeBuilder[it->second];
@@ -144,7 +226,7 @@ GlobalTypeRewriter::TypeMap GlobalTypeRewriter::rebuildTypes(
for (auto [type, index] : typeIndices) {
oldToNewTypes[type] = newTypes[index];
}
- mapTypeNames(oldToNewTypes);
+ mapTypeNamesAndIndices(oldToNewTypes);
return oldToNewTypes;
}
@@ -268,7 +350,7 @@ void GlobalTypeRewriter::mapTypes(const TypeMap& oldToNewTypes) {
}
}
-void GlobalTypeRewriter::mapTypeNames(const TypeMap& oldToNewTypes) {
+void GlobalTypeRewriter::mapTypeNamesAndIndices(const TypeMap& oldToNewTypes) {
// Update type names to avoid duplicates.
std::unordered_set<Name> typeNames;
for (auto& [type, info] : wasm.typeNames) {
@@ -281,16 +363,21 @@ void GlobalTypeRewriter::mapTypeNames(const TypeMap& oldToNewTypes) {
}
if (auto it = wasm.typeNames.find(old); it != wasm.typeNames.end()) {
- wasm.typeNames[new_] = wasm.typeNames[old];
+ auto& oldNames = it->second;
+ wasm.typeNames[new_] = oldNames;
// Use the existing name in the new type, as usually it completely
// replaces the old. Rename the old name in a unique way to avoid
// confusion in the case that it remains used.
- auto deduped =
- Names::getValidName(wasm.typeNames[old].name,
- [&](Name test) { return !typeNames.count(test); });
- wasm.typeNames[old].name = deduped;
+ auto deduped = Names::getValidName(
+ oldNames.name, [&](Name test) { return !typeNames.count(test); });
+ oldNames.name = deduped;
typeNames.insert(deduped);
}
+ if (auto it = wasm.typeIndices.find(old); it != wasm.typeIndices.end()) {
+ // It's ok if we end up with duplicate indices. Ties will be resolved in
+ // some arbitrary manner.
+ wasm.typeIndices[new_] = it->second;
+ }
}
}
diff --git a/src/ir/type-updating.h b/src/ir/type-updating.h
index a8e071fb6..fe1cd2806 100644
--- a/src/ir/type-updating.h
+++ b/src/ir/type-updating.h
@@ -371,8 +371,9 @@ public:
// Users of `mapTypes` may want to update the type names according to their
// mapping. This is not done automatically in `mapTypes` because other users
- // may want the names to reflect that types have been replaced.
- void mapTypeNames(const TypeMap& oldToNewTypes);
+ // may want the names to reflect that types have been replaced. Do the same
+ // mapping for recorded type indices.
+ void mapTypeNamesAndIndices(const TypeMap& oldToNewTypes);
// Subclasses can implement these methods to modify the new set of types that
// we map to. By default, we simply copy over the types, and these functions
diff --git a/src/parser/parse-3-implicit-types.cpp b/src/parser/parse-3-implicit-types.cpp
index 3a3a867e1..cf13ae0f7 100644
--- a/src/parser/parse-3-implicit-types.cpp
+++ b/src/parser/parse-3-implicit-types.cpp
@@ -29,6 +29,10 @@ parseImplicitTypeDefs(ParseDeclsCtx& decls,
WithPosition with(ctx, pos);
CHECK_ERR(typeuse(ctx));
}
+ // Record type indices now that all the types have been parsed.
+ for (Index i = 0; i < types.size(); ++i) {
+ decls.wasm.typeIndices.insert({types[i], i});
+ }
return Ok{};
}
diff --git a/src/passes/MinimizeRecGroups.cpp b/src/passes/MinimizeRecGroups.cpp
index e89d98442..0faf297f5 100644
--- a/src/passes/MinimizeRecGroups.cpp
+++ b/src/passes/MinimizeRecGroups.cpp
@@ -857,7 +857,7 @@ struct MinimizeRecGroups : Pass {
}
GlobalTypeRewriter rewriter(wasm);
rewriter.mapTypes(oldToNew);
- rewriter.mapTypeNames(oldToNew);
+ rewriter.mapTypeNamesAndIndices(oldToNew);
}
};
diff --git a/src/passes/RoundTrip.cpp b/src/passes/RoundTrip.cpp
index 129b89ac8..123752f79 100644
--- a/src/passes/RoundTrip.cpp
+++ b/src/passes/RoundTrip.cpp
@@ -40,6 +40,11 @@ struct RoundTrip : public Pass {
// the target features section has been stripped. We also need them in order
// to tell the builder which features to build with.
auto features = module->features;
+
+ // We need to know whether we should preserve the type order when we read
+ // the module back in.
+ bool preserveTypeOrder = !module->typeIndices.empty();
+
// Write, clear, and read the module
WasmBinaryWriter(module, buffer, getPassOptions()).write();
ModuleUtils::clearModule(*module);
@@ -53,6 +58,10 @@ struct RoundTrip : public Pass {
std::cerr << '\n';
Fatal() << "error in parsing wasm binary";
}
+
+ if (!preserveTypeOrder) {
+ module->typeIndices.clear();
+ }
}
};
diff --git a/src/support/topological_sort.h b/src/support/topological_sort.h
index b75f6b78d..be5e49b8c 100644
--- a/src/support/topological_sort.h
+++ b/src/support/topological_sort.h
@@ -42,10 +42,17 @@ using Graph = std::vector<std::vector<Index>>;
// Return a topological sort of the vertices in the given adjacency graph.
inline std::vector<Index> sort(const Graph& graph);
+// Return the topological sort of the vertices in the given adjacency graph that
+// is lexicographically minimal with respect to the provided comparator on
+// vertex indices. Implemented using a min-heap internally.
+template<typename F = std::less<Index>>
+std::vector<Index> minSort(const Graph& graph, F cmp = std::less<Index>{});
+
// A utility that finds a topological sort of a graph with arbitrary element
// types. The provided iterators must be to pairs of elements and collections of
// their children.
-template<typename It> decltype(auto) sortOf(It begin, It end) {
+template<typename It, typename SortT, SortT Sort, typename... Args>
+decltype(auto) sortOfImpl(It begin, It end, Args... args) {
using T = std::remove_cv_t<typename It::value_type::first_type>;
std::unordered_map<T, Index> indices;
std::vector<T> elements;
@@ -67,17 +74,23 @@ template<typename It> decltype(auto) sortOf(It begin, It end) {
// Compute the topological order and convert back to original elements.
std::vector<T> order;
order.reserve(elements.size());
- for (auto i : sort(indexGraph)) {
+ for (auto i : Sort(indexGraph, std::forward<Args>(args)...)) {
order.emplace_back(std::move(elements[i]));
}
return order;
}
-// Return the topological sort of the vertices in the given adjacency graph that
-// is lexicographically minimal with respect to the provided comparator on
-// vertex indices. Implemented using a min-heap internally.
-template<typename F = std::less<Index>>
-std::vector<Index> minSort(const Graph& graph, F cmp = std::less<Index>{});
+template<typename It> decltype(auto) sortOf(It begin, It end) {
+ return sortOfImpl<It, std::vector<Index> (&)(const Graph&), sort>(begin, end);
+}
+
+template<typename It, typename Cmp>
+decltype(auto) minSortOf(It begin, It end, Cmp cmp) {
+ return sortOfImpl<It,
+ std::vector<Index> (&)(const Graph&, Cmp),
+ minSort,
+ Cmp>(begin, end, cmp);
+}
} // namespace TopologicalSort
diff --git a/src/tools/tool-options.h b/src/tools/tool-options.h
index bfa9a9b5c..f900d76ba 100644
--- a/src/tools/tool-options.h
+++ b/src/tools/tool-options.h
@@ -31,6 +31,7 @@ struct ToolOptions : public Options {
PassOptions passOptions;
bool quiet = false;
+ bool preserveTypeOrder = false;
IRProfile profile = IRProfile::Normal;
constexpr static const char* ToolOptionsCategory = "Tool options";
@@ -158,6 +159,16 @@ struct ToolOptions : public Options {
[this](Options*, const std::string&) {
passOptions.closedWorld = true;
})
+ .add(
+ "--preserve-type-order",
+ "",
+ "Preserve the order of types from the input (useful for debugging and "
+ "testing)",
+ ToolOptionsCategory,
+ Options::Arguments::Zero,
+ [&](Options* o, const std::string& arguments) {
+ preserveTypeOrder = true;
+ })
.add("--generate-stack-ir",
"",
"generate StackIR during writing",
@@ -213,11 +224,17 @@ struct ToolOptions : public Options {
return *this;
}
- void applyFeatures(Module& module) const {
+ void applyOptionsBeforeParse(Module& module) const {
module.features.enable(enabledFeatures);
module.features.disable(disabledFeatures);
}
+ void applyOptionsAfterParse(Module& module) const {
+ if (!preserveTypeOrder) {
+ module.typeIndices.clear();
+ }
+ }
+
virtual void addPassArg(const std::string& key, const std::string& value) {
passOptions.arguments[key] = value;
}
diff --git a/src/tools/wasm-as.cpp b/src/tools/wasm-as.cpp
index a767e6908..73ae82134 100644
--- a/src/tools/wasm-as.cpp
+++ b/src/tools/wasm-as.cpp
@@ -107,13 +107,15 @@ int main(int argc, const char* argv[]) {
auto input(read_file<std::string>(options.extra["infile"], Flags::Text));
Module wasm;
- options.applyFeatures(wasm);
+ options.applyOptionsBeforeParse(wasm);
auto parsed = WATParser::parseModule(wasm, input);
if (auto* err = parsed.getErr()) {
Fatal() << err->msg;
}
+ options.applyOptionsAfterParse(wasm);
+
if (options.extra["validate"] != "none") {
if (options.debug) {
std::cerr << "Validating..." << std::endl;
diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp
index 6809fa32a..d74790b2a 100644
--- a/src/tools/wasm-ctor-eval.cpp
+++ b/src/tools/wasm-ctor-eval.cpp
@@ -1445,7 +1445,7 @@ int main(int argc, const char* argv[]) {
options.parse(argc, argv);
Module wasm;
- options.applyFeatures(wasm);
+ options.applyOptionsBeforeParse(wasm);
{
if (options.debug) {
@@ -1460,6 +1460,8 @@ int main(int argc, const char* argv[]) {
}
}
+ options.applyOptionsAfterParse(wasm);
+
if (!WasmValidator().validate(wasm)) {
std::cout << wasm << '\n';
Fatal() << "error in validating input";
diff --git a/src/tools/wasm-dis.cpp b/src/tools/wasm-dis.cpp
index f9f303359..1603736ce 100644
--- a/src/tools/wasm-dis.cpp
+++ b/src/tools/wasm-dis.cpp
@@ -64,7 +64,7 @@ int main(int argc, const char* argv[]) {
std::cerr << "parsing binary..." << std::endl;
}
Module wasm;
- options.applyFeatures(wasm);
+ options.applyOptionsBeforeParse(wasm);
try {
ModuleReader().readBinary(options.extra["infile"], wasm, sourceMapFilename);
} catch (ParseException& p) {
@@ -82,6 +82,8 @@ int main(int argc, const char* argv[]) {
Fatal() << "error in parsing wasm source mapping";
}
+ options.applyOptionsAfterParse(wasm);
+
// TODO: Validation. However, validating would mean that users are forced to
// run with wasm-dis -all or such, to enable the features (unless the
// features section is present, but that's rare in general). It would be
diff --git a/src/tools/wasm-emscripten-finalize.cpp b/src/tools/wasm-emscripten-finalize.cpp
index 6b4e994ac..505e78349 100644
--- a/src/tools/wasm-emscripten-finalize.cpp
+++ b/src/tools/wasm-emscripten-finalize.cpp
@@ -196,7 +196,7 @@ int main(int argc, const char* argv[]) {
auto writeOutput = outfile.size() > 0 || !emitBinary;
Module wasm;
- options.applyFeatures(wasm);
+ options.applyOptionsBeforeParse(wasm);
ModuleReader reader;
// If we are not writing output then we definitely don't need to read debug
// info. However, if we emit output then definitely load the names section so
@@ -226,6 +226,8 @@ int main(int argc, const char* argv[]) {
Fatal() << "error in parsing wasm source map";
}
+ options.applyOptionsAfterParse(wasm);
+
BYN_TRACE_WITH_TYPE("emscripten-dump", "Module before:\n");
BYN_DEBUG_WITH_TYPE("emscripten-dump", std::cerr << &wasm);
diff --git a/src/tools/wasm-merge.cpp b/src/tools/wasm-merge.cpp
index 449f0cfdb..3de228350 100644
--- a/src/tools/wasm-merge.cpp
+++ b/src/tools/wasm-merge.cpp
@@ -695,7 +695,7 @@ Input source maps can be specified by adding an -ism option right after the modu
currModule = laterInput.get();
}
- options.applyFeatures(*currModule);
+ options.applyOptionsBeforeParse(*currModule);
ModuleReader reader;
try {
@@ -705,6 +705,8 @@ Input source maps can be specified by adding an -ism option right after the modu
Fatal() << "error in parsing wasm input: " << inputFile;
}
+ options.applyOptionsAfterParse(*currModule);
+
if (options.passOptions.validate) {
if (!WasmValidator().validate(*currModule)) {
std::cout << *currModule << '\n';
diff --git a/src/tools/wasm-metadce.cpp b/src/tools/wasm-metadce.cpp
index 9cc06375e..41dcf6ad4 100644
--- a/src/tools/wasm-metadce.cpp
+++ b/src/tools/wasm-metadce.cpp
@@ -486,7 +486,7 @@ int main(int argc, const char* argv[]) {
}
Module wasm;
- options.applyFeatures(wasm);
+ options.applyOptionsBeforeParse(wasm);
{
if (options.debug) {
@@ -502,6 +502,8 @@ int main(int argc, const char* argv[]) {
}
}
+ options.applyOptionsAfterParse(wasm);
+
if (options.passOptions.validate) {
if (!WasmValidator().validate(wasm)) {
std::cout << wasm << '\n';
diff --git a/src/tools/wasm-opt.cpp b/src/tools/wasm-opt.cpp
index d488171fb..3e1152179 100644
--- a/src/tools/wasm-opt.cpp
+++ b/src/tools/wasm-opt.cpp
@@ -243,7 +243,7 @@ int main(int argc, const char* argv[]) {
options.parse(argc, argv);
Module wasm;
- options.applyFeatures(wasm);
+ options.applyOptionsBeforeParse(wasm);
BYN_TRACE("reading...\n");
@@ -294,6 +294,8 @@ int main(int argc, const char* argv[]) {
"request for silly amounts of memory)";
}
+ options.applyOptionsAfterParse(wasm);
+
if (options.passOptions.validate) {
if (!WasmValidator().validate(wasm, options.passOptions)) {
exitOnInvalidWasm("error validating input");
diff --git a/src/tools/wasm-reduce.cpp b/src/tools/wasm-reduce.cpp
index c276296ad..8d9858b78 100644
--- a/src/tools/wasm-reduce.cpp
+++ b/src/tools/wasm-reduce.cpp
@@ -362,6 +362,9 @@ struct Reducer
void loadWorking() {
module = std::make_unique<Module>();
+
+ toolOptions.applyOptionsBeforeParse(*module);
+
ModuleReader reader;
try {
reader.read(working, *module);
@@ -371,15 +374,14 @@ struct Reducer
Fatal() << "error in parsing working wasm binary";
}
+ toolOptions.applyOptionsAfterParse(*module);
+
// If there is no features section, assume we may need them all (without
// this, a module with no features section but that uses e.g. atomics and
// bulk memory would not work).
if (!module->hasFeaturesSection) {
module->features = FeatureSet::All;
}
- // Apply features the user passed on the commandline.
- toolOptions.applyFeatures(*module);
-
builder = std::make_unique<Builder>(*module);
setModule(module.get());
}
diff --git a/src/tools/wasm-split/wasm-split.cpp b/src/tools/wasm-split/wasm-split.cpp
index eed26f1e1..c1ec6052f 100644
--- a/src/tools/wasm-split/wasm-split.cpp
+++ b/src/tools/wasm-split/wasm-split.cpp
@@ -38,7 +38,7 @@ using namespace wasm;
namespace {
void parseInput(Module& wasm, const WasmSplitOptions& options) {
- options.applyFeatures(wasm);
+ options.applyOptionsBeforeParse(wasm);
ModuleReader reader;
reader.setProfile(options.profile);
try {
@@ -52,6 +52,8 @@ void parseInput(Module& wasm, const WasmSplitOptions& options) {
"request for silly amounts of memory)";
}
+ options.applyOptionsAfterParse(wasm);
+
if (options.passOptions.validate && !WasmValidator().validate(wasm)) {
Fatal() << "error validating input";
}
diff --git a/src/tools/wasm2js.cpp b/src/tools/wasm2js.cpp
index 286f89890..2c48e5be0 100644
--- a/src/tools/wasm2js.cpp
+++ b/src/tools/wasm2js.cpp
@@ -786,7 +786,9 @@ void AssertionEmitter::emit() {
if (auto* mod = std::get_if<WASTModule>(&cmd)) {
if (auto* w = std::get_if<std::shared_ptr<Module>>(mod)) {
wasm = *w;
- options.applyFeatures(*wasm);
+ // We have already done the parse, but we still do this to apply the
+ // features from the command line.
+ options.applyOptionsBeforeParse(*wasm);
std::stringstream funcNameS;
funcNameS << ASM_FUNC << i;
std::stringstream moduleNameS;
@@ -928,6 +930,7 @@ int main(int argc, const char* argv[]) {
// is defined.
if (binaryInput) {
wasm = std::make_shared<Module>();
+ options.applyOptionsBeforeParse(*wasm);
ModuleReader reader;
reader.read(input, *wasm, "");
} else {
@@ -946,6 +949,9 @@ int main(int argc, const char* argv[]) {
if (auto* mod = std::get_if<WASTModule>(&(*script)[0].cmd)) {
if (auto* w = std::get_if<std::shared_ptr<Module>>(mod)) {
wasm = *w;
+ // This isn't actually before the parse, but we can't apply the
+ // feature options any earlier. FIXME.
+ options.applyOptionsBeforeParse(*wasm);
}
}
if (!wasm) {
@@ -965,7 +971,7 @@ int main(int argc, const char* argv[]) {
Fatal() << "error: modules with multiple tables are not supported yet.";
}
- options.applyFeatures(*wasm);
+ options.applyOptionsAfterParse(*wasm);
if (options.passOptions.validate) {
if (!WasmValidator().validate(*wasm)) {
std::cout << *wasm << '\n';
diff --git a/src/wasm.h b/src/wasm.h
index a7ad6ec6c..a86f77013 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -2308,6 +2308,7 @@ public:
Name name;
std::unordered_map<HeapType, TypeNames> typeNames;
+ std::unordered_map<HeapType, Index> typeIndices;
MixedArena allocator;
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index 3c8cc86df..151a7f911 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -2464,7 +2464,12 @@ void WasmBinaryReader::readTypes() {
if (auto* err = result.getError()) {
Fatal() << "Invalid type: " << err->reason << " at index " << err->index;
}
- types = *result;
+ types = std::move(*result);
+
+ // Record the type indices.
+ for (Index i = 0; i < types.size(); ++i) {
+ wasm.typeIndices.insert({types[i], i});
+ }
}
Name WasmBinaryReader::getFunctionName(Index index) {