Skip to content

[CtorEval] Skip uninitialized non-nullable locals in partial evaluation - #9280

Open
tlively wants to merge 2 commits into
mainfrom
ctor-eval-uninit-non-nullable-local
Open

tlively wants to merge 2 commits into
mainfrom
ctor-eval-uninit-non-nullable-local

Conversation

@tlively

@tlively tlively commented Oct 19, 2026

Copy link
Copy Markdown
Member

When evalCtor partially evaluates a function, non-parameter locals start in FunctionScope with Literal::makeZeros(type), which produces a null reference placeholder for non-nullable reference locals. If a non-nullable local was not yet written before partial evaluation stopped, emitting a local.set with that serialized null expression produced invalid Wasm. Furthermore, wrapping the initial local.sets in a separate inner block before the copied function body prevented initialized non-nullable locals from structurally dominating uses in the remaining body. Only emit initial local.sets when the serialized expression is a valid subtype of the local type, and insert them directly at the start of the copied body block.

When evalCtor partially evaluates a function, non-parameter locals start in FunctionScope with Literal::makeZeros(type), which produces a null reference placeholder for non-nullable reference locals. If a non-nullable local was not yet written before partial evaluation stopped, emitting a local.set with that serialized null expression produced invalid Wasm. Furthermore, wrapping the initial local.sets in a separate inner block before the copied function body prevented initialized non-nullable locals from structurally dominating uses in the remaining body. Only emit initial local.sets when the serialized expression is a valid subtype of the local type, and insert them directly at the start of the copied body block.
@tlively
tlively requested a review from a team as a code owner October 19, 2026 23:22
@tlively
tlively requested review from aheejin and removed request for a team October 19, 2026 23:22

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates wasm-ctor-eval.cpp to skip uninitialized non-nullable locals when prepending local sets to the function body, avoiding invalid default null values. A corresponding test case has been added to verify this behavior. Feedback on the changes highlights a performance concern where repeatedly calling insertAt on copyBody->list results in O(N * M) complexity, and suggests optimizing this to O(N + M) by constructing a new vector and assigning it in a single operation.

Comment thread src/tools/wasm-ctor-eval.cpp Outdated
Comment on lines 1340 to 1346
Index insertIndex = 0;
for (Index i = 0; i < localExprs.size(); ++i) {
setsBlock->list.push_back(builder.makeLocalSet(i, localExprs[i]));
if (Type::isSubType(localExprs[i]->type, func->getLocalType(i))) {
copyBody->list.insertAt(insertIndex++,
builder.makeLocalSet(i, localExprs[i]));
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Inserting elements one by one at the beginning of copyBody->list using insertAt has $O(N \cdot M)$ complexity, where $N$ is the number of locals and $M$ is the size of the function body. For large functions with many instructions and locals, this repeated shifting of the entire function body can cause significant compile-time overhead.

Instead, we can construct a new vector, reserve the total required capacity, populate it, and set it on copyBody->list in a single $O(N + M)$ operation.

      std::vector<Expression*> newList;
      newList.reserve(localExprs.size() + copyBody->list.size());
      for (Index i = 0; i < localExprs.size(); ++i) {
        if (Type::isSubType(localExprs[i]->type, func->getLocalType(i))) {
          newList.push_back(builder.makeLocalSet(i, localExprs[i]));
        }
      }
      newList.insert(newList.end(), copyBody->list.begin(), copyBody->list.end());
      copyBody->list.set(newList);

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant