On Wednesday, May 13, 2020 at 1:54:09 PM UTC-4, Jason Orendorff wrote:
> Ted and I had a fun conversation just now about bindings in generators.
> Jan, I'd like to know what you think about this.
> 
> Currently, in generators, all bindings—arguments, locals, etc.—are
> marked as "aliased". It's slow. Two possible fixes:
> 
> 1.  LOCALS ON GENERATOR
> 
>     Add bytecode instructions for getting and setting locals in a
>     generator. Just as non-aliased locals in normal functions are
>     optimized into stack slots, in generators optimize them into slots
>     on the GeneratorObject, where these special instructions can reach
>     them.
> 
>     The expression stack remains as-is: on yield, copy it from the stack
>     to the generator; on resume, copy it back.
> 
> 2.  FRAME ON GENERATOR
> 
>     When calling a generator, create a normal interpreter/baseline stack
>     frame, but on the heap instead of the stack, a part of the
>     GeneratorObject. Copy the arguments there. That is actually going to
>     be our frame. It is not physically located on the stack.
> 
>     In Interpreter.cpp, make `REGS.fp` point to the current stack frame
>     even when it's allocated in a generator, and likewise `REGS.sp`.
>     Same for the corresponding registers in Baseline/ blinterp. All uses
>     of `BaselineFrameReg` we looked at would still work if it pointed to
>     a heap-allocated frame.
> 
>     The CPU's `rsp` would still point to the C stack as usual.
> 
>     Now just remove the special case in the frontend that marks all
>     bindings in generators as aliased. Emit normal GetLocal, SetLocal,
>     GetArg, etc. instructions. "Everything" "just" works.
> 
> Approach 1 is more flexible and calls might be faster. In approach 2,
> `yield` and resume are faster, and we avoid having new opcodes. However,
> the complexity settles at boundaries between execution modes, which
> would now have to cope with frames possibly being noncontiguous and
> stored on the heap.
> 
> What do you think?
> 
> -j

My initial instinct is that #2 adds more weird corner cases than #1, which 
makes me lean towards #1 unless there's a significant performance gap.

How many new opcodes are we talking for #1? (I assume that we've considered and 
rejected the option of having the normal opcodes do something different when 
we're inside a generator because we don't want to add extra branches to the hot 
path.)

Do we have any intuition for how big the expression stack tends to be when 
yielding? If it's small/empty, does that minimize #2's advantage of not having 
to copy the expression stack?

How do calls work in #2? Would we have to add new call opcodes?

- Iain
_______________________________________________
dev-tech-js-engine-internals mailing list
dev-tech-js-engine-internals@lists.mozilla.org
https://lists.mozilla.org/listinfo/dev-tech-js-engine-internals

Reply via email to