Valhalla Calling Convention
1. 返回值类型
SharedRuntime::generate_buffered_inline_type_adapter会生成pack和unpack的handler,这两个handler会放到InlineKlass的后面(就像signature handler一样)。pack和unpack是针对buffer object的操作:
- pack handler:把fields从寄存器打包到buffer object里 [register->buffer]
- unpack handler:把拆开buffer object,把里面的fields放到寄存器[buffer->register]
pack handler和unpack handler都要求buffer object必须放到rax。
InlineTypeReturnedAsFields做的事情是,直接返回fields而不是primitive type reference,相当于返回一个结构体给caller,而不是返回一个对象。它需要修改C1,C2,Interp等各处,并考虑两者交互的情况。
C1
C1 return val时候,把val拆开到register
void LIR_Assembler::return_op(LIR_Opr result, C1SafepointPollStub* code_stub) {
...
ciMethod* method = compilation()->method();
ciType* return_type = method->return_type();
if (InlineTypeReturnedAsFields && return_type->is_inlinetype()) {
ciInlineKlass* vk = return_type->as_inline_klass();
if (vk->can_be_returned_as_fields()) {
#ifndef _LP64
Unimplemented();
#else
address unpack_handler = vk->unpack_handler();
assert(unpack_handler != NULL, "must be");
__ call(RuntimeAddress(unpack_handler));
// At this point, rax points to the value object (for interpreter or C1 caller).
// The fields of the object are copied into registers (for C2 caller).
#endif
}
}
...
}
C1 Foo f = call foo,从寄存器里面拿fields,放到f里面
void LIR_Assembler::emit_call(LIR_OpJavaCall* op) {
verify_oop_map(op->info());
// must align calls sites, otherwise they can't be updated atomically
align_call(op->code());
// emit the static call stub stuff out of line
emit_static_call_stub();
CHECK_BAILOUT();
switch (op->code()) {
case lir_static_call:
case lir_dynamic_call:
call(op, relocInfo::static_call_type);
break;
case lir_optvirtual_call:
call(op, relocInfo::opt_virtual_call_type);
break;
case lir_icvirtual_call:
ic_call(op);
break;
case lir_virtual_call:
vtable_call(op);
break;
default:
fatal("unexpected op code: %s", op->name());
break;
}
// JSR 292
// Record if this method has MethodHandle invokes.
if (op->is_method_handle_invoke()) {
compilation()->set_has_method_handle_invokes(true);
}
ciInlineKlass* vk;
if (op->maybe_return_as_fields(&vk)) {
int offset = store_inline_type_fields_to_buf(vk);
add_call_info(offset, op->info(), true);
}
...
}
C2
void Compile::return_values(JVMState* jvms) {
GraphKit kit(jvms);
Node* ret = new ReturnNode(TypeFunc::Parms,
kit.control(),
kit.i_o(),
kit.reset_memory(),
kit.frameptr(),
kit.returnadr());
// Add zero or 1 return values
int ret_size = tf()->range_sig()->cnt() - TypeFunc::Parms;
if (ret_size > 0) {
kit.inc_sp(-ret_size); // pop the return value(s)
kit.sync_jvms();
Node* res = kit.argument(0);
if (tf()->returns_inline_type_as_fields()) {
// Multiple return values (inline type fields): add as many edges
// to the Return node as returned values.
assert(res->is_InlineType(), "what else supports multi value return?");
InlineTypeNode* vt = res->as_InlineType();
ret->add_req_batch(NULL, tf()->range_cc()->cnt() - TypeFunc::Parms);
if (vt->is_allocated(&kit.gvn()) && !StressInlineTypeReturnedAsFields) {
ret->init_req(TypeFunc::Parms, vt->get_oop());
} else {
ret->init_req(TypeFunc::Parms, vt->tagged_klass(kit.gvn()));
}
uint idx = TypeFunc::Parms + 1;
vt->pass_fields(&kit, ret, idx);
} else {
ret->add_req(res);
// Note: The second dummy edge is not needed by a ReturnNode.
}
}
// bind it to root
root()->add_req(ret);
record_for_igvn(ret);
initial_gvn()->transform_no_reclaim(ret);
}
Interp
(return字节码和throw stub会有remove_activation)
void InterpreterMacroAssembler::remove_activation(...) {
...
if (state == atos && InlineTypeReturnedAsFields) {
Label skip;
// Test if the return type is an inline type
movptr(rdi, Address(rbp, frame::interpreter_frame_method_offset * wordSize));
movptr(rdi, Address(rdi, Method::const_offset()));
load_unsigned_byte(rdi, Address(rdi, ConstMethod::result_type_offset()));
cmpl(rdi, T_INLINE_TYPE);
jcc(Assembler::notEqual, skip);
// We are returning an inline type, load its fields into registers
#ifndef _LP64
super_call_VM_leaf(StubRoutines::load_inline_type_fields_in_regs());
#else
// Load fields from a buffered value with an inline class specific handler
Register tmp_load_klass = LP64_ONLY(rscratch1) NOT_LP64(noreg);
load_klass(rdi, rax, tmp_load_klass);
movptr(rdi, Address(rdi, InstanceKlass::adr_inlineklass_fixed_block_offset()));
movptr(rdi, Address(rdi, InlineKlass::unpack_handler_offset()));
testptr(rdi, rdi);
jcc(Assembler::equal, skip);
call(rdi);
#endif
// call above kills the value in rbx. Reload it.
movptr(rbx, Address(rbp, frame::interpreter_frame_sender_sp_offset * wordSize));
bind(skip);
}
...
}
StubGenerator && JavaCalls
address generate_call_stub(address& return_address) {
// call java
...
__ BIND(is_value);
if (InlineTypeReturnedAsFields) {
// Check for flattened return value
__ testptr(rax, 1);
__ jcc(Assembler::zero, is_long);
// Load pack handler address
__ andptr(rax, -2);
__ movptr(rax, Address(rax, InstanceKlass::adr_inlineklass_fixed_block_offset()));
__ movptr(rbx, Address(rax, InlineKlass::pack_handler_jobject_offset()));
// Call pack handler to initialize the buffer
__ call(rbx);
__ jmp(exit);
}
...
}
void JavaCalls::call_helper(...) {
if (InlineTypeReturnedAsFields && result->get_type() == T_INLINE_TYPE) {
// Pre allocate a buffered inline type in case the result is returned
// flattened by compiled code
InlineKlass* vk = method->returned_inline_type(thread);
if (vk->can_be_returned_as_fields()) {
oop instance = vk->allocate_instance(CHECK);
value_buffer = JNIHandles::make_local(thread, instance);
result->set_jobject(value_buffer);
}
}
// do call
...
}
2. 传入值类型
InlineTypeReturnedAsFields做的事情是把primitive type的对象的字段当作实参传递,而不是传递这个对象引用。