-
Notifications
You must be signed in to change notification settings - Fork 749
Implement op_call for fast-jit #1075
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
Implement op_call for fast-jit #1075
Conversation
| /* No frame space for the spill area. */ | ||
| return 0; | ||
| #endif | ||
| if ((i + stride) * 4 > rc->cc->spill_cache_size) |
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.
not sure about it. The original logic is to use rc->spill_slots as memory. If tend to use spill_cache_size in WASMInterpFrame, maybe we should also modify the other
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.
Here the original logic is to check whether there is enough free space in the spill area:
if (rc->cc->interp_frame_size + (i + stride) * 4
> rc->cc->total_frame_size)
/* No frame space for the spill area. */
return 0;The spill area resides in the end of the stack frame, from cc->interp_frame_size to cc->total_frame_size:
/**
* Calculate the size of interpreter area of frame of a method.
*
* @param all_cell_num number of all cells including local variables
* and the working stack slots
*
* @return the size of interpreter area of the frame
*/
static inline unsigned
jeff_interp_interp_frame_size (unsigned all_cell_num)
{
return align_uint (offsetof (JeffInterpFrame, lp) + all_cell_num * 5, 4);
}
/**
* Calculate the total frame size of a method, including the Java
* frame and the preserved space for JITed code.
*
* @param all_cell_num number of all cells including local variables
* and the working stack slots
*
* @return the total frame size of the method
*/
static inline unsigned
jeff_interp_total_frame_size (unsigned all_cell_num)
{
/* TODO: reduce the size after optimizing the regalloc of JIT. */
return offsetof (JeffInterpFrame, lp) + all_cell_num * 8 + 4 * 16;
}In current design, we reserve a space inside stack frame but not in the end for the register allocator to spill. The start off set is cc->spill_cache_offset, and the size is cc->spill_cache_size. My understanding is that the logic is similar to the logic of original code.
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.
But seems like in rc_alloc_spill_slot, the search part, for (i = 0; i < rc->spill_slot_num; i += stride) is looking for enough space for different values, accroding to get_reg_stride. Every JitReg is a uint32, why using stride instead of a fixed size?
| /* TODO: check the spill */ | ||
| return jit_cc_new_const_I32( | ||
| cc, /*cc->interp_frame_size + jit_cc_get_const_I32 (cc, slot) * 4*/ 0); | ||
| return jit_cc_new_const_I32(cc, cc->spill_cache_offset |
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.
it may should longer than 16
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.
Same as above, the original code is:
return jit_cc_new_const_I32(
cc, cc->interp_frame_size + jit_cc_get_const_I32 (cc, slot) * 4);I just replace cc->interp_frame_size to cc->spill_cache_offset
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 mean spill_cache is a uint32 array with 16 elements only, it might not be long enough.
core/iwasm/fast-jit/jit_regalloc.c
Outdated
| { | ||
| /* TODO: how to set error */ | ||
| /*jit_set_error (JIT_ERROR_UNSUPPORTED_HREG);*/ | ||
| jit_set_last_error(rc->cc, "unsupported hard register num"); |
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.
it should mean there is no hardware register in this kind.
BTW, if no hardware register, we should spill all, right?
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.
Yes, we may change the error message.
If all hardware registers are used, we should spill part of them out to spill area, and allocate them for new virtual registers, and in some time, spill them in.
If no hardware registers are configured, e.g. no int64 registers in x86-32, register allocator cannot work if there are JIT IRs using i64 registers. Here is to check this situation.
And normally in this case, the codegen lower pass (lower_cg) should translate int64 related IRs into int32 related IRs, e.g. (convert one i64 IR ADD I8, I9, I10 into two or more i32 IRs, e.g. ADD ixx, ixx, ixx)
| } | ||
| } | ||
|
|
||
| /* Commit sp as the callee may use it to store the results */ |
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.
To be clarified: the callee and the caller are using the one operand stack in JitFrame or not?
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.
The jit functions use the same operand stack as interpreter. The stack frame (struct WASMInterpFrame) is located in (or allocated from) operand stack, and stack frames are contiguous, for example func A calls B, B calls C, the stack frames may be like:
operand stack
|--------------------|------------------|------------------|--------------------|
implicit frame stack frame A stack frame B stack frame CAnd jit frame is the concept of compilation time, it is destroyed when cc is destroyed. JitFrame for A simulates the behaviour of stack frame A, JitFrame for B simulates the behaviour of stack frame B.
Here we pop parameters of A from stack frame A, and store them into stack frame B's local area (WASMInterpFrame->lp), so when B runs, it can read the parameters.
|
LGTM 👍 |
Translate WASM_OP_CALL into JIT IR in the frontend, and translate
JIT_OP_CALLBC and JIT_OP_CALLNATIVE in the backend.
For calling wasm native API, simply call wasm_interp_call_func_native
to reduce the complexity.
And fix some issues, including wasm loader, frontend, register allocator,
and code gen.