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的对象的字段当作实参传递,而不是传递这个对象引用。

原文地址:https://www.cnblogs.com/kelthuzadx/p/15726437.html