Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Are functions still required to be pre-declared as referable? #499

Closed
titzer opened this issue Dec 18, 2023 · 8 comments
Closed

Are functions still required to be pre-declared as referable? #499

titzer opened this issue Dec 18, 2023 · 8 comments

Comments

@titzer
Copy link
Contributor

titzer commented Dec 18, 2023

The current reference interpreter passes this test (excerpted from type-equivalence.wast):

(module
  (type $s0 (func (param i32)))
  (type $s1 (func (param i32 (ref $s0))))
  (type $s2 (func (param i32 (ref $s0))))
  (type $t1 (func (param (ref $s1))))
  (type $t2 (func (param (ref $s2))))

  (func $s1 (type $s1))
  (func $s2 (type $s2))
  (func $f1 (type $t1))
  (func $f2 (type $t2))
  (table funcref (elem $f1 $f2 $s1 $s2))

  (func (export "run")
    (call_indirect (type $t1) (ref.func $s1) (i32.const 0))
    (call_indirect (type $t1) (ref.func $s1) (i32.const 1))
    (call_indirect (type $t1) (ref.func $s2) (i32.const 0))
    (call_indirect (type $t1) (ref.func $s2) (i32.const 1))
    (call_indirect (type $t2) (ref.func $s1) (i32.const 0))
    (call_indirect (type $t2) (ref.func $s1) (i32.const 1))
    (call_indirect (type $t2) (ref.func $s2) (i32.const 0))
    (call_indirect (type $t2) (ref.func $s2) (i32.const 1))
  )
)
(assert_return (invoke "run"))

However, when translating it to .bin.wast, it does not pre-refer to the functions in the table. IIUC this is a change from previous behavior. Do we still require functions to be pre-declared (in elements) to be used with ref.func?

@titzer
Copy link
Contributor Author

titzer commented Dec 18, 2023

Update: it appears that the reference interpreter has just changed to use constant initializers (ref.func in init expressions). I guess this is intended as having been "referenced" since all such element sections can appear before bodies? If so, I'll close this issue.

@tlively
Copy link
Member

tlively commented Dec 18, 2023

I don't know what the intention is here. I don't recall ever having discussed it 😅

@conrad-watt
Copy link
Contributor

conrad-watt commented Dec 19, 2023

IIRC we explicitly decided that the occurrence of a function index in a global/table initialiser, active/passive elem segment, or export would also (in addition to the "normal" declarative elem segment case) count as "referencing" for the purposes of allowing ref.func in code. This is reflected in the live spec, but I'll try to dig up a meeting or issue where this decision was recorded.

I remember @rossberg being against this relaxing at the time :)

@conrad-watt
Copy link
Contributor

WebAssembly/reference-types#31
WebAssembly/reference-types#76

See @lukewagner's comment towards the end, which I think pretty much sums up the eventual decision

@rossberg What I proposed is that, in your example, since the ref.global is outside the code section, it doesn't need to have been declared. Implementation-wise, everything before the code section is effectively one big declaration for the code section, so there's no practical benefit in your example requiring the ref.global to have already been declared.

@titzer
Copy link
Contributor Author

titzer commented Dec 19, 2023

@conrad-watt Thanks for doing the legwork to dig this up. It seems the most consistent that all element segments behave the same (counting as a reference), regardless of whether they were function indexes or ref.funcs. Looks like we just didn't have a test for that case yet until recently here.

@conrad-watt
Copy link
Contributor

It seems the most consistent that all element segments behave the same (counting as a reference), regardless of whether they were function indexes or ref.funcs

agree - especially since we previously said that occurrences of ref.func $i in global initialisers should cause $i to be counted as "declared".

@rossberg
Copy link
Member

Right, IIRC, it was the group's sentiment to count every occurrence of a function reference outside a function body to be sufficient as a "forward declaration". That's what spec and interpreter implement. (This forward declaration thing probably is one of Wasm's biggest hacks.)

Legacy element segments with just function indices are desugared into reference instructions by the spec, so they naturally behave consistently.

FWIW re the OP: the text format never implies hidden forward declarations. What you see is what you get when converting to binary.

@titzer
Copy link
Contributor Author

titzer commented Dec 19, 2023

@rossberg Thanks, I think we can close this now.

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

No branches or pull requests

4 participants