From 8978612d89c4f36d389e53969b8413c3dbce1d08 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Sun, 20 Sep 2026 15:16:15 -0700 Subject: [PATCH 1/3] Do not create invalid public types in MergeSimilarFunctions When similar functions differ only in the targets of their respective Call expressions, MergeSimilarFunctions can merge the functions and pass the correct call target in as a function reference. If the call targets were not previously referenced, they might previously have had private types. In an open world where `func` is exposed on the boundary, this transformation will make the private types public. Since public types can have stricter validation rules than private types (e.g. public types may not contain exact references when custom descriptors are disallowed), this could previously create invalid public types. Fix the bug by adding a check that the call target has a valid public type before doing the optimization. --- src/passes/MergeSimilarFunctions.cpp | 13 ++ src/wasm/wasm-validator.cpp | 4 + .../dae-merge-similar-functions-exact.wast | 131 ++++++++++++++++++ 3 files changed, 148 insertions(+) create mode 100644 test/lit/passes/dae-merge-similar-functions-exact.wast diff --git a/src/passes/MergeSimilarFunctions.cpp b/src/passes/MergeSimilarFunctions.cpp index 6b6aeca09d1..06a03ff5233 100644 --- a/src/passes/MergeSimilarFunctions.cpp +++ b/src/passes/MergeSimilarFunctions.cpp @@ -79,6 +79,7 @@ #include "ir/manipulation.h" #include "ir/module-utils.h" #include "ir/names.h" +#include "ir/public-type-validator.h" #include "ir/utils.h" #include "opt-utils.h" #include "pass.h" @@ -248,6 +249,18 @@ bool MergeSimilarFunctions::areInEquvalentClass(Function* lhs, if (lhsCallee->type != rhsCallee->type) { return false; } + // Parameterizing direct calls to different functions requires creating + // `ref.func` and `call_ref` instructions for them. In an open world, this + // can cause a previously private function signature to become public (for + // instance, if `funcref` is publicly exposed). Do not parameterize the + // call if the callee's signature is not a valid public type (e.g., if it + // contains an exact reference when custom descriptors are disabled). + if (lhsCast->target != rhsCast->target && + getPassOptions().worldMode == WorldMode::Open && + !PublicTypeValidator(module->features) + .isValidPublicType(lhsCallee->type.getHeapType())) { + return false; + } // Arguments operands should be also equivalent ignoring constants. for (Index i = 0; i < lhsCast->operands.size(); i++) { diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 42da1dfb6c4..03a445d33bd 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -250,6 +250,10 @@ void validateExactReferences(Module& module, ValidationInfo& info) { return; } + // TODO: This only checks directly exposed root types. To catch all invalid + // public exact references (such as types reachable from exposed types or + // subtypes of exposed `funcref` in open-world mode), we should check all + // public heap types if we can do so without making validation too expensive. for (auto& [type, _] : ModuleUtils::getExposedPublicHeapTypes(module)) { for (auto child : type.getTypeChildren()) { if (child.isExact()) { diff --git a/test/lit/passes/dae-merge-similar-functions-exact.wast b/test/lit/passes/dae-merge-similar-functions-exact.wast new file mode 100644 index 00000000000..93dce3fd691 --- /dev/null +++ b/test/lit/passes/dae-merge-similar-functions-exact.wast @@ -0,0 +1,131 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. +;; RUN: foreach %s %t wasm-opt --merge-similar-functions --minimize-rec-groups -all --disable-custom-descriptors -S -o - | filecheck %s + +;; Regression test for a bug where MergeSimilarFunctions parameterized direct +;; calls to unreferenced functions ($callee1 and $callee2) whose private +;; signature ($priv) contained an exact reference, making $priv public when +;; custom descriptors were disabled and causing it to collide with the existing +;; inexact public signature ($pub) in MinimizeRecGroups. + +(module + ;; CHECK: (type $pub (func (result (ref $0)))) + (type $0 (func)) + (type $pub (func (result (ref $0)))) + ;; $priv is only used by unreferenced functions ($callee1 and $callee2), so it + ;; is initially a private type and is allowed to contain `(ref (exact $0))` in + ;; the IR even with --disable-custom-descriptors. Because it stays private, + ;; MinimizeRecGroups rewrites it to the equivalent inexact public type $pub. + (type $priv (func (result (ref (exact $0))))) + + ;; CHECK: (type $0 (func)) + + ;; Exporting a funcref table makes all referenced (non-private) function + ;; signatures public in open-world mode. + ;; CHECK: (table $t 1 1 funcref) + (table $t (export "t") 1 1 funcref) + ;; Referencing $pub-fn in an element segment makes $pub a public signature. + ;; CHECK: (elem $e (i32.const 0) $pub-fn) + (elem $e (i32.const 0) (ref.func $pub-fn)) + + ;; CHECK: (export "t" (table $t)) + + ;; CHECK: (export "caller1" (func $caller1)) + + ;; CHECK: (export "caller2" (func $caller2)) + + ;; CHECK: (func $target (type $0) + ;; CHECK-NEXT: ) + (func $target (type $0)) + + ;; CHECK: (func $pub-fn (type $pub) (result (ref $0)) + ;; CHECK-NEXT: (ref.func $target) + ;; CHECK-NEXT: ) + (func $pub-fn (type $pub) (result (ref $0)) + ;; Referenced in $e, so its signature $pub is public from the start. + (ref.func $target) + ) + + ;; CHECK: (func $callee1 (type $pub) (result (ref $0)) + ;; CHECK-NEXT: (ref.func $target) + ;; CHECK-NEXT: ) + (func $callee1 (type $priv) (result (ref (exact $0))) + ;; Unreferenced helper with private signature $priv. Without the fix, + ;; MergeSimilarFunctions would take `(ref.func $callee1)` in a thunk for + ;; $caller1 and call it via `(call_ref $priv)` in a shared helper function, + ;; promoting $priv from a private type to a public type that collides with + ;; $pub in MinimizeRecGroups once --disable-custom-descriptors erases + ;; `exact`. + (ref.func $target) + ) + + ;; CHECK: (func $callee2 (type $pub) (result (ref $0)) + ;; CHECK-NEXT: (ref.func $target) + ;; CHECK-NEXT: ) + (func $callee2 (type $priv) (result (ref (exact $0))) + ;; Second unreferenced helper with private signature $priv. Without the fix, + ;; MergeSimilarFunctions would likewise take `(ref.func $callee2)` in a + ;; thunk for $caller2 and pass it to the shared helper function. + (ref.func $target) + ) + + ;; CHECK: (func $caller1 (type $pub) (result (ref $0)) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (call $callee1) + ;; CHECK-NEXT: ) + (func $caller1 (export "caller1") (result (ref $0)) + ;; Identical to $caller2 except for calling $callee1 instead of $callee2. + ;; Without the fix, MergeSimilarFunctions would merge $caller1 and $caller2 + ;; into a shared function taking `(param (ref $priv))` and replace + ;; $caller1's body with `(return_call $byn$mgfn-shared$caller1 (ref.func + ;; $callee1))`. With the fix, MergeSimilarFunctions sees that $priv is not a + ;; valid public type without custom descriptors and leaves $caller1 + ;; unmerged. + (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) + (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) + (call $callee1) + ) + + ;; CHECK: (func $caller2 (type $pub) (result (ref $0)) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (call $callee2) + ;; CHECK-NEXT: ) + (func $caller2 (export "caller2") (result (ref $0)) + ;; Identical to $caller1 except for calling $callee2 instead of $callee1. + ;; Without the fix, MergeSimilarFunctions would replace $caller2's body with + ;; `(return_call $byn$mgfn-shared$caller1 (ref.func $callee2))`. With the + ;; fix, $caller2 is left unmerged so $priv remains private. + (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) + (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) + (call $callee2) + ) +) From 75d7fd2a8368067aaaa699d6e672ce47cd781e2f Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Mon, 21 Sep 2026 10:06:37 -0700 Subject: [PATCH 2/3] fix test --- .../dae-merge-similar-functions-exact.wast | 75 +++++++++++-------- 1 file changed, 44 insertions(+), 31 deletions(-) diff --git a/test/lit/passes/dae-merge-similar-functions-exact.wast b/test/lit/passes/dae-merge-similar-functions-exact.wast index 93dce3fd691..af2ffed3ac0 100644 --- a/test/lit/passes/dae-merge-similar-functions-exact.wast +++ b/test/lit/passes/dae-merge-similar-functions-exact.wast @@ -1,5 +1,5 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. -;; RUN: foreach %s %t wasm-opt --merge-similar-functions --minimize-rec-groups -all --disable-custom-descriptors -S -o - | filecheck %s +;; RUN: wasm-opt %s --dae --merge-similar-functions --minimize-rec-groups -all --disable-custom-descriptors -S -o - | filecheck %s ;; Regression test for a bug where MergeSimilarFunctions parameterized direct ;; calls to unreferenced functions ($callee1 and $callee2) whose private @@ -8,24 +8,25 @@ ;; inexact public signature ($pub) in MinimizeRecGroups. (module - ;; CHECK: (type $pub (func (result (ref $0)))) + ;; CHECK: (type $0 (func)) (type $0 (func)) + ;; CHECK: (type $pub (func (result (ref $0)))) (type $pub (func (result (ref $0)))) - ;; $priv is only used by unreferenced functions ($callee1 and $callee2), so it - ;; is initially a private type and is allowed to contain `(ref (exact $0))` in - ;; the IR even with --disable-custom-descriptors. Because it stays private, - ;; MinimizeRecGroups rewrites it to the equivalent inexact public type $pub. - (type $priv (func (result (ref (exact $0))))) - - ;; CHECK: (type $0 (func)) ;; Exporting a funcref table makes all referenced (non-private) function ;; signatures public in open-world mode. + ;; CHECK: (rec + ;; CHECK-NEXT: (type $2 (struct)) + + ;; CHECK: (type $3 (func (result (ref (exact $0))))) + ;; CHECK: (table $t 1 1 funcref) (table $t (export "t") 1 1 funcref) ;; Referencing $pub-fn in an element segment makes $pub a public signature. ;; CHECK: (elem $e (i32.const 0) $pub-fn) - (elem $e (i32.const 0) (ref.func $pub-fn)) + (elem $e (i32.const 0) $pub-fn) + + ;; CHECK: (elem declare func $target) ;; CHECK: (export "t" (table $t)) @@ -45,30 +46,34 @@ (ref.func $target) ) - ;; CHECK: (func $callee1 (type $pub) (result (ref $0)) + ;; CHECK: (func $callee1 (type $3) (result (ref (exact $0))) ;; CHECK-NEXT: (ref.func $target) ;; CHECK-NEXT: ) - (func $callee1 (type $priv) (result (ref (exact $0))) - ;; Unreferenced helper with private signature $priv. Without the fix, - ;; MergeSimilarFunctions would take `(ref.func $callee1)` in a thunk for - ;; $caller1 and call it via `(call_ref $priv)` in a shared helper function, - ;; promoting $priv from a private type to a public type that collides with - ;; $pub in MinimizeRecGroups once --disable-custom-descriptors erases - ;; `exact`. + (func $callee1 (result (ref $0)) + ;; Unreferenced helper. DAE refines its return type to `(ref (exact $0))`. + ;; Without the fix in MergeSimilarFunctions, MergeSimilarFunctions would + ;; take `(ref.func $callee1)` in a thunk for $caller1 and call it via + ;; `call_ref` in a shared helper, promoting its refined signature to a + ;; public type that collides with $pub in MinimizeRecGroups once + ;; --disable-custom-descriptors erases `exact`. (ref.func $target) ) - ;; CHECK: (func $callee2 (type $pub) (result (ref $0)) + ;; CHECK: (func $callee2 (type $3) (result (ref (exact $0))) ;; CHECK-NEXT: (ref.func $target) ;; CHECK-NEXT: ) - (func $callee2 (type $priv) (result (ref (exact $0))) - ;; Second unreferenced helper with private signature $priv. Without the fix, - ;; MergeSimilarFunctions would likewise take `(ref.func $callee2)` in a - ;; thunk for $caller2 and pass it to the shared helper function. + (func $callee2 (result (ref $0)) + ;; Second unreferenced helper whose return type is refined to + ;; `(ref (exact $0))` by DAE. (ref.func $target) ) ;; CHECK: (func $caller1 (type $pub) (result (ref $0)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (call $callee1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: (nop) @@ -88,19 +93,26 @@ ;; CHECK-NEXT: (call $callee1) ;; CHECK-NEXT: ) (func $caller1 (export "caller1") (result (ref $0)) - ;; Identical to $caller2 except for calling $callee1 instead of $callee2. + ;; Wrapping the first call in `ref.as_non_null` ensures DAE does not treat + ;; the call as dropped and instead refines $callee1's return type. ;; Without the fix, MergeSimilarFunctions would merge $caller1 and $caller2 - ;; into a shared function taking `(param (ref $priv))` and replace - ;; $caller1's body with `(return_call $byn$mgfn-shared$caller1 (ref.func - ;; $callee1))`. With the fix, MergeSimilarFunctions sees that $priv is not a - ;; valid public type without custom descriptors and leaves $caller1 + ;; into a shared function taking a parameter of $callee1's refined signature + ;; and replace $caller1's body with a thunk passing `(ref.func $callee1)`. + ;; With the fix, MergeSimilarFunctions sees that the refined signature is + ;; not a valid public type without custom descriptors and leaves $caller1 ;; unmerged. + (drop (ref.as_non_null (call $callee1))) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (call $callee1) ) ;; CHECK: (func $caller2 (type $pub) (result (ref $0)) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (call $callee2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: (nop) @@ -121,9 +133,10 @@ ;; CHECK-NEXT: ) (func $caller2 (export "caller2") (result (ref $0)) ;; Identical to $caller1 except for calling $callee2 instead of $callee1. - ;; Without the fix, MergeSimilarFunctions would replace $caller2's body with - ;; `(return_call $byn$mgfn-shared$caller1 (ref.func $callee2))`. With the - ;; fix, $caller2 is left unmerged so $priv remains private. + ;; Without the fix, MergeSimilarFunctions would replace $caller2's body + ;; with a thunk passing `(ref.func $callee2)`. With the fix, $caller2 is + ;; left unmerged so the refined signature remains private. + (drop (ref.as_non_null (call $callee2))) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (call $callee2) From 9ed942fb6c80bb2ddcdf16b065cfd9c8cf08b2e9 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Mon, 21 Sep 2026 10:22:53 -0700 Subject: [PATCH 3/3] address comments --- src/passes/MergeSimilarFunctions.cpp | 2 +- .../dae-merge-similar-functions-exact.wast | 81 ++++++++++++++++++- 2 files changed, 80 insertions(+), 3 deletions(-) diff --git a/src/passes/MergeSimilarFunctions.cpp b/src/passes/MergeSimilarFunctions.cpp index 06a03ff5233..a4632f5fe77 100644 --- a/src/passes/MergeSimilarFunctions.cpp +++ b/src/passes/MergeSimilarFunctions.cpp @@ -255,7 +255,7 @@ bool MergeSimilarFunctions::areInEquvalentClass(Function* lhs, // instance, if `funcref` is publicly exposed). Do not parameterize the // call if the callee's signature is not a valid public type (e.g., if it // contains an exact reference when custom descriptors are disabled). - if (lhsCast->target != rhsCast->target && + if (lhsCallee != rhsCallee && getPassOptions().worldMode == WorldMode::Open && !PublicTypeValidator(module->features) .isValidPublicType(lhsCallee->type.getHeapType())) { diff --git a/test/lit/passes/dae-merge-similar-functions-exact.wast b/test/lit/passes/dae-merge-similar-functions-exact.wast index af2ffed3ac0..c102a35e277 100644 --- a/test/lit/passes/dae-merge-similar-functions-exact.wast +++ b/test/lit/passes/dae-merge-similar-functions-exact.wast @@ -1,5 +1,11 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. -;; RUN: wasm-opt %s --dae --merge-similar-functions --minimize-rec-groups -all --disable-custom-descriptors -S -o - | filecheck %s + +;; RUN: wasm-opt %s --dae --merge-similar-functions --minimize-rec-groups \ +;; RUN: -all --disable-custom-descriptors -S -o - | filecheck %s + +;; RUN: wasm-opt %s --dae --merge-similar-functions --minimize-rec-groups \ +;; RUN: -all --disable-custom-descriptors --closed-world -S -o - \ +;; RUN: | filecheck %s --check-prefix=CLOSD ;; Regression test for a bug where MergeSimilarFunctions parameterized direct ;; calls to unreferenced functions ($callee1 and $callee2) whose private @@ -9,8 +15,10 @@ (module ;; CHECK: (type $0 (func)) + ;; CLOSD: (type $0 (func)) (type $0 (func)) ;; CHECK: (type $pub (func (result (ref $0)))) + ;; CLOSD: (type $pub (func (result (ref $0)))) (type $pub (func (result (ref $0)))) ;; Exporting a funcref table makes all referenced (non-private) function @@ -21,9 +29,18 @@ ;; CHECK: (type $3 (func (result (ref (exact $0))))) ;; CHECK: (table $t 1 1 funcref) + ;; CLOSD: (rec + ;; CLOSD-NEXT: (type $2 (struct)) + + ;; CLOSD: (type $3 (func (result (ref (exact $0))))) + + ;; CLOSD: (type $4 (func (param (ref $3)) (result (ref $0)))) + + ;; CLOSD: (table $t 1 1 funcref) (table $t (export "t") 1 1 funcref) ;; Referencing $pub-fn in an element segment makes $pub a public signature. ;; CHECK: (elem $e (i32.const 0) $pub-fn) + ;; CLOSD: (elem $e (i32.const 0) $pub-fn) (elem $e (i32.const 0) $pub-fn) ;; CHECK: (elem declare func $target) @@ -36,11 +53,24 @@ ;; CHECK: (func $target (type $0) ;; CHECK-NEXT: ) + ;; CLOSD: (elem declare func $callee1 $callee2 $target) + + ;; CLOSD: (export "t" (table $t)) + + ;; CLOSD: (export "caller1" (func $caller1)) + + ;; CLOSD: (export "caller2" (func $caller2)) + + ;; CLOSD: (func $target (type $0) + ;; CLOSD-NEXT: ) (func $target (type $0)) ;; CHECK: (func $pub-fn (type $pub) (result (ref $0)) ;; CHECK-NEXT: (ref.func $target) ;; CHECK-NEXT: ) + ;; CLOSD: (func $pub-fn (type $pub) (result (ref $0)) + ;; CLOSD-NEXT: (ref.func $target) + ;; CLOSD-NEXT: ) (func $pub-fn (type $pub) (result (ref $0)) ;; Referenced in $e, so its signature $pub is public from the start. (ref.func $target) @@ -49,6 +79,9 @@ ;; CHECK: (func $callee1 (type $3) (result (ref (exact $0))) ;; CHECK-NEXT: (ref.func $target) ;; CHECK-NEXT: ) + ;; CLOSD: (func $callee1 (type $3) (result (ref (exact $0))) + ;; CLOSD-NEXT: (ref.func $target) + ;; CLOSD-NEXT: ) (func $callee1 (result (ref $0)) ;; Unreferenced helper. DAE refines its return type to `(ref (exact $0))`. ;; Without the fix in MergeSimilarFunctions, MergeSimilarFunctions would @@ -62,6 +95,9 @@ ;; CHECK: (func $callee2 (type $3) (result (ref (exact $0))) ;; CHECK-NEXT: (ref.func $target) ;; CHECK-NEXT: ) + ;; CLOSD: (func $callee2 (type $3) (result (ref (exact $0))) + ;; CLOSD-NEXT: (ref.func $target) + ;; CLOSD-NEXT: ) (func $callee2 (result (ref $0)) ;; Second unreferenced helper whose return type is refined to ;; `(ref (exact $0))` by DAE. @@ -92,6 +128,11 @@ ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: (call $callee1) ;; CHECK-NEXT: ) + ;; CLOSD: (func $caller1 (type $pub) (result (ref $0)) + ;; CLOSD-NEXT: (return_call $byn$mgfn-shared$caller1 + ;; CLOSD-NEXT: (ref.func $callee1) + ;; CLOSD-NEXT: ) + ;; CLOSD-NEXT: ) (func $caller1 (export "caller1") (result (ref $0)) ;; Wrapping the first call in `ref.as_non_null` ensures DAE does not treat ;; the call as dropped and instead refines $callee1's return type. @@ -100,7 +141,10 @@ ;; and replace $caller1's body with a thunk passing `(ref.func $callee1)`. ;; With the fix, MergeSimilarFunctions sees that the refined signature is ;; not a valid public type without custom descriptors and leaves $caller1 - ;; unmerged. + ;; unmerged. The nops are so MergeSimilarFunctions would think this function + ;; and $caller2 are otherwise profitable to merge. + ;; + ;; We can still optimize with --closed-world. (drop (ref.as_non_null (call $callee1))) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) (nop) @@ -131,6 +175,11 @@ ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: (call $callee2) ;; CHECK-NEXT: ) + ;; CLOSD: (func $caller2 (type $pub) (result (ref $0)) + ;; CLOSD-NEXT: (return_call $byn$mgfn-shared$caller1 + ;; CLOSD-NEXT: (ref.func $callee2) + ;; CLOSD-NEXT: ) + ;; CLOSD-NEXT: ) (func $caller2 (export "caller2") (result (ref $0)) ;; Identical to $caller1 except for calling $callee2 instead of $callee1. ;; Without the fix, MergeSimilarFunctions would replace $caller2's body @@ -142,3 +191,31 @@ (call $callee2) ) ) +;; CLOSD: (func $byn$mgfn-shared$caller1 (type $4) (param $0 (ref $3)) (result (ref $0)) +;; CLOSD-NEXT: (drop +;; CLOSD-NEXT: (ref.as_non_null +;; CLOSD-NEXT: (call_ref $3 +;; CLOSD-NEXT: (local.get $0) +;; CLOSD-NEXT: ) +;; CLOSD-NEXT: ) +;; CLOSD-NEXT: ) +;; CLOSD-NEXT: (nop) +;; CLOSD-NEXT: (nop) +;; CLOSD-NEXT: (nop) +;; CLOSD-NEXT: (nop) +;; CLOSD-NEXT: (nop) +;; CLOSD-NEXT: (nop) +;; CLOSD-NEXT: (nop) +;; CLOSD-NEXT: (nop) +;; CLOSD-NEXT: (nop) +;; CLOSD-NEXT: (nop) +;; CLOSD-NEXT: (nop) +;; CLOSD-NEXT: (nop) +;; CLOSD-NEXT: (nop) +;; CLOSD-NEXT: (nop) +;; CLOSD-NEXT: (nop) +;; CLOSD-NEXT: (nop) +;; CLOSD-NEXT: (call_ref $3 +;; CLOSD-NEXT: (local.get $0) +;; CLOSD-NEXT: ) +;; CLOSD-NEXT: )