On 9/8/15 9:35 AM, Philip Reames wrote:> On the VM state synching side of things, I'm curious about the tradeoffs > involved in the approach you've used. I'm guessing from what you said > that you're essentially pre-reserving a set of allocas for the spill > locations, somehow registering those with your runtime once, then > emitting stores down the unlikely path into those allocas. Is that > roughly right?It's even simpler than that: the stores to spill VM state go directly to the VM locations where those values belong. We only ever side-exit at bytecode boundaries, so every live value has a place to live on the eval stack or in a local variable (every PHP function has a fixed number of local variables, and space for those is allocated on the eval stack). This means that the side-exit path doesn't have to know if the next bytecode is going to executed by the interpreter or by more jitted code, since they'll both read values from the same locations. There are some downsides to this, of course, and we've been thinking about ways to have a faster ABI between snippets of jitted code, like passing the top n elements of the eval stack in registers. But we have no concrete plans to do that in the near future.> How are you handling things like constants and duplicate values > appearing in the VM state? Our experience has been that constants are > fairly common and so are duplicate values (particularly when combined > with GC state). It would seem like your frame sizes would be inflated > if you had to pre-reserve space for each constant and each copy of a > value. Have you found this to be true? If so, has it been problematic > for you?If I'm understanding this question correctly it doesn't apply to our situation given my answer to the previous question, but let me know if that's not the case and I can try to expand :). We don't currently have a GC - everything is done using reference counting, though we do have a few people working on changing that. -Brett
On 09/08/2015 05:29 PM, Brett Simmers wrote:> On 9/8/15 9:35 AM, Philip Reames wrote: >> On the VM state synching side of things, I'm curious about the tradeoffs >> involved in the approach you've used. I'm guessing from what you said >> that you're essentially pre-reserving a set of allocas for the spill >> locations, somehow registering those with your runtime once, then >> emitting stores down the unlikely path into those allocas. Is that >> roughly right? > > It's even simpler than that: the stores to spill VM state go directly > to the VM locations where those values belong. We only ever side-exit > at bytecode boundaries, so every live value has a place to live on the > eval stack or in a local variable (every PHP function has a fixed > number of local variables, and space for those is allocated on the > eval stack). This means that the side-exit path doesn't have to know > if the next bytecode is going to executed by the interpreter or by > more jitted code, since they'll both read values from the same locations.Ah, gotcha. I'd assumed your were intermixing your language and execution stacks. Yeah, if you're maintaining them separately, transitioning becomes much easier. Just as a sanity check, the compiled code does end up forwarding all the loads and keeping everything in registers within a single compilation right? I'd assume you'd have to for decent performance, but your description almost makes it sound like it doesn't. Philip
On 9/10/15 9:43 AM, Philip Reames wrote:> On 09/08/2015 05:29 PM, Brett Simmers wrote: >> On 9/8/15 9:35 AM, Philip Reames wrote: >>> On the VM state synching side of things, I'm curious about the tradeoffs >>> involved in the approach you've used. I'm guessing from what you said >>> that you're essentially pre-reserving a set of allocas for the spill >>> locations, somehow registering those with your runtime once, then >>> emitting stores down the unlikely path into those allocas. Is that >>> roughly right? >> >> It's even simpler than that: the stores to spill VM state go directly >> to the VM locations where those values belong. We only ever side-exit >> at bytecode boundaries, so every live value has a place to live on the >> eval stack or in a local variable (every PHP function has a fixed >> number of local variables, and space for those is allocated on the >> eval stack). This means that the side-exit path doesn't have to know >> if the next bytecode is going to executed by the interpreter or by >> more jitted code, since they'll both read values from the same locations. > Ah, gotcha. I'd assumed your were intermixing your language and > execution stacks. Yeah, if you're maintaining them separately, > transitioning becomes much easier. > > Just as a sanity check, the compiled code does end up forwarding all the > loads and keeping everything in registers within a single compilation > right? I'd assume you'd have to for decent performance, but your > description almost makes it sound like it doesn't.Ah yeah, forgot to mention that we had a separate eval stack :). We've tossed around a few ideas about how to merge the C++ and PHP stacks but it would be a lot of work and I don't expect it to happen any time soon. And yeah, within a single compilation unit we keep whatever we can in registers. There are a few cases where I've seen our optimizations miss something that LLVM cleaned up later but those are rare and tend to be in patterns that we haven't seen show up in hot code. -Brett