From e180fa54817c2ad5f409d1f562e486436804a2e3 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Wed, 18 Mar 2026 13:01:14 -0700 Subject: [PATCH] [NFC] Remove TypeUpdating::canHandleAsLocal This method originally handled non-nullable types, which early in the design of Wasm GC could not be stored in locals. Now they can be stored in locals, so this function does nothing more than check if the type is concrete. The property that any type can be stored in locals is important enough that it is almost certain that all future WebAssembly extensions will preserve it, so canHandleAsLocal will probably never have a non-trivial implementation again. Remove this utility and simplify all its former users. --- src/ir/type-updating.cpp | 5 ----- src/ir/type-updating.h | 4 ---- src/passes/Heap2Local.cpp | 35 ++++------------------------- src/passes/Inlining.cpp | 15 ------------- src/passes/LocalCSE.cpp | 3 +-- src/passes/OptimizeInstructions.cpp | 4 ---- src/passes/call-utils.h | 3 +-- src/passes/param-utils.cpp | 9 -------- src/tools/fuzzing/fuzzing.cpp | 8 ++----- 9 files changed, 8 insertions(+), 78 deletions(-) diff --git a/src/ir/type-updating.cpp b/src/ir/type-updating.cpp index db37b20e66d..9a2ad2bd352 100644 --- a/src/ir/type-updating.cpp +++ b/src/ir/type-updating.cpp @@ -408,11 +408,6 @@ Type GlobalTypeRewriter::getTempTupleType(Tuple tuple) { namespace TypeUpdating { -bool canHandleAsLocal(Type type) { - // TODO: Inline this into its callers. - return type.isConcrete(); -} - void handleNonDefaultableLocals(Function* func, Module& wasm) { if (!wasm.features.hasReferenceTypes()) { // No references, so no non-nullable ones at all. diff --git a/src/ir/type-updating.h b/src/ir/type-updating.h index d9ee174ce7c..52a3e09027f 100644 --- a/src/ir/type-updating.h +++ b/src/ir/type-updating.h @@ -556,10 +556,6 @@ class TypeMapper : public GlobalTypeRewriter { namespace TypeUpdating { -// Checks whether a type is valid as a local, or whether -// handleNonDefaultableLocals() can handle it if not. -bool canHandleAsLocal(Type type); - // Finds non-nullable locals, which are currently not supported, and handles // them. Atm this turns them into nullable ones, and adds ref.as_non_null on // their uses (which keeps the type of the users identical). diff --git a/src/passes/Heap2Local.cpp b/src/passes/Heap2Local.cpp index 7224f25168b..00bd1ad2cf2 100644 --- a/src/passes/Heap2Local.cpp +++ b/src/passes/Heap2Local.cpp @@ -1534,12 +1534,10 @@ struct Heap2Local { // constant indexes, so they are effectively structs, and turning them into // such allows uniform handling later. for (auto* allocation : finder.arrayNews) { - // The point of this optimization is to replace heap allocations with - // locals, so we must be able to place the data in locals. - if (!canHandleAsLocals(allocation->type)) { + if (allocation->type == Type::unreachable) { + // Leave this for DCE. continue; } - EscapeAnalyzer analyzer( localGraph, parents, branchTargets, passOptions, wasm); if (!analyzer.escapes(allocation)) { @@ -1554,11 +1552,10 @@ struct Heap2Local { // Next, process all structNews. for (auto* allocation : finder.structNews) { - // As above, we must be able to use locals for this data. - if (!canHandleAsLocals(allocation->type)) { + if (allocation->type == Type::unreachable) { + // Leave this for DCE. continue; } - // Check for escaping, noting relevant information as we go. If this does // not escape, optimize it into locals. EscapeAnalyzer analyzer( @@ -1576,30 +1573,6 @@ struct Heap2Local { EHUtils::handleBlockNestedPops(func, wasm); } } - - bool canHandleAsLocal(const Field& field) { - return TypeUpdating::canHandleAsLocal(field.type); - } - - bool canHandleAsLocals(Type type) { - if (type == Type::unreachable) { - return false; - } - - auto heapType = type.getHeapType(); - if (heapType.isStruct()) { - auto& fields = heapType.getStruct().fields; - for (auto field : fields) { - if (!canHandleAsLocal(field)) { - return false; - } - } - return true; - } - - assert(heapType.isArray()); - return canHandleAsLocal(heapType.getArray().element); - } }; struct Heap2LocalPass : public WalkerPass> { diff --git a/src/passes/Inlining.cpp b/src/passes/Inlining.cpp index fac71163c1c..ff5cac479b0 100644 --- a/src/passes/Inlining.cpp +++ b/src/passes/Inlining.cpp @@ -186,17 +186,6 @@ struct FunctionInfo { } }; -static bool canHandleParams(Function* func) { - // We cannot inline a function if we cannot handle placing its params in a - // locals, as all params become locals. - for (auto param : func->getParams()) { - if (!TypeUpdating::canHandleAsLocal(param)) { - return false; - } - } - return true; -} - using NameInfoMap = std::unordered_map; struct FunctionInfoScanner @@ -246,10 +235,6 @@ struct FunctionInfoScanner void visitFunction(Function* curr) { auto& info = infos[curr->name]; - if (!canHandleParams(curr)) { - info.inliningMode = InliningMode::Uninlineable; - } - info.size = Measurer::measure(curr->body); // If the body is a simple instruction with roughly the same encoded size as diff --git a/src/passes/LocalCSE.cpp b/src/passes/LocalCSE.cpp index 73720bed430..a5221ed0079 100644 --- a/src/passes/LocalCSE.cpp +++ b/src/passes/LocalCSE.cpp @@ -354,8 +354,7 @@ struct Scanner // them is not cheap, so leave them for later, after we know if there // actually are any requests for reuse of this value (which is rare). if (!curr->type.isConcrete() || curr->is() || - curr->is() || Properties::isConstantExpression(curr) || - !TypeUpdating::canHandleAsLocal(curr->type)) { + curr->is() || Properties::isConstantExpression(curr)) { return false; } diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 770cf75c391..0d01aef29a7 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -1432,10 +1432,6 @@ struct OptimizeInstructions // The call_ref is not reached; leave this for DCE. return; } - if (!TypeUpdating::canHandleAsLocal(lastOperandType)) { - // We cannot create a local, so we must give up. - return; - } Index tempLocal = builder.addVar( getFunction(), TypeUpdating::getValidLocalType(lastOperandType, features)); diff --git a/src/passes/call-utils.h b/src/passes/call-utils.h index 10106a75658..51b8b3fa4ec 100644 --- a/src/passes/call-utils.h +++ b/src/passes/call-utils.h @@ -101,8 +101,7 @@ convertToDirectCalls(T* curr, // execute first, so we'll use locals for them all. First, see if any are // unreachable, and if so stop trying to optimize and leave this for DCE. for (auto* operand : operands) { - if (operand->type == Type::unreachable || - !TypeUpdating::canHandleAsLocal(operand->type)) { + if (operand->type == Type::unreachable) { return nullptr; } } diff --git a/src/passes/param-utils.cpp b/src/passes/param-utils.cpp index 0b7aee94344..0d46345a14a 100644 --- a/src/passes/param-utils.cpp +++ b/src/passes/param-utils.cpp @@ -131,15 +131,6 @@ RemovalOutcome removeParameter(const std::vector& funcs, } } - // The type must be valid for us to handle as a local (since we - // replace the parameter with a local). - // TODO: if there are no references at all, we can avoid creating a - // local - bool typeIsValid = TypeUpdating::canHandleAsLocal(first->getLocalType(index)); - if (!typeIsValid) { - return Failure; - } - // We can do it! // Remove the parameter from the function. We must add a new local diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index 8ee053eb371..fd1f206bf93 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -1687,11 +1687,7 @@ Function* TranslateToFuzzReader::addFunction() { Index numVars = upToSquared(fuzzParams->MAX_VARS); for (Index i = 0; i < numVars; i++) { - auto type = getConcreteType(); - if (!TypeUpdating::canHandleAsLocal(type)) { - type = Type::i32; - } - func->vars.push_back(type); + func->vars.push_back(getConcreteType()); } // Generate the function creation context after we filled in locals, which it // will scan. @@ -3122,7 +3118,7 @@ Expression* TranslateToFuzzReader::makeLocalGet(Type type) { // the time), or emit a local.get of a new local, or emit a local.tee of a new // local. auto choice = upTo(3); - if (choice == 0 || !TypeUpdating::canHandleAsLocal(type)) { + if (choice == 0) { return makeConst(type); } // Otherwise, add a new local. If the type is not non-nullable then we may