首页 > 其他分享 >VM例程调用

VM例程调用

时间:2022-09-21 23:13:47浏览次数:65  
标签:调用 java thread 例程 VM call last rax

VM例程调用

也就是模板解释器代码(或者被jit编译的代码)执行过程中调用VM例程的过程。

从模板解释器调用

一些模板解释器的代码会调用虚拟机中的例程,比如newarray

void TemplateTable::newarray() {
  transition(itos, atos);
  Register rarg1 = LP64_ONLY(c_rarg1) NOT_LP64(rdx);
  __ load_unsigned_byte(rarg1, at_bcp(1));
  // 此时rax存储了数组的长度,而rarg1存放了数组元素的类型,同时指定了rax存放返回值。
  call_VM(rax, CAST_FROM_FN_PTR(address, InterpreterRuntime::newarray),
          rarg1, rax);
}

被调用的InterpreterRuntime::newarray实际也并不复杂,我们不对oopFactory::new_typeArray进一步深入:

IRT_ENTRY(void, InterpreterRuntime::newarray(JavaThread* thread, BasicType type, jint size))
  oop obj = oopFactory::new_typeArray(type, size, CHECK);
  thread->set_vm_result(obj);
IRT_END

这里调用了TemplateTable::call_VM,其实现非常简单,直接略过来到MacroAssembler::call_VM

// 似乎不只是从解释器中会调用,在编译代码似乎也会使用call_VM()
void MacroAssembler::call_VM(Register oop_result, // rax
                             address entry_point, // InterpreterRuntime::newarray
                             Register arg_1,	  // rarg1
                             Register arg_2,	  // rax
                             bool check_exceptions) {
  Label C, E;
  call(C, relocInfo::none);
  jmp(E);

  bind(C);

  LP64_ONLY(assert(arg_1 != c_rarg2, "smashed arg"));

  // 将寄存器值移动到指定的寄存器,满足调用规约。
  pass_arg2(this, arg_2);
  pass_arg1(this, arg_1);
  call_VM_helper(oop_result, entry_point, 2, check_exceptions);
  ret(0);

  bind(E);
}

这里使用了一个call来进行跳转,而跳转的位置只是跳转到一个很近的地方,这么做的目的大概是为了获取到返回地址,我目前还不太清楚这么做的目的,因为我看到的执行路径上并没有使用pc的地方,这可能是处于某种调用规约吧。这个推入的地址会在19中被弹出,随后执行9生成的jmp。

跳转到C后继续执行,会进入到MacroAssembler::call_VM_helper

void MacroAssembler::call_VM_helper(Register oop_result, address entry_point, int number_of_arguments, bool check_exceptions) {

  // Calculate the value for last_Java_sp
  // somewhat subtle. call_VM does an intermediate call
  // which places a return address on the stack just under the
  // stack pointer as the user finsihed with it. This allows
  // use to retrieve last_Java_pc from last_Java_sp[-1].
  // On 32bit we then have to push additional args on the stack to accomplish
  // the actual requested call. On 64bit call_VM only can use register args
  // so the only extra space is the return address that call_VM created.
  // This hopefully explains the calculations here.

  // We've pushed one address, correct last_Java_sp
  lea(rax, Address(rsp, wordSize)); // rax为推入返回地址前的指针

  call_VM_base(oop_result, noreg, rax, entry_point, number_of_arguments, check_exceptions);

}

此时栈的情况如下:

stackframe-call_VM.drawio

来到MacroAssembler::call_VM_base,删掉不必要的代码之后其实非常简洁,因为要做的事情前面都做的差不多了:

void MacroAssembler::call_VM_base(Register oop_result,   		// rax
                                  Register java_thread,  		// noreg
                                  Register last_java_sp, 		// rax
                                  address  entry_point,  		// InterpreterRuntime::newarray
                                  int      number_of_arguments, // 2
                                  bool     check_exceptions) {
  // determine java_thread register
  if (!java_thread->is_valid()) {
    java_thread = r15_thread;
  }
    
  // determine last_java_sp register
  if (!last_java_sp->is_valid()) {
    last_java_sp = rsp;
  }

  // push java thread (becomes first argument of C function)
  LP64_ONLY(mov(c_rarg0, r15_thread)); // thread作为第0个参数,其中保存了rbp和rsp,rbp可以获取许多结构。

  // 设置最后一个java栈的有关信息,放入thread对象中的位置,在VM例程中可以用来生成用于遍历Java栈的对象。
  // Only interpreter should have to set fp
  set_last_Java_frame(java_thread, last_java_sp, rbp, NULL);

  // do the call, remove parameters
  MacroAssembler::call_VM_leaf_base(entry_point, number_of_arguments);
    
  // reset last Java frame
  // Only interpreter should have to clear fp
  reset_last_Java_frame(java_thread, true);

  // get oop result if there is one and reset the value in the thread
  if (oop_result->is_valid()) {
    get_vm_result(oop_result, java_thread);
  }
}

主要就做了两件事情,第一个是获取了thread指针,并将其作为第0个参数,另外一个是在thread中设置了last_frame,这个结构在例程中可以用来获取栈中一些数据,比如可以获取到调用者有关的信息,我们当前的栈还是解释器栈,所以能够获取那些信息可以去看图1。第二个就是将thread对象中存储的返回值放入到了oop_result,也就是rax中。

由于MacroAssembler::call_VM_leaf_base非常简单,所以也略过了,至此按照正常的返回方式就可以返回到调用者中去了。

从jit代码调用

标签:调用,java,thread,例程,VM,call,last,rax
From: https://www.cnblogs.com/AANA/p/16717552.html

相关文章

  • JVM方法调用
    JVM方法调用以下内容基本上是对于[1]的整理和一些补充。下面的内容以x86为例,其他平台下会有所不同,可能有一些内容暂时还不好理解,不过配合其他内容多看几遍应该还是不成问......
  • JVM方法调用——java之间
    Java方法之间解释方法到解释方法进入解释方法到解释方法是最为简单的一种情况,最常见的调用是invokevirtual。有关的代码在TemplateTable::invokevirtual中:voidTemplat......
  • vm虚拟机安装centos并配置网络
     第三步:    第二步:先配置,网卡为DHCP和    第一步骤: ......
  • Idea插件SequenceDiagram快速查看方法调用
    Idea打开setting->plugins安装插件SequenceDiagram快速查看方法调用在方法名上右键点击SequenceDiagram即可生成方法调用图最上面一行为该方法涉及的类名,下面的白色字......
  • uniapp H5与原生安卓的数据互通和方法调用
    1、准备我这里是uniapp与原生安卓之间的相互调用,也就是原生安卓内嵌H5页面,下面先来准备一下安卓端的代码。(1)、初始化的MainActivity类定义一个WebViewprivateWebView......
  • 【Node】NVM 切换 Node 版本安装使用
    安装流程参考windows切换Node版本......
  • 同步方法里调用异步方法会卡死
    C#同步方法中调用异步方法 一、结果:关于ThreadPool中的线程调用算法,其实很简单,每个线程都有一个自己的工作队列localqueue,此外线程池中还有一个globalqueue全局工......
  • react hooks组件父组件调用子组件方法
    函数组件父组件调用子组件方法需要使用useImperativeHandle和forwardRef两个方法1.子组件    2.父组件 注意:一定要使用ref来接从子组件传过来的实例值,用......
  • JVM崩溃
    https://blog.csdn.net/qq_32447301/article/details/79780099 最近两天测试环境有一个服务总是会挂(两到三天一次),JVM虚拟机总是会崩溃。所以有必要了解JVM崩溃的原因是什......
  • 使用V2V功能将VMware平台虚拟机迁移至OpenStack平台
     1、进入虚拟机备份系统  2、选择【虚拟机保护】——【恢复】,新建恢复任务,选择需要进行跨平台恢复与迁移的源虚拟化平台【VMwarevSphere】,勾选需要恢复的备份点,点......