Hotspot JVM 底层C/C++ 源码 入门--3 _call_stub_entry

回顾之前

static CallStub call_stub(){
	return (CallStub)(castable_address(_call_stub_entry));
}

JVM 通过call_stub()函数返回一个CallStub类型函数指针,即_call_stub_entry,再通过_call_stub_entry指向某个函数地址从而调用函数

StubRoutines::_call_stub_entry = generate_call_stub(StubRoutines::_call_stub_return_address)

_call_stub_entry初始化由generate_call_stub(),顾名思义就是产生函数首地址

address generate_call_stub(address& return_address) {
	StubCodeMark mark(this, "StubRoutines", "call_stub");
	address start = __ pc();//获取当前入口的内存地址,因为后续会继续向代码空间在写入汇编指令,后面返回start即可

	// stub code parameters / addresses
	assert(frame::entry_frame_call_wrapper_offset == 2, "adjust this code");
	bool  sse_save = false;
	const Address rsp_after_call(rbp, -4 * wordSize); // same as in generate_catch_exception()!
	const int     locals_count_in_bytes  (4*wordSize);
	//...
	const Address result        (rbp,  3 * wordSize);
	const Address result_type   (rbp,  4 * wordSize);
	const Address method        (rbp,  5 * wordSize);
	const Address entry_point   (rbp,  6 * wordSize);
	const Address parameters    (rbp,  7 * wordSize);
	const Address parameter_size(rbp,  8 * wordSize);
	const Address thread        (rbp,  9 * wordSize); // same as in generate_catch_exception()!
	sse_save =  UseSSE > 0;

	// stub code
	__ enter();//实现:push(rbp);mov(rbp,rsp);均调用emit()写入机器码
	__ movptr(rcx, parameter_size);//类似          // parameter counter
	__ shlptr(rcx, Interpreter::logStackElementSize); // convert parameter count to bytes
	__ addptr(rcx, locals_count_in_bytes);       // reserve space for register saves
	__ subptr(rsp, rcx);
	__ andptr(rsp, -(StackAlignmentInBytes));    // Align stack

	// save rdi, rsi, & rbx, according to C calling conventions
	__ movptr(saved_rdi, rdi);
	__ movptr(saved_rsi, rsi);
	__ movptr(saved_rbx, rbx);
	// save and initialize %mxcsr
	//...
	// interpreter uses xmm0 for return values
	if (UseSSE >= 1) {
	  __ movflt(Address(rdi, 0), xmm0);
	} else {
	  __ fstp_s(Address(rdi, 0));
	}
	__ jmp(exit);

	__ BIND(is_double);
	// interpreter uses xmm0 for return values
	if (UseSSE >= 2) {
	  __ movdbl(Address(rdi, 0), xmm0);
	} else {
	  __ fstp_d(Address(rdi, 0));
	}
	__ jmp(exit);

	return start;
}

pc()

第一行代码 address start = ___pc();保存当前例程所写入对应的机器码的起始位置

address pc() const {
    return _code_pos;
}

JVM启动过程中会生成很多机器码(例程)如函数调用,字节码例程,异常处理等

每一个例程都在一段连续的内存中,生成第一个例程时pc()返回0,生成了长度为20字节的机器码,而当生成第二个例程时,pc()返回20

Hotspot JVM 底层C/C++ 源码 入门--3 _call_stub_entry_第1张图片

JVM中每一个例程都对应一个generate()来生成机器码,通过全局的_code_pos来记录例程的相应信息

参数

// stub code parameters / addresses
	assert(frame::entry_frame_call_wrapper_offset == 2, "adjust this code");
	bool  sse_save = false;
	const Address rsp_after_call(rbp, -4 * wordSize); // same as in generate_catch_exception()!
	const int     locals_count_in_bytes  (4*wordSize);
	//...
	const Address result        (rbp,  3 * wordSize);
	const Address result_type   (rbp,  4 * wordSize);
	const Address method        (rbp,  5 * wordSize);
	const Address entry_point   (rbp,  6 * wordSize);

先回顾汇编调用call时堆栈模型(基础)

Hotspot JVM 底层C/C++ 源码 入门--3 _call_stub_entry_第2张图片

JVM在调用CallStub时传递8个参数,推断一下入参根据ebp的相对位置

参数 位置
(address)&link 8(%ebp)
result_val_address 12(%ebp)
result_type 16(%ebp)
... ...

Hotspot JVM 底层C/C++ 源码 入门--3 _call_stub_entry_第3张图片

__ enter();//实现:push(rbp);mov(rbp,rsp);均调用emit()写入机器码
	__ movptr(rcx, parameter_size);//类似          // parameter counter
	__ shlptr(rcx, Interpreter::logStackElementSize); // convert parameter count to bytes
	__ addptr(rcx, locals_count_in_bytes);       // reserve space for register saves
	__ subptr(rsp, rcx);
	__ andptr(rsp, -(StackAlignmentInBytes));    // Align stack

JVM在对被调用的java函数的堆栈大小计算,最终如下

Hotspot JVM 底层C/C++ 源码 入门--3 _call_stub_entry_第4张图片

根据parameters开始将参数压栈(汇编基础)

Hotspot JVM 底层C/C++ 源码 入门--3 _call_stub_entry_第5张图片

经过堆栈之间的处理,函数调用的资源已经准备充足,正式通过entry_point开始调用函数

// call Java function
    __ BIND(parameters_done);
    __ movptr(rbx, method);           // get Method*
    __ movptr(rax, entry_point);      // get entry_point
    __ mov(rsi, rsp);                 // set sender sp
    BLOCK_COMMENT("call Java function");
    __ call(rax);

    BLOCK_COMMENT("call_stub_return_address:");
    return_address = __ pc();

简单,将java函数的method对象首地址保存在ebx,entry_point到eax中,总体如下

Hotspot JVM 底层C/C++ 源码 入门--3 _call_stub_entry_第6张图片

开始call entry_point.

你可能感兴趣的:(jvm)