Skip to content

Conversation

@wenyongh
Copy link
Collaborator

@wenyongh wenyongh commented Apr 7, 2022

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.

/* No frame space for the spill area. */
return 0;
#endif
if ((i + stride) * 4 > rc->cc->spill_cache_size)
Copy link
Contributor

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

Copy link
Collaborator Author

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.

Copy link
Contributor

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
Copy link
Contributor

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

Copy link
Collaborator Author

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

Copy link
Contributor

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.

{
/* TODO: how to set error */
/*jit_set_error (JIT_ERROR_UNSUPPORTED_HREG);*/
jit_set_last_error(rc->cc, "unsupported hard register num");
Copy link
Contributor

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?

Copy link
Collaborator Author

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 */
Copy link
Contributor

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?

Copy link
Collaborator Author

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 C

And 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.

@lum1n0us
Copy link
Contributor

LGTM 👍

@wenyongh wenyongh merged commit 3b7bc63 into bytecodealliance:dev/fast_jit Apr 10, 2022
@wenyongh wenyongh deleted the implement_fast_jit_op_call branch April 10, 2022 13:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants