From 3b1d58aadd37ce8758447c9685d5c3a7bec9c418 Mon Sep 17 00:00:00 2001 From: Thomas Lively <7121787+tlively@users.noreply.github.com> Date: Tue, 1 Dec 2020 18:41:33 -0800 Subject: [module-splitting] Allow splitting with non-const table offsets (#3408) Extend the splitting logic to handle splitting modules with a single table segment with a non-const offset. In this situation the placeholder function names are interpreted as offsets from the table base global rather than absolute indices into the table. Since addition is not allowed in segment offset expressions, the secondary module's segment must start at the same place as the first table's segment. That means that some primary functions must be duplicated in the secondary segment to fill any gaps. They are exported and imported as necessary. --- test/example/module-splitting.cpp | 101 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) (limited to 'test/example/module-splitting.cpp') diff --git a/test/example/module-splitting.cpp b/test/example/module-splitting.cpp index 50fb0f4c5..bba0bf0a7 100644 --- a/test/example/module-splitting.cpp +++ b/test/example/module-splitting.cpp @@ -127,6 +127,17 @@ int main() { ) ))"); + // Non-deferred function in table at non-const offset + do_test({"foo"}, R"( + (module + (import "env" "base" (global $base i32)) + (table $table 1 funcref) + (elem (global.get $base) $foo) + (func $foo (param i32) (result i32) + (local.get 0) + ) + ))"); + // Non-deferred imported function do_test({"foo"}, R"( (module @@ -142,6 +153,16 @@ int main() { (export "foo" (func $foo)) ))"); + // Non-deferred exported imported function in table at a non-const offset + do_test({"foo"}, R"( + (module + (import "env" "base" (global $base i32)) + (import "env" "foo" (func $foo (param i32) (result i32))) + (table $table 1000 funcref) + (elem (global.get $base) $foo) + (export "foo" (func $foo)) + ))"); + // Deferred function do_test({}, R"( (module @@ -180,6 +201,31 @@ int main() { ) ))"); + // Deferred exported function in table at a non-const offset + do_test({}, R"( + (module + (import "env" "base" (global $base i32)) + (table $table 1000 funcref) + (elem (global.get $base) $foo) + (export "foo" (func $foo)) + (func $foo (param i32) (result i32) + (local.get 0) + ) + ))"); + + // Deferred exported function in table at an offset from a non-const base + do_test({"null"}, R"( + (module + (import "env" "base" (global $base i32)) + (table $table 1000 funcref) + (elem (global.get $base) $null $foo) + (export "foo" (func $foo)) + (func $null) + (func $foo (param i32) (result i32) + (local.get 0) + ) + ))"); + // Non-deferred function calling non-deferred function do_test({"foo", "bar"}, R"( (module @@ -255,6 +301,26 @@ int main() { ) ))"); + // Mixed table 1 with non-const offset + do_test({"bar", "quux"}, R"( + (module + (import "env" "base" (global $base i32)) + (table $table 4 funcref) + (elem (global.get $base) $foo $bar $baz $quux) + (func $foo + (nop) + ) + (func $bar + (nop) + ) + (func $baz + (nop) + ) + (func $quux + (nop) + ) + ))"); + // Mixed table 2 do_test({"baz"}, R"( (module @@ -274,6 +340,41 @@ int main() { ) ))"); + // Mixed table 2 with non-const offset + do_test({"baz"}, R"( + (module + (import "env" "base" (global $base i32)) + (table $table 4 funcref) + (elem (global.get $base) $foo $bar $baz $quux) + (func $foo + (nop) + ) + (func $bar + (nop) + ) + (func $baz + (nop) + ) + (func $quux + (nop) + ) + ))"); + + // `foo` is exported both because it is called by `bar` and because it is in a + // table gap + do_test({"foo"}, R"( + (module + (import "env" "base" (global $base i32)) + (table $table 2 funcref) + (elem (global.get $base) $foo $bar) + (func $foo + (nop) + ) + (func $bar + (call $foo) + ) + ))"); + // Mutual recursion with table growth do_test({"foo"}, R"( (module -- cgit v1.2.3