From f8086adbf030b26eb7ce75e5d1834ae8134c689e Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 31 May 2024 14:55:57 -0700 Subject: [PATCH] Optimize ReorderGlobals ordering with a new algorithm (#6625) The old ordering in that pass did a topological sort while sorting by uses both within topological groups and between them. That could be unoptimal in some cases, however, and actually on J2CL output this pass made the binary larger, which is how we noticed this. The problem is that such a toplogical sort keeps topological groups in place, but it can be useful to interleave them sometimes. Imagine this: $c - $a / $e \ $d - $b Here $e depends on $c, etc. The optimal order may interleave the two arms here, e.g. $a, $b, $d, $c, $e. That is because the dependencies define a partial order, and so the arms here are actually independent. Sorting by toplogical depth first might help in some cases, but also is not optimal in general, as we may want to mix toplogical depths: $a, $c, $b, $d, $e does so, and it may be the best ordering. This PR implements a natural greedy algorithm that picks the global with the highest use count at each step, out of the set of possible globals, which is the set of globals that have no unresolved dependencies. So we start by picking the first global with no dependencies and add at at the front; then that unlocks anything that depended on it and we pick from that set, and so forth. This may also not be optimal, but it is easy to make it more flexible by customizing the counts, and we consider 4 sorts here: * Set all counts to 0. This means we only take into account dependencies, and we break ties by the original order, so this is as close to the original order as we can be. * Use the actual use counts. This is the simple greedy algorithm. * Set the count of each global to also contain the counts of its children, so the count is the total that might be unlocked. This gives more weight to globals that can unlock more later, so it is less greedy. * As last, but weight children's counts lower in an exponential way, which makes sense as they may depend on other globals too. In practice it is simple to generate cases where 1, 2, or 3 is optimal (see new tests), but on real-world J2CL I see that 4 (with a particular exponential coefficient) is best, so the pass computes all 4 and picks the best. As a result it will never worsen the size and it has a good chance of improving. The differences between these are small, so in theory we could pick any of them, but given they are all modifications of a single algorithm it is very easy to compute them all with little code complexity. The benefits are rather small here, but this can save a few hundred bytes on a multi-MB Java file. This comes at a tiny compile time cost, but seems worth it for the new guarantee to never regress size. --- src/passes/ReorderGlobals.cpp | 397 ++++++++-- test/lit/merge/names.wat | 12 +- test/lit/merge/renamings.wat | 11 +- test/lit/passes/j2cl.wast | 25 +- test/lit/passes/reorder-globals-real.wast | 894 ++++++++++++++++++++++ test/lit/passes/reorder-globals.wast | 547 ++++++++++++- 6 files changed, 1774 insertions(+), 112 deletions(-) create mode 100644 test/lit/passes/reorder-globals-real.wast diff --git a/src/passes/ReorderGlobals.cpp b/src/passes/ReorderGlobals.cpp index 9b5940a214b..4602f328496 100644 --- a/src/passes/ReorderGlobals.cpp +++ b/src/passes/ReorderGlobals.cpp @@ -19,6 +19,17 @@ // binaries because fewer bytes are needed to encode references to frequently // used globals. // +// This pass also sorts by dependencies, as each global can only appear after +// those it refers to. Other passes can use this internally to fix the ordering +// after they add new globals. +// +// The "always" variant of this pass will always sort globals, even if there are +// so few they all fit in a single LEB. In "always" mode we sort regardless and +// we measure size by considering each subsequent index to have a higher cost. +// That is, in reality the size of all globals up to 128 is a single byte, and +// then the LEB grows to 2, while in "always" mode we increase the size by 1/128 +// for each global in a smooth manner. "Always" is mainly useful for testing. +// #include "memory" @@ -29,14 +40,15 @@ namespace wasm { -using NameCountMap = std::unordered_map>; +// We'll count uses in parallel. +using AtomicNameCountMap = std::unordered_map>; struct UseCountScanner : public WalkerPass> { bool isFunctionParallel() override { return true; } bool modifiesBinaryenIR() override { return false; } - UseCountScanner(NameCountMap& counts) : counts(counts) {} + UseCountScanner(AtomicNameCountMap& counts) : counts(counts) {} std::unique_ptr create() override { return std::make_unique(counts); @@ -53,112 +65,355 @@ struct UseCountScanner : public WalkerPass> { } private: - NameCountMap& counts; + AtomicNameCountMap& counts; }; struct ReorderGlobals : public Pass { - // Whether to always reorder globals, even if there are very few and the - // benefit is minor. That is useful for testing, and also internally in passes - // that use us to reorder them so dependencies appear first (that is, if a - // pass ends up with an earlier global reading a later one, the sorting in - // this pass will reorder them properly; we need to take those dependencies - // into account anyhow here). bool always; ReorderGlobals(bool always) : always(always) {} + // For efficiency we will use global indices rather than names. That is, we + // use the index of the global in the original ordering to identify each + // global. A different ordering is then a vector of new indices, saying where + // each one moves, which is logically a mapping between indices. + using IndexIndexMap = std::vector; + + // 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 + // considering fractional sums of them later. + using IndexCountMap = std::vector; + + // We must take into account dependencies, so that globals appear before + // their users in other globals: + // + // (global $a i32 (i32.const 10)) + // (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 it depends on. We + // also build the reverse map, of those that it is depended upon by. + struct Dependencies { + std::unordered_map> dependsOn; + std::unordered_map> dependedUpon; + }; + void run(Module* module) override { - if (module->globals.size() < 128 && !always) { + auto& globals = module->globals; + + if (globals.size() < 128 && !always) { // The module has so few globals that they all fit in a single-byte U32LEB // value, so no reordering we can do can actually decrease code size. Note // that this is the common case with wasm MVP modules where the only // globals are typically the stack pointer and perhaps a handful of others // (however, features like wasm GC there may be a great many globals). + // TODO: we still need to sort here to fix dependencies sometimes return; } - NameCountMap counts; + AtomicNameCountMap atomicCounts; // Fill in info, as we'll operate on it in parallel. - for (auto& global : module->globals) { - counts[global->name]; + for (auto& global : globals) { + atomicCounts[global->name]; } // Count uses. - UseCountScanner scanner(counts); + UseCountScanner scanner(atomicCounts); scanner.run(getPassRunner(), module); scanner.runOnModuleCode(getPassRunner(), module); - // Do a toplogical sort to ensure we keep dependencies before the things - // that need them. For example, if $b's definition depends on $a then $b - // must appear later: - // - // (global $a i32 (i32.const 10)) - // (global $b i32 (global.get $a)) ;; $b depends on $a - // - struct DependencySort : TopologicalSort { - Module& wasm; - const NameCountMap& counts; - - std::unordered_map> deps; - - DependencySort(Module& wasm, const NameCountMap& counts) - : wasm(wasm), counts(counts) { - // Sort a list of global names by their counts. - auto sort = [&](std::vector& globals) { - std::stable_sort( - globals.begin(), globals.end(), [&](const Name& a, const Name& b) { - return counts.at(a) < counts.at(b); - }); - }; - - // Sort the globals. - std::vector sortedNames; - for (auto& global : wasm.globals) { - sortedNames.push_back(global->name); - } - sort(sortedNames); + // Switch to non-atomic for all further processing, and convert names to + // indices. + std::unordered_map originalIndices; + for (Index i = 0; i < globals.size(); i++) { + originalIndices[globals[i]->name] = i; + } + IndexCountMap counts(globals.size()); + for (auto& [name, count] : atomicCounts) { + counts[originalIndices[name]] = count; + } - // Everything is a root (we need to emit all globals). - for (auto global : sortedNames) { - push(global); + // Compute dependencies. + Dependencies deps; + for (Index i = 0; i < globals.size(); i++) { + auto& global = globals[i]; + if (!global->imported()) { + for (auto* get : FindAll(global->init).list) { + auto getIndex = originalIndices[get->name]; + deps.dependsOn[i].insert(getIndex); + deps.dependedUpon[getIndex].insert(i); } + } + } + + // Compute various sorting options. All the options use a variation of the + // algorithm in doSort() below, see there for more details; the only + // difference between the sorts is in the use counts we provide it. + struct SortAndSize { + IndexIndexMap sort; + double size; + SortAndSize(IndexIndexMap&& sort, double size) + : sort(std::move(sort)), size(size) {} + }; + std::vector options; + auto addOption = [&](const IndexCountMap& customCounts) { + // Compute the sort using custom counts that guide us how to order. + auto sort = doSort(customCounts, deps, module); + // Compute the size using the true counts. + auto size = computeSize(sort, counts); + options.emplace_back(std::move(sort), size); + }; - // The dependencies are the globals referred to. - for (auto& global : wasm.globals) { - if (global->imported()) { - continue; - } - std::vector vec; - for (auto* get : FindAll(global->init).list) { - vec.push_back(get->name); - } - sort(vec); - deps[global->name] = std::move(vec); + // Compute the closest thing we can to the original, unoptimized sort, by + // setting all counts to 0 there, so it only takes into account dependencies + // and the original ordering and nothing else. + // + // We put this sort first because if they all end up with equal size we + // prefer it (as it avoids pointless changes). + IndexCountMap zeroes(globals.size()); + addOption(zeroes); + + // A simple sort that uses the counts. As the algorithm in doSort() is + // greedy (see below), this is a pure greedy sort (at each point it time it + // picks the global with the highest count that it can). + addOption(counts); + + // We can make the sort less greedy by adding to each global's count some of + // the count of its children. Then we'd still be picking in a greedy manner + // but our choices would be influenced by the bigger picture of what can be + // unlocked by emitting a particular global (it may have a low count itself, + // but allow emitting something that depends on it that has a high count). + // We try two variations of this, one which is a simple sum (add the + // dependent's counts to the global's) and one which exponentially decreases + // them (that is, we add a small multiple of the dependent's counts, which + // may help as the dependents also depend on other things potentially, so a + // simple sum may be too naive). + double const EXPONENTIAL_FACTOR = 0.095; + IndexCountMap sumCounts(globals.size()), exponentialCounts(globals.size()); + + struct Sort : public TopologicalSort { + const Dependencies& deps; + + Sort(Index numGlobals, const Dependencies& deps) : deps(deps) { + for (Index i = 0; i < numGlobals; i++) { + push(i); } } - void pushPredecessors(Name global) { - for (auto pred : deps[global]) { - push(pred); + void pushPredecessors(Index global) { + auto iter = deps.dependedUpon.find(global); + if (iter == deps.dependedUpon.end()) { + return; + } + for (auto dep : iter->second) { + push(dep); } } - }; + } sort(globals.size(), deps); - std::unordered_map sortedIndexes; - for (auto global : DependencySort(*module, counts)) { - auto index = sortedIndexes.size(); - sortedIndexes[global] = index; + for (auto global : sort) { + // We can compute this global's count as in the sorted order all the + // values it cares about are resolved. Start with the self-count, then + // add the deps. + sumCounts[global] = exponentialCounts[global] = counts[global]; + for (auto dep : deps.dependedUpon[global]) { + sumCounts[global] += sumCounts[dep]; + exponentialCounts[global] += + EXPONENTIAL_FACTOR * exponentialCounts[dep]; + } } - std::sort( - module->globals.begin(), - module->globals.end(), - [&](const std::unique_ptr& a, const std::unique_ptr& b) { - return sortedIndexes[a->name] < sortedIndexes[b->name]; - }); + addOption(sumCounts); + addOption(exponentialCounts); + // Pick the best out of all the options we computed. + IndexIndexMap* best = nullptr; + double bestSize; + for (auto& [sort, size] : options) { + if (!best || size < bestSize) { + best = &sort; + bestSize = size; + } + } + + // Apply the indices we computed. + std::vector> old(std::move(globals)); + globals.resize(old.size()); + for (Index i = 0; i < old.size(); i++) { + globals[(*best)[i]] = std::move(old[i]); + } module->updateMaps(); } + + IndexIndexMap doSort(const IndexCountMap& counts, + const Dependencies& originalDeps, + Module* module) { + auto& globals = module->globals; + + // Copy the deps as we will operate on them as we go. + auto deps = originalDeps; + + // To sort the globals we do a simple greedy approach of always picking the + // global with the highest count at every point in time, subject to the + // constraint that we can only emit globals that have all of their + // dependencies already emitted. To do so we keep a list of the "available" + // globals, which are those with no remaining dependencies. Then by keeping + // the list of available globals in heap form we can simply pop the largest + // from the heap each time, and add new available ones as they become so. + // + // Other approaches here could be to do a topological sort, but the optimal + // order may not require strict ordering by topological depth, e.g.: + /* + // $c - $a + // / + // $e + // \ + // $d - $b + */ + // Here $e depends on $c and $d, $c depends on $a, and $d on $b. This is a + // partial order, as $d can be before or after $a, for example. As a result, + // if we sorted topologically by sub-trees here then we'd keep $c and $a + // together, and $d and $b, but a better order might interleave them. A good + // order also may not keep topological depths separated, e.g. we may want to + // put $a in between $c and $d despite it having a greater depth. + // + // The greedy approach here may also be unoptimal, however. Consider that we + // might see that the best available global is $a, but if we popped $b + // instead that could unlock $c which depends on $b, and $c may have a much + // higher use count than $a. For that reason we try several variations of + // this with different counts, see earlier. + std::vector availableHeap; + + // Comparison function. Given a and b, returns if a should be before b. This + // is used in a heap, where "highest" means "popped first", so see the notes + // below on how we order. + auto cmp = [&](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. + auto aImported = globals[a]->imported(); + auto bImported = globals[b]->imported(); + // The highest items will be popped first off the heap, so we want imports + // to be at higher indexes, that is, + // + // unimported, unimported, imported, imported. + // + // Then the imports are popped first. + if (aImported != bImported) { + return bImported; + } + + // Sort by the counts. We want higher counts at higher indexes so they are + // popped first, that is, + // + // 10, 20, 30, 40 + // + auto aCount = counts[a]; + auto bCount = counts[b]; + if (aCount != bCount) { + return aCount < bCount; + } + + // Break ties using the original order, which means just using the + // indices we have. We need lower indexes at the top so they are popped + // first, that is, + // + // 3, 2, 1, 0 + // + return a > b; + }; + + // Push an item that just became available to the available heap. + auto push = [&](Index global) { + availableHeap.push_back(global); + std::push_heap(availableHeap.begin(), availableHeap.end(), cmp); + }; + + // The initially available globals are those with no dependencies. + for (Index i = 0; i < globals.size(); i++) { + if (deps.dependsOn[i].empty()) { + push(i); + } + } + + // Pop off the heap: Emit the global and its final, sorted index. Keep + // doing that until we finish processing all the globals. + IndexIndexMap sortedindices(globals.size()); + Index numSortedindices = 0; + while (!availableHeap.empty()) { + std::pop_heap(availableHeap.begin(), availableHeap.end(), cmp); + auto global = availableHeap.back(); + sortedindices[global] = numSortedindices++; + availableHeap.pop_back(); + + // Each time we pop we emit the global, which means anything that only + // depended on it becomes available to be popped as well. + for (auto other : deps.dependedUpon[global]) { + assert(deps.dependsOn[other].count(global)); + deps.dependsOn[other].erase(global); + if (deps.dependsOn[other].empty()) { + push(other); + } + } + } + + // All globals must have been handled. Cycles would prevent this, but they + // cannot exist in valid IR. + assert(numSortedindices == globals.size()); + + return sortedindices; + } + + // Given an indexing of the globals and the counts of how many times each is + // used, estimate the size of relevant parts of the wasm binary (that is, of + // LEBs in global.gets). + double computeSize(IndexIndexMap& indices, IndexCountMap& counts) { + // |indices| maps each old index to its new position in the sort. We need + // the reverse map here, which at index 0 has the old index of the global + // that will be first, and so forth. + IndexIndexMap actualOrder(indices.size()); + for (Index i = 0; i < indices.size(); i++) { + // Each global has a unique index, so we only replace 0's here, and they + // must be in bounds. + assert(indices[i] < indices.size()); + assert(actualOrder[indices[i]] == 0); + + actualOrder[indices[i]] = i; + } + + if (always) { + // In this mode we gradually increase the cost of later globals, in an + // unrealistic but smooth manner. + double total = 0; + for (Index i = 0; i < actualOrder.size(); i++) { + // Multiply the count for this global by a smoothed LEB factor, which + // starts at 1 (for 1 byte) at index 0, and then increases linearly with + // i, so that after 128 globals we reach 2 (which is the true index at + // which the LEB size normally jumps from 1 to 2), and so forth. + total += counts[actualOrder[i]] * (1.0 + (i / 128.0)); + } + return total; + } + + // The total size we are computing. + double total = 0; + // 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; + for (Index i = 0; i < actualOrder.size(); i++) { + if (i == nextSizeIncrease) { + sizeInBits++; + // At the current size we have 7 * sizeInBits bits to use. For example, + // at index 0 the size is 1 and we'll compute 128 here, and indeed after + // emitting 128 globals (0,..,127) the 129th (at index 128) requires a + // larger LEB. + nextSizeIncrease = 1 << (7 * sizeInBits); + } + total += counts[actualOrder[i]] * sizeInBits; + } + return total; + } }; Pass* createReorderGlobalsPass() { return new ReorderGlobals(false); } diff --git a/test/lit/merge/names.wat b/test/lit/merge/names.wat index 4562af58bc3..c4e81464960 100644 --- a/test/lit/merge/names.wat +++ b/test/lit/merge/names.wat @@ -12,13 +12,13 @@ ;; CHECK: (type $4 (func (param (ref $u)))) - ;; CHECK: (global $global$0 i32 (i32.const 0)) + ;; CHECK: (global $glob0 i32 (i32.const 0)) - ;; CHECK: (global $glob2 i32 (i32.const 0)) + ;; CHECK: (global $global$1 i32 (i32.const 0)) - ;; CHECK: (global $global$2 i32 (i32.const 0)) + ;; CHECK: (global $glob2 i32 (i32.const 0)) - ;; CHECK: (global $glob0 i32 (i32.const 0)) + ;; CHECK: (global $global$3 i32 (i32.const 0)) ;; CHECK: (memory $mem0 0) @@ -54,7 +54,7 @@ ;; CHECK: (export "g0" (global $glob0)) - ;; CHECK: (export "g1" (global $global$2)) + ;; CHECK: (export "g1" (global $global$1)) ;; CHECK: (export "m0" (memory $mem0)) @@ -80,7 +80,7 @@ ;; CHECK: (export "g2" (global $glob2)) - ;; CHECK: (export "g3" (global $global$0)) + ;; CHECK: (export "g3" (global $global$3)) ;; CHECK: (export "tag2" (tag $tag2)) diff --git a/test/lit/merge/renamings.wat b/test/lit/merge/renamings.wat index 4d1c32c5af1..c6a22542ac5 100644 --- a/test/lit/merge/renamings.wat +++ b/test/lit/merge/renamings.wat @@ -23,21 +23,20 @@ ;; CHECK: (import "elsewhere" "some.tag" (tag $imported (param f64))) - ;; CHECK: (global $bar_2 i32 (i32.const 4)) - - ;; CHECK: (global $other i32 (i32.const 3)) - - ;; CHECK: (global $bar i32 (i32.const 2)) - ;; CHECK: (global $foo i32 (i32.const 1)) (global $foo i32 (i32.const 1)) ;; This global has a conflict in second.wat, and so second.wat's $bar ;; will be renamed. + ;; CHECK: (global $bar i32 (i32.const 2)) (global $bar i32 (i32.const 2)) ;; This memory has a conflict in second.wat, and so second.wat's $foo ;; will be renamed. + ;; CHECK: (global $other i32 (i32.const 3)) + + ;; CHECK: (global $bar_2 i32 (i32.const 4)) + ;; CHECK: (memory $foo 10 20) (memory $foo 10 20) diff --git a/test/lit/passes/j2cl.wast b/test/lit/passes/j2cl.wast index 1963203a88b..b8081d8b22c 100644 --- a/test/lit/passes/j2cl.wast +++ b/test/lit/passes/j2cl.wast @@ -6,10 +6,9 @@ (module ;; CHECK: (type $0 (func)) - ;; CHECK: (global $field-f64@Foo f64 (f64.const 1)) - ;; CHECK: (global $field-i32@Foo i32 (i32.const 1)) (global $field-i32@Foo (mut i32) (i32.const 0)) + ;; CHECK: (global $field-f64@Foo f64 (f64.const 1)) (global $field-f64@Foo (mut f64) (f64.const 0)) ;; CHECK: (func $clinit__@Foo (type $0) @@ -29,20 +28,18 @@ ;; CHECK: (type $1 (func)) - ;; CHECK: (global $field2@Foo (mut anyref) (ref.null none)) - ;; CHECK: (global $referredField@Foo i32 (i32.const 42)) (global $referredField@Foo i32 (i32.const 42)) - ;; CHECK: (global $field1@Foo anyref (struct.new $A - ;; CHECK-NEXT: (global.get $referredField@Foo) - ;; CHECK-NEXT: )) - ;; CHECK: (global $referredFieldMut@Foo (mut i32) (i32.const 42)) (global $referredFieldMut@Foo (mut i32) (i32.const 42)) + ;; CHECK: (global $field1@Foo anyref (struct.new $A + ;; CHECK-NEXT: (global.get $referredField@Foo) + ;; CHECK-NEXT: )) (global $field1@Foo (mut anyref) (ref.null none)) + ;; CHECK: (global $field2@Foo (mut anyref) (ref.null none)) (global $field2@Foo (mut anyref) (ref.null none)) ;; CHECK: (global $field3@Foo anyref (global.get $field1@Foo)) @@ -78,11 +75,10 @@ (type $A (struct)) ;; CHECK: (type $1 (func)) - ;; CHECK: (global $field-any@Foo (mut anyref) (struct.new_default $A)) - ;; CHECK: (global $field-i32@Foo (mut i32) (i32.const 2)) (global $field-i32@Foo (mut i32) (i32.const 2)) + ;; CHECK: (global $field-any@Foo (mut anyref) (struct.new_default $A)) (global $field-any@Foo (mut anyref) (struct.new $A)) ;; CHECK: (func $clinit__@Foo (type $1) @@ -156,13 +152,11 @@ (module ;; CHECK: (type $0 (func (result i32))) - ;; CHECK: (global $$var3@Zoo i32 (i32.const 2)) - - ;; CHECK: (global $$var2@Zoo i32 (i32.const 2)) - ;; CHECK: (global $$var1@Zoo i32 (i32.const 2)) (global $$var1@Zoo (mut i32) (i32.const 0)) + ;; CHECK: (global $$var2@Zoo i32 (i32.const 2)) (global $$var2@Zoo (mut i32) (i32.const 0)) + ;; CHECK: (global $$var3@Zoo i32 (i32.const 2)) (global $$var3@Zoo (mut i32) (i32.const 0)) ;; CHECK: (func $getVar1__@Zoo (type $0) (result i32) @@ -211,10 +205,9 @@ ;; CHECK: (type $1 (func (result i32))) - ;; CHECK: (global $$var2@Zoo (mut i32) (i32.const 3)) - ;; CHECK: (global $$var1@Zoo (mut i32) (i32.const 2)) (global $$var1@Zoo (mut i32) (i32.const 2)) + ;; CHECK: (global $$var2@Zoo (mut i32) (i32.const 3)) (global $$var2@Zoo (mut i32) (i32.const 3)) diff --git a/test/lit/passes/reorder-globals-real.wast b/test/lit/passes/reorder-globals-real.wast new file mode 100644 index 00000000000..87ccc92c9e8 --- /dev/null +++ b/test/lit/passes/reorder-globals-real.wast @@ -0,0 +1,894 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. + +;; Similar to reorder-globals, but this runs the "real" version, without +;; "-always". That is, this tests the production code. The downside is we need +;; 128+ globals to see any changes here, so we keep most testing in the other +;; file. + +;; RUN: foreach %s %t wasm-opt -all --reorder-globals -S -o - | filecheck %s + +;; A situation where the simple greedy sort fails to be optimal. We have 129 +;; globals, enough for the LEB size to grow by 1 for the last. One global, +;; |other|, is independent of the rest. The second is in a chain with all the +;; others: +;; +;; global1 <- global2 <- .. <- global128 +;; +;; other has a higher count than global1, so if we are greedy we pick it. But +;; global128 has the highest count by far, so it is actually worth emitting the +;; entire chain first, and only then other, which is the original order. +(module + ;; CHECK: (global $global1 i32 (i32.const 1)) + (global $global1 i32 (i32.const 1)) + ;; CHECK: (global $global2 i32 (global.get $global1)) + (global $global2 i32 (global.get $global1)) + ;; CHECK: (global $global3 i32 (global.get $global2)) + (global $global3 i32 (global.get $global2)) + ;; CHECK: (global $global4 i32 (global.get $global3)) + (global $global4 i32 (global.get $global3)) + ;; CHECK: (global $global5 i32 (global.get $global4)) + (global $global5 i32 (global.get $global4)) + ;; CHECK: (global $global6 i32 (global.get $global5)) + (global $global6 i32 (global.get $global5)) + ;; CHECK: (global $global7 i32 (global.get $global6)) + (global $global7 i32 (global.get $global6)) + ;; CHECK: (global $global8 i32 (global.get $global7)) + (global $global8 i32 (global.get $global7)) + ;; CHECK: (global $global9 i32 (global.get $global8)) + (global $global9 i32 (global.get $global8)) + ;; CHECK: (global $global10 i32 (global.get $global9)) + (global $global10 i32 (global.get $global9)) + ;; CHECK: (global $global11 i32 (global.get $global10)) + (global $global11 i32 (global.get $global10)) + ;; CHECK: (global $global12 i32 (global.get $global11)) + (global $global12 i32 (global.get $global11)) + ;; CHECK: (global $global13 i32 (global.get $global12)) + (global $global13 i32 (global.get $global12)) + ;; CHECK: (global $global14 i32 (global.get $global13)) + (global $global14 i32 (global.get $global13)) + ;; CHECK: (global $global15 i32 (global.get $global14)) + (global $global15 i32 (global.get $global14)) + ;; CHECK: (global $global16 i32 (global.get $global15)) + (global $global16 i32 (global.get $global15)) + ;; CHECK: (global $global17 i32 (global.get $global16)) + (global $global17 i32 (global.get $global16)) + ;; CHECK: (global $global18 i32 (global.get $global17)) + (global $global18 i32 (global.get $global17)) + ;; CHECK: (global $global19 i32 (global.get $global18)) + (global $global19 i32 (global.get $global18)) + ;; CHECK: (global $global20 i32 (global.get $global19)) + (global $global20 i32 (global.get $global19)) + ;; CHECK: (global $global21 i32 (global.get $global20)) + (global $global21 i32 (global.get $global20)) + ;; CHECK: (global $global22 i32 (global.get $global21)) + (global $global22 i32 (global.get $global21)) + ;; CHECK: (global $global23 i32 (global.get $global22)) + (global $global23 i32 (global.get $global22)) + ;; CHECK: (global $global24 i32 (global.get $global23)) + (global $global24 i32 (global.get $global23)) + ;; CHECK: (global $global25 i32 (global.get $global24)) + (global $global25 i32 (global.get $global24)) + ;; CHECK: (global $global26 i32 (global.get $global25)) + (global $global26 i32 (global.get $global25)) + ;; CHECK: (global $global27 i32 (global.get $global26)) + (global $global27 i32 (global.get $global26)) + ;; CHECK: (global $global28 i32 (global.get $global27)) + (global $global28 i32 (global.get $global27)) + ;; CHECK: (global $global29 i32 (global.get $global28)) + (global $global29 i32 (global.get $global28)) + ;; CHECK: (global $global30 i32 (global.get $global29)) + (global $global30 i32 (global.get $global29)) + ;; CHECK: (global $global31 i32 (global.get $global30)) + (global $global31 i32 (global.get $global30)) + ;; CHECK: (global $global32 i32 (global.get $global31)) + (global $global32 i32 (global.get $global31)) + ;; CHECK: (global $global33 i32 (global.get $global32)) + (global $global33 i32 (global.get $global32)) + ;; CHECK: (global $global34 i32 (global.get $global33)) + (global $global34 i32 (global.get $global33)) + ;; CHECK: (global $global35 i32 (global.get $global34)) + (global $global35 i32 (global.get $global34)) + ;; CHECK: (global $global36 i32 (global.get $global35)) + (global $global36 i32 (global.get $global35)) + ;; CHECK: (global $global37 i32 (global.get $global36)) + (global $global37 i32 (global.get $global36)) + ;; CHECK: (global $global38 i32 (global.get $global37)) + (global $global38 i32 (global.get $global37)) + ;; CHECK: (global $global39 i32 (global.get $global38)) + (global $global39 i32 (global.get $global38)) + ;; CHECK: (global $global40 i32 (global.get $global39)) + (global $global40 i32 (global.get $global39)) + ;; CHECK: (global $global41 i32 (global.get $global40)) + (global $global41 i32 (global.get $global40)) + ;; CHECK: (global $global42 i32 (global.get $global41)) + (global $global42 i32 (global.get $global41)) + ;; CHECK: (global $global43 i32 (global.get $global42)) + (global $global43 i32 (global.get $global42)) + ;; CHECK: (global $global44 i32 (global.get $global43)) + (global $global44 i32 (global.get $global43)) + ;; CHECK: (global $global45 i32 (global.get $global44)) + (global $global45 i32 (global.get $global44)) + ;; CHECK: (global $global46 i32 (global.get $global45)) + (global $global46 i32 (global.get $global45)) + ;; CHECK: (global $global47 i32 (global.get $global46)) + (global $global47 i32 (global.get $global46)) + ;; CHECK: (global $global48 i32 (global.get $global47)) + (global $global48 i32 (global.get $global47)) + ;; CHECK: (global $global49 i32 (global.get $global48)) + (global $global49 i32 (global.get $global48)) + ;; CHECK: (global $global50 i32 (global.get $global49)) + (global $global50 i32 (global.get $global49)) + ;; CHECK: (global $global51 i32 (global.get $global50)) + (global $global51 i32 (global.get $global50)) + ;; CHECK: (global $global52 i32 (global.get $global51)) + (global $global52 i32 (global.get $global51)) + ;; CHECK: (global $global53 i32 (global.get $global52)) + (global $global53 i32 (global.get $global52)) + ;; CHECK: (global $global54 i32 (global.get $global53)) + (global $global54 i32 (global.get $global53)) + ;; CHECK: (global $global55 i32 (global.get $global54)) + (global $global55 i32 (global.get $global54)) + ;; CHECK: (global $global56 i32 (global.get $global55)) + (global $global56 i32 (global.get $global55)) + ;; CHECK: (global $global57 i32 (global.get $global56)) + (global $global57 i32 (global.get $global56)) + ;; CHECK: (global $global58 i32 (global.get $global57)) + (global $global58 i32 (global.get $global57)) + ;; CHECK: (global $global59 i32 (global.get $global58)) + (global $global59 i32 (global.get $global58)) + ;; CHECK: (global $global60 i32 (global.get $global59)) + (global $global60 i32 (global.get $global59)) + ;; CHECK: (global $global61 i32 (global.get $global60)) + (global $global61 i32 (global.get $global60)) + ;; CHECK: (global $global62 i32 (global.get $global61)) + (global $global62 i32 (global.get $global61)) + ;; CHECK: (global $global63 i32 (global.get $global62)) + (global $global63 i32 (global.get $global62)) + ;; CHECK: (global $global64 i32 (global.get $global63)) + (global $global64 i32 (global.get $global63)) + ;; CHECK: (global $global65 i32 (global.get $global64)) + (global $global65 i32 (global.get $global64)) + ;; CHECK: (global $global66 i32 (global.get $global65)) + (global $global66 i32 (global.get $global65)) + ;; CHECK: (global $global67 i32 (global.get $global66)) + (global $global67 i32 (global.get $global66)) + ;; CHECK: (global $global68 i32 (global.get $global67)) + (global $global68 i32 (global.get $global67)) + ;; CHECK: (global $global69 i32 (global.get $global68)) + (global $global69 i32 (global.get $global68)) + ;; CHECK: (global $global70 i32 (global.get $global69)) + (global $global70 i32 (global.get $global69)) + ;; CHECK: (global $global71 i32 (global.get $global70)) + (global $global71 i32 (global.get $global70)) + ;; CHECK: (global $global72 i32 (global.get $global71)) + (global $global72 i32 (global.get $global71)) + ;; CHECK: (global $global73 i32 (global.get $global72)) + (global $global73 i32 (global.get $global72)) + ;; CHECK: (global $global74 i32 (global.get $global73)) + (global $global74 i32 (global.get $global73)) + ;; CHECK: (global $global75 i32 (global.get $global74)) + (global $global75 i32 (global.get $global74)) + ;; CHECK: (global $global76 i32 (global.get $global75)) + (global $global76 i32 (global.get $global75)) + ;; CHECK: (global $global77 i32 (global.get $global76)) + (global $global77 i32 (global.get $global76)) + ;; CHECK: (global $global78 i32 (global.get $global77)) + (global $global78 i32 (global.get $global77)) + ;; CHECK: (global $global79 i32 (global.get $global78)) + (global $global79 i32 (global.get $global78)) + ;; CHECK: (global $global80 i32 (global.get $global79)) + (global $global80 i32 (global.get $global79)) + ;; CHECK: (global $global81 i32 (global.get $global80)) + (global $global81 i32 (global.get $global80)) + ;; CHECK: (global $global82 i32 (global.get $global81)) + (global $global82 i32 (global.get $global81)) + ;; CHECK: (global $global83 i32 (global.get $global82)) + (global $global83 i32 (global.get $global82)) + ;; CHECK: (global $global84 i32 (global.get $global83)) + (global $global84 i32 (global.get $global83)) + ;; CHECK: (global $global85 i32 (global.get $global84)) + (global $global85 i32 (global.get $global84)) + ;; CHECK: (global $global86 i32 (global.get $global85)) + (global $global86 i32 (global.get $global85)) + ;; CHECK: (global $global87 i32 (global.get $global86)) + (global $global87 i32 (global.get $global86)) + ;; CHECK: (global $global88 i32 (global.get $global87)) + (global $global88 i32 (global.get $global87)) + ;; CHECK: (global $global89 i32 (global.get $global88)) + (global $global89 i32 (global.get $global88)) + ;; CHECK: (global $global90 i32 (global.get $global89)) + (global $global90 i32 (global.get $global89)) + ;; CHECK: (global $global91 i32 (global.get $global90)) + (global $global91 i32 (global.get $global90)) + ;; CHECK: (global $global92 i32 (global.get $global91)) + (global $global92 i32 (global.get $global91)) + ;; CHECK: (global $global93 i32 (global.get $global92)) + (global $global93 i32 (global.get $global92)) + ;; CHECK: (global $global94 i32 (global.get $global93)) + (global $global94 i32 (global.get $global93)) + ;; CHECK: (global $global95 i32 (global.get $global94)) + (global $global95 i32 (global.get $global94)) + ;; CHECK: (global $global96 i32 (global.get $global95)) + (global $global96 i32 (global.get $global95)) + ;; CHECK: (global $global97 i32 (global.get $global96)) + (global $global97 i32 (global.get $global96)) + ;; CHECK: (global $global98 i32 (global.get $global97)) + (global $global98 i32 (global.get $global97)) + ;; CHECK: (global $global99 i32 (global.get $global98)) + (global $global99 i32 (global.get $global98)) + ;; CHECK: (global $global100 i32 (global.get $global99)) + (global $global100 i32 (global.get $global99)) + ;; CHECK: (global $global101 i32 (global.get $global100)) + (global $global101 i32 (global.get $global100)) + ;; CHECK: (global $global102 i32 (global.get $global101)) + (global $global102 i32 (global.get $global101)) + ;; CHECK: (global $global103 i32 (global.get $global102)) + (global $global103 i32 (global.get $global102)) + ;; CHECK: (global $global104 i32 (global.get $global103)) + (global $global104 i32 (global.get $global103)) + ;; CHECK: (global $global105 i32 (global.get $global104)) + (global $global105 i32 (global.get $global104)) + ;; CHECK: (global $global106 i32 (global.get $global105)) + (global $global106 i32 (global.get $global105)) + ;; CHECK: (global $global107 i32 (global.get $global106)) + (global $global107 i32 (global.get $global106)) + ;; CHECK: (global $global108 i32 (global.get $global107)) + (global $global108 i32 (global.get $global107)) + ;; CHECK: (global $global109 i32 (global.get $global108)) + (global $global109 i32 (global.get $global108)) + ;; CHECK: (global $global110 i32 (global.get $global109)) + (global $global110 i32 (global.get $global109)) + ;; CHECK: (global $global111 i32 (global.get $global110)) + (global $global111 i32 (global.get $global110)) + ;; CHECK: (global $global112 i32 (global.get $global111)) + (global $global112 i32 (global.get $global111)) + ;; CHECK: (global $global113 i32 (global.get $global112)) + (global $global113 i32 (global.get $global112)) + ;; CHECK: (global $global114 i32 (global.get $global113)) + (global $global114 i32 (global.get $global113)) + ;; CHECK: (global $global115 i32 (global.get $global114)) + (global $global115 i32 (global.get $global114)) + ;; CHECK: (global $global116 i32 (global.get $global115)) + (global $global116 i32 (global.get $global115)) + ;; CHECK: (global $global117 i32 (global.get $global116)) + (global $global117 i32 (global.get $global116)) + ;; CHECK: (global $global118 i32 (global.get $global117)) + (global $global118 i32 (global.get $global117)) + ;; CHECK: (global $global119 i32 (global.get $global118)) + (global $global119 i32 (global.get $global118)) + ;; CHECK: (global $global120 i32 (global.get $global119)) + (global $global120 i32 (global.get $global119)) + ;; CHECK: (global $global121 i32 (global.get $global120)) + (global $global121 i32 (global.get $global120)) + ;; CHECK: (global $global122 i32 (global.get $global121)) + (global $global122 i32 (global.get $global121)) + ;; CHECK: (global $global123 i32 (global.get $global122)) + (global $global123 i32 (global.get $global122)) + ;; CHECK: (global $global124 i32 (global.get $global123)) + (global $global124 i32 (global.get $global123)) + ;; CHECK: (global $global125 i32 (global.get $global124)) + (global $global125 i32 (global.get $global124)) + ;; CHECK: (global $global126 i32 (global.get $global125)) + (global $global126 i32 (global.get $global125)) + ;; CHECK: (global $global127 i32 (global.get $global126)) + (global $global127 i32 (global.get $global126)) + ;; CHECK: (global $global128 i32 (global.get $global127)) + (global $global128 i32 (global.get $global127)) + + ;; CHECK: (global $other i32 (i32.const 0)) + (global $other i32 (i32.const 0)) + + ;; CHECK: (func $uses (type $0) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $global128) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $global128) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $global128) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $global128) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $global128) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $global128) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $global128) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $global128) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $global128) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $global128) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $uses + ;; Aside from the uses in the globals themselves (which means one use for + ;; each of global1..global127), we add two uses of other, to make it + ;; have a higher count than global1, and 10 uses of global128, to make it + ;; have the highest count by far. + (drop (global.get $other)) + (drop (global.get $other)) + + (drop (global.get $global128)) + (drop (global.get $global128)) + (drop (global.get $global128)) + (drop (global.get $global128)) + (drop (global.get $global128)) + (drop (global.get $global128)) + (drop (global.get $global128)) + (drop (global.get $global128)) + (drop (global.get $global128)) + (drop (global.get $global128)) + ) +) + +;; As above, but now the greedy sort is optimal. We remove all uses of +;; $global128, so $other has the highest count and there is no other factor that +;; matters here, so emitting $other first is best. +(module + ;; CHECK: (global $other i32 (i32.const 0)) + + ;; CHECK: (global $global1 i32 (i32.const 1)) + (global $global1 i32 (i32.const 1)) + ;; CHECK: (global $global2 i32 (global.get $global1)) + (global $global2 i32 (global.get $global1)) + ;; CHECK: (global $global3 i32 (global.get $global2)) + (global $global3 i32 (global.get $global2)) + ;; CHECK: (global $global4 i32 (global.get $global3)) + (global $global4 i32 (global.get $global3)) + ;; CHECK: (global $global5 i32 (global.get $global4)) + (global $global5 i32 (global.get $global4)) + ;; CHECK: (global $global6 i32 (global.get $global5)) + (global $global6 i32 (global.get $global5)) + ;; CHECK: (global $global7 i32 (global.get $global6)) + (global $global7 i32 (global.get $global6)) + ;; CHECK: (global $global8 i32 (global.get $global7)) + (global $global8 i32 (global.get $global7)) + ;; CHECK: (global $global9 i32 (global.get $global8)) + (global $global9 i32 (global.get $global8)) + ;; CHECK: (global $global10 i32 (global.get $global9)) + (global $global10 i32 (global.get $global9)) + ;; CHECK: (global $global11 i32 (global.get $global10)) + (global $global11 i32 (global.get $global10)) + ;; CHECK: (global $global12 i32 (global.get $global11)) + (global $global12 i32 (global.get $global11)) + ;; CHECK: (global $global13 i32 (global.get $global12)) + (global $global13 i32 (global.get $global12)) + ;; CHECK: (global $global14 i32 (global.get $global13)) + (global $global14 i32 (global.get $global13)) + ;; CHECK: (global $global15 i32 (global.get $global14)) + (global $global15 i32 (global.get $global14)) + ;; CHECK: (global $global16 i32 (global.get $global15)) + (global $global16 i32 (global.get $global15)) + ;; CHECK: (global $global17 i32 (global.get $global16)) + (global $global17 i32 (global.get $global16)) + ;; CHECK: (global $global18 i32 (global.get $global17)) + (global $global18 i32 (global.get $global17)) + ;; CHECK: (global $global19 i32 (global.get $global18)) + (global $global19 i32 (global.get $global18)) + ;; CHECK: (global $global20 i32 (global.get $global19)) + (global $global20 i32 (global.get $global19)) + ;; CHECK: (global $global21 i32 (global.get $global20)) + (global $global21 i32 (global.get $global20)) + ;; CHECK: (global $global22 i32 (global.get $global21)) + (global $global22 i32 (global.get $global21)) + ;; CHECK: (global $global23 i32 (global.get $global22)) + (global $global23 i32 (global.get $global22)) + ;; CHECK: (global $global24 i32 (global.get $global23)) + (global $global24 i32 (global.get $global23)) + ;; CHECK: (global $global25 i32 (global.get $global24)) + (global $global25 i32 (global.get $global24)) + ;; CHECK: (global $global26 i32 (global.get $global25)) + (global $global26 i32 (global.get $global25)) + ;; CHECK: (global $global27 i32 (global.get $global26)) + (global $global27 i32 (global.get $global26)) + ;; CHECK: (global $global28 i32 (global.get $global27)) + (global $global28 i32 (global.get $global27)) + ;; CHECK: (global $global29 i32 (global.get $global28)) + (global $global29 i32 (global.get $global28)) + ;; CHECK: (global $global30 i32 (global.get $global29)) + (global $global30 i32 (global.get $global29)) + ;; CHECK: (global $global31 i32 (global.get $global30)) + (global $global31 i32 (global.get $global30)) + ;; CHECK: (global $global32 i32 (global.get $global31)) + (global $global32 i32 (global.get $global31)) + ;; CHECK: (global $global33 i32 (global.get $global32)) + (global $global33 i32 (global.get $global32)) + ;; CHECK: (global $global34 i32 (global.get $global33)) + (global $global34 i32 (global.get $global33)) + ;; CHECK: (global $global35 i32 (global.get $global34)) + (global $global35 i32 (global.get $global34)) + ;; CHECK: (global $global36 i32 (global.get $global35)) + (global $global36 i32 (global.get $global35)) + ;; CHECK: (global $global37 i32 (global.get $global36)) + (global $global37 i32 (global.get $global36)) + ;; CHECK: (global $global38 i32 (global.get $global37)) + (global $global38 i32 (global.get $global37)) + ;; CHECK: (global $global39 i32 (global.get $global38)) + (global $global39 i32 (global.get $global38)) + ;; CHECK: (global $global40 i32 (global.get $global39)) + (global $global40 i32 (global.get $global39)) + ;; CHECK: (global $global41 i32 (global.get $global40)) + (global $global41 i32 (global.get $global40)) + ;; CHECK: (global $global42 i32 (global.get $global41)) + (global $global42 i32 (global.get $global41)) + ;; CHECK: (global $global43 i32 (global.get $global42)) + (global $global43 i32 (global.get $global42)) + ;; CHECK: (global $global44 i32 (global.get $global43)) + (global $global44 i32 (global.get $global43)) + ;; CHECK: (global $global45 i32 (global.get $global44)) + (global $global45 i32 (global.get $global44)) + ;; CHECK: (global $global46 i32 (global.get $global45)) + (global $global46 i32 (global.get $global45)) + ;; CHECK: (global $global47 i32 (global.get $global46)) + (global $global47 i32 (global.get $global46)) + ;; CHECK: (global $global48 i32 (global.get $global47)) + (global $global48 i32 (global.get $global47)) + ;; CHECK: (global $global49 i32 (global.get $global48)) + (global $global49 i32 (global.get $global48)) + ;; CHECK: (global $global50 i32 (global.get $global49)) + (global $global50 i32 (global.get $global49)) + ;; CHECK: (global $global51 i32 (global.get $global50)) + (global $global51 i32 (global.get $global50)) + ;; CHECK: (global $global52 i32 (global.get $global51)) + (global $global52 i32 (global.get $global51)) + ;; CHECK: (global $global53 i32 (global.get $global52)) + (global $global53 i32 (global.get $global52)) + ;; CHECK: (global $global54 i32 (global.get $global53)) + (global $global54 i32 (global.get $global53)) + ;; CHECK: (global $global55 i32 (global.get $global54)) + (global $global55 i32 (global.get $global54)) + ;; CHECK: (global $global56 i32 (global.get $global55)) + (global $global56 i32 (global.get $global55)) + ;; CHECK: (global $global57 i32 (global.get $global56)) + (global $global57 i32 (global.get $global56)) + ;; CHECK: (global $global58 i32 (global.get $global57)) + (global $global58 i32 (global.get $global57)) + ;; CHECK: (global $global59 i32 (global.get $global58)) + (global $global59 i32 (global.get $global58)) + ;; CHECK: (global $global60 i32 (global.get $global59)) + (global $global60 i32 (global.get $global59)) + ;; CHECK: (global $global61 i32 (global.get $global60)) + (global $global61 i32 (global.get $global60)) + ;; CHECK: (global $global62 i32 (global.get $global61)) + (global $global62 i32 (global.get $global61)) + ;; CHECK: (global $global63 i32 (global.get $global62)) + (global $global63 i32 (global.get $global62)) + ;; CHECK: (global $global64 i32 (global.get $global63)) + (global $global64 i32 (global.get $global63)) + ;; CHECK: (global $global65 i32 (global.get $global64)) + (global $global65 i32 (global.get $global64)) + ;; CHECK: (global $global66 i32 (global.get $global65)) + (global $global66 i32 (global.get $global65)) + ;; CHECK: (global $global67 i32 (global.get $global66)) + (global $global67 i32 (global.get $global66)) + ;; CHECK: (global $global68 i32 (global.get $global67)) + (global $global68 i32 (global.get $global67)) + ;; CHECK: (global $global69 i32 (global.get $global68)) + (global $global69 i32 (global.get $global68)) + ;; CHECK: (global $global70 i32 (global.get $global69)) + (global $global70 i32 (global.get $global69)) + ;; CHECK: (global $global71 i32 (global.get $global70)) + (global $global71 i32 (global.get $global70)) + ;; CHECK: (global $global72 i32 (global.get $global71)) + (global $global72 i32 (global.get $global71)) + ;; CHECK: (global $global73 i32 (global.get $global72)) + (global $global73 i32 (global.get $global72)) + ;; CHECK: (global $global74 i32 (global.get $global73)) + (global $global74 i32 (global.get $global73)) + ;; CHECK: (global $global75 i32 (global.get $global74)) + (global $global75 i32 (global.get $global74)) + ;; CHECK: (global $global76 i32 (global.get $global75)) + (global $global76 i32 (global.get $global75)) + ;; CHECK: (global $global77 i32 (global.get $global76)) + (global $global77 i32 (global.get $global76)) + ;; CHECK: (global $global78 i32 (global.get $global77)) + (global $global78 i32 (global.get $global77)) + ;; CHECK: (global $global79 i32 (global.get $global78)) + (global $global79 i32 (global.get $global78)) + ;; CHECK: (global $global80 i32 (global.get $global79)) + (global $global80 i32 (global.get $global79)) + ;; CHECK: (global $global81 i32 (global.get $global80)) + (global $global81 i32 (global.get $global80)) + ;; CHECK: (global $global82 i32 (global.get $global81)) + (global $global82 i32 (global.get $global81)) + ;; CHECK: (global $global83 i32 (global.get $global82)) + (global $global83 i32 (global.get $global82)) + ;; CHECK: (global $global84 i32 (global.get $global83)) + (global $global84 i32 (global.get $global83)) + ;; CHECK: (global $global85 i32 (global.get $global84)) + (global $global85 i32 (global.get $global84)) + ;; CHECK: (global $global86 i32 (global.get $global85)) + (global $global86 i32 (global.get $global85)) + ;; CHECK: (global $global87 i32 (global.get $global86)) + (global $global87 i32 (global.get $global86)) + ;; CHECK: (global $global88 i32 (global.get $global87)) + (global $global88 i32 (global.get $global87)) + ;; CHECK: (global $global89 i32 (global.get $global88)) + (global $global89 i32 (global.get $global88)) + ;; CHECK: (global $global90 i32 (global.get $global89)) + (global $global90 i32 (global.get $global89)) + ;; CHECK: (global $global91 i32 (global.get $global90)) + (global $global91 i32 (global.get $global90)) + ;; CHECK: (global $global92 i32 (global.get $global91)) + (global $global92 i32 (global.get $global91)) + ;; CHECK: (global $global93 i32 (global.get $global92)) + (global $global93 i32 (global.get $global92)) + ;; CHECK: (global $global94 i32 (global.get $global93)) + (global $global94 i32 (global.get $global93)) + ;; CHECK: (global $global95 i32 (global.get $global94)) + (global $global95 i32 (global.get $global94)) + ;; CHECK: (global $global96 i32 (global.get $global95)) + (global $global96 i32 (global.get $global95)) + ;; CHECK: (global $global97 i32 (global.get $global96)) + (global $global97 i32 (global.get $global96)) + ;; CHECK: (global $global98 i32 (global.get $global97)) + (global $global98 i32 (global.get $global97)) + ;; CHECK: (global $global99 i32 (global.get $global98)) + (global $global99 i32 (global.get $global98)) + ;; CHECK: (global $global100 i32 (global.get $global99)) + (global $global100 i32 (global.get $global99)) + ;; CHECK: (global $global101 i32 (global.get $global100)) + (global $global101 i32 (global.get $global100)) + ;; CHECK: (global $global102 i32 (global.get $global101)) + (global $global102 i32 (global.get $global101)) + ;; CHECK: (global $global103 i32 (global.get $global102)) + (global $global103 i32 (global.get $global102)) + ;; CHECK: (global $global104 i32 (global.get $global103)) + (global $global104 i32 (global.get $global103)) + ;; CHECK: (global $global105 i32 (global.get $global104)) + (global $global105 i32 (global.get $global104)) + ;; CHECK: (global $global106 i32 (global.get $global105)) + (global $global106 i32 (global.get $global105)) + ;; CHECK: (global $global107 i32 (global.get $global106)) + (global $global107 i32 (global.get $global106)) + ;; CHECK: (global $global108 i32 (global.get $global107)) + (global $global108 i32 (global.get $global107)) + ;; CHECK: (global $global109 i32 (global.get $global108)) + (global $global109 i32 (global.get $global108)) + ;; CHECK: (global $global110 i32 (global.get $global109)) + (global $global110 i32 (global.get $global109)) + ;; CHECK: (global $global111 i32 (global.get $global110)) + (global $global111 i32 (global.get $global110)) + ;; CHECK: (global $global112 i32 (global.get $global111)) + (global $global112 i32 (global.get $global111)) + ;; CHECK: (global $global113 i32 (global.get $global112)) + (global $global113 i32 (global.get $global112)) + ;; CHECK: (global $global114 i32 (global.get $global113)) + (global $global114 i32 (global.get $global113)) + ;; CHECK: (global $global115 i32 (global.get $global114)) + (global $global115 i32 (global.get $global114)) + ;; CHECK: (global $global116 i32 (global.get $global115)) + (global $global116 i32 (global.get $global115)) + ;; CHECK: (global $global117 i32 (global.get $global116)) + (global $global117 i32 (global.get $global116)) + ;; CHECK: (global $global118 i32 (global.get $global117)) + (global $global118 i32 (global.get $global117)) + ;; CHECK: (global $global119 i32 (global.get $global118)) + (global $global119 i32 (global.get $global118)) + ;; CHECK: (global $global120 i32 (global.get $global119)) + (global $global120 i32 (global.get $global119)) + ;; CHECK: (global $global121 i32 (global.get $global120)) + (global $global121 i32 (global.get $global120)) + ;; CHECK: (global $global122 i32 (global.get $global121)) + (global $global122 i32 (global.get $global121)) + ;; CHECK: (global $global123 i32 (global.get $global122)) + (global $global123 i32 (global.get $global122)) + ;; CHECK: (global $global124 i32 (global.get $global123)) + (global $global124 i32 (global.get $global123)) + ;; CHECK: (global $global125 i32 (global.get $global124)) + (global $global125 i32 (global.get $global124)) + ;; CHECK: (global $global126 i32 (global.get $global125)) + (global $global126 i32 (global.get $global125)) + ;; CHECK: (global $global127 i32 (global.get $global126)) + (global $global127 i32 (global.get $global126)) + ;; CHECK: (global $global128 i32 (global.get $global127)) + (global $global128 i32 (global.get $global127)) + + (global $other i32 (i32.const 0)) + + ;; CHECK: (func $uses (type $0) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $uses + (drop (global.get $other)) + (drop (global.get $other)) + ) +) + +;; As the last testcase, but one global fewer. As a result we only need a single +;; LEB byte for them all, and we do not bother changing the sort here. +(module + ;; CHECK: (global $global1 i32 (i32.const 1)) + (global $global1 i32 (i32.const 1)) + ;; CHECK: (global $global2 i32 (global.get $global1)) + (global $global2 i32 (global.get $global1)) + ;; CHECK: (global $global3 i32 (global.get $global2)) + (global $global3 i32 (global.get $global2)) + ;; CHECK: (global $global4 i32 (global.get $global3)) + (global $global4 i32 (global.get $global3)) + ;; CHECK: (global $global5 i32 (global.get $global4)) + (global $global5 i32 (global.get $global4)) + ;; CHECK: (global $global6 i32 (global.get $global5)) + (global $global6 i32 (global.get $global5)) + ;; CHECK: (global $global7 i32 (global.get $global6)) + (global $global7 i32 (global.get $global6)) + ;; CHECK: (global $global8 i32 (global.get $global7)) + (global $global8 i32 (global.get $global7)) + ;; CHECK: (global $global9 i32 (global.get $global8)) + (global $global9 i32 (global.get $global8)) + ;; CHECK: (global $global10 i32 (global.get $global9)) + (global $global10 i32 (global.get $global9)) + ;; CHECK: (global $global11 i32 (global.get $global10)) + (global $global11 i32 (global.get $global10)) + ;; CHECK: (global $global12 i32 (global.get $global11)) + (global $global12 i32 (global.get $global11)) + ;; CHECK: (global $global13 i32 (global.get $global12)) + (global $global13 i32 (global.get $global12)) + ;; CHECK: (global $global14 i32 (global.get $global13)) + (global $global14 i32 (global.get $global13)) + ;; CHECK: (global $global15 i32 (global.get $global14)) + (global $global15 i32 (global.get $global14)) + ;; CHECK: (global $global16 i32 (global.get $global15)) + (global $global16 i32 (global.get $global15)) + ;; CHECK: (global $global17 i32 (global.get $global16)) + (global $global17 i32 (global.get $global16)) + ;; CHECK: (global $global18 i32 (global.get $global17)) + (global $global18 i32 (global.get $global17)) + ;; CHECK: (global $global19 i32 (global.get $global18)) + (global $global19 i32 (global.get $global18)) + ;; CHECK: (global $global20 i32 (global.get $global19)) + (global $global20 i32 (global.get $global19)) + ;; CHECK: (global $global21 i32 (global.get $global20)) + (global $global21 i32 (global.get $global20)) + ;; CHECK: (global $global22 i32 (global.get $global21)) + (global $global22 i32 (global.get $global21)) + ;; CHECK: (global $global23 i32 (global.get $global22)) + (global $global23 i32 (global.get $global22)) + ;; CHECK: (global $global24 i32 (global.get $global23)) + (global $global24 i32 (global.get $global23)) + ;; CHECK: (global $global25 i32 (global.get $global24)) + (global $global25 i32 (global.get $global24)) + ;; CHECK: (global $global26 i32 (global.get $global25)) + (global $global26 i32 (global.get $global25)) + ;; CHECK: (global $global27 i32 (global.get $global26)) + (global $global27 i32 (global.get $global26)) + ;; CHECK: (global $global28 i32 (global.get $global27)) + (global $global28 i32 (global.get $global27)) + ;; CHECK: (global $global29 i32 (global.get $global28)) + (global $global29 i32 (global.get $global28)) + ;; CHECK: (global $global30 i32 (global.get $global29)) + (global $global30 i32 (global.get $global29)) + ;; CHECK: (global $global31 i32 (global.get $global30)) + (global $global31 i32 (global.get $global30)) + ;; CHECK: (global $global32 i32 (global.get $global31)) + (global $global32 i32 (global.get $global31)) + ;; CHECK: (global $global33 i32 (global.get $global32)) + (global $global33 i32 (global.get $global32)) + ;; CHECK: (global $global34 i32 (global.get $global33)) + (global $global34 i32 (global.get $global33)) + ;; CHECK: (global $global35 i32 (global.get $global34)) + (global $global35 i32 (global.get $global34)) + ;; CHECK: (global $global36 i32 (global.get $global35)) + (global $global36 i32 (global.get $global35)) + ;; CHECK: (global $global37 i32 (global.get $global36)) + (global $global37 i32 (global.get $global36)) + ;; CHECK: (global $global38 i32 (global.get $global37)) + (global $global38 i32 (global.get $global37)) + ;; CHECK: (global $global39 i32 (global.get $global38)) + (global $global39 i32 (global.get $global38)) + ;; CHECK: (global $global40 i32 (global.get $global39)) + (global $global40 i32 (global.get $global39)) + ;; CHECK: (global $global41 i32 (global.get $global40)) + (global $global41 i32 (global.get $global40)) + ;; CHECK: (global $global42 i32 (global.get $global41)) + (global $global42 i32 (global.get $global41)) + ;; CHECK: (global $global43 i32 (global.get $global42)) + (global $global43 i32 (global.get $global42)) + ;; CHECK: (global $global44 i32 (global.get $global43)) + (global $global44 i32 (global.get $global43)) + ;; CHECK: (global $global45 i32 (global.get $global44)) + (global $global45 i32 (global.get $global44)) + ;; CHECK: (global $global46 i32 (global.get $global45)) + (global $global46 i32 (global.get $global45)) + ;; CHECK: (global $global47 i32 (global.get $global46)) + (global $global47 i32 (global.get $global46)) + ;; CHECK: (global $global48 i32 (global.get $global47)) + (global $global48 i32 (global.get $global47)) + ;; CHECK: (global $global49 i32 (global.get $global48)) + (global $global49 i32 (global.get $global48)) + ;; CHECK: (global $global50 i32 (global.get $global49)) + (global $global50 i32 (global.get $global49)) + ;; CHECK: (global $global51 i32 (global.get $global50)) + (global $global51 i32 (global.get $global50)) + ;; CHECK: (global $global52 i32 (global.get $global51)) + (global $global52 i32 (global.get $global51)) + ;; CHECK: (global $global53 i32 (global.get $global52)) + (global $global53 i32 (global.get $global52)) + ;; CHECK: (global $global54 i32 (global.get $global53)) + (global $global54 i32 (global.get $global53)) + ;; CHECK: (global $global55 i32 (global.get $global54)) + (global $global55 i32 (global.get $global54)) + ;; CHECK: (global $global56 i32 (global.get $global55)) + (global $global56 i32 (global.get $global55)) + ;; CHECK: (global $global57 i32 (global.get $global56)) + (global $global57 i32 (global.get $global56)) + ;; CHECK: (global $global58 i32 (global.get $global57)) + (global $global58 i32 (global.get $global57)) + ;; CHECK: (global $global59 i32 (global.get $global58)) + (global $global59 i32 (global.get $global58)) + ;; CHECK: (global $global60 i32 (global.get $global59)) + (global $global60 i32 (global.get $global59)) + ;; CHECK: (global $global61 i32 (global.get $global60)) + (global $global61 i32 (global.get $global60)) + ;; CHECK: (global $global62 i32 (global.get $global61)) + (global $global62 i32 (global.get $global61)) + ;; CHECK: (global $global63 i32 (global.get $global62)) + (global $global63 i32 (global.get $global62)) + ;; CHECK: (global $global64 i32 (global.get $global63)) + (global $global64 i32 (global.get $global63)) + ;; CHECK: (global $global65 i32 (global.get $global64)) + (global $global65 i32 (global.get $global64)) + ;; CHECK: (global $global66 i32 (global.get $global65)) + (global $global66 i32 (global.get $global65)) + ;; CHECK: (global $global67 i32 (global.get $global66)) + (global $global67 i32 (global.get $global66)) + ;; CHECK: (global $global68 i32 (global.get $global67)) + (global $global68 i32 (global.get $global67)) + ;; CHECK: (global $global69 i32 (global.get $global68)) + (global $global69 i32 (global.get $global68)) + ;; CHECK: (global $global70 i32 (global.get $global69)) + (global $global70 i32 (global.get $global69)) + ;; CHECK: (global $global71 i32 (global.get $global70)) + (global $global71 i32 (global.get $global70)) + ;; CHECK: (global $global72 i32 (global.get $global71)) + (global $global72 i32 (global.get $global71)) + ;; CHECK: (global $global73 i32 (global.get $global72)) + (global $global73 i32 (global.get $global72)) + ;; CHECK: (global $global74 i32 (global.get $global73)) + (global $global74 i32 (global.get $global73)) + ;; CHECK: (global $global75 i32 (global.get $global74)) + (global $global75 i32 (global.get $global74)) + ;; CHECK: (global $global76 i32 (global.get $global75)) + (global $global76 i32 (global.get $global75)) + ;; CHECK: (global $global77 i32 (global.get $global76)) + (global $global77 i32 (global.get $global76)) + ;; CHECK: (global $global78 i32 (global.get $global77)) + (global $global78 i32 (global.get $global77)) + ;; CHECK: (global $global79 i32 (global.get $global78)) + (global $global79 i32 (global.get $global78)) + ;; CHECK: (global $global80 i32 (global.get $global79)) + (global $global80 i32 (global.get $global79)) + ;; CHECK: (global $global81 i32 (global.get $global80)) + (global $global81 i32 (global.get $global80)) + ;; CHECK: (global $global82 i32 (global.get $global81)) + (global $global82 i32 (global.get $global81)) + ;; CHECK: (global $global83 i32 (global.get $global82)) + (global $global83 i32 (global.get $global82)) + ;; CHECK: (global $global84 i32 (global.get $global83)) + (global $global84 i32 (global.get $global83)) + ;; CHECK: (global $global85 i32 (global.get $global84)) + (global $global85 i32 (global.get $global84)) + ;; CHECK: (global $global86 i32 (global.get $global85)) + (global $global86 i32 (global.get $global85)) + ;; CHECK: (global $global87 i32 (global.get $global86)) + (global $global87 i32 (global.get $global86)) + ;; CHECK: (global $global88 i32 (global.get $global87)) + (global $global88 i32 (global.get $global87)) + ;; CHECK: (global $global89 i32 (global.get $global88)) + (global $global89 i32 (global.get $global88)) + ;; CHECK: (global $global90 i32 (global.get $global89)) + (global $global90 i32 (global.get $global89)) + ;; CHECK: (global $global91 i32 (global.get $global90)) + (global $global91 i32 (global.get $global90)) + ;; CHECK: (global $global92 i32 (global.get $global91)) + (global $global92 i32 (global.get $global91)) + ;; CHECK: (global $global93 i32 (global.get $global92)) + (global $global93 i32 (global.get $global92)) + ;; CHECK: (global $global94 i32 (global.get $global93)) + (global $global94 i32 (global.get $global93)) + ;; CHECK: (global $global95 i32 (global.get $global94)) + (global $global95 i32 (global.get $global94)) + ;; CHECK: (global $global96 i32 (global.get $global95)) + (global $global96 i32 (global.get $global95)) + ;; CHECK: (global $global97 i32 (global.get $global96)) + (global $global97 i32 (global.get $global96)) + ;; CHECK: (global $global98 i32 (global.get $global97)) + (global $global98 i32 (global.get $global97)) + ;; CHECK: (global $global99 i32 (global.get $global98)) + (global $global99 i32 (global.get $global98)) + ;; CHECK: (global $global100 i32 (global.get $global99)) + (global $global100 i32 (global.get $global99)) + ;; CHECK: (global $global101 i32 (global.get $global100)) + (global $global101 i32 (global.get $global100)) + ;; CHECK: (global $global102 i32 (global.get $global101)) + (global $global102 i32 (global.get $global101)) + ;; CHECK: (global $global103 i32 (global.get $global102)) + (global $global103 i32 (global.get $global102)) + ;; CHECK: (global $global104 i32 (global.get $global103)) + (global $global104 i32 (global.get $global103)) + ;; CHECK: (global $global105 i32 (global.get $global104)) + (global $global105 i32 (global.get $global104)) + ;; CHECK: (global $global106 i32 (global.get $global105)) + (global $global106 i32 (global.get $global105)) + ;; CHECK: (global $global107 i32 (global.get $global106)) + (global $global107 i32 (global.get $global106)) + ;; CHECK: (global $global108 i32 (global.get $global107)) + (global $global108 i32 (global.get $global107)) + ;; CHECK: (global $global109 i32 (global.get $global108)) + (global $global109 i32 (global.get $global108)) + ;; CHECK: (global $global110 i32 (global.get $global109)) + (global $global110 i32 (global.get $global109)) + ;; CHECK: (global $global111 i32 (global.get $global110)) + (global $global111 i32 (global.get $global110)) + ;; CHECK: (global $global112 i32 (global.get $global111)) + (global $global112 i32 (global.get $global111)) + ;; CHECK: (global $global113 i32 (global.get $global112)) + (global $global113 i32 (global.get $global112)) + ;; CHECK: (global $global114 i32 (global.get $global113)) + (global $global114 i32 (global.get $global113)) + ;; CHECK: (global $global115 i32 (global.get $global114)) + (global $global115 i32 (global.get $global114)) + ;; CHECK: (global $global116 i32 (global.get $global115)) + (global $global116 i32 (global.get $global115)) + ;; CHECK: (global $global117 i32 (global.get $global116)) + (global $global117 i32 (global.get $global116)) + ;; CHECK: (global $global118 i32 (global.get $global117)) + (global $global118 i32 (global.get $global117)) + ;; CHECK: (global $global119 i32 (global.get $global118)) + (global $global119 i32 (global.get $global118)) + ;; CHECK: (global $global120 i32 (global.get $global119)) + (global $global120 i32 (global.get $global119)) + ;; CHECK: (global $global121 i32 (global.get $global120)) + (global $global121 i32 (global.get $global120)) + ;; CHECK: (global $global122 i32 (global.get $global121)) + (global $global122 i32 (global.get $global121)) + ;; CHECK: (global $global123 i32 (global.get $global122)) + (global $global123 i32 (global.get $global122)) + ;; CHECK: (global $global124 i32 (global.get $global123)) + (global $global124 i32 (global.get $global123)) + ;; CHECK: (global $global125 i32 (global.get $global124)) + (global $global125 i32 (global.get $global124)) + ;; CHECK: (global $global126 i32 (global.get $global125)) + (global $global126 i32 (global.get $global125)) + ;; CHECK: (global $global127 i32 (global.get $global126)) + (global $global127 i32 (global.get $global126)) + + ;; $global128 was removed + + ;; CHECK: (global $other i32 (i32.const 0)) + (global $other i32 (i32.const 0)) + + ;; CHECK: (func $uses (type $0) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $uses + (drop (global.get $other)) + (drop (global.get $other)) + ) +) diff --git a/test/lit/passes/reorder-globals.wast b/test/lit/passes/reorder-globals.wast index 6017f09ad41..542311d59d9 100644 --- a/test/lit/passes/reorder-globals.wast +++ b/test/lit/passes/reorder-globals.wast @@ -145,8 +145,6 @@ ;; As above, but without dependencies, so now $c is first and then $b. (module - - ;; CHECK: (global $c i32 (i32.const 30)) ;; CHECK: (global $b i32 (i32.const 20)) @@ -180,10 +178,10 @@ ) ) -;; As above, but a mixed case: $b depends on $a but $c has no dependencies. $c -;; can be first. +;; As above, but a mixed case: $b depends on $a but $c has no dependencies, and +;; the counts are $c with the most, followed by $b, and then $a. $c can be +;; first here, but $b must follow $a. (module - ;; CHECK: (global $c i32 (i32.const 30)) ;; CHECK: (global $a i32 (i32.const 10)) @@ -197,6 +195,12 @@ ;; CHECK-NEXT: (global.get $b) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $b) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (global.get $c) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop @@ -204,6 +208,10 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $uses + ;; ($a already has one use in the global $b) + (drop + (global.get $b) + ) (drop (global.get $b) ) @@ -213,27 +221,111 @@ (drop (global.get $c) ) + (drop + (global.get $c) + ) ) ) -;; Another mixed case, now with $c depending on $b. $b can be before $a. +;; As above, but with the counts adjusted: before we had $c, $b, $a from most to +;; least uses, and now $b, $c, $a. +;; +;; A greedy sort would do $c, $a, $b (as the first choice is between $c and $a, +;; and $c wins), but that leaves $b, the highest count, for the end. The +;; smoothed-out LEB costs (1 byte at the start, +1/128 each index later) are: +;; +;; $c $a $b +;; 1 * 2 + 129/128 * 1 + 130/128 * 3 = 775/128 +;; +;; The original sort is +;; +;; $a $b $c +;; 1 * 1 + 129/128 * 3 + 130/128 * 2 = 775/128 +;; +;; As they are equal we prefer the original order. (module + ;; CHECK: (global $a i32 (i32.const 10)) + (global $a i32 (i32.const 10)) + ;; CHECK: (global $b i32 (global.get $a)) + (global $b i32 (global.get $a)) + ;; CHECK: (global $c i32 (i32.const 30)) + (global $c i32 (i32.const 30)) + ;; CHECK: (func $uses (type $0) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $b) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $b) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $b) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $uses + (drop + (global.get $b) + ) + (drop + (global.get $b) + ) + (drop + (global.get $b) + ) + (drop + (global.get $c) + ) + (drop + (global.get $c) + ) + ) +) - ;; CHECK: (global $b i32 (i32.const 20)) - - ;; CHECK: (global $c i32 (global.get $b)) - +;; As above, but with the counts adjusted to $b, $a, $c. +(module ;; CHECK: (global $a i32 (i32.const 10)) (global $a i32 (i32.const 10)) - (global $b i32 (i32.const 20)) - (global $c i32 (global.get $b)) + ;; CHECK: (global $b i32 (global.get $a)) + (global $b i32 (global.get $a)) + ;; CHECK: (global $c i32 (i32.const 30)) + (global $c i32 (i32.const 30)) ;; CHECK: (func $uses (type $0) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (global.get $b) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $b) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $uses + (drop + (global.get $b) + ) + (drop + (global.get $b) + ) + ) +) + +;; As above, but with the counts adjusted to $c, $a, $b. +(module + ;; CHECK: (global $c i32 (i32.const 30)) + + ;; CHECK: (global $a i32 (i32.const 10)) + (global $a i32 (i32.const 10)) + ;; CHECK: (global $b i32 (global.get $a)) + (global $b i32 (global.get $a)) + (global $c i32 (i32.const 30)) + + ;; CHECK: (func $uses (type $0) + ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (global.get $c) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop @@ -242,11 +334,63 @@ ;; CHECK-NEXT: ) (func $uses (drop - (global.get $b) + (global.get $c) ) (drop (global.get $c) ) + ) +) + +;; As above, but with the counts adjusted to $a, $b, $c. +(module + ;; CHECK: (global $a i32 (i32.const 10)) + (global $a i32 (i32.const 10)) + ;; CHECK: (global $b i32 (global.get $a)) + (global $b i32 (global.get $a)) + ;; CHECK: (global $c i32 (i32.const 30)) + (global $c i32 (i32.const 30)) + + ;; CHECK: (func $uses (type $0) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $a) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $b) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $uses + (drop + (global.get $a) + ) + (drop + (global.get $b) + ) + ) +) + +;; As above, but with the counts adjusted to $a, $c, $b. +(module + ;; CHECK: (global $a i32 (i32.const 10)) + (global $a i32 (i32.const 10)) + ;; CHECK: (global $c i32 (i32.const 30)) + + ;; CHECK: (global $b i32 (global.get $a)) + (global $b i32 (global.get $a)) + (global $c i32 (i32.const 30)) + + ;; CHECK: (func $uses (type $0) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $a) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $uses + (drop + (global.get $a) + ) (drop (global.get $c) ) @@ -292,3 +436,380 @@ ) ) ) + +;; Lower letters have lower counts: $a has the least, and $e has the most. +;; +;; Dependency graph (left depends on right): +;; +;; $c - $a +;; / +;; $e +;; \ +;; $d - $b +;; +;; $e has the most uses, followed by $c and $d. $a and $b have a reverse +;; ordering from their dependers, so a naive topological sort will fail to +;; be optimal. There are multiple optimal orders however, including: +;; +;; $b, $a, $c, $d, $e +;; $b, $d, $a, $c, $e +;; +;; $b and $e must be at the edges, but there is no single way to sort the +;; others: the first sorting here puts $a before both $d (though $a has +;; lower count) while the second puts $d before $c. Our greedy algorithm +;; picks the second order here. +(module + ;; CHECK: (global $b i32 (i32.const 20)) + + ;; CHECK: (global $d i32 (global.get $b)) + + ;; CHECK: (global $a i32 (i32.const 10)) + (global $a i32 (i32.const 10)) + + (global $b i32 (i32.const 20)) + + ;; CHECK: (global $c i32 (global.get $a)) + (global $c i32 (global.get $a)) + + (global $d i32 (global.get $b)) + + ;; CHECK: (global $e i32 (i32.add + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: (global.get $d) + ;; CHECK-NEXT: )) + (global $e i32 (i32.add + (global.get $c) + (global.get $d) + )) + + ;; CHECK: (func $uses (type $0) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $e) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $e) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $e) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $e) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $e) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $d) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $d) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $d) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $b) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $uses + ;; $a, $b, $c, $d each have one already from the globals. Add more so that + ;; $a has the least, and $e has the most + (drop (global.get $e)) + (drop (global.get $e)) + (drop (global.get $e)) + (drop (global.get $e)) + (drop (global.get $e)) + + (drop (global.get $d)) + (drop (global.get $d)) + (drop (global.get $d)) + + (drop (global.get $c)) + (drop (global.get $c)) + + (drop (global.get $b)) + ) +) + +;; As above, but add a direct dep from $d to $a: +;; +;; $c - $a +;; / / +;; $e / <-- this was added +;; \ / +;; $d - $b +;; +;; This forces $a to appear before $d: the order goes from before, which was +;; $b, $d, $a, $c, $e +;; to +;; $b, $a, $d, $c, $e +(module + ;; CHECK: (global $b i32 (i32.const 20)) + + ;; CHECK: (global $a i32 (i32.const 10)) + (global $a i32 (i32.const 10)) + + (global $b i32 (i32.const 20)) + + ;; CHECK: (global $d i32 (i32.add + ;; CHECK-NEXT: (global.get $b) + ;; CHECK-NEXT: (global.get $a) + ;; CHECK-NEXT: )) + + ;; CHECK: (global $c i32 (global.get $a)) + (global $c i32 (global.get $a)) + + (global $d i32 (i32.add + (global.get $b) + (global.get $a) ;; this was added + )) + + ;; CHECK: (global $e i32 (i32.add + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: (global.get $d) + ;; CHECK-NEXT: )) + (global $e i32 (i32.add + (global.get $c) + (global.get $d) + )) + + ;; CHECK: (func $uses (type $0) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $e) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $e) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $e) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $e) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $e) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $e) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $d) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $d) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $d) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $d) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $b) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $b) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $uses + ;; $b, $c, $d each have one already from the globals, and $a has two. Add + ;; more so that $a has the least, and $e has the most. + (drop (global.get $e)) + (drop (global.get $e)) + (drop (global.get $e)) + (drop (global.get $e)) + (drop (global.get $e)) + (drop (global.get $e)) + + (drop (global.get $d)) + (drop (global.get $d)) + (drop (global.get $d)) + (drop (global.get $d)) + + (drop (global.get $c)) + (drop (global.get $c)) + (drop (global.get $c)) + + (drop (global.get $b)) + (drop (global.get $b)) + ) +) + +;; A situation where the simple greedy sort fails to be optimal. We have a +;; chain and one more independent global +;; +;; a <- b <- c +;; +;; other +;; +;; The candidates for the first global emitted are a and other, as they have no +;; dependencies, and other has a higher count so greedy sorting would pick it. +;; however, c has the highest count by far, so it is worth being less greedy and +;; doing a just in order to be able to do b and then c, and to emit other last. +;; In other words, the original order is best. +(module + ;; CHECK: (global $a i32 (i32.const 0)) + (global $a i32 (i32.const 0)) + ;; CHECK: (global $b i32 (global.get $a)) + (global $b i32 (global.get $a)) + ;; CHECK: (global $c i32 (global.get $b)) + (global $c i32 (global.get $b)) + + ;; CHECK: (global $other i32 (i32.const 1)) + (global $other i32 (i32.const 1)) + + ;; CHECK: (func $uses (type $0) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $uses + ;; Ten uses for $c, far more than all the rest combined. + (drop (global.get $c)) + (drop (global.get $c)) + (drop (global.get $c)) + (drop (global.get $c)) + (drop (global.get $c)) + (drop (global.get $c)) + (drop (global.get $c)) + (drop (global.get $c)) + (drop (global.get $c)) + (drop (global.get $c)) + + ;; Two uses for other, which is more than $a's single use. + (drop (global.get $other)) + (drop (global.get $other)) + ) +) + +;; As above, but with the original order a little different. This is again a +;; case where the greedy sort is unoptimal (see above), but now also the +;; original sort is unoptimal as well, and instead we use the "sum" sort, which +;; counts the sum of the uses of a global and all the things it depends on and +;; uses that as the count for that global (which in effect means that we take +;; into consideration not only its own size but the entire size that it may +;; unlock, which happens to work well here). +;; +;; The only change in the input compared to the previous test is that $other +;; was moved up to between $b and $c. Sum sort works well here because the +;; first comparison is $a and $other, and sum takes into account $b and $c in +;; $a's favor, so it wins. Likewise $b and $c win against $other as well, so +;; the order is $a, $b, $c, $other which is optimal here. +(module + ;; CHECK: (global $a i32 (i32.const 0)) + (global $a i32 (i32.const 0)) + ;; CHECK: (global $b i32 (global.get $a)) + (global $b i32 (global.get $a)) + + ;; CHECK: (global $c i32 (global.get $b)) + + ;; CHECK: (global $other i32 (i32.const 1)) + (global $other i32 (i32.const 1)) + + (global $c i32 (global.get $b)) + + ;; CHECK: (func $uses (type $0) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $other) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $uses + ;; Ten uses for $c, far more than all the rest combined. + (drop (global.get $c)) + (drop (global.get $c)) + (drop (global.get $c)) + (drop (global.get $c)) + (drop (global.get $c)) + (drop (global.get $c)) + (drop (global.get $c)) + (drop (global.get $c)) + (drop (global.get $c)) + (drop (global.get $c)) + + ;; Two uses for other, which is more than $a's single use. + (drop (global.get $other)) + (drop (global.get $other)) + ) +) +