diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/passes/MinimizeRecGroups.cpp | 70 | ||||
-rw-r--r-- | src/passes/ReorderGlobals.cpp | 12 | ||||
-rw-r--r-- | src/support/topological_orders.h | 66 |
3 files changed, 76 insertions, 72 deletions
diff --git a/src/passes/MinimizeRecGroups.cpp b/src/passes/MinimizeRecGroups.cpp index 728cb306e..3a61e1e0b 100644 --- a/src/passes/MinimizeRecGroups.cpp +++ b/src/passes/MinimizeRecGroups.cpp @@ -108,7 +108,7 @@ struct TypeSCCs // with the most compact encoding. struct BrandTypeIterator { // See `initFieldOptions` for the 18 options. - static constexpr size_t optionCount = 18; + static constexpr Index optionCount = 18; static std::array<Field, optionCount> fieldOptions; static void initFieldOptions(); @@ -146,7 +146,7 @@ struct BrandTypeIterator { } BrandTypeIterator& operator++() { - for (size_t i = fields.size(); i > 0; --i) { + for (Index i = fields.size(); i > 0; --i) { if (fields[i - 1].advance()) { return *this; } @@ -162,15 +162,15 @@ struct BrandTypeIterator { }; // Create an adjacency list with edges from supertype to subtypes. -std::vector<std::vector<size_t>> +std::vector<std::vector<Index>> createSubtypeGraph(const std::vector<HeapType>& types) { - std::unordered_map<HeapType, size_t> indices; + std::unordered_map<HeapType, Index> indices; for (auto type : types) { indices.insert({type, indices.size()}); } - std::vector<std::vector<size_t>> subtypeGraph(types.size()); - for (size_t i = 0; i < types.size(); ++i) { + std::vector<std::vector<Index>> subtypeGraph(types.size()); + for (Index i = 0; i < types.size(); ++i) { if (auto super = types[i].getDeclaredSuperType()) { if (auto it = indices.find(*super); it != indices.end()) { subtypeGraph[it->second].push_back(i); @@ -199,13 +199,13 @@ struct GroupClassInfo { // group, offset by 1 iff there is a brand type. Used to ensure that we only // find emit permutations that respect the constraint that supertypes must be // ordered before subtypes. - std::vector<std::vector<size_t>> subtypeGraph; + std::vector<std::vector<Index>> subtypeGraph; // A generator of valid permutations of the components in this class. TopologicalOrders orders; // Initialize `subtypeGraph` and `orders` based on the canonical ordering // encoded by the group and permutation in `info`. - static std::vector<std::vector<size_t>> initSubtypeGraph(RecGroupInfo& info); + static std::vector<std::vector<Index>> initSubtypeGraph(RecGroupInfo& info); GroupClassInfo(RecGroupInfo& info); void advance() { @@ -224,7 +224,7 @@ struct GroupClassInfo { // beginning of the canonical order. subtypeGraph.insert(subtypeGraph.begin(), {{}}); // Adjust indices. - for (size_t i = 1; i < subtypeGraph.size(); ++i) { + for (Index i = 1; i < subtypeGraph.size(); ++i) { for (auto& edge : subtypeGraph[i]) { ++edge; } @@ -255,7 +255,7 @@ struct RecGroupInfo { // get that other component's types into the canonical shape before using a // fresh permutation to re-shuffle them into their final shape. Only set for // groups belonging to nontrivial equivalence classes. - std::vector<size_t> permutation; + std::vector<Index> permutation; // Does this group include a brand type that does not correspond to a type in // the original module? bool hasBrand = false; @@ -265,13 +265,13 @@ struct RecGroupInfo { std::optional<GroupClassInfo> classInfo; }; -std::vector<std::vector<size_t>> +std::vector<std::vector<Index>> GroupClassInfo::initSubtypeGraph(RecGroupInfo& info) { assert(!info.classInfo); assert(info.permutation.size() == info.group.size()); std::vector<HeapType> canonical(info.group.size()); - for (size_t i = 0; i < info.group.size(); ++i) { + for (Index i = 0; i < info.group.size(); ++i) { canonical[info.permutation[i]] = info.group[i]; } @@ -291,7 +291,7 @@ void GroupClassInfo::permute(RecGroupInfo& info) { // First, un-permute the group to get back to the canonical order, offset by 1 // if we are newly inserting a brand. std::vector<HeapType> canonical(info.group.size() + insertingBrand); - for (size_t i = 0; i < info.group.size(); ++i) { + for (Index i = 0; i < info.group.size(); ++i) { canonical[info.permutation[i] + insertingBrand] = info.group[i]; } // Update the brand. @@ -304,7 +304,7 @@ void GroupClassInfo::permute(RecGroupInfo& info) { } // Finally, re-permute with the new permutation.. info.permutation = *orders; - for (size_t i = 0; i < info.group.size(); ++i) { + for (Index i = 0; i < info.group.size(); ++i) { info.group[i] = canonical[info.permutation[i]]; } } @@ -341,14 +341,14 @@ struct MinimizeRecGroups : Pass { // A global ordering on all types, including public types. Used to define a // total ordering on rec group shapes. - std::unordered_map<HeapType, size_t> typeIndices; + std::unordered_map<HeapType, Index> typeIndices; // As we process strongly connected components, we will construct output // recursion groups here. std::vector<RecGroupInfo> groups; // For each shape of rec group we have created, its index in `groups`. - std::unordered_map<RecGroupShape, size_t> groupShapeIndices; + std::unordered_map<RecGroupShape, Index> groupShapeIndices; // When we find that two groups are isomorphic to (i.e. permutations of) each // other, we combine their equivalence classes and choose a new class @@ -360,7 +360,7 @@ struct MinimizeRecGroups : Pass { // with the shape of any existing group. If there is a conflict, we need to // update the group's shape and try again. Maintain a stack of group indices // whose shapes we need to check for uniqueness to avoid deep recursions. - std::vector<size_t> shapesToUpdate; + std::vector<Index> shapesToUpdate; void run(Module* module) override { // There are no recursion groups to minimize if GC is not enabled. @@ -382,7 +382,7 @@ struct MinimizeRecGroups : Pass { // Compute the strongly connected components and ensure they form distinct // recursion groups. for (auto scc : TypeSCCs(types)) { - [[maybe_unused]] size_t index = equivalenceClasses.addSet(); + [[maybe_unused]] Index index = equivalenceClasses.addSet(); assert(index == groups.size()); groups.emplace_back(); @@ -392,7 +392,7 @@ struct MinimizeRecGroups : Pass { auto deps = createSubtypeGraph(sccTypes); auto permutation = *TopologicalOrders(deps).begin(); groups.back().group.resize(sccTypes.size()); - for (size_t i = 0; i < sccTypes.size(); ++i) { + for (Index i = 0; i < sccTypes.size(); ++i) { groups.back().group[i] = sccTypes[permutation[i]]; } assert(shapesToUpdate.empty()); @@ -420,7 +420,7 @@ struct MinimizeRecGroups : Pass { } } - void updateShape(size_t group) { + void updateShape(Index group) { auto [it, inserted] = groupShapeIndices.insert({RecGroupShape(groups[group].group), group}); if (inserted) { @@ -454,13 +454,13 @@ struct MinimizeRecGroups : Pass { // // These possibilities are handled in order below. - size_t other = it->second; + Index other = it->second; auto& groupInfo = groups[group]; auto& otherInfo = groups[other]; - size_t groupRep = equivalenceClasses.getRoot(group); - size_t otherRep = equivalenceClasses.getRoot(other); + Index groupRep = equivalenceClasses.getRoot(group); + Index otherRep = equivalenceClasses.getRoot(other); // Case 1A: We have found a permutation of this group that has the same // shape as a previous permutation of this group. Skip the rest of the @@ -504,7 +504,7 @@ struct MinimizeRecGroups : Pass { if (groups[groupRep].classInfo) { auto& classInfo = *groups[groupRep].classInfo; - [[maybe_unused]] size_t unionRep = + [[maybe_unused]] Index unionRep = equivalenceClasses.getUnion(groupRep, otherRep); assert(groupRep == unionRep); @@ -525,7 +525,7 @@ struct MinimizeRecGroups : Pass { if (groups[otherRep].classInfo) { auto& classInfo = *groups[otherRep].classInfo; - [[maybe_unused]] size_t unionRep = + [[maybe_unused]] Index unionRep = equivalenceClasses.getUnion(otherRep, groupRep); assert(otherRep == unionRep); @@ -572,7 +572,7 @@ struct MinimizeRecGroups : Pass { shapesToUpdate.push_back(other); } - std::vector<size_t> + std::vector<Index> getCanonicalPermutation(const std::vector<HeapType>& types) { // The correctness of this part depends on some interesting properties of // strongly connected graphs with ordered, directed edges. A permutation of @@ -664,7 +664,7 @@ struct MinimizeRecGroups : Pass { // Compute the orderings generated by DFS on each type. std::unordered_set<HeapType> typeSet(types.begin(), types.end()); std::vector<std::vector<HeapType>> dfsOrders(types.size()); - for (size_t i = 0; i < types.size(); ++i) { + for (Index i = 0; i < types.size(); ++i) { dfsOrders[i].reserve(types.size()); std::vector<HeapType> workList; workList.push_back(types[i]); @@ -712,7 +712,7 @@ struct MinimizeRecGroups : Pass { // taken from each equivalence class by sorting them by order of appearance // in the least order, which ensures the final order remains independent of // the initial order. - std::unordered_map<HeapType, size_t> indexInLeastOrder; + std::unordered_map<HeapType, Index> indexInLeastOrder; for (auto type : leastOrder) { indexInLeastOrder.insert({type, indexInLeastOrder.size()}); } @@ -725,7 +725,7 @@ struct MinimizeRecGroups : Pass { } std::vector<HeapType> finalOrder; finalOrder.reserve(types.size()); - for (size_t i = 0; i < classSize; ++i) { + for (Index i = 0; i < classSize; ++i) { for (auto& [shape, members] : typeClasses) { finalOrder.push_back(members[i]); } @@ -733,11 +733,11 @@ struct MinimizeRecGroups : Pass { // Now what we actually want is the permutation that takes us from the final // canonical order to the original order of the types. - std::unordered_map<HeapType, size_t> indexInFinalOrder; + std::unordered_map<HeapType, Index> indexInFinalOrder; for (auto type : finalOrder) { indexInFinalOrder.insert({type, indexInFinalOrder.size()}); } - std::vector<size_t> permutation; + std::vector<Index> permutation; permutation.reserve(types.size()); for (auto type : types) { permutation.push_back(indexInFinalOrder.at(type)); @@ -747,10 +747,10 @@ struct MinimizeRecGroups : Pass { void rewriteTypes(Module& wasm) { // Map types to indices in the builder. - std::unordered_map<HeapType, size_t> outputIndices; - size_t i = 0; + std::unordered_map<HeapType, Index> outputIndices; + Index i = 0; for (const auto& group : groups) { - for (size_t j = 0; j < group.group.size(); ++j) { + for (Index j = 0; j < group.group.size(); ++j) { // Skip the brand if it exists. if (!group.hasBrand || group.permutation[j] != 0) { outputIndices.insert({group.group[j], i}); @@ -781,7 +781,7 @@ struct MinimizeRecGroups : Pass { std::unordered_map<HeapType, HeapType> oldToNew; i = 0; for (const auto& group : groups) { - for (size_t j = 0; j < group.group.size(); ++j) { + for (Index j = 0; j < group.group.size(); ++j) { // Skip the brand again if it exists. if (!group.hasBrand || group.permutation[j] != 0) { oldToNew[group.group[j]] = newTypes[i]; diff --git a/src/passes/ReorderGlobals.cpp b/src/passes/ReorderGlobals.cpp index c5432f006..af0b9572a 100644 --- a/src/passes/ReorderGlobals.cpp +++ b/src/passes/ReorderGlobals.cpp @@ -77,7 +77,7 @@ struct ReorderGlobals : public Pass { // use the index of the global in the original ordering to identify each // global. A different ordering is then a vector of old indices, saying where // each element comes from, which is logically a mapping between indices. - using IndexIndexMap = std::vector<size_t>; + using IndexIndexMap = std::vector<Index>; // We will also track counts of uses for each global. We use floating-point // values here since while the initial counts are integers, we will be @@ -126,7 +126,7 @@ struct ReorderGlobals : public Pass { // (global $b i32 (global.get $a)) ;; $b depends on $a; $a must be first // // To do so we construct a map from each global to those that depends on it. - std::vector<std::unordered_set<size_t>> dependentSets(globals.size()); + std::vector<std::unordered_set<Index>> dependentSets(globals.size()); for (Index i = 0; i < globals.size(); i++) { auto& global = globals[i]; if (!global->imported()) { @@ -138,7 +138,7 @@ struct ReorderGlobals : public Pass { } TopologicalSort::Graph deps; deps.reserve(globals.size()); - for (size_t i = 0; i < globals.size(); ++i) { + for (Index i = 0; i < globals.size(); ++i) { deps.emplace_back(dependentSets[i].begin(), dependentSets[i].end()); } @@ -241,7 +241,7 @@ struct ReorderGlobals : public Pass { // Now use that optimal order to create an ordered graph that includes the // dependencies. The final order will be the minimum topological sort of // this graph. - return TopologicalSort::minSort(deps, [&](size_t a, size_t b) { + return TopologicalSort::minSort(deps, [&](Index a, Index b) { // Imports always go first. The binary writer takes care of this itself // anyhow, but it is better to do it here in the IR so we can actually // see what the final layout will be. @@ -287,8 +287,8 @@ struct ReorderGlobals : public Pass { // Track the size in bits and the next index at which the size increases. At // the first iteration we'll compute the size of the LEB for index 0, and so // forth. - size_t sizeInBits = 0; - size_t nextSizeIncrease = 0; + Index sizeInBits = 0; + Index nextSizeIncrease = 0; for (Index i = 0; i < indices.size(); i++) { if (i == nextSizeIncrease) { sizeInBits++; diff --git a/src/support/topological_orders.h b/src/support/topological_orders.h index 925ad1117..bc6609ae0 100644 --- a/src/support/topological_orders.h +++ b/src/support/topological_orders.h @@ -27,22 +27,27 @@ #include <variant> #include <vector> +#include "support/index.h" + namespace wasm { namespace TopologicalSort { -// An adjacency list containing edges from vertices to their successors. -using Graph = std::vector<std::vector<size_t>>; +// An adjacency list containing edges from vertices to their successors. Uses +// `Index` because we are primarily sorting elements of Wasm modules. If we ever +// need to sort signficantly larger objects, we might need to switch to +// `size_t` or make this a template parameter. +using Graph = std::vector<std::vector<Index>>; // Return a topological sort of the vertices in the given adjacency graph. -inline std::vector<size_t> sort(const Graph& graph); +inline std::vector<Index> sort(const Graph& graph); // 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) { using T = std::remove_cv_t<typename It::value_type::first_type>; - std::unordered_map<T, size_t> indices; + std::unordered_map<T, Index> indices; std::vector<T> elements; // Assign indices to each element. for (auto it = begin; it != end; ++it) { @@ -71,8 +76,8 @@ template<typename It> decltype(auto) sortOf(It begin, It end) { // 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<size_t>> -std::vector<size_t> minSort(const Graph& graph, F cmp = std::less<size_t>{}); +template<typename F = std::less<Index>> +std::vector<Index> minSort(const Graph& graph, F cmp = std::less<Index>{}); } // namespace TopologicalSort @@ -88,10 +93,10 @@ using TopologicalOrders = TopologicalOrdersImpl<std::monostate>; // detail of `minSort`. template<typename Cmp> struct TopologicalOrdersImpl { using Graph = TopologicalSort::Graph; - using value_type = const std::vector<size_t>; + using value_type = const std::vector<Index>; using difference_type = std::ptrdiff_t; - using reference = const std::vector<size_t>&; - using pointer = const std::vector<size_t>*; + using reference = const std::vector<Index>&; + using pointer = const std::vector<Index>*; using iterator_category = std::input_iterator_tag; // Takes an adjacency list, where the list for each vertex is a sorted list of @@ -108,8 +113,8 @@ template<typename Cmp> struct TopologicalOrdersImpl { bool operator!=(const TopologicalOrdersImpl& other) const { return !(*this == other); } - const std::vector<size_t>& operator*() const { return buf; } - const std::vector<size_t>* operator->() const { return &buf; } + const std::vector<Index>& operator*() const { return buf; } + const std::vector<Index>* operator->() const { return &buf; } TopologicalOrdersImpl& operator++() { // Find the last selector that can be advanced, popping any that cannot. std::optional<Selector> next; @@ -149,7 +154,7 @@ private: selectors.reserve(graph.size()); selectors.push_back({0, 0, 0}); auto& first = selectors.back(); - for (size_t i = 0; i < graph.size(); ++i) { + for (Index i = 0; i < graph.size(); ++i) { if (indegrees[i] == 0) { if constexpr (useMinHeap) { pushChoice(i); @@ -172,14 +177,14 @@ private: // The current in-degrees for each vertex. When a vertex is appended to our // permutation, the in-degrees of its children are decremented and those that // go to zero become available for the next selection. - std::vector<size_t> indegrees; + std::vector<Index> indegrees; // The buffer in which we are constructing a permutation. It contains a // sequence of selected vertices followed by a sequence of possible choices // for the next vertex. - std::vector<size_t> buf; + std::vector<Index> buf; // When we are finding a minimal topological order, store the possible // choices in this separate min-heap instead of directly in `buf`. - std::vector<size_t> choiceHeap; + std::vector<Index> choiceHeap; // When we are finding a minimal topological order, use this function to // compare possible choices. Empty iff we are not finding a minimal // topological order. @@ -192,11 +197,11 @@ private: struct Selector { // The start index of the sequence of available choices. Also the index // where we place the current choice. - size_t start; + Index start; // The number of choices we have. - size_t count; + Index count; // The index of the current choice in the original order. - size_t index; + Index index; // Select the next available vertex, decrement in-degrees, and update the // sequence of available vertices. Return the Selector for the next vertex. @@ -246,7 +251,7 @@ private: // we leave everything how we found it so the previous selector can make // its next selection without observing anything having changed in the // meantime. - for (size_t i = 1; i < count; ++i) { + for (Index i = 1; i < count; ++i) { ctx.buf[start + i - 1] = ctx.buf[start + i]; } ctx.buf[start + count - 1] = unselected; @@ -259,16 +264,16 @@ private: } }; - void pushChoice(size_t choice) { + void pushChoice(Index choice) { choiceHeap.push_back(choice); - std::push_heap(choiceHeap.begin(), - choiceHeap.end(), - [&](size_t a, size_t b) { return cmp(b, a); }); + std::push_heap(choiceHeap.begin(), choiceHeap.end(), [&](Index a, Index b) { + return cmp(b, a); + }); } - size_t popChoice() { - std::pop_heap(choiceHeap.begin(), - choiceHeap.end(), - [&](size_t a, size_t b) { return cmp(b, a); }); + Index popChoice() { + std::pop_heap(choiceHeap.begin(), choiceHeap.end(), [&](Index a, Index b) { + return cmp(b, a); + }); auto choice = choiceHeap.back(); choiceHeap.pop_back(); return choice; @@ -278,17 +283,16 @@ private: // Empty if we've already seen every possible ordering. std::vector<Selector> selectors; - friend std::vector<size_t> TopologicalSort::minSort<Cmp>(const Graph&, Cmp); + friend std::vector<Index> TopologicalSort::minSort<Cmp>(const Graph&, Cmp); }; namespace TopologicalSort { -std::vector<size_t> sort(const Graph& graph) { +std::vector<Index> sort(const Graph& graph) { return *TopologicalOrders(graph); } -template<typename Cmp> -std::vector<size_t> minSort(const Graph& graph, Cmp cmp) { +template<typename Cmp> std::vector<Index> minSort(const Graph& graph, Cmp cmp) { return *TopologicalOrdersImpl<Cmp>(graph, cmp); } |