-
Notifications
You must be signed in to change notification settings - Fork 96
Jump-and-link instruction #925
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
Conversation
netrome
left a comment
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.
This should be reviewed by someone who understands the VM better than I, but I don't spot any obvious errors at least.
| assert_success(&receipts); | ||
|
|
||
| if let Some(Receipt::Log { ra, .. }) = receipts.first() { | ||
| assert_eq!(*ra, skip, "Expected correct number of skipped instructions"); |
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.
Shouldn't ra be 2 here since we skipped 3 instructions?
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.
No. We compute 5-1-1 = 3. If that feels confusing, the test could be rewritten to use addi instead. Let me know if you'd prefer that.
| } | ||
|
|
||
| #[test] | ||
| fn jump_and_link__recursive_fibonacci() { |
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.
I will skip this function from the review=D
Looks like it works, so very cool that you managed to write it with just instructions=)
Maybe we should analyze some sway compiler output examples to see if there are many cases of registers being allocated for capturing results and never getting used afterwards? This should likely be done as a separate cleanup PR anyways. |
I did quickly check some compiler output, and it seems either super rare or nonexisting. And yep definitely a separate PR in any case. |
Closes #627. VM issue: FuelLabs/fuel-vm#857. VM PR: FuelLabs/fuel-vm#925. The design is quite similar of the RISC-V of the same name. `JAL $ra $rb imm` stores the address of the next instruction to `$ra`, so that register can be used as a return address from the subroutine. If `ra` is `$zero`, the value is discarded instead, so this can be used as a jump without having to trash a register. After storing the return address, it jumps to instruction at memory address `$rb + imm * 4`. The main purpose of this instruction is efficient subroutine-calling and returning. `JAL $ret_addr $subroutine_addr 0` is used to perform the call, and `JAL $zero $ret_addr 0` returns from it. For nexted function calls, the callee is responsible for storing the `$ret_addr`. The following snippet shows a minimal program using the functionality: ```rust // main function jal $ret_addr $pc 2 // call subroutine ret $zero // end program // subroutine /* subroutine body comes here */ jal $zero $ret_addr 0 // Return from the subroutine ``` ### Fibonacci example To show off how compact code this makes, I wrote a small fibonacci function using it. The function here uses the following register-based ABI: * Function argument and return value `$fnarg` in `0x10` * Function return address `$return_addr` in `0x11` Also the code uses the following locals: `$local1: 0x12, $local2: 0x13, $local3: 0x14` (named for `pshl/popl`) ```rust // Set argument movi $fnarg 10 // <- this computes fibo(10), i.e. 10th fibonacci number, 55 // Main function jal $return_addr $pc 3 // <- offset to the subroutine log $fnarg $zero $zero $zero ret $one // Fibonacci subroutine // fibo(0) = 0, fibo(1) = 1, fibo(n) = fibo(n-1) + fibo(n-2) pshl 0b11110 // Save return_address and local{1,2,3} // Compute fn pointer to the current function and place it in local3 subi $local3 $pc 4 // <- subtract 4 to get prev instruction start // If n < 2 no computation needed movi $local1 2 lt $local1 $fnarg $local1 jnzf $local1 $zero 8 // Skip over computation // Else call self with n - 1 and n - 2 and sum those subi $local2 $fnarg 2 // Save n - 2 to local2 subi $fnarg $fnarg 1 // n -= 1 jal $return_addr $local3 0 // Call self move $local1 $fnarg // Copy result to local1 move $fnarg $local2 // Restore n - 2 from local2 jal $return_addr $local3 0 // Call self move $local2 $fnarg // Copy result to local2 add $fnarg $local1 $local2 // result = local1 + local2 // Computation ends here this is where jnzf jumps to popl 0b11110 // Restore return_address and local{1,2,3} jal $zero $return_addr 0 // Return from subroutine ``` ### Before requesting review - [x] I have reviewed the changes myself
Closes #857. See the spec PR for full description: FuelLabs/fuel-specs#630.
Adds
JAL $rA $rB imminstruction that sets$ra = $pc + 4and then jump setting$pc = $rB + imm * 4. Allows$rAto be$zeroin which case the value is discarded instead.Open questions:
$rA? Since we're always within valid memory before the jump, worst that can happen that the panic is delayed until trying to use that address.Checklist
Before requesting review