From 5c3e64461aa8cf4e8b0e7a8b5c0421451dfc0fa0 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Tue, 11 Jun 2024 13:42:13 -0700 Subject: [PATCH] Fix wasm-split bug in absence of active element segments The module splitting code incorrectly assumed that there would be at least one active element segment and failed to initialize the table slot manager with a function table if that was not the case. Fix the bug by setting the table even when there are no active segments and add a test. Fixes #6572. --- src/ir/module-splitting.cpp | 20 +++++++---- test/lit/wasm-split/no-active-segment.wast | 41 ++++++++++++++++++++++ 2 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 test/lit/wasm-split/no-active-segment.wast diff --git a/src/ir/module-splitting.cpp b/src/ir/module-splitting.cpp index 966fb677968..50809a9a4df 100644 --- a/src/ir/module-splitting.cpp +++ b/src/ir/module-splitting.cpp @@ -148,6 +148,8 @@ void TableSlotManager::addSlot(Name func, Slot slot) { TableSlotManager::TableSlotManager(Module& module) : module(module) { // TODO: Reject or handle passive element segments + // TODO: If reference types are enabled, just create a fresh table to make bad + // interactions with user code impossible. auto funcref = Type(HeapType::func, Nullable); auto it = std::find_if( module.tables.begin(), @@ -163,13 +165,17 @@ TableSlotManager::TableSlotManager(Module& module) : module(module) { activeTableSegments.push_back(segment); }); - // If there is exactly one table segment and that segment has a non-constant - // offset, append new items to the end of that segment. In all other cases, - // append new items at constant offsets after all existing items at constant - // offsets. - if (activeTableSegments.size() == 1 && - activeTableSegments[0]->type == funcref && - !activeTableSegments[0]->offset->is()) { + if (activeTableSegments.empty()) { + // There are no active segments, so we will lazily create one and start + // filling it at index 0. + activeBase = {activeTable->name, "", 0}; + } else if (activeTableSegments.size() == 1 && + activeTableSegments[0]->type == funcref && + !activeTableSegments[0]->offset->is()) { + // If there is exactly one table segment and that segment has a non-constant + // offset, append new items to the end of that segment. In all other cases, + // append new items at constant offsets after all existing items at constant + // offsets. assert(activeTableSegments[0]->offset->is() && "Unexpected initializer instruction"); activeSegment = activeTableSegments[0]; diff --git a/test/lit/wasm-split/no-active-segment.wast b/test/lit/wasm-split/no-active-segment.wast new file mode 100644 index 00000000000..ad83b0480a7 --- /dev/null +++ b/test/lit/wasm-split/no-active-segment.wast @@ -0,0 +1,41 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. + +;; Test that splitting succeeds even if there are no active segments for the function table. + +;; RUN: wasm-split %s --split-funcs=foo -g -o1 %t.1.wasm -o2 %t.2.wasm +;; RUN: wasm-dis %t.1.wasm | filecheck %s --check-prefix PRIMARY +;; RUN: wasm-dis %t.2.wasm | filecheck %s --check-prefix SECONDARY + +(module + (table 0 funcref) + (export "foo" (func $foo)) + ;; SECONDARY: (type $0 (func)) + + ;; SECONDARY: (import "primary" "table" (table $timport$0 1 funcref)) + + ;; SECONDARY: (elem $0 (i32.const 0) $foo) + + ;; SECONDARY: (func $foo + ;; SECONDARY-NEXT: (nop) + ;; SECONDARY-NEXT: ) + (func $foo + (nop) + ) +) +;; PRIMARY: (type $0 (func)) + +;; PRIMARY: (import "placeholder" "0" (func $placeholder_0)) + +;; PRIMARY: (table $0 1 funcref) + +;; PRIMARY: (elem $0 (i32.const 0) $placeholder_0) + +;; PRIMARY: (export "foo" (func $0)) + +;; PRIMARY: (export "table" (table $0)) + +;; PRIMARY: (func $0 +;; PRIMARY-NEXT: (call_indirect (type $0) +;; PRIMARY-NEXT: (i32.const 0) +;; PRIMARY-NEXT: ) +;; PRIMARY-NEXT: )