From 596198772f3d95bbd6c3861bd9e39b94f7fede38 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Fri, 20 Feb 2026 19:35:22 -0800 Subject: [PATCH] Fix determinism bug in type name updating There was previously a determinism bug where the result of updating type names could depend on the iteration order of the oldToNewTypes map. The bug occurred when the new types were a shuffling of the old types. The update loop updated the module's type names in-place for the new types, and those in-place updates could affect later results if the updated new types were later visited as old types. Fix the bug by collecting all changes to apply before applying them to the module's types and indices. This does not affect any existing tests, but it will unbreak CI for #8217. --- src/ir/type-updating.cpp | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/ir/type-updating.cpp b/src/ir/type-updating.cpp index a6f1dc525d1..5d2a9539757 100644 --- a/src/ir/type-updating.cpp +++ b/src/ir/type-updating.cpp @@ -128,6 +128,7 @@ GlobalTypeRewriter::rebuildTypes(std::vector types) { for (Index i = 0; i < types.size(); ++i) { typeIndices[types[i]] = i; } + assert(typeIndices.size() == types.size()); if (typeIndices.size() == 0) { return {}; @@ -326,34 +327,47 @@ void GlobalTypeRewriter::mapTypes(const TypeMap& oldToNewTypes) { } void GlobalTypeRewriter::mapTypeNamesAndIndices(const TypeMap& oldToNewTypes) { - // Update type names to avoid duplicates. - std::unordered_set typeNames; + // Track all the existing names to avoid creating duplicates. + std::unordered_set seenTypeNames; for (auto& [type, info] : wasm.typeNames) { - typeNames.insert(info.name); - } + seenTypeNames.insert(info.name); + } + // Collect new and updated type names and indices. Do not mutate the module's + // names and indices until the end to avoid iteration order affecting the + // results in the case where oldToNewTypes maps old types to different old + // types. + std::unordered_map newTypeNames; + std::unordered_map newTypeIndices; for (auto& [old, new_] : oldToNewTypes) { if (old == new_) { // The type is being mapped to itself; no need to rename anything. continue; } - if (auto it = wasm.typeNames.find(old); it != wasm.typeNames.end()) { - auto& oldNames = it->second; - wasm.typeNames[new_] = oldNames; + auto& names = it->second; + newTypeNames[new_] = names; // Use the existing name in the new type, as usually it completely // replaces the old. Rename the old name in a unique way to avoid // confusion in the case that it remains used. auto deduped = Names::getValidName( - oldNames.name, [&](Name test) { return !typeNames.count(test); }); - oldNames.name = deduped; - typeNames.insert(deduped); + names.name, [&](Name test) { return !seenTypeNames.count(test); }); + names.name = deduped; + // Do not overwrite the entry for the old type if it has already appeared + // as a new type. + if (newTypeNames.insert({old, names}).second) { + seenTypeNames.insert(names.name); + } } if (auto it = wasm.typeIndices.find(old); it != wasm.typeIndices.end()) { // It's ok if we end up with duplicate indices. Ties will be resolved in // some arbitrary manner. - wasm.typeIndices[new_] = it->second; + newTypeIndices[new_] = it->second; } } + newTypeNames.merge(wasm.typeNames); + wasm.typeNames = std::move(newTypeNames); + newTypeIndices.merge(wasm.typeIndices); + wasm.typeIndices = std::move(newTypeIndices); } Type GlobalTypeRewriter::getTempType(Type type) {