/* This function is called to execute an EBC CALLEX instruction. The function check the callee's content to see whether it is common native code or a thunk to another piece of EBC code. If the callee is common native code, use EbcLLCAllEXASM to manipulate, otherwise, set the VM->IP to target EBC code directly to avoid another VM be startup which cost time and stack space. */
VOID EbcLLCALLEX(IN VM_CONTEXT *VmPtr, IN UINTN FuncAddr, IN UINTN NewStackPointer, IN VOID *FramePtr, IN UINT8 Size)
/* This function is called to execute an EBC CALLEX instruction. The function check the callee's content to see whether it is common native code or a thunk to another piece of EBC code. If the callee is common native code, use EbcLLCAllEXASM to manipulate, otherwise, set the VM->IP to target EBC code directly to avoid another VM be startup which cost time and stack space. */
VOID EbcLLCALLEX(IN VM_CONTEXT *VmPtr, IN UINTN FuncAddr, IN UINTN NewStackPointer, IN VOID *FramePtr, IN UINT8 Size)
{
  CONST EBC_INSTRUCTION_BUFFER  *InstructionBuffer;
  InstructionBuffer = (EBC_INSTRUCTION_BUFFER *)FuncAddr;
  if (CompareMem (
        InstructionBuffer,
        &mEbcInstructionBufferTemplate,
        sizeof (EBC_INSTRUCTION_BUFFER) - 2 * sizeof (UINT64)
        ) == 0)
  {
    VmPtr->Gpr[0] -= 8;
    VmWriteMemN (VmPtr, (UINTN)VmPtr->Gpr[0], (UINTN)FramePtr);
    VmPtr->FramePtr = (VOID *)(UINTN)VmPtr->Gpr[0];
    VmPtr->Gpr[0]  -= 8;
    VmWriteMem64 (VmPtr, (UINTN)VmPtr->Gpr[0], (UINT64)(UINTN)(VmPtr->Ip + Size));
    VmPtr->Ip = (VMIP)InstructionBuffer->EbcEntryPoint;
  } else {
    VmPtr->Gpr[7] = EbcLLCALLEXNative (FuncAddr, NewStackPointer, FramePtr);
    VmPtr->Ip += Size;
  }
}