-
-
Notifications
You must be signed in to change notification settings - Fork 413
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
Register VM #3798
base: main
Are you sure you want to change the base?
Register VM #3798
Conversation
Test262 conformance changes
Fixed tests (5):
|
7351c87
to
039bd2b
Compare
f98cd87
to
1e0bf01
Compare
The failing test is due to #3907 , in any case I'll work on always returning i32 for increment. |
5fd2167
to
50e8339
Compare
@HalidOdat Do you have the time to work on this at the moment? If not, I can see if I can make some progress on the PR. |
I'm quite busy at the moment, but feel free to work on it! 😊 |
50e8339
to
2bab5ac
Compare
0cecbb6
to
aeb2e2f
Compare
@jedel1043 @HalidOdat Could you give this a look? I was looking into making some some changes to some opcodes, but didn't want to rebase this all the time, so it would be nice if we could merge it soon. |
- Add todo for storing fp in CallFrame - Start on moving to register VM - Add more binary ops - Add helpter transition methods - Register based logical ops - Register InPrivate - Register GetFunction - Some class opcodes - Register post and pre increment and decrement
aeb2e2f
to
e83eb8c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, doing a first pass on this and architecturally I'm wondering why we need to pass the registers as if they were "external state" from the VM.
I would think of registers as being "global memory addresses" that you can use while running in a VM, but this treats them as "function memory addresses", with the disadvantage of having to reserve a new set of registers each time you make a call, even if you don't use all of them.
Can we treat them as "proper" registers, pushing and popping from the stack when we need to avoid clobbering, or are there some limitations with this approach that I'm not seeing right now?
As far as I can see that approach would be a lot more complex, both from the runtime and probably especially in the bytecompiler. With the current approach, we have the benefit of having a pretty simple mapping of Also, I have not tested this, but I would guess that having a fixed number of registers that spill over to the stack would cause more copys than this approach when copying from and to the stack. Or at least the performance with this approach is probably more consistent, since we have the max number of used registers and can allocate the required registers in one allocation (if alloc is needed).
Since the register storage is essentially a sliding window, note that after we hit the maxium call depth for a program, there will be no allocations.
Do you mean why the extra |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the delay! Amazing work @raskad! :)
I agree with @jedel1043 that registers shouldn't ideally be treated as external state (like V8 does it all in one stack). However, @raskad's approach simplifies the runtime. It’s a big step forward, and we can refactor it in future PRs. Besides that it looks great to me!
Can't approve "my" PR, so just leaving leaving this PR comment 😄
This PR transitions the current stack based VM to a register based VM.
There are a many changes in this, but I will try to highlight the most important ones.
Vm
struct into a dedicatedRegisters
struct that is passed to the vm execution functions together with theContext
. The reason for this is mostly to be able to get references to register values without borrowing theContext
. In some opcodes, theClone
rate ofJsObject
s significantly increased with the move to registers, because I had to clone all register values before operating on them, because the operation required a&mut Context
.Dup
,Swap
,RotateLeft
andRotateRight
are gone. Working with registers turns out to be way more intuitive than working on a stack.u8
,u16
andu32
sizes. I think we should probably look into reworking the opcode generation macros, to possibly add bothemit
andread_operands
functions. This should also make the code a lot safer, since right now we have to manually implement all operand reading and writing.Call
/New
opcodes use the stack for all arguments and the return values. Also the return value and returned resume kind for yielding opcodes (Await
, etc.) still use the stack. Currently I'm not sure how / if we want to change this. There are some significant challanges here, especially with how our function calls currently work.instruction_operands
print function for all opcodes. A significant amout of lines changed is just that.JsObject
s and the associated gc. Currently the object clone rate is still higher than with a the stack vm. I measured this on theEarleyBoyer
benchmark and last time I checked, the clone rate is ~1,35B vs ~1,42B. In my local benchmark runs the performance regression is between ~1% and ~3%.