Closed Bug 1780819 Opened 1 year ago Closed 1 year ago

Allow non-nullable references in locals


(Core :: JavaScript: WebAssembly, task, P3)




105 Branch
Tracking Status
firefox105 --- fixed


(Reporter: rhunt, Assigned: yury)


(Blocks 1 open bug)



(2 files)

Function-references adds non-nullable references. By default they cannot be used in locals because locals default to null until they are stored into. The let instruction was added to resolve this, but was removed due to complexity for engines and producers.

Discussion is being had in [1] for an alternative.


It looks like consensus was reached around option '1a' [1].

This option is roughly: Track initialisation status of locals during validation and only allow local.get after a local.set/tee in the same or a surrounding block. with the initialisation status of a local being reset at the end of the block they are initialised in.


Roughly this will allow:

  (type $t (func))
    (local (ref $t))
      (local.set 0 ref.func 0)
        (drop local.get 0)

Where the local is initialized in the same block or ancestor of the block it is used from.

Attached file nnl-test.js

I tried something new and wrote a test case generator here which should enumerate all the interesting possibilities here. Hopefully it should be easy to read, tried to add comments about what test cases should be passing/failing and why.

Here is my understanding of how we'll likely implement this.

  1. Move the ValTypeVector that stores locals to be inside OpIter
  • This is current stored in each compiler and passed to readLocalGet/etc as a parameter
  1. Store the number of params of the func in OpIter
  • the local index space starts with the first N params, params can be non-nullable locals and don't need to initialized before being set because they are initialized on enrry
  1. Create a bit vector with length equal to the number of non-param locals in OpIter
  • everything defaults to 0, not initialized
  1. Create a stack of 'InitializedLocal' in OpIter
  • struct InitializedLocal { index: u32 }
  1. Every local.set/tee $index performs:
  • If the bit for the local is unset, mark it as set and push an InitializedLocal for the index
  1. Every local.get $index performs:
  • Fail if the bit is unset
  1. Every 'begin' of a block records the height of the initialized local stack on the ControlItemStack, similar to how we do the value stack
  2. Every 'end' of a block pops each item from the initialized local stack to the height on entry
  • Every popped initialized local resets the bit to 0

This creates an 'undo trail' for whether a local is initialized in a block or ancestor blocks. This was not my idea, but proposed as an efficient implementation of validation on the F-R github. It adds some time overhead for managing the initialized local stack, but avoids having to copy the bitset of initialized locals each time you enter a new block.

  • Allows non-defaultable locals to be present
  • Adds UnsetLocalsState to track state (as defined by spec)
  • Add testing of generated sequences of blocks and locals operations
Assignee: nobody → ydelendik
Pushed by
Allow non-nullable references in locals. r=rhunt
Closed: 1 year ago
Resolution: --- → FIXED
Target Milestone: --- → 105 Branch
You need to log in before you can comment on or make changes to this bug.