diff options
-rw-r--r-- | src/ir/module-splitting.cpp | 11 | ||||
-rw-r--r-- | src/tools/wasm-split/wasm-split.cpp | 11 | ||||
-rw-r--r-- | test/example/module-splitting.txt | 105 | ||||
-rw-r--r-- | test/lit/wasm-split/basic.wast | 1 | ||||
-rw-r--r-- | test/lit/wasm-split/initial-table-used.wast | 22 | ||||
-rw-r--r-- | test/lit/wasm-split/initial-table.wast | 3 | ||||
-rw-r--r-- | test/lit/wasm-split/jspi-secondary-export.wast | 2 | ||||
-rw-r--r-- | test/lit/wasm-split/jspi.wast | 2 | ||||
-rw-r--r-- | test/lit/wasm-split/minimized-exports.wast | 15 | ||||
-rw-r--r-- | test/lit/wasm-split/module-names.wast | 2 | ||||
-rw-r--r-- | test/lit/wasm-split/name-collision.wast | 14 | ||||
-rw-r--r-- | test/lit/wasm-split/passive.wast | 4 | ||||
-rw-r--r-- | test/lit/wasm-split/ref.func.wast | 8 | ||||
-rw-r--r-- | test/lit/wasm-split/segments.wast | 31 |
14 files changed, 110 insertions, 121 deletions
diff --git a/src/ir/module-splitting.cpp b/src/ir/module-splitting.cpp index adda1f4a6..777818689 100644 --- a/src/ir/module-splitting.cpp +++ b/src/ir/module-splitting.cpp @@ -316,6 +316,7 @@ struct ModuleSplitter { void exportImportCalledPrimaryFunctions(); void setupTablePatching(); void shareImportableItems(); + void removeUnusedSecondaryElements(); ModuleSplitter(Module& primary, const Config& config) : config(config), secondaryPtr(initSecondary(primary)), primary(primary), @@ -334,6 +335,7 @@ struct ModuleSplitter { exportImportCalledPrimaryFunctions(); setupTablePatching(); shareImportableItems(); + removeUnusedSecondaryElements(); } }; @@ -872,6 +874,15 @@ void ModuleSplitter::shareImportableItems() { } } +void ModuleSplitter::removeUnusedSecondaryElements() { + // TODO: It would be better to be more selective about only exporting and + // importing those items that the secondary module needs. This would reduce + // code size in the primary module as well. + PassRunner runner(&secondary); + runner.add("remove-unused-module-elements"); + runner.run(); +} + } // anonymous namespace Results splitFunctions(Module& primary, const Config& config) { diff --git a/src/tools/wasm-split/wasm-split.cpp b/src/tools/wasm-split/wasm-split.cpp index ea1734b6b..2b66d1164 100644 --- a/src/tools/wasm-split/wasm-split.cpp +++ b/src/tools/wasm-split/wasm-split.cpp @@ -69,11 +69,15 @@ uint64_t hashFile(const std::string& filename) { return uint64_t(digest); } -void adjustTableSize(Module& wasm, int initialSize) { +void adjustTableSize(Module& wasm, int initialSize, bool secondary = false) { if (initialSize < 0) { return; } if (wasm.tables.empty()) { + if (secondary) { + // It's not a problem if the table is not used in the secondary module. + return; + } Fatal() << "--initial-table used but there is no table"; } @@ -336,7 +340,7 @@ void splitModule(const WasmSplitOptions& options) { auto& secondary = splitResults.secondary; adjustTableSize(wasm, options.initialTableSize); - adjustTableSize(*secondary, options.initialTableSize); + adjustTableSize(*secondary, options.initialTableSize, /*secondary=*/true); if (options.symbolMap) { writeSymbolMap(wasm, options.primaryOutput + ".symbols"); @@ -435,9 +439,6 @@ void multiSplitModule(const WasmSplitOptions& options) { // TODO: symbolMap, placeholderMap, emitModuleNames // TODO: Support --emit-text and use .wast in that case. auto moduleName = options.outPrefix + mod + ".wasm"; - PassRunner runner(&*splitResults.secondary); - runner.add("remove-unused-module-elements"); - runner.run(); writeModule(*splitResults.secondary, moduleName, options); } writeModule(wasm, options.output, options); diff --git a/test/example/module-splitting.txt b/test/example/module-splitting.txt index b72867dda..fe878d739 100644 --- a/test/example/module-splitting.txt +++ b/test/example/module-splitting.txt @@ -33,11 +33,6 @@ After: ) Secondary: (module - (type $0 (func (param i32))) - (import "primary" "%memory" (memory $mem 3 42 shared)) - (import "primary" "%table" (table $tab 3 42 funcref)) - (import "primary" "%global" (global $glob (mut i32))) - (import "primary" "%tag" (tag $e (param i32))) ) @@ -64,11 +59,6 @@ After: ) Secondary: (module - (type $0 (func (param i32))) - (import "primary" "%memory" (memory $mem 3 42 shared)) - (import "primary" "%table" (table $tab 3 42 funcref)) - (import "primary" "%global" (global $glob (mut i32))) - (import "primary" "%tag" (tag $e (param i32))) ) @@ -99,11 +89,6 @@ After: ) Secondary: (module - (type $0 (func (param i32))) - (import "primary" "mem" (memory $mem 3 42 shared)) - (import "primary" "tab" (table $tab 3 42 funcref)) - (import "primary" "glob" (global $glob (mut i32))) - (import "primary" "e" (tag $e (param i32))) ) @@ -171,7 +156,6 @@ After: ) Secondary: (module - (import "primary" "%table" (table $table 1 funcref)) ) @@ -197,7 +181,6 @@ After: ) Secondary: (module - (import "primary" "%table" (table $table 2 funcref)) ) @@ -226,8 +209,6 @@ After: ) Secondary: (module - (import "primary" "%table" (table $table 1 funcref)) - (import "primary" "%global" (global $base i32)) ) @@ -256,8 +237,6 @@ After: ) Secondary: (module - (import "primary" "%table" (table $table 2 funcref)) - (import "primary" "%global" (global $base i32)) ) @@ -297,7 +276,6 @@ After: ) Secondary: (module - (import "primary" "%table" (table $table 1000 funcref)) ) @@ -324,8 +302,6 @@ After: ) Secondary: (module - (import "primary" "%table" (table $table 1000 funcref)) - (import "primary" "%global" (global $base i32)) ) @@ -342,10 +318,6 @@ After: ) Secondary: (module - (type $0 (func (param i32) (result i32))) - (func $foo (type $0) (param $0 i32) (result i32) - (local.get $0) - ) ) @@ -415,8 +387,7 @@ Secondary: (module (type $0 (func (param i32) (result i32))) (import "primary" "%table_1" (table $0 1 funcref)) - (import "primary" "%table" (table $table 1 funcref)) - (elem $0 (table $0) (i32.const 0) func $foo) + (elem $0 (i32.const 0) $foo) (func $foo (type $0) (param $0 i32) (result i32) (local.get $0) ) @@ -454,8 +425,7 @@ Secondary: (module (type $0 (func (param i32) (result i32))) (import "primary" "%table_1" (table $0 1 funcref)) - (import "primary" "%table" (table $table 2 funcref)) - (elem $0 (table $0) (i32.const 0) func $foo) + (elem $0 (i32.const 0) $foo) (func $foo (type $0) (param $0 i32) (result i32) (local.get $0) ) @@ -501,8 +471,7 @@ Secondary: (module (type $0 (func (param i32) (result i32))) (import "primary" "%table_2" (table $0 1 funcref)) - (import "primary" "%table" (table $table 1000 funcref)) - (elem $0 (table $0) (i32.const 0) func $foo) + (elem $0 (i32.const 0) $foo) (func $foo (type $0) (param $0 i32) (result i32) (local.get $0) ) @@ -551,9 +520,7 @@ Secondary: (module (type $0 (func (param i32) (result i32))) (import "primary" "%table_2" (table $0 1 funcref)) - (import "primary" "%table" (table $table 1000 funcref)) - (import "primary" "%global" (global $base i32)) - (elem $0 (table $0) (i32.const 0) func $foo) + (elem $0 (i32.const 0) $foo) (func $foo (type $0) (param $0 i32) (result i32) (local.get $0) ) @@ -602,9 +569,7 @@ Secondary: (module (type $0 (func (param i32) (result i32))) (import "primary" "%table_2" (table $0 1 funcref)) - (import "primary" "%table" (table $table 1000 funcref)) - (import "primary" "%global" (global $base i32)) - (elem $0 (table $0) (i32.const 0) func $foo) + (elem $0 (i32.const 0) $foo) (func $foo (type $0) (param $0 i32) (result i32) (local.get $0) ) @@ -661,9 +626,7 @@ Secondary: (module (type $0 (func (param i32) (result i32))) (import "primary" "%table_2" (table $0 1 funcref)) - (import "primary" "%table" (table $table 1000 funcref)) - (import "primary" "%global" (global $base i32)) - (elem $0 (table $0) (i32.const 0) func $foo) + (elem $0 (i32.const 0) $foo) (func $foo (type $0) (param $0 i32) (result i32) (local.get $0) ) @@ -717,11 +680,6 @@ After: ) Secondary: (module - (type $0 (func)) - (import "primary" "%bar" (func $bar (type $0))) - (func $foo (type $0) - (call $bar) - ) ) @@ -776,13 +734,6 @@ After: ) Secondary: (module - (type $0 (func)) - (func $bar (type $0) - (nop) - ) - (func $foo (type $0) - (call $bar) - ) ) @@ -879,8 +830,7 @@ Secondary: (module (type $0 (func)) (import "primary" "%table_1" (table $0 2 funcref)) - (import "primary" "%table" (table $table 4 funcref)) - (elem $0 (table $0) (i32.const 0) func $foo $baz) + (elem $0 (i32.const 0) $foo $baz) (func $baz (type $0) (nop) ) @@ -944,9 +894,7 @@ Secondary: (module (type $0 (func)) (import "primary" "%table_1" (table $0 2 funcref)) - (import "primary" "%table" (table $table 4 funcref)) - (import "primary" "%global" (global $base i32)) - (elem $0 (table $0) (i32.const 0) func $foo $baz) + (elem $0 (i32.const 0) $foo $baz) (func $baz (type $0) (nop) ) @@ -1010,8 +958,7 @@ Secondary: (module (type $0 (func)) (import "primary" "%table_1" (table $0 3 funcref)) - (import "primary" "%table" (table $table 4 funcref)) - (elem $0 (table $0) (i32.const 0) func $foo $bar $quux) + (elem $0 (i32.const 0) $foo $bar $quux) (func $bar (type $0) (nop) ) @@ -1081,9 +1028,7 @@ Secondary: (module (type $0 (func)) (import "primary" "%table_1" (table $0 3 funcref)) - (import "primary" "%table" (table $table 4 funcref)) - (import "primary" "%global" (global $base i32)) - (elem $0 (table $0) (i32.const 0) func $foo $bar $quux) + (elem $0 (i32.const 0) $foo $bar $quux) (func $bar (type $0) (nop) ) @@ -1136,10 +1081,8 @@ Secondary: (module (type $0 (func)) (import "primary" "%table_2" (table $0 1 funcref)) - (import "primary" "%table" (table $table 2 funcref)) - (import "primary" "%global" (global $base i32)) (import "primary" "%foo" (func $foo (type $0))) - (elem $0 (table $0) (i32.const 0) func $bar) + (elem $0 (i32.const 0) $bar) (func $bar (type $0) (call $foo) ) @@ -1185,9 +1128,8 @@ Secondary: (module (type $0 (func (param i32) (result i32))) (import "primary" "%table_2" (table $0 1 funcref)) - (import "primary" "%table" (table $table 1 1 funcref)) (import "primary" "%foo" (func $foo (type $0) (param i32) (result i32))) - (elem $0 (table $0) (i32.const 0) func $bar) + (elem $0 (i32.const 0) $bar) (func $bar (type $0) (param $0 i32) (result i32) (call $foo (i32.const 1) @@ -1279,28 +1221,5 @@ Minimized names primary: Minimized names secondary: (module - (type $0 (func)) - (import "primary" "%a" (func $0 (type $0))) - (import "primary" "%c" (func $1 (type $0))) - (import "primary" "%d" (func $2 (type $0))) - (import "primary" "already_exported" (func $3 (type $0))) - (import "primary" "%e" (func $4 (type $0))) - (import "primary" "%f" (func $5 (type $0))) - (import "primary" "%g" (func $6 (type $0))) - (import "primary" "%b" (func $7 (type $0))) - (import "primary" "%h" (func $8 (type $0))) - (import "primary" "%i" (func $9 (type $0))) - (func $call (type $0) - (call $0) - (call $1) - (call $2) - (call $3) - (call $4) - (call $5) - (call $6) - (call $7) - (call $8) - (call $9) - ) ) diff --git a/test/lit/wasm-split/basic.wast b/test/lit/wasm-split/basic.wast index 99bf17260..1390f1a63 100644 --- a/test/lit/wasm-split/basic.wast +++ b/test/lit/wasm-split/basic.wast @@ -174,7 +174,6 @@ ;; KEEP-BOTH-PRIMARY-NEXT: ) ;; KEEP-BOTH-SECONDARY: (module -;; KEEP-BOTH-SECONDARY-NEXT: (import "primary" "%table" (table $table 1 1 funcref)) ;; KEEP-BOTH-SECONDARY-NEXT: ) ;; SPLIT-BAR-SUPERSEDE: warning: function bar was to be kept in primary module. However it will now be split out into secondary module. diff --git a/test/lit/wasm-split/initial-table-used.wast b/test/lit/wasm-split/initial-table-used.wast new file mode 100644 index 000000000..23ae068c6 --- /dev/null +++ b/test/lit/wasm-split/initial-table-used.wast @@ -0,0 +1,22 @@ +;; Test that the --initial-table flag works as expected when the secondary +;; module uses the table. + +;; RUN: wasm-split %s --instrument --initial-table=1234 -S | filecheck %s + +;; RUN: wasm-split %s -g -o1 %t.1.wasm -o2 %t.2.wasm --split-funcs=use-table --initial-table=1234 +;; RUN: wasm-dis %t.1.wasm | filecheck %s +;; RUN: wasm-dis %t.2.wasm | filecheck %s + +;; CHECK: (table $table 1234 funcref) + +(module + (table $table 3 funcref) + (func $use-table + (call_indirect + (i32.const 0) + ) + ) + (func $use-use-table + (call $use-table) + ) +) diff --git a/test/lit/wasm-split/initial-table.wast b/test/lit/wasm-split/initial-table.wast index 534f7f3a9..cd2de4c55 100644 --- a/test/lit/wasm-split/initial-table.wast +++ b/test/lit/wasm-split/initial-table.wast @@ -1,10 +1,9 @@ -;; Test that the --initial-table flag works as expected +;; Test that the --initial-table flag works as expected. ;; RUN: wasm-split %s --instrument --initial-table=1234 -S | filecheck %s ;; RUN: wasm-split %s -g -o1 %t.1.wasm -o2 %t.2.wasm --initial-table=1234 ;; RUN: wasm-dis %t.1.wasm | filecheck %s -;; RUN: wasm-dis %t.2.wasm | filecheck %s ;; CHECK: (table $table 1234 funcref) diff --git a/test/lit/wasm-split/jspi-secondary-export.wast b/test/lit/wasm-split/jspi-secondary-export.wast index 553f0abd1..6e89bab28 100644 --- a/test/lit/wasm-split/jspi-secondary-export.wast +++ b/test/lit/wasm-split/jspi-secondary-export.wast @@ -48,8 +48,6 @@ ;; SECONDARY: (import "primary" "%table" (table $timport$0 1 funcref)) - ;; SECONDARY: (import "primary" "load_secondary_module_status" (global $gimport$0 (mut i32))) - ;; SECONDARY: (elem $0 (i32.const 0) $bar) ;; SECONDARY: (func $bar (param $0 i32) (result i32) diff --git a/test/lit/wasm-split/jspi.wast b/test/lit/wasm-split/jspi.wast index e7d6814b7..d86775c82 100644 --- a/test/lit/wasm-split/jspi.wast +++ b/test/lit/wasm-split/jspi.wast @@ -48,8 +48,6 @@ ;; SECONDARY: (import "primary" "%table" (table $timport$0 1 funcref)) - ;; SECONDARY: (import "primary" "load_secondary_module_status" (global $gimport$0 (mut i32))) - ;; SECONDARY: (import "primary" "foo" (func $foo (param i32) (result i32))) ;; SECONDARY: (elem $0 (i32.const 0) $bar) diff --git a/test/lit/wasm-split/minimized-exports.wast b/test/lit/wasm-split/minimized-exports.wast index 4ee73a35b..e938f4ab6 100644 --- a/test/lit/wasm-split/minimized-exports.wast +++ b/test/lit/wasm-split/minimized-exports.wast @@ -1,23 +1,34 @@ -;; RUN: wasm-split %s --keep-funcs=foo,bar --export-prefix='%' -o1 %t.1.wasm -o2 %t.2.wasm +;; RUN: wasm-split %s --keep-funcs=foo,bar --export-prefix='%' -o1 %t.1.wasm -o2 %t.2.wasm --no-placeholders ;; RUN: wasm-dis %t.1.wasm | filecheck %s --check-prefix PRIMARY ;; RUN: wasm-dis %t.2.wasm | filecheck %s --check-prefix SECONDARY ;; PRIMARY: (module ;; PRIMARY-NEXT: (type $0 (func)) +;; PRIMARY-NEXT: (table $0 1 funcref) +;; PRIMARY-NEXT: (elem $0 (table $0) (i32.const 0) funcref (item (ref.null nofunc))) +;; PRIMARY-NEXT: (export "baz" (func $2) ;; PRIMARY-NEXT: (export "%a" (func $1)) ;; PRIMARY-NEXT: (export "%b" (func $0)) +;; PRIMARY-NEXT: (export "%table" (table $0)) ;; PRIMARY-NEXT: (func $0 ;; PRIMARY-NEXT: (nop) ;; PRIMARY-NEXT: ) ;; PRIMARY-NEXT: (func $1 ;; PRIMARY-NEXT: (nop) ;; PRIMARY-NEXT: ) +;; PRIMARY-NEXT: (func $2 +;; PRIMARY-NEXT: (call_indirect (type $0) +;; PRIMARY-NEXT: (i32.const 0) +;; PRIMARY-NEXT: ) +;; PRIMARY-NEXT: ) ;; PRIMARY-NEXT: ) ;; SECONDARY: (module ;; SECONDARY-NEXT: (type $0 (func)) +;; SECONDARY-NEXT: (import "primary" "%table" (table $timport$0 1 funcref)) ;; SECONDARY-NEXT: (import "primary" "%a" (func $fimport$0)) ;; SECONDARY-NEXT: (import "primary" "%b" (func $fimport$1)) +;; SECONDARY-NEXT: (elem $0 (i32.const 0) $0) ;; SECONDARY-NEXT: (func $0 ;; SECONDARY-NEXT: (call $fimport$1) ;; SECONDARY-NEXT: (call $fimport$0) @@ -31,7 +42,7 @@ (func $bar (nop) ) - (func $baz + (func $baz (export "baz") (call $foo) (call $bar) ) diff --git a/test/lit/wasm-split/module-names.wast b/test/lit/wasm-split/module-names.wast index 9ed80ae7c..96dc96c3b 100644 --- a/test/lit/wasm-split/module-names.wast +++ b/test/lit/wasm-split/module-names.wast @@ -16,7 +16,7 @@ (module (func $foo - (nop) + (call $bar) ) (func $bar (nop) diff --git a/test/lit/wasm-split/name-collision.wast b/test/lit/wasm-split/name-collision.wast index ccb615906..bb227f415 100644 --- a/test/lit/wasm-split/name-collision.wast +++ b/test/lit/wasm-split/name-collision.wast @@ -2,7 +2,7 @@ ;; non-function exports would result in the wrong import names being used in the ;; secondary module. -;; RUN: wasm-split %s -o1 %t.1.wasm -o2 %t.2.wasm +;; RUN: wasm-split %s -o1 %t.1.wasm -o2 %t.2.wasm --split-funcs=use ;; RUN: wasm-dis %t.2.wasm | filecheck %s ;; CHECK-NOT: (import "primary" "memory" (table @@ -13,4 +13,16 @@ (memory $collide 1 1) (export "table" (table $collide)) (export "memory" (memory $collide)) + (func $use + (call_indirect + (i32.const 0) + ) + (i32.store + (i32.const 0) + (i32.const 0) + ) + ) + (func $use-use + (call $use) + ) ) diff --git a/test/lit/wasm-split/passive.wast b/test/lit/wasm-split/passive.wast index 322288201..743d7d14e 100644 --- a/test/lit/wasm-split/passive.wast +++ b/test/lit/wasm-split/passive.wast @@ -37,9 +37,7 @@ ;; SECONDARY: (import "primary" "table_1" (table $timport$0 1 funcref)) - ;; SECONDARY: (import "primary" "table" (table $table 3 funcref)) - - ;; SECONDARY: (elem $0 (table $timport$0) (i32.const 0) func $second-in-table) + ;; SECONDARY: (elem $0 (i32.const 0) $second-in-table) ;; SECONDARY: (func $second-in-table (type $0) ;; SECONDARY-NEXT: (nop) diff --git a/test/lit/wasm-split/ref.func.wast b/test/lit/wasm-split/ref.func.wast index 39cce5dea..d9a30890a 100644 --- a/test/lit/wasm-split/ref.func.wast +++ b/test/lit/wasm-split/ref.func.wast @@ -65,15 +65,9 @@ ;; SECONDARY: (import "primary" "table_2" (table $timport$0 2 funcref)) - ;; SECONDARY: (import "primary" "table" (table $table 1 1 funcref)) - - ;; SECONDARY: (import "primary" "global" (global $glob1 (ref func))) - - ;; SECONDARY: (import "primary" "global_4" (global $glob2 (ref func))) - ;; SECONDARY: (import "primary" "prime" (func $prime (type $0))) - ;; SECONDARY: (elem $0 (table $timport$0) (i32.const 0) func $second $second-in-table) + ;; SECONDARY: (elem $0 (i32.const 0) $second $second-in-table) ;; SECONDARY: (elem declare func $prime) diff --git a/test/lit/wasm-split/segments.wast b/test/lit/wasm-split/segments.wast index 7a14afc42..215afd247 100644 --- a/test/lit/wasm-split/segments.wast +++ b/test/lit/wasm-split/segments.wast @@ -1,6 +1,6 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. -;; RUN: wasm-split %s -all --keep-funcs=foo -g -o1 %t.1.wasm -o2 %t.2.wasm +;; RUN: wasm-split %s -all --keep-funcs=use-funcs -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 @@ -14,17 +14,25 @@ ;; PRIMARY: (type $elem-array (array externref)) (type $elem-array (array externref)) + ;; PRIMARY: (import "placeholder" "0" (func $placeholder_0)) + ;; PRIMARY: (memory $mem 0) (memory $mem 0) ;; PRIMARY: (data $data "hello world") (data $data "hello world") + ;; PRIMARY: (table $0 1 funcref) + ;; PRIMARY: (elem $elem externref) (elem $elem externref) + ;; PRIMARY: (elem $1 (i32.const 0) $placeholder_0) + ;; PRIMARY: (export "memory" (memory $mem)) + ;; PRIMARY: (export "table" (table $0)) + ;; PRIMARY: (func $data.drop ;; PRIMARY-NEXT: (data.drop $data) ;; PRIMARY-NEXT: ) @@ -83,7 +91,9 @@ ;; SECONDARY: (type $0 (func)) - ;; SECONDARY: (import "primary" "memory" (memory $mem 0)) + ;; SECONDARY: (import "primary" "table" (table $timport$0 1 funcref)) + + ;; SECONDARY: (elem $0 (i32.const 0) $no-segment) ;; SECONDARY: (func $no-segment ;; SECONDARY-NEXT: (nop) @@ -91,4 +101,21 @@ (func $no-segment (nop) ) + + ;; PRIMARY: (func $use-funcs + ;; PRIMARY-NEXT: (call $data.drop) + ;; PRIMARY-NEXT: (call $memory.init) + ;; PRIMARY-NEXT: (call $array.new_data) + ;; PRIMARY-NEXT: (call $array.new_elem) + ;; PRIMARY-NEXT: (call_indirect (type $0) + ;; PRIMARY-NEXT: (i32.const 0) + ;; PRIMARY-NEXT: ) + ;; PRIMARY-NEXT: ) + (func $use-funcs + (call $data.drop) + (call $memory.init) + (call $array.new_data) + (call $array.new_elem) + (call $no-segment) + ) ) |